@oh-my-pi/pi-coding-agent 17.3.5 → 17.3.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +80 -0
- package/dist/{CHANGELOG-tt9k4jpr.md → CHANGELOG-vr9cckb4.md} +80 -0
- package/dist/cli.js +2993 -3001
- package/dist/docs-index.generated.txt +1 -1
- package/dist/{tool-views.generated-jdfmzwmn.js → tool-views.generated-dd2km5r2.js} +19 -19
- package/dist/types/advisor/advise-tool.d.ts +4 -2
- package/dist/types/cli/auth-broker-cli.d.ts +15 -0
- package/dist/types/cli/stats-cli.d.ts +1 -6
- package/dist/types/cli/update-cli.d.ts +8 -0
- package/dist/types/cli-commands.d.ts +10 -2
- package/dist/types/commands/stats.d.ts +4 -0
- package/dist/types/config/settings-schema.d.ts +28 -0
- package/dist/types/config/settings.d.ts +9 -0
- package/dist/types/extensibility/extensions/runner.d.ts +23 -4
- package/dist/types/extensibility/extensions/types.d.ts +58 -0
- package/dist/types/launch/presence.d.ts +4 -1
- package/dist/types/mcp/oauth-credentials.d.ts +23 -0
- package/dist/types/mcp/oauth-flow.d.ts +11 -0
- package/dist/types/mnemopi/backend.d.ts +12 -0
- package/dist/types/modes/components/tool-execution.d.ts +12 -0
- package/dist/types/modes/controllers/input-controller.d.ts +2 -0
- package/dist/types/modes/interactive-mode.d.ts +25 -3
- package/dist/types/modes/types.d.ts +10 -0
- package/dist/types/session/agent-session.d.ts +3 -0
- package/dist/types/session/prewalk.d.ts +4 -0
- package/dist/types/session/session-entries.d.ts +0 -1
- package/dist/types/session/session-manager.d.ts +12 -0
- package/dist/types/session/session-stats.d.ts +13 -1
- package/dist/types/session/skill-title-input.d.ts +13 -0
- package/dist/types/slash-commands/helpers/stats-dashboard.d.ts +1 -0
- package/dist/types/subprocess/worker-client.d.ts +7 -4
- package/dist/types/task/label.d.ts +2 -0
- package/dist/types/task/render.d.ts +2 -0
- package/dist/types/tiny/completion-prompt.d.ts +2 -0
- package/dist/types/tiny/title-client.d.ts +6 -4
- package/dist/types/tiny/title-protocol.d.ts +1 -0
- package/dist/types/tiny/worker.d.ts +27 -0
- package/dist/types/tools/bash.d.ts +1 -1
- package/dist/types/tools/file-write-fallback.d.ts +124 -0
- package/dist/types/tools/index.d.ts +1 -0
- package/dist/types/tools/path-utils.d.ts +23 -0
- package/dist/types/tools/read-format.d.ts +6 -0
- package/dist/types/tools/read-summary.d.ts +7 -1
- package/dist/types/utils/block-context.d.ts +14 -0
- package/dist/types/utils/fetch-timeout.d.ts +15 -0
- package/dist/types/utils/git.d.ts +25 -1
- package/dist/types/web/search/providers/tinyfish.d.ts +4 -0
- package/package.json +13 -13
- package/src/advisor/advise-tool.ts +5 -3
- package/src/cli/auth-broker-cli.ts +36 -1
- package/src/cli/profile-bootstrap.ts +2 -6
- package/src/cli/stats-cli.ts +6 -72
- package/src/cli/update-cli.ts +63 -11
- package/src/cli-commands.ts +61 -7
- package/src/commands/completions.ts +2 -1
- package/src/commands/stats.ts +7 -4
- package/src/commit/agentic/index.ts +15 -2
- package/src/commit/git/diff.ts +6 -2
- package/src/config/model-resolver.ts +52 -6
- package/src/config/models-config.ts +2 -2
- package/src/config/settings-schema.ts +33 -0
- package/src/config/settings.ts +159 -30
- package/src/discovery/helpers.ts +45 -2
- package/src/discovery/omp-plugins.ts +2 -1
- package/src/discovery/opencode.ts +56 -3
- package/src/edit/hashline/filesystem.ts +9 -3
- package/src/edit/modes/patch.ts +31 -5
- package/src/eval/js/process-entry.ts +4 -4
- package/src/export/html/tool-views.generated.js +19 -19
- package/src/extensibility/extensions/loader.ts +11 -0
- package/src/extensibility/extensions/runner.ts +118 -5
- package/src/extensibility/extensions/types.ts +60 -0
- package/src/extensibility/extensions/wrapper.ts +10 -1
- package/src/extensibility/plugins/legacy-pi-compat.ts +47 -0
- package/src/launch/client.ts +9 -4
- package/src/launch/presence.ts +19 -4
- package/src/lsp/defaults.json +1 -1
- package/src/lsp/writethrough.ts +20 -10
- package/src/mcp/manager.ts +41 -20
- package/src/mcp/oauth-credentials.ts +38 -0
- package/src/mcp/oauth-flow.ts +21 -0
- package/src/mcp/tool-bridge.ts +32 -16
- package/src/mnemopi/backend.ts +35 -3
- package/src/modes/components/model-hub.ts +37 -4
- package/src/modes/components/settings-selector.ts +17 -11
- package/src/modes/components/tool-execution.ts +97 -29
- package/src/modes/components/tree-selector.ts +7 -2
- package/src/modes/controllers/event-controller.ts +12 -2
- package/src/modes/controllers/input-controller.ts +64 -27
- package/src/modes/controllers/mcp-command-controller.ts +13 -4
- package/src/modes/interactive-mode.ts +79 -11
- package/src/modes/types.ts +11 -0
- package/src/prompts/system/memory-extraction-system.md +5 -22
- package/src/prompts/system/system-prompt.md +1 -1
- package/src/session/agent-session.ts +52 -6
- package/src/session/messages.ts +6 -0
- package/src/session/prewalk.ts +25 -7
- package/src/session/session-entries.ts +0 -1
- package/src/session/session-maintenance.ts +10 -1
- package/src/session/session-manager.ts +15 -0
- package/src/session/session-stats.ts +24 -3
- package/src/session/settings-stream-fn.ts +7 -0
- package/src/session/skill-title-input.ts +32 -0
- package/src/session/turn-recovery.ts +23 -18
- package/src/slash-commands/builtin-session.ts +1 -1
- package/src/slash-commands/helpers/stats-dashboard.ts +23 -9
- package/src/subprocess/worker-client.ts +8 -5
- package/src/task/executor.ts +11 -0
- package/src/task/index.ts +2 -0
- package/src/task/label.ts +14 -1
- package/src/task/persisted-revive.ts +13 -0
- package/src/task/render.ts +1 -1
- package/src/task/structured-subagent.ts +5 -2
- package/src/tiny/completion-prompt.ts +16 -0
- package/src/tiny/title-client.ts +15 -6
- package/src/tiny/title-protocol.ts +8 -1
- package/src/tiny/worker.ts +21 -19
- package/src/tools/bash.ts +7 -1
- package/src/tools/file-write-fallback.ts +467 -0
- package/src/tools/index.ts +1 -0
- package/src/tools/path-utils.ts +79 -0
- package/src/tools/read-format.ts +16 -2
- package/src/tools/read-summary.ts +9 -4
- package/src/tools/read.ts +306 -72
- package/src/utils/block-context.ts +15 -1
- package/src/utils/fetch-timeout.ts +33 -0
- package/src/utils/git.ts +54 -11
- package/src/web/search/providers/browser-page.ts +21 -3
- package/src/web/search/providers/tinyfish.ts +26 -0
|
@@ -23,6 +23,8 @@ type TaskRenderOptions = RenderResultOptions & {
|
|
|
23
23
|
renderContext?: TaskRenderContext;
|
|
24
24
|
};
|
|
25
25
|
export declare function formatTaskId(id: string): string;
|
|
26
|
+
/** Dim `⟨agent⟩` badge for a non-default agent type; empty for the generic worker. */
|
|
27
|
+
export declare function agentTypeBadge(agent: string | undefined, theme: Theme): string;
|
|
26
28
|
/**
|
|
27
29
|
* Render the tool call arguments.
|
|
28
30
|
*/
|
|
@@ -18,6 +18,11 @@ export interface TinyTitleGenerateOptions {
|
|
|
18
18
|
signal?: AbortSignal;
|
|
19
19
|
systemPrompt?: string;
|
|
20
20
|
}
|
|
21
|
+
export interface TinyModelCompletionOptions {
|
|
22
|
+
maxTokens?: number;
|
|
23
|
+
signal?: AbortSignal;
|
|
24
|
+
systemPrompt?: string;
|
|
25
|
+
}
|
|
21
26
|
/**
|
|
22
27
|
* Hidden subcommand on the main CLI that boots the tiny-model worker in the
|
|
23
28
|
* spawned subprocess. Kept in sync with the dispatch in `cli.ts`.
|
|
@@ -62,10 +67,7 @@ export declare class TinyTitleClient {
|
|
|
62
67
|
prewarm(modelKey: string): void;
|
|
63
68
|
generate(modelKey: string, message: string, signal?: AbortSignal): Promise<string | null>;
|
|
64
69
|
generate(modelKey: string, message: string, options?: TinyTitleGenerateOptions): Promise<string | null>;
|
|
65
|
-
complete(modelKey: string, prompt: string, options?:
|
|
66
|
-
maxTokens?: number;
|
|
67
|
-
signal?: AbortSignal;
|
|
68
|
-
}): Promise<string | null>;
|
|
70
|
+
complete(modelKey: string, prompt: string, options?: TinyModelCompletionOptions): Promise<string | null>;
|
|
69
71
|
downloadModel(modelKey: string, options?: TinyTitleDownloadOptions): Promise<TinyTitleDownloadResult>;
|
|
70
72
|
terminate(): Promise<void>;
|
|
71
73
|
}
|
|
@@ -1,2 +1,29 @@
|
|
|
1
|
+
import type { ProgressInfo, TextGenerationPipeline, StoppingCriteria as TransformersStoppingCriteria } from "@huggingface/transformers";
|
|
2
|
+
import { type TransformersRuntimeMetadata } from "../subprocess/worker-runtime.js";
|
|
3
|
+
import { type TinyModelDevice } from "./device.js";
|
|
4
|
+
import { type TinyModelDtype } from "./dtype.js";
|
|
1
5
|
import type { TinyTitleTransport } from "./title-protocol.js";
|
|
6
|
+
export interface TransformersRuntime extends TransformersRuntimeMetadata {
|
|
7
|
+
env: {
|
|
8
|
+
cacheDir?: string;
|
|
9
|
+
allowLocalModels?: boolean;
|
|
10
|
+
logLevel?: unknown;
|
|
11
|
+
};
|
|
12
|
+
LogLevel: {
|
|
13
|
+
ERROR: unknown;
|
|
14
|
+
};
|
|
15
|
+
StoppingCriteria: new () => TransformersStoppingCriteria;
|
|
16
|
+
pipeline: (task: "text-generation", model: string, options: {
|
|
17
|
+
device: TinyModelDevice;
|
|
18
|
+
dtype: TinyModelDtype;
|
|
19
|
+
progress_callback: (info: ProgressInfo) => void;
|
|
20
|
+
}) => Promise<TextGenerationPipeline>;
|
|
21
|
+
}
|
|
22
|
+
/** Stops generation at the first occurrence of `text` in the *generated* tokens.
|
|
23
|
+
*
|
|
24
|
+
* The window must be anchored to the generation boundary, not to the end of the
|
|
25
|
+
* whole sequence: a prompt that itself contains the stop string (chat-level
|
|
26
|
+
* few-shot examples ending in `</title>`, for instance) would otherwise match on
|
|
27
|
+
* prompt tokens and stop before the model emits anything. */
|
|
28
|
+
export declare function createStopOnTextCriteria(transformers: TransformersRuntime, tokenizer: TextGenerationPipeline["tokenizer"], text: string): TransformersStoppingCriteria;
|
|
2
29
|
export declare function startTinyTitleWorker(transport: TinyTitleTransport): void;
|
|
@@ -40,7 +40,7 @@ export declare function wrapShellLineForClientTerminal(line: string, shellConfig
|
|
|
40
40
|
* while false positives remain actionable through user policy control.
|
|
41
41
|
* New patterns should target shapes that are virtually never legitimate in automation.
|
|
42
42
|
*/
|
|
43
|
-
export declare const CRITICAL_BASH_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
43
|
+
export declare const CRITICAL_BASH_PATTERNS: readonly [RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp, RegExp];
|
|
44
44
|
declare const bashSchemaBase: import("@oh-my-pi/omptype").FluentType<{
|
|
45
45
|
command: string;
|
|
46
46
|
cwd?: string | undefined;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import type { BunFile } from "bun";
|
|
2
|
+
import type { ExtensionContext } from "../extensibility/extensions/types.js";
|
|
3
|
+
/** A denied write, captured for a registered fallback to retry through a privileged channel. */
|
|
4
|
+
export interface FileWriteFallbackRequest {
|
|
5
|
+
/**
|
|
6
|
+
* Absolute, symlink-resolved path to write the bytes to.
|
|
7
|
+
*
|
|
8
|
+
* This is where the failed in-process write would itself have landed, which is
|
|
9
|
+
* not necessarily the path the tool was given: `open` follows every component,
|
|
10
|
+
* so a link anywhere in that path redirects the bytes. Resolving it here is
|
|
11
|
+
* what lets a handler's allowlist see the real destination instead of a
|
|
12
|
+
* lexically innocent path, so a handler MUST treat this as authoritative and
|
|
13
|
+
* MUST NOT re-derive the target from anything else.
|
|
14
|
+
*/
|
|
15
|
+
dst: string;
|
|
16
|
+
/**
|
|
17
|
+
* Session the denied write was issued from, or `undefined` when the mutation
|
|
18
|
+
* did not happen inside a tool call (an external `applyPatch` caller, a test).
|
|
19
|
+
*
|
|
20
|
+
* The registry is process-wide, so a handler can be consulted for a write from
|
|
21
|
+
* a session other than the one whose extension registered it. Compare this with
|
|
22
|
+
* `ctx.sessionManager.getSessionId()` to tell the two apart — a handler that
|
|
23
|
+
* prompts through `ctx.ui` needs to, since that UI belongs to ITS session and
|
|
24
|
+
* not necessarily to the one being asked about.
|
|
25
|
+
*/
|
|
26
|
+
sessionId: string | undefined;
|
|
27
|
+
/** The exact bytes the tool intended to write. */
|
|
28
|
+
content: string;
|
|
29
|
+
/**
|
|
30
|
+
* The error that proves the write hit a permission boundary. Usually the write's
|
|
31
|
+
* own `EPERM`/`EACCES`/`EROFS`; for a write into a directory the host may not
|
|
32
|
+
* create, the denial raised by creating that directory, in which case `dst`'s
|
|
33
|
+
* parent may not exist yet and the handler is responsible for creating it.
|
|
34
|
+
*/
|
|
35
|
+
cause: unknown;
|
|
36
|
+
}
|
|
37
|
+
/** Extension-authored handler. Return `true` once `content` is durably on disk at `dst`. */
|
|
38
|
+
export type FileWriteFallbackHandler = (req: FileWriteFallbackRequest, ctx: ExtensionContext) => Promise<boolean>;
|
|
39
|
+
/** A handler already bound to its owning extension's live context. */
|
|
40
|
+
type BoundFileWriteFallbackHandler = (req: FileWriteFallbackRequest) => Promise<boolean>;
|
|
41
|
+
/** A denied unlink, captured for a registered fallback to perform through a privileged channel. */
|
|
42
|
+
export interface FileDeleteFallbackRequest {
|
|
43
|
+
/**
|
|
44
|
+
* Absolute, symlink-resolved path the unlink was denied for.
|
|
45
|
+
*
|
|
46
|
+
* Every component ABOVE the last is resolved, so a handler cannot be walked
|
|
47
|
+
* outside its allowed roots through a link in the path. The last component is
|
|
48
|
+
* deliberately NOT resolved, because `unlink` removes a link itself rather
|
|
49
|
+
* than its target — which is also why this may still name a symlink.
|
|
50
|
+
*/
|
|
51
|
+
dst: string;
|
|
52
|
+
/** The `EPERM`/`EACCES`/`EROFS` that proves the unlink hit a permission boundary. */
|
|
53
|
+
cause: unknown;
|
|
54
|
+
/**
|
|
55
|
+
* Whether `dst` was confirmed to be a plain regular file before diverting.
|
|
56
|
+
*
|
|
57
|
+
* `false` means the seam could not establish that, either because the target's
|
|
58
|
+
* own metadata is behind the same boundary that denied the unlink — the common
|
|
59
|
+
* sandbox case, since `unlink` on a directory also reports `EPERM` on Darwin —
|
|
60
|
+
* or because `dst` is a symlink.
|
|
61
|
+
*
|
|
62
|
+
* A handler MUST remove `dst` with a plain unlink. It MUST NOT remove it
|
|
63
|
+
* recursively, and MUST NOT resolve the path first: when this is `false` the
|
|
64
|
+
* target may be a directory, and resolving a symlink would delete whatever it
|
|
65
|
+
* points at instead of the link.
|
|
66
|
+
*/
|
|
67
|
+
confirmedFile: boolean;
|
|
68
|
+
/** See {@link FileWriteFallbackRequest.sessionId}. */
|
|
69
|
+
sessionId: string | undefined;
|
|
70
|
+
}
|
|
71
|
+
/** Extension-authored handler. Return `true` once `dst` is gone from disk. */
|
|
72
|
+
export type FileDeleteFallbackHandler = (req: FileDeleteFallbackRequest, ctx: ExtensionContext) => Promise<boolean>;
|
|
73
|
+
/** A handler already bound to its owning extension's live context. */
|
|
74
|
+
type BoundFileDeleteFallbackHandler = (req: FileDeleteFallbackRequest) => Promise<boolean>;
|
|
75
|
+
/** True for `EPERM`, `EACCES`, and `EROFS` — the sandbox-boundary write failures this seam exists for. */
|
|
76
|
+
export declare function isPermissionDeniedError(error: unknown): boolean;
|
|
77
|
+
/** Whether any fallback is registered. Lets a caller skip work that only this seam needs. */
|
|
78
|
+
export declare function hasFileWriteFallback(): boolean;
|
|
79
|
+
/**
|
|
80
|
+
* Append a fallback writer, consulted in registration order when a direct write is
|
|
81
|
+
* permission-denied. Returns a disposer that removes this exact registration; the
|
|
82
|
+
* runner calls it on session shutdown so no handler outlives its session.
|
|
83
|
+
*/
|
|
84
|
+
export declare function addFileWriteFallback(handler: BoundFileWriteFallbackHandler): () => void;
|
|
85
|
+
/** Whether any delete fallback is registered. */
|
|
86
|
+
export declare function hasFileDeleteFallback(): boolean;
|
|
87
|
+
/**
|
|
88
|
+
* Append a fallback deleter, consulted in registration order when a direct unlink is
|
|
89
|
+
* permission-denied. Deliberately a separate registry from
|
|
90
|
+
* {@link addFileWriteFallback}: a write handler brokers `content` to `dst`, and
|
|
91
|
+
* handing it a request with no content would let it "broker" an empty write and
|
|
92
|
+
* truncate the file it was asked to remove. Opting in is explicit for that reason.
|
|
93
|
+
*/
|
|
94
|
+
export declare function addFileDeleteFallback(handler: BoundFileDeleteFallbackHandler): () => void;
|
|
95
|
+
/**
|
|
96
|
+
* Name the session whose tool call is about to run, so a denied mutation inside it
|
|
97
|
+
* can tell a handler where the request came from.
|
|
98
|
+
*
|
|
99
|
+
* Entered once per tool call by `ExtensionToolWrapper` (`extensibility/extensions/
|
|
100
|
+
* wrapper.ts`), which `sdk.ts` puts around the whole tool registry whenever an
|
|
101
|
+
* `ExtensionRunner` exists — so the component that owns the handlers is the one
|
|
102
|
+
* naming its own session, and no caller has to thread an `AgentToolContext`
|
|
103
|
+
* through for attribution to work.
|
|
104
|
+
*
|
|
105
|
+
* That covers the deferred LSP write batch too: a batch id belongs to one
|
|
106
|
+
* assistant turn of one session, and its flush is awaited inside a tool call of
|
|
107
|
+
* that same session, so a write performed during a later call of the group is
|
|
108
|
+
* still attributed to the session that issued it.
|
|
109
|
+
*
|
|
110
|
+
* Deliberately NOT a general "current session" accessor: nothing else enters this
|
|
111
|
+
* scope, so outside a tool call it is empty by design — an external `applyPatch`
|
|
112
|
+
* caller reports `undefined` rather than borrowing someone else's identity.
|
|
113
|
+
*/
|
|
114
|
+
export declare function withFileMutationSession<T>(sessionId: string | undefined, fn: () => T): T;
|
|
115
|
+
/**
|
|
116
|
+
* Remove a file, consulting registered delete fallbacks when the unlink is denied.
|
|
117
|
+
*
|
|
118
|
+
* Unlike the write path there is no masked-`ENOENT` case to see through: nothing is
|
|
119
|
+
* created on the way, so an `ENOENT` here means the file genuinely is not there and
|
|
120
|
+
* must propagate — `edit`'s `REM` turns it into a `NotFoundError`.
|
|
121
|
+
*/
|
|
122
|
+
export declare function deleteFileWithFallback(dst: string, file?: BunFile): Promise<void>;
|
|
123
|
+
export declare function writeFileWithFallback(dst: string, content: string, file?: BunFile): Promise<void>;
|
|
124
|
+
export {};
|
|
@@ -50,6 +50,7 @@ export * from "./debug.js";
|
|
|
50
50
|
export * from "./essential-tools.js";
|
|
51
51
|
export * from "./eval.js";
|
|
52
52
|
export * from "./eval-backends.js";
|
|
53
|
+
export * from "./file-write-fallback.js";
|
|
53
54
|
export * from "./gh.js";
|
|
54
55
|
export * from "./glob.js";
|
|
55
56
|
export * from "./grep.js";
|
|
@@ -155,6 +155,29 @@ export declare function resolveToCwd(filePath: string, cwd: string): string;
|
|
|
155
155
|
* The cwd itself is rejected: a download names a file, never the directory.
|
|
156
156
|
*/
|
|
157
157
|
export declare function confineToWorkspace(filePath: string, cwd: string): string | null;
|
|
158
|
+
/**
|
|
159
|
+
* Resolve the path a syscall on `filePath` would really act on, or `null` when
|
|
160
|
+
* that cannot be established.
|
|
161
|
+
*
|
|
162
|
+
* A lexical path is not a destination. The kernel follows every component above
|
|
163
|
+
* the last, so `ws/link/file` under a `ws/link -> /elsewhere` link lands outside
|
|
164
|
+
* `ws` while still looking relative and `..`-free. Handing such a path to a
|
|
165
|
+
* privileged helper defeats the defence a helper author reaches for first — a
|
|
166
|
+
* prefix allowlist passes, because the link sits inside the allowed root while
|
|
167
|
+
* its target does not. Callers that hand a path to something more privileged
|
|
168
|
+
* than the syscall that just failed resolve it here first.
|
|
169
|
+
*
|
|
170
|
+
* Rejecting symlinked components outright is not an option: `/var` and `/tmp`
|
|
171
|
+
* are links on macOS, so every path under `os.tmpdir()` traverses one. They are
|
|
172
|
+
* resolved instead, and only a path whose real destination cannot be established
|
|
173
|
+
* is refused, because "where would this land" then has no answer to hand over.
|
|
174
|
+
* {@link confineToWorkspace} refuses an unresolvable link for the same reason.
|
|
175
|
+
*
|
|
176
|
+
* @param followFinal `true` for a syscall that follows a link at the final
|
|
177
|
+
* component (`open`, so every write), `false` for one that acts on the link
|
|
178
|
+
* itself (`unlink`) and therefore needs it left alone.
|
|
179
|
+
*/
|
|
180
|
+
export declare function resolveSyscallTarget(filePath: string, followFinal: boolean): Promise<string | null>;
|
|
158
181
|
export declare function formatPathRelativeToCwd(filePath: string, cwd: string, options?: {
|
|
159
182
|
trailingSlash?: boolean;
|
|
160
183
|
}): string;
|
|
@@ -10,6 +10,12 @@ export interface HashlineHeaderContext {
|
|
|
10
10
|
}
|
|
11
11
|
export declare function formatReadHashlineHeader(displayPath: string, tag: string): string;
|
|
12
12
|
export declare function readHashlineHeaderContext(session: ToolSession, absolutePath: string, cwd: string): Promise<HashlineHeaderContext>;
|
|
13
|
+
/**
|
|
14
|
+
* {@link readHashlineHeaderContext} for a caller that already holds the file's
|
|
15
|
+
* full text, so the file is not reopened just to hash it. Line endings are
|
|
16
|
+
* normalized here, exactly as the reading variant does.
|
|
17
|
+
*/
|
|
18
|
+
export declare function hashlineHeaderContextForText(session: ToolSession, absolutePath: string, cwd: string, fullText: string): HashlineHeaderContext;
|
|
13
19
|
export declare function hashlineHeaderContext(displayPath: string, tag: string): HashlineHeaderContext;
|
|
14
20
|
export declare function prependHashlineHeader(text: string, context: HashlineHeaderContext | undefined): string;
|
|
15
21
|
export declare function formatTextWithMode(text: string, startNum: number, shouldAddHashLines: boolean, shouldAddLineNumbers: boolean): string;
|
|
@@ -10,7 +10,13 @@ export declare function routeReadThroughBridge(session: ToolSession, absolutePat
|
|
|
10
10
|
line?: number;
|
|
11
11
|
limit?: number;
|
|
12
12
|
}): Promise<string> | undefined;
|
|
13
|
-
|
|
13
|
+
/**
|
|
14
|
+
* Structural summary of `absolutePath`, or `null` when the file is too large,
|
|
15
|
+
* too short, or unparseable. `diskText` lets a caller that already read the file
|
|
16
|
+
* hand those bytes over instead of forcing a second read; an ACP bridge still
|
|
17
|
+
* wins, since the editor's buffer is the source of truth.
|
|
18
|
+
*/
|
|
19
|
+
export declare function trySummarize(session: ToolSession, absolutePath: string, fileSize: number, signal?: AbortSignal, diskText?: string): Promise<SummaryResult | null>;
|
|
14
20
|
export declare function renderSummary(session: ToolSession, summary: SummaryResult): {
|
|
15
21
|
text: string;
|
|
16
22
|
displayText: string;
|
|
@@ -6,6 +6,20 @@ export interface LineSpan {
|
|
|
6
6
|
export interface BlockContextSource {
|
|
7
7
|
path?: string;
|
|
8
8
|
lang?: string;
|
|
9
|
+
/**
|
|
10
|
+
* The whole source `fullLines` was split from, when the caller still holds it.
|
|
11
|
+
* Supplying it skips re-joining every line into a fresh whole-file string on
|
|
12
|
+
* the way to the parser. It MUST be the same content as `fullLines`; a
|
|
13
|
+
* differing trailing newline is the only tolerated variation, since it moves
|
|
14
|
+
* no node's line number.
|
|
15
|
+
*
|
|
16
|
+
* Every current supplier derives both from one buffer in the same breath, so
|
|
17
|
+
* the two cannot drift. Do NOT set it on a source object that is reused
|
|
18
|
+
* across two different line arrays — a before/after diff pair, say — because
|
|
19
|
+
* the boundary lines tree-sitter reports would then be indexed into the wrong
|
|
20
|
+
* array and surface off-by-N context rows.
|
|
21
|
+
*/
|
|
22
|
+
text?: string;
|
|
9
23
|
}
|
|
10
24
|
export type LineEntry = {
|
|
11
25
|
kind: "line";
|
|
@@ -2,3 +2,18 @@
|
|
|
2
2
|
export declare function withTimeoutSignal(timeoutMs: number, signal?: AbortSignal): AbortSignal;
|
|
3
3
|
/** Detect a timeout raised by an abortable fetch. */
|
|
4
4
|
export declare function isTimeoutError(error: unknown): boolean;
|
|
5
|
+
/**
|
|
6
|
+
* Detect Bun's `UnsupportedProxyProtocol` fetch rejection, raised when a proxy
|
|
7
|
+
* env var (`HTTPS_PROXY`, `ALL_PROXY`, …) points at a scheme it cannot drive —
|
|
8
|
+
* most commonly a SOCKS proxy (`socks5://`, `socks5h://`). The raw error tells
|
|
9
|
+
* the caller to "pass `verbose: true` in the second argument to fetch()", which
|
|
10
|
+
* is meaningless from a CLI, so callers translate it into
|
|
11
|
+
* {@link unsupportedProxyMessage}.
|
|
12
|
+
*/
|
|
13
|
+
export declare function isUnsupportedProxyError(error: unknown): boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Build an actionable CLI message for an {@link isUnsupportedProxyError} failure,
|
|
16
|
+
* naming any set proxy env var whose scheme is not `http(s)://` so the user can
|
|
17
|
+
* see exactly which variable to change.
|
|
18
|
+
*/
|
|
19
|
+
export declare function unsupportedProxyMessage(env?: Record<string, string | undefined>): string;
|
|
@@ -3,6 +3,8 @@ export interface GitCommandResult {
|
|
|
3
3
|
exitCode: number;
|
|
4
4
|
stdout: string;
|
|
5
5
|
stderr: string;
|
|
6
|
+
/** True when stdout or stderr hit {@link GIT_COMMAND_OUTPUT_LIMIT_BYTES} and the captured text is incomplete. */
|
|
7
|
+
truncated: boolean;
|
|
6
8
|
}
|
|
7
9
|
export interface GitRepository {
|
|
8
10
|
commonDir: string;
|
|
@@ -55,6 +57,7 @@ export interface DiffOptions {
|
|
|
55
57
|
readonly numstat?: boolean;
|
|
56
58
|
readonly signal?: AbortSignal;
|
|
57
59
|
readonly stat?: boolean;
|
|
60
|
+
readonly requireComplete?: boolean;
|
|
58
61
|
}
|
|
59
62
|
export interface StatusOptions {
|
|
60
63
|
readonly pathspecs?: readonly string[];
|
|
@@ -136,6 +139,19 @@ export declare class GitCommandError extends Error {
|
|
|
136
139
|
readonly result: GitCommandResult;
|
|
137
140
|
constructor(args: readonly string[], result: GitCommandResult);
|
|
138
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* A git subprocess produced more output than {@link GIT_COMMAND_OUTPUT_LIMIT_BYTES}
|
|
144
|
+
* and its captured stdout was truncated. Thrown only for callers that opt into
|
|
145
|
+
* completeness via `diff({ requireComplete: true })`, where operating on a partial
|
|
146
|
+
* diff would silently corrupt downstream parsing — e.g. the split-commit builder,
|
|
147
|
+
* which would otherwise throw a misleading "No diff found" for files sorting after
|
|
148
|
+
* a large binary blob whose base85 payload pushed the diff past the cap.
|
|
149
|
+
*/
|
|
150
|
+
export declare class GitOutputTruncatedError extends Error {
|
|
151
|
+
readonly args: readonly string[];
|
|
152
|
+
readonly result: GitCommandResult;
|
|
153
|
+
constructor(args: readonly string[], result: GitCommandResult);
|
|
154
|
+
}
|
|
139
155
|
/** Default deadline for git and gh subprocesses spawned by the coding agent. */
|
|
140
156
|
export declare const GIT_COMMAND_TIMEOUT_MS: number;
|
|
141
157
|
/**
|
|
@@ -367,7 +383,15 @@ export declare const patch: {
|
|
|
367
383
|
canApply(cwd: string, patchPath: string, options?: Omit<PatchOptions, "check">): Promise<boolean>;
|
|
368
384
|
/** Check if a patch string can be applied cleanly. */
|
|
369
385
|
canApplyText(cwd: string, patchText: string, options?: Omit<PatchOptions, "check">): Promise<boolean>;
|
|
370
|
-
/**
|
|
386
|
+
/**
|
|
387
|
+
* Join patch parts into a single patch string.
|
|
388
|
+
*
|
|
389
|
+
* Each part is terminated with a single `\n` if it lacks one, then parts are
|
|
390
|
+
* concatenated verbatim — matching git's native multi-file diff layout. Parts
|
|
391
|
+
* are NOT separated by an extra blank line and trailing newlines are NOT
|
|
392
|
+
* stripped: a `GIT binary patch` block ends in a blank line that
|
|
393
|
+
* `git apply --binary` requires, and stripping it corrupts the patch (#8899).
|
|
394
|
+
*/
|
|
371
395
|
join(parts: string[]): string;
|
|
372
396
|
};
|
|
373
397
|
export declare const cherryPick: ((cwd: string, revision: string, signal?: AbortSignal) => Promise<void>) & {
|
|
@@ -15,6 +15,10 @@ export interface TinyFishSearchParams {
|
|
|
15
15
|
page?: number;
|
|
16
16
|
include_domains?: string[];
|
|
17
17
|
exclude_domains?: string[];
|
|
18
|
+
/** ISO 3166-1 alpha-2 region, e.g. `IT`. Geolocates results. */
|
|
19
|
+
location?: string;
|
|
20
|
+
/** ISO 639-1 language, e.g. `it`. */
|
|
21
|
+
language?: string;
|
|
18
22
|
signal?: AbortSignal;
|
|
19
23
|
timeoutMs?: number;
|
|
20
24
|
fetch?: FetchImpl;
|
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.3.
|
|
4
|
+
"version": "17.3.8",
|
|
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,18 +48,18 @@
|
|
|
48
48
|
},
|
|
49
49
|
"dependencies": {
|
|
50
50
|
"@babel/parser": "^7.29.7",
|
|
51
|
-
"@oh-my-pi/hashline": "17.3.
|
|
52
|
-
"@oh-my-pi/omp-stats": "17.3.
|
|
53
|
-
"@oh-my-pi/omptype": "17.3.
|
|
54
|
-
"@oh-my-pi/pi-agent-core": "17.3.
|
|
55
|
-
"@oh-my-pi/pi-ai": "17.3.
|
|
56
|
-
"@oh-my-pi/pi-catalog": "17.3.
|
|
57
|
-
"@oh-my-pi/pi-mnemopi": "17.3.
|
|
58
|
-
"@oh-my-pi/pi-natives": "17.3.
|
|
59
|
-
"@oh-my-pi/pi-tui": "17.3.
|
|
60
|
-
"@oh-my-pi/pi-utils": "17.3.
|
|
61
|
-
"@oh-my-pi/pi-wire": "17.3.
|
|
62
|
-
"@oh-my-pi/snapcompact": "17.3.
|
|
51
|
+
"@oh-my-pi/hashline": "17.3.8",
|
|
52
|
+
"@oh-my-pi/omp-stats": "17.3.8",
|
|
53
|
+
"@oh-my-pi/omptype": "17.3.8",
|
|
54
|
+
"@oh-my-pi/pi-agent-core": "17.3.8",
|
|
55
|
+
"@oh-my-pi/pi-ai": "17.3.8",
|
|
56
|
+
"@oh-my-pi/pi-catalog": "17.3.8",
|
|
57
|
+
"@oh-my-pi/pi-mnemopi": "17.3.8",
|
|
58
|
+
"@oh-my-pi/pi-natives": "17.3.8",
|
|
59
|
+
"@oh-my-pi/pi-tui": "17.3.8",
|
|
60
|
+
"@oh-my-pi/pi-utils": "17.3.8",
|
|
61
|
+
"@oh-my-pi/pi-wire": "17.3.8",
|
|
62
|
+
"@oh-my-pi/snapcompact": "17.3.8",
|
|
63
63
|
"@opentelemetry/api": "^1.9.1",
|
|
64
64
|
"@opentelemetry/api-logs": "^0.220.0",
|
|
65
65
|
"@opentelemetry/context-async-hooks": "^2.9.0",
|
|
@@ -110,8 +110,10 @@ export function isAdvisorInterruptImmuneTurnActive(opts: {
|
|
|
110
110
|
* auto-resume anything, so it is delivered live. Parking it during an active
|
|
111
111
|
* run instead strands it (it never reaches the running agent) and the withheld
|
|
112
112
|
* notes dump as one burst at the next user prompt — the bug this guards.
|
|
113
|
-
* - During the post-interrupt immune-turn window, further `concern
|
|
114
|
-
*
|
|
113
|
+
* - During the post-interrupt immune-turn window, further `concern` notes are
|
|
114
|
+
* downgraded to asides; preservation still wins. A `blocker` is exempt: it
|
|
115
|
+
* means the agent handed off broken or unexercised work, so it still steers a
|
|
116
|
+
* triggered turn even right after a prior interrupt (#5628).
|
|
115
117
|
*/
|
|
116
118
|
export function resolveAdvisorDeliveryChannel(opts: {
|
|
117
119
|
severity: AdvisorSeverity | undefined;
|
|
@@ -127,7 +129,7 @@ export function resolveAdvisorDeliveryChannel(opts: {
|
|
|
127
129
|
if (opts.autoResumeSuppressed && (opts.aborting || !opts.streaming)) return "preserve";
|
|
128
130
|
if (opts.terminalAnswerNoQueuedWork && opts.severity !== "blocker" && !opts.streaming && !opts.aborting)
|
|
129
131
|
return "preserve";
|
|
130
|
-
if (opts.interruptImmuneTurnActive) return "aside";
|
|
132
|
+
if (opts.interruptImmuneTurnActive && opts.severity !== "blocker") return "aside";
|
|
131
133
|
return "steer";
|
|
132
134
|
}
|
|
133
135
|
|
|
@@ -33,10 +33,14 @@ import {
|
|
|
33
33
|
SqliteAuthCredentialStore,
|
|
34
34
|
} from "@oh-my-pi/pi-ai";
|
|
35
35
|
import { AuthBrokerClient, DEFAULT_AUTH_BROKER_BIND, startAuthBroker } from "@oh-my-pi/pi-ai/auth-broker";
|
|
36
|
+
import { refreshOAuthToken } from "@oh-my-pi/pi-ai/oauth";
|
|
37
|
+
import type { OAuthCredentials } from "@oh-my-pi/pi-ai/oauth/types";
|
|
36
38
|
import { $which, APP_NAME, getAgentDbPath, getConfigRootDir, isEnoent, logger, VERSION } from "@oh-my-pi/pi-utils";
|
|
37
39
|
import chalk from "@oh-my-pi/pi-utils/chalk";
|
|
38
40
|
import { setTransports as setLoggerTransports } from "@oh-my-pi/pi-utils/logger";
|
|
39
41
|
import { $ } from "bun";
|
|
42
|
+
import { refreshManagedMcpOAuthCredential } from "../mcp/oauth-credentials";
|
|
43
|
+
import { isManagedMCPOAuthCredentialId, mcpOAuthServerUrlFromCredentialId } from "../mcp/oauth-flow";
|
|
40
44
|
import { resolveAuthBrokerConfig } from "../session/auth-broker-config";
|
|
41
45
|
|
|
42
46
|
export type AuthBrokerAction = "serve" | "token" | "login" | "logout" | "status" | "import" | "migrate" | "list";
|
|
@@ -119,6 +123,34 @@ async function ensureToken(): Promise<string> {
|
|
|
119
123
|
return token;
|
|
120
124
|
}
|
|
121
125
|
|
|
126
|
+
/**
|
|
127
|
+
* OAuth refresh handler for `omp auth-broker serve`'s {@link AuthStorage}.
|
|
128
|
+
*
|
|
129
|
+
* The vault holds provider OAuth rows AND OMP-managed `mcp_oauth:*` rows.
|
|
130
|
+
* Provider rows refresh through the per-provider registry. MCP rows are
|
|
131
|
+
* self-describing — the embedded token endpoint and client credentials are the
|
|
132
|
+
* only refresh material — so they refresh with a generic `refresh_token` grant.
|
|
133
|
+
* The serve process never loads the MCP manager, so this is the only place that
|
|
134
|
+
* teaches the broker to refresh MCP tokens; without it
|
|
135
|
+
* `POST /v1/credential/:id/refresh` fails with "Unknown OAuth provider" and the
|
|
136
|
+
* background refresher lets MCP access tokens expire (issue #8933).
|
|
137
|
+
*/
|
|
138
|
+
export function refreshBrokerOAuthCredential(
|
|
139
|
+
provider: string,
|
|
140
|
+
credential: OAuthCredential,
|
|
141
|
+
signal?: AbortSignal,
|
|
142
|
+
): Promise<OAuthCredentials> {
|
|
143
|
+
if (isManagedMCPOAuthCredentialId(provider)) {
|
|
144
|
+
return refreshManagedMcpOAuthCredential(credential, {
|
|
145
|
+
serverUrl: mcpOAuthServerUrlFromCredentialId(provider),
|
|
146
|
+
signal,
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
// Non-MCP rows: same per-provider path AuthStorage would take by default
|
|
150
|
+
// (the serve process registers no custom OAuth providers).
|
|
151
|
+
return refreshOAuthToken(provider as OAuthProvider, credential);
|
|
152
|
+
}
|
|
153
|
+
|
|
122
154
|
async function runServe(flags: AuthBrokerCommandArgs["flags"]): Promise<void> {
|
|
123
155
|
// The broker is a long-running headless service: route structured logs to
|
|
124
156
|
// stdout so a process supervisor (pm2, journald, k8s) captures them, and
|
|
@@ -129,7 +161,10 @@ async function runServe(flags: AuthBrokerCommandArgs["flags"]): Promise<void> {
|
|
|
129
161
|
const token = await ensureToken();
|
|
130
162
|
const dbPath = getAgentDbPath();
|
|
131
163
|
const store = await SqliteAuthCredentialStore.open(dbPath);
|
|
132
|
-
const storage = new AuthStorage(store
|
|
164
|
+
const storage = new AuthStorage(store, {
|
|
165
|
+
refreshOAuthCredential: (provider, _credentialId, credential, signal) =>
|
|
166
|
+
refreshBrokerOAuthCredential(provider, credential, signal),
|
|
167
|
+
});
|
|
133
168
|
await storage.reload();
|
|
134
169
|
const handle = startAuthBroker({
|
|
135
170
|
storage,
|
|
@@ -33,7 +33,7 @@
|
|
|
33
33
|
* them also activates (`omp --print --profile work`).
|
|
34
34
|
*/
|
|
35
35
|
|
|
36
|
-
import { isSubcommand } from "../cli-commands";
|
|
36
|
+
import { isSubcommand, LAUNCH_FLAG_COMMANDS } from "../cli-commands";
|
|
37
37
|
import {
|
|
38
38
|
EXTENSION_SHADOWABLE_STRING_FLAGS,
|
|
39
39
|
isUnknownLongValueCandidate,
|
|
@@ -43,10 +43,6 @@ import {
|
|
|
43
43
|
STRING_VALUE_FLAGS,
|
|
44
44
|
} from "./flag-tables";
|
|
45
45
|
|
|
46
|
-
function isProfileBootstrapSubcommand(arg: string): boolean {
|
|
47
|
-
return arg === "launch" || arg === "acp";
|
|
48
|
-
}
|
|
49
|
-
|
|
50
46
|
function needsBoundaryAfterGlobalStrip(stripped: readonly string[]): boolean {
|
|
51
47
|
const previous = stripped[stripped.length - 1];
|
|
52
48
|
return (
|
|
@@ -222,7 +218,7 @@ export function extractProfileFlags(argv: readonly string[]): ProfileBootstrapRe
|
|
|
222
218
|
// any other token has been forwarded, later subcommand names are launch text.
|
|
223
219
|
// `launch` and `acp` are explicit spellings of launch-shaped commands, so
|
|
224
220
|
// global launch flags that follow them must still be extracted.
|
|
225
|
-
if (canDispatchSubcommand && isSubcommand(arg) &&
|
|
221
|
+
if (canDispatchSubcommand && isSubcommand(arg) && LAUNCH_FLAG_COMMANDS[arg] !== true) {
|
|
226
222
|
sawSubcommand = true;
|
|
227
223
|
}
|
|
228
224
|
canDispatchSubcommand = false;
|