@oh-my-pi/pi-coding-agent 16.3.0 → 16.3.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +91 -0
- package/dist/cli.js +3855 -3798
- package/dist/types/cli/update-cli.d.ts +1 -0
- package/dist/types/cli/update-cli.test.d.ts +1 -0
- package/dist/types/commands/update.d.ts +5 -0
- package/dist/types/config/config-file.d.ts +1 -1
- package/dist/types/config/settings-schema.d.ts +20 -0
- package/dist/types/dap/client.d.ts +9 -1
- package/dist/types/discovery/helpers.d.ts +2 -0
- package/dist/types/edit/file-snapshot-store.d.ts +9 -1
- package/dist/types/edit/hashline/execute.d.ts +1 -1
- package/dist/types/edit/hashline/params.d.ts +3 -6
- package/dist/types/edit/renderer.d.ts +1 -0
- package/dist/types/extensibility/plugins/parser.d.ts +2 -1
- package/dist/types/hindsight/client.d.ts +15 -11
- package/dist/types/hindsight/client.test.d.ts +1 -0
- package/dist/types/irc/bus.d.ts +5 -4
- package/dist/types/mcp/smithery-registry.d.ts +1 -0
- package/dist/types/mcp/smithery-registry.test.d.ts +1 -0
- package/dist/types/modes/components/advisor-message.d.ts +4 -2
- package/dist/types/modes/components/tool-execution.d.ts +9 -6
- package/dist/types/modes/components/transcript-container.d.ts +1 -0
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/session/agent-session.d.ts +13 -13
- package/dist/types/session/indexed-session-storage.d.ts +2 -2
- package/dist/types/session/session-storage.d.ts +31 -3
- package/dist/types/ssh/__tests__/connection-manager-timeout.test.d.ts +1 -0
- package/dist/types/ssh/__tests__/sshfs-mount.test.d.ts +1 -0
- package/dist/types/ssh/connection-manager.d.ts +19 -0
- package/dist/types/ssh/sshfs-mount.d.ts +10 -1
- package/dist/types/subprocess/worker-client.d.ts +20 -6
- package/dist/types/task/discovery.d.ts +5 -2
- package/dist/types/task/renderer.d.ts +1 -0
- package/dist/types/tools/ast-grep.d.ts +4 -2
- package/dist/types/tools/bash.d.ts +27 -0
- package/dist/types/tools/browser/render.d.ts +2 -0
- package/dist/types/tools/debug.d.ts +1 -0
- package/dist/types/tools/eval-render.d.ts +2 -0
- package/dist/types/tools/glob.d.ts +4 -2
- package/dist/types/tools/grep.d.ts +4 -3
- package/dist/types/tools/path-utils.d.ts +7 -0
- package/dist/types/tools/renderers.d.ts +24 -0
- package/dist/types/tools/ssh.d.ts +4 -1
- package/dist/types/tts/index.d.ts +2 -0
- package/dist/types/tts/speakable.d.ts +47 -0
- package/dist/types/tts/speech-enhancer.d.ts +46 -0
- package/dist/types/tts/streaming-player.d.ts +1 -2
- package/dist/types/tts/tts-client.d.ts +11 -10
- package/dist/types/tts/tts-protocol.d.ts +7 -0
- package/dist/types/tts/vocalizer.d.ts +15 -8
- package/dist/types/utils/fetch-timeout.d.ts +4 -0
- package/dist/types/utils/git.d.ts +2 -0
- package/dist/types/web/search/providers/base.d.ts +1 -0
- package/dist/types/web/search/providers/gemini.d.ts +1 -0
- package/package.json +12 -12
- package/scripts/generate-legacy-pi-bundled-registry.ts +8 -2
- package/src/advisor/__tests__/advisor.test.ts +1 -1
- package/src/cli/gallery-fixtures/fs.ts +2 -2
- package/src/cli/gallery-fixtures/search.ts +2 -2
- package/src/cli/models-cli.ts +19 -0
- package/src/cli/update-cli.test.ts +28 -0
- package/src/cli/update-cli.ts +35 -8
- package/src/cli.ts +27 -5
- package/src/commands/update.ts +8 -2
- package/src/config/config-file.ts +6 -6
- package/src/config/model-resolver.ts +31 -10
- package/src/config/settings-schema.ts +21 -0
- package/src/cursor.ts +1 -1
- package/src/dap/client.ts +134 -36
- package/src/discovery/helpers.ts +8 -0
- package/src/edit/file-snapshot-store.ts +12 -1
- package/src/edit/hashline/diff.ts +4 -13
- package/src/edit/hashline/execute.ts +1 -1
- package/src/edit/hashline/params.ts +5 -12
- package/src/edit/renderer.ts +4 -2
- package/src/edit/streaming.ts +15 -5
- package/src/export/html/tool-views.generated.js +34 -34
- package/src/extensibility/custom-tools/loader.ts +3 -3
- package/src/extensibility/extensions/loader.ts +10 -3
- package/src/extensibility/legacy-pi-coding-agent-shim.ts +2 -2
- package/src/extensibility/plugins/installer.ts +12 -3
- package/src/extensibility/plugins/legacy-pi-compat.ts +87 -11
- package/src/extensibility/plugins/loader.ts +30 -1
- package/src/extensibility/plugins/manager.ts +32 -8
- package/src/extensibility/plugins/parser.ts +7 -5
- package/src/extensibility/tool-event-input.ts +1 -1
- package/src/hindsight/client.test.ts +33 -0
- package/src/hindsight/client.ts +42 -22
- package/src/internal-urls/docs-index.generated.txt +1 -1
- package/src/irc/bus.ts +5 -4
- package/src/main.ts +7 -1
- package/src/mcp/oauth-flow.ts +93 -4
- package/src/mcp/smithery-auth.ts +3 -0
- package/src/mcp/smithery-connect.ts +9 -0
- package/src/mcp/smithery-registry.test.ts +51 -0
- package/src/mcp/smithery-registry.ts +27 -4
- package/src/modes/components/__tests__/move-overlay.test.ts +72 -1
- package/src/modes/components/advisor-message.ts +13 -10
- package/src/modes/components/assistant-message.ts +1 -1
- package/src/modes/components/move-overlay.ts +35 -23
- package/src/modes/components/status-line/component.ts +11 -4
- package/src/modes/components/tool-execution.ts +119 -20
- package/src/modes/components/transcript-container.ts +26 -0
- package/src/modes/components/tree-selector.ts +10 -3
- package/src/modes/controllers/event-controller.ts +23 -3
- package/src/modes/controllers/tool-args-reveal.ts +1 -1
- package/src/modes/interactive-mode.ts +1 -0
- package/src/modes/rpc/rpc-client.ts +29 -16
- package/src/modes/theme/shimmer.ts +49 -15
- package/src/modes/theme/theme.ts +7 -0
- package/src/prompts/goals/goal-mode-context.md +4 -0
- package/src/prompts/goals/goal-todo-context.md +10 -2
- package/src/prompts/system/speech-rewrite.md +15 -0
- package/src/prompts/system/tiny-title-system.md +1 -1
- package/src/prompts/system/title-system-marker.md +2 -1
- package/src/prompts/system/title-system.md +2 -1
- package/src/prompts/tools/ast-grep.md +3 -3
- package/src/prompts/tools/glob.md +1 -1
- package/src/prompts/tools/grep.md +1 -1
- package/src/sdk.ts +8 -5
- package/src/session/agent-session.ts +329 -79
- package/src/session/indexed-session-storage.ts +40 -3
- package/src/session/session-history-format.ts +6 -2
- package/src/session/session-manager.ts +83 -14
- package/src/session/session-storage.ts +112 -26
- package/src/slash-commands/helpers/usage-report.ts +6 -1
- package/src/ssh/__tests__/connection-manager-timeout.test.ts +61 -0
- package/src/ssh/__tests__/sshfs-mount.test.ts +13 -0
- package/src/ssh/connection-manager.ts +44 -11
- package/src/ssh/sshfs-mount.ts +27 -4
- package/src/subprocess/worker-client.ts +161 -10
- package/src/task/discovery.ts +25 -2
- package/src/task/executor.ts +24 -4
- package/src/task/render.ts +26 -12
- package/src/task/renderer.ts +1 -0
- package/src/task/worktree.ts +144 -16
- package/src/tiny/text.ts +109 -17
- package/src/tools/ast-grep.ts +20 -17
- package/src/tools/bash.ts +46 -9
- package/src/tools/browser/render.ts +2 -0
- package/src/tools/debug.ts +1 -0
- package/src/tools/eval-render.ts +5 -2
- package/src/tools/gh-renderer.ts +3 -0
- package/src/tools/glob.ts +24 -20
- package/src/tools/grep.ts +39 -37
- package/src/tools/path-utils.ts +55 -10
- package/src/tools/read.ts +23 -6
- package/src/tools/renderers.ts +24 -0
- package/src/tools/ssh.ts +25 -7
- package/src/tts/index.ts +2 -0
- package/src/tts/speakable.ts +382 -0
- package/src/tts/speech-enhancer.ts +204 -0
- package/src/tts/streaming-player.ts +71 -16
- package/src/tts/tts-client.ts +11 -10
- package/src/tts/tts-protocol.ts +14 -5
- package/src/tts/tts-worker.ts +52 -49
- package/src/tts/vocalizer.ts +277 -46
- package/src/utils/clipboard.ts +49 -12
- package/src/utils/fetch-timeout.ts +10 -0
- package/src/utils/git.ts +8 -0
- package/src/web/search/index.ts +8 -0
- package/src/web/search/providers/base.ts +1 -0
- package/src/web/search/providers/gemini.ts +23 -6
|
@@ -7,7 +7,7 @@ import type { ToolSession } from ".";
|
|
|
7
7
|
import type { OutputMeta } from "./output-meta";
|
|
8
8
|
declare const astGrepSchema: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
9
9
|
pat: string;
|
|
10
|
-
|
|
10
|
+
path?: string | undefined;
|
|
11
11
|
skip?: number | undefined;
|
|
12
12
|
}, {}>;
|
|
13
13
|
export interface AstGrepToolDetails {
|
|
@@ -44,7 +44,7 @@ export declare class AstGrepTool implements AgentTool<typeof astGrepSchema, AstG
|
|
|
44
44
|
readonly description: string;
|
|
45
45
|
readonly parameters: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
46
46
|
pat: string;
|
|
47
|
-
|
|
47
|
+
path?: string | undefined;
|
|
48
48
|
skip?: number | undefined;
|
|
49
49
|
}, {}>;
|
|
50
50
|
readonly strict = true;
|
|
@@ -55,6 +55,8 @@ export declare class AstGrepTool implements AgentTool<typeof astGrepSchema, AstG
|
|
|
55
55
|
}
|
|
56
56
|
interface AstGrepRenderArgs {
|
|
57
57
|
pat?: string;
|
|
58
|
+
path?: string | string[];
|
|
59
|
+
/** Legacy pre-`path` argument name; kept so historical transcripts still render a scope. */
|
|
58
60
|
paths?: string[];
|
|
59
61
|
skip?: number;
|
|
60
62
|
}
|
|
@@ -5,6 +5,33 @@ import { type Theme } from "../modes/theme/theme";
|
|
|
5
5
|
import type { ToolSession } from ".";
|
|
6
6
|
import { type OutputMeta } from "./output-meta";
|
|
7
7
|
export declare const BASH_DEFAULT_PREVIEW_LINES = 10;
|
|
8
|
+
/**
|
|
9
|
+
* Shape a shell command line for an ACP-conformant `terminal/create` request.
|
|
10
|
+
*
|
|
11
|
+
* ACP's `command` field is documented as the executable and `args` as its
|
|
12
|
+
* argv tail (see https://agentclientprotocol.com/protocol/v1/terminals), so a
|
|
13
|
+
* spec-conformant client `spawn(command, args)`s them directly — no implicit
|
|
14
|
+
* shell. A raw `bash` tool line ("git status && echo x | head") therefore has
|
|
15
|
+
* to be wrapped in an explicit shell invocation, otherwise the client tries
|
|
16
|
+
* to spawn the whole line as argv[0] and fails with `ENOENT` for anything
|
|
17
|
+
* containing a space, pipe, `&&`, redirect, or `$(...)`.
|
|
18
|
+
*
|
|
19
|
+
* The wrap reuses the same shell binary + args the local `bash-executor` would
|
|
20
|
+
* pick via `settings.getShellConfig()` — Git Bash / `bash.exe` on Windows,
|
|
21
|
+
* `$SHELL` (bash/zsh) with the `sh` fallback on POSIX — so the ACP path
|
|
22
|
+
* preserves `bash` tool semantics (`$VAR`, `$(...)`, `source`, POSIX quoting,
|
|
23
|
+
* `-l`) instead of dropping to `cmd.exe` on Windows. The agent host's shell
|
|
24
|
+
* path is used as a proxy for the client's, matching the near-universal
|
|
25
|
+
* ACP deployment shape of an editor spawning omp as a co-hosted subprocess.
|
|
26
|
+
*/
|
|
27
|
+
export declare function wrapShellLineForClientTerminal(line: string, shellConfig: {
|
|
28
|
+
shell: string;
|
|
29
|
+
args: string[];
|
|
30
|
+
prefix?: string | undefined;
|
|
31
|
+
}): {
|
|
32
|
+
command: string;
|
|
33
|
+
args: string[];
|
|
34
|
+
};
|
|
8
35
|
/**
|
|
9
36
|
* Bash patterns flagged as safety critical for approval policy.
|
|
10
37
|
*
|
|
@@ -35,6 +35,8 @@ interface BrowserRenderContext {
|
|
|
35
35
|
previewLines?: number;
|
|
36
36
|
}
|
|
37
37
|
export declare const browserToolRenderer: {
|
|
38
|
+
animatedPendingPreview: (args: unknown) => boolean;
|
|
39
|
+
animatedPartialResult: (args: unknown) => boolean;
|
|
38
40
|
renderCall(args: BrowserRenderArgs, options: RenderResultOptions, theme: Theme): Component;
|
|
39
41
|
renderResult(result: {
|
|
40
42
|
content: Array<{
|
|
@@ -85,6 +85,7 @@ interface DebugToolDetails {
|
|
|
85
85
|
interface DebugRenderArgs extends Partial<DebugParams> {
|
|
86
86
|
}
|
|
87
87
|
export declare const debugToolRenderer: {
|
|
88
|
+
animatedPartialResult: boolean;
|
|
88
89
|
renderCall(args: DebugRenderArgs, _options: RenderResultOptions, theme: Theme): Component;
|
|
89
90
|
renderResult(result: {
|
|
90
91
|
content: Array<{
|
|
@@ -40,6 +40,8 @@ interface EvalRenderContext {
|
|
|
40
40
|
*/
|
|
41
41
|
export declare function upsertStatusEvent(events: EvalStatusEvent[], event: EvalStatusEvent): void;
|
|
42
42
|
export declare const evalToolRenderer: {
|
|
43
|
+
animatedPendingPreview: boolean;
|
|
44
|
+
animatedPartialResult: boolean;
|
|
43
45
|
renderCall(args: EvalRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component;
|
|
44
46
|
renderResult(result: {
|
|
45
47
|
content: Array<{
|
|
@@ -7,7 +7,7 @@ import { type TruncationResult } from "../session/streaming-output";
|
|
|
7
7
|
import type { ToolSession } from ".";
|
|
8
8
|
import { type OutputMeta } from "./output-meta";
|
|
9
9
|
declare const findSchema: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
10
|
-
|
|
10
|
+
path?: string | undefined;
|
|
11
11
|
hidden?: boolean | undefined;
|
|
12
12
|
gitignore?: boolean | undefined;
|
|
13
13
|
limit?: number | undefined;
|
|
@@ -64,7 +64,7 @@ export declare class GlobTool implements AgentTool<typeof findSchema, GlobToolDe
|
|
|
64
64
|
readonly label = "Glob";
|
|
65
65
|
readonly description: string;
|
|
66
66
|
readonly parameters: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
67
|
-
|
|
67
|
+
path?: string | undefined;
|
|
68
68
|
hidden?: boolean | undefined;
|
|
69
69
|
gitignore?: boolean | undefined;
|
|
70
70
|
limit?: number | undefined;
|
|
@@ -75,6 +75,8 @@ export declare class GlobTool implements AgentTool<typeof findSchema, GlobToolDe
|
|
|
75
75
|
execute(_toolCallId: string, params: typeof findSchema.infer, signal?: AbortSignal, onUpdate?: AgentToolUpdateCallback<GlobToolDetails>, _context?: AgentToolContext): Promise<AgentToolResult<GlobToolDetails>>;
|
|
76
76
|
}
|
|
77
77
|
interface GlobRenderArgs {
|
|
78
|
+
path?: string | string[];
|
|
79
|
+
/** Legacy pre-`path` argument name; kept so historical transcripts still render a scope. */
|
|
78
80
|
paths?: string | string[];
|
|
79
81
|
limit?: number;
|
|
80
82
|
}
|
|
@@ -7,13 +7,12 @@ import type { ToolSession } from ".";
|
|
|
7
7
|
import type { OutputMeta } from "./output-meta";
|
|
8
8
|
declare const searchSchema: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
9
9
|
pattern: string;
|
|
10
|
-
|
|
10
|
+
path?: string | undefined;
|
|
11
11
|
case?: boolean | undefined;
|
|
12
12
|
gitignore?: boolean | undefined;
|
|
13
13
|
skip?: number | null | undefined;
|
|
14
14
|
}, {}>;
|
|
15
15
|
export type GrepToolInput = typeof searchSchema.infer;
|
|
16
|
-
export declare function toPathList(input: string | string[] | undefined): string[];
|
|
17
16
|
/** Maximum number of distinct files surfaced in a single response. The
|
|
18
17
|
* agent paginates further pages via `skip`. */
|
|
19
18
|
export declare const DEFAULT_FILE_LIMIT = 20;
|
|
@@ -66,7 +65,7 @@ export declare class GrepTool implements AgentTool<typeof searchSchema, GrepTool
|
|
|
66
65
|
readonly description: string;
|
|
67
66
|
readonly parameters: import("arktype/internal/variants/object.ts").ObjectType<{
|
|
68
67
|
pattern: string;
|
|
69
|
-
|
|
68
|
+
path?: string | undefined;
|
|
70
69
|
case?: boolean | undefined;
|
|
71
70
|
gitignore?: boolean | undefined;
|
|
72
71
|
skip?: number | null | undefined;
|
|
@@ -77,6 +76,8 @@ export declare class GrepTool implements AgentTool<typeof searchSchema, GrepTool
|
|
|
77
76
|
}
|
|
78
77
|
interface GrepRenderArgs {
|
|
79
78
|
pattern: string;
|
|
79
|
+
path?: string | string[];
|
|
80
|
+
/** Legacy pre-`path` argument name; kept so historical transcripts still render a scope. */
|
|
80
81
|
paths?: string | string[];
|
|
81
82
|
case?: boolean;
|
|
82
83
|
gitignore?: boolean;
|
|
@@ -117,6 +117,13 @@ export declare function formatPathRelativeToCwd(filePath: string, cwd: string, o
|
|
|
117
117
|
*/
|
|
118
118
|
export declare function stripOuterDoubleQuotes(input: string): string;
|
|
119
119
|
export declare function normalizePathLikeInput(input: string): string;
|
|
120
|
+
/**
|
|
121
|
+
* Normalize a path argument that may arrive as a single string, a JSON-encoded
|
|
122
|
+
* string array (`'["a.ts"]'`), or an actual array into a flat `string[]`.
|
|
123
|
+
* Delimited single strings (`"a.ts b.ts"`) are left for
|
|
124
|
+
* {@link expandDelimitedPathEntries} to split.
|
|
125
|
+
*/
|
|
126
|
+
export declare function toPathList(input: string | string[] | undefined): string[];
|
|
120
127
|
export declare function hasGlobPathChars(filePath: string): boolean;
|
|
121
128
|
type PathEntrySplitter = (item: string) => {
|
|
122
129
|
basePath: string;
|
|
@@ -41,5 +41,29 @@ export type ToolRenderer = {
|
|
|
41
41
|
* with the final render and may commit like any settled stream.
|
|
42
42
|
*/
|
|
43
43
|
provisionalPartialResult?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Whether the renderer's pending-call path visibly consumes
|
|
46
|
+
* `options.spinnerFrame`. Used to avoid scheduling repaint ticks for live
|
|
47
|
+
* partial calls whose bytes cannot change between spinner frames.
|
|
48
|
+
*/
|
|
49
|
+
animatedPendingPreview?: boolean | ((args: unknown) => boolean);
|
|
50
|
+
/**
|
|
51
|
+
* Whether the renderer's partial-result path visibly consumes
|
|
52
|
+
* `options.spinnerFrame`.
|
|
53
|
+
*/
|
|
54
|
+
animatedPartialResult?: boolean | ((args: unknown) => boolean);
|
|
55
|
+
/**
|
|
56
|
+
* Whether replacing a streamed pending placeholder with the first result
|
|
57
|
+
* requires a full viewport repaint. Use for merged renderers whose pending
|
|
58
|
+
* streamed args may have committed placeholder rows that the result render
|
|
59
|
+
* re-anchors instead of preserving.
|
|
60
|
+
*/
|
|
61
|
+
forceFirstResultViewportRepaint?: boolean;
|
|
62
|
+
/**
|
|
63
|
+
* Whether settling a provisional partial result into the final render requires
|
|
64
|
+
* a full viewport repaint. Use when the result renderer changes chrome or
|
|
65
|
+
* frame topology at `options.isPartial: true -> false`.
|
|
66
|
+
*/
|
|
67
|
+
forceResultViewportRepaintOnSettle?: boolean;
|
|
44
68
|
};
|
|
45
69
|
export declare const toolRenderers: Record<string, ToolRenderer>;
|
|
@@ -55,7 +55,8 @@ interface SshRenderContext {
|
|
|
55
55
|
totalVisualLines?: number;
|
|
56
56
|
}
|
|
57
57
|
export declare const sshToolRenderer: {
|
|
58
|
-
|
|
58
|
+
animatedPendingPreview: boolean;
|
|
59
|
+
renderCall(args: SshRenderArgs, options: RenderResultOptions, uiTheme: Theme): Component;
|
|
59
60
|
renderResult(result: {
|
|
60
61
|
content: Array<{
|
|
61
62
|
type: string;
|
|
@@ -69,5 +70,7 @@ export declare const sshToolRenderer: {
|
|
|
69
70
|
mergeCallAndResult: boolean;
|
|
70
71
|
provisionalPendingPreview: boolean;
|
|
71
72
|
provisionalPartialResult: boolean;
|
|
73
|
+
forceFirstResultViewportRepaint: boolean;
|
|
74
|
+
forceResultViewportRepaintOnSettle: boolean;
|
|
72
75
|
};
|
|
73
76
|
export {};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Streaming markdown → speakable-segment transform for assistant speech.
|
|
3
|
+
*
|
|
4
|
+
* Sits between the assistant's raw streaming text deltas and the TTS engine,
|
|
5
|
+
* deciding both *what* is worth speaking and *when* a piece of text is ready
|
|
6
|
+
* to synthesize. Three passes:
|
|
7
|
+
*
|
|
8
|
+
* 1. Block pass (per character, stateful): drops fenced code blocks and table
|
|
9
|
+
* rows, strips heading/bullet/blockquote markers (numbered-list markers are
|
|
10
|
+
* spoken as "1, …"), and turns newlines into hard segment breaks.
|
|
11
|
+
* 2. Segmentation (stateful): emits a segment the moment a sentence boundary
|
|
12
|
+
* appears — no next-sentence confirmation, which is what made the previous
|
|
13
|
+
* engine-side splitter stall a full sentence behind generation. The first
|
|
14
|
+
* segment cuts early at a clause boundary for fast time-to-first-audio, and
|
|
15
|
+
* over-long unpunctuated runs are force-split so no segment exceeds the
|
|
16
|
+
* synthesizer's input budget.
|
|
17
|
+
* 3. Inline normalization (per segment): markdown links speak their label,
|
|
18
|
+
* bare URLs speak their host, inline-code ticks and emphasis markers are
|
|
19
|
+
* stripped, multi-directory file paths collapse to their basename, HTML
|
|
20
|
+
* tags are dropped, and whitespace is collapsed. Segments with no letters
|
|
21
|
+
* or digits left are not spoken at all.
|
|
22
|
+
*
|
|
23
|
+
* Pure and synchronous — the vocalizer owns timers (idle flush) and the
|
|
24
|
+
* session lifecycle, so this class stays trivially unit-testable.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* One per utterance. Feed raw assistant deltas through {@link push}; each call
|
|
28
|
+
* returns the segments that became ready to speak. {@link flush} drains the
|
|
29
|
+
* remainder at message end; {@link flushIdle} drains it when generation stalls
|
|
30
|
+
* mid-sentence so speech doesn't sit on buffered text through a tool call.
|
|
31
|
+
*/
|
|
32
|
+
export declare class SpeakableStream {
|
|
33
|
+
#private;
|
|
34
|
+
/** Consume a raw delta; returns segments now ready to speak, in order. */
|
|
35
|
+
push(delta: string): string[];
|
|
36
|
+
/** Message end: drain everything left, including a trailing partial sentence. */
|
|
37
|
+
flush(): string[];
|
|
38
|
+
/**
|
|
39
|
+
* Generation stalled (tool call, thinking block): speak what we have rather
|
|
40
|
+
* than sit silent on buffered text. Keeps block state so the stream resumes
|
|
41
|
+
* afterwards, and refuses stubby mid-sentence fragments — the buffer must be
|
|
42
|
+
* a complete thought (trailing sentence punctuation) or at least
|
|
43
|
+
* {@link MIN_SEGMENT} long, so a stall right after "The" stays silent
|
|
44
|
+
* instead of turning into choppy one-word speech.
|
|
45
|
+
*/
|
|
46
|
+
flushIdle(): string[];
|
|
47
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import type { ModelRegistry } from "../config/model-registry";
|
|
2
|
+
import type { Settings } from "../config/settings";
|
|
3
|
+
/** Session-scoped dependencies; mirrors the auto-thinking classifier's deps. */
|
|
4
|
+
export interface SpeechEnhancerDeps {
|
|
5
|
+
settings: Settings;
|
|
6
|
+
registry: ModelRegistry;
|
|
7
|
+
sessionId: string;
|
|
8
|
+
metadataResolver?: (provider: string) => Record<string, unknown> | undefined;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Rewrites one markdown block into spoken prose via the tiny/smol role.
|
|
12
|
+
* Constructed per session by the event controller and handed to the vocalizer.
|
|
13
|
+
*/
|
|
14
|
+
export declare class SpeechEnhancer {
|
|
15
|
+
#private;
|
|
16
|
+
constructor(deps: SpeechEnhancerDeps);
|
|
17
|
+
/**
|
|
18
|
+
* Rewrite `block` for speech. Returns the spoken text (empty string when
|
|
19
|
+
* the model judged the block unspeakable — pure code/markup), or null when
|
|
20
|
+
* the rewrite failed, timed out, or no model/key resolved; the caller then
|
|
21
|
+
* falls back to mechanical normalization.
|
|
22
|
+
*/
|
|
23
|
+
rewrite(block: string, signal?: AbortSignal): Promise<string | null>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Fence-aware paragraph accumulator over raw streaming deltas. One instance
|
|
27
|
+
* per utterance.
|
|
28
|
+
*/
|
|
29
|
+
export declare class BlockAccumulator {
|
|
30
|
+
#private;
|
|
31
|
+
/** Feed a delta; returns the blocks it completed, in order. */
|
|
32
|
+
push(delta: string): string[];
|
|
33
|
+
/**
|
|
34
|
+
* Message end: drain everything. An unterminated code fence is dropped from
|
|
35
|
+
* its opening line onward (a truncated block is never worth speaking); the
|
|
36
|
+
* prose before it still comes out.
|
|
37
|
+
*/
|
|
38
|
+
flush(): string | null;
|
|
39
|
+
/**
|
|
40
|
+
* Generation stalled: drain the pending partial block — unless we are
|
|
41
|
+
* inside a code fence, where the only thing buffered is code and speaking
|
|
42
|
+
* or rewriting half a fence would re-introduce vocalized code. Fence state
|
|
43
|
+
* is preserved so the eventual closing fence still matches.
|
|
44
|
+
*/
|
|
45
|
+
flushPartial(): string | null;
|
|
46
|
+
}
|
|
@@ -12,8 +12,7 @@ export interface StreamingPlayerLookup {
|
|
|
12
12
|
* and plays it to the default output device. An empty list means no streaming
|
|
13
13
|
* backend is available and the caller should fall back to per-file playback.
|
|
14
14
|
*
|
|
15
|
-
* - darwin:
|
|
16
|
-
* per-file fallback.
|
|
15
|
+
* - darwin: `ffmpeg` (AudioToolbox output device) → sox's `play` (coreaudio).
|
|
17
16
|
* - linux/other POSIX: `ffmpeg` (`-f pulse` then `-f alsa`) → `paplay`/`aplay`
|
|
18
17
|
* raw fallbacks.
|
|
19
18
|
* - win32: none (PowerShell `SoundPlayer` is file-only).
|
|
@@ -17,7 +17,7 @@ export interface TtsStreamOptions {
|
|
|
17
17
|
voice?: string;
|
|
18
18
|
signal?: AbortSignal;
|
|
19
19
|
}
|
|
20
|
-
/** One synthesized
|
|
20
|
+
/** One synthesized segment of a streaming session, in emission order. */
|
|
21
21
|
export interface TtsAudioChunk {
|
|
22
22
|
index: number;
|
|
23
23
|
text: string;
|
|
@@ -25,10 +25,10 @@ export interface TtsAudioChunk {
|
|
|
25
25
|
sampleRate: number;
|
|
26
26
|
}
|
|
27
27
|
/**
|
|
28
|
-
* A live streaming-synthesis session. Feed
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
* finishes draining the closed input.
|
|
28
|
+
* A live streaming-synthesis session. Feed complete speakable segments with
|
|
29
|
+
* {@link push} (the worker synthesizes each push as-is) and close the input
|
|
30
|
+
* with {@link end}; `chunks` yields each segment's audio as soon as it is
|
|
31
|
+
* ready, then completes once the worker finishes draining the closed input.
|
|
32
32
|
*/
|
|
33
33
|
export interface TtsStreamHandle {
|
|
34
34
|
push(text: string): void;
|
|
@@ -51,11 +51,12 @@ export declare class TtsClient {
|
|
|
51
51
|
onProgress(listener: (event: TtsProgressEvent) => void): () => void;
|
|
52
52
|
synthesize(modelKey: string, text: string, options?: TtsSynthesizeOptions): Promise<TtsAudio | null>;
|
|
53
53
|
/**
|
|
54
|
-
* Open a streaming-synthesis session.
|
|
55
|
-
* returned handle's `push`/`end`; audio is emitted one
|
|
56
|
-
* a time via `chunks`, so playback can begin before the full text is
|
|
57
|
-
* Returns an inert handle (immediately-ended `chunks`) for unknown
|
|
58
|
-
* an already-aborted signal, and fails the iterator if the worker
|
|
54
|
+
* Open a streaming-synthesis session. Complete speakable segments are fed
|
|
55
|
+
* through the returned handle's `push`/`end`; audio is emitted one segment
|
|
56
|
+
* at a time via `chunks`, so playback can begin before the full text is
|
|
57
|
+
* known. Returns an inert handle (immediately-ended `chunks`) for unknown
|
|
58
|
+
* models or an already-aborted signal, and fails the iterator if the worker
|
|
59
|
+
* cannot spawn.
|
|
59
60
|
*/
|
|
60
61
|
synthesizeStream(modelKey: string, options?: TtsStreamOptions): TtsStreamHandle;
|
|
61
62
|
downloadModel(modelKey: string, options?: TtsDownloadOptions): Promise<boolean>;
|
|
@@ -91,5 +91,12 @@ export type TtsWorkerOutbound = {
|
|
|
91
91
|
*/
|
|
92
92
|
export interface TtsTransport {
|
|
93
93
|
send(message: TtsWorkerOutbound): void;
|
|
94
|
+
/**
|
|
95
|
+
* Send and resolve once the message has drained into the IPC channel.
|
|
96
|
+
* Streaming synthesis awaits this per audio chunk: ONNX inference blocks
|
|
97
|
+
* the worker's event loop for seconds at a time, so fire-and-forget sends
|
|
98
|
+
* queue unflushed until the session ends and arrive as one burst.
|
|
99
|
+
*/
|
|
100
|
+
sendAndFlush(message: TtsWorkerOutbound): Promise<void>;
|
|
94
101
|
onMessage(handler: (message: TtsWorkerInbound) => void): () => void;
|
|
95
102
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type SpeechEnhancer } from "./speech-enhancer";
|
|
1
2
|
export interface VocalizerPlayer {
|
|
2
3
|
start(sampleRate: number): void;
|
|
3
4
|
write(pcm: Float32Array): void;
|
|
@@ -8,16 +9,22 @@ export interface VocalizerPlayer {
|
|
|
8
9
|
export declare class Vocalizer {
|
|
9
10
|
#private;
|
|
10
11
|
constructor(createPlayer?: () => VocalizerPlayer);
|
|
12
|
+
/** Wire (or drop) the per-session enhanced-rewrite service. */
|
|
13
|
+
setEnhancer(enhancer: SpeechEnhancer | null): void;
|
|
11
14
|
/**
|
|
12
|
-
* Stream a delta of assistant text into the
|
|
13
|
-
* is disabled. The
|
|
14
|
-
*
|
|
15
|
+
* Stream a delta of assistant text into the pipeline. No-op when
|
|
16
|
+
* vocalization is disabled. The synthesis session (worker, player) is only
|
|
17
|
+
* opened once the first speakable segment exists, so a reply that
|
|
18
|
+
* normalizes to silence (pure code, tables, URLs) costs nothing. The
|
|
19
|
+
* trailing partial is flushed by {@link flush} or the idle timer. The
|
|
20
|
+
* pipeline (enhanced vs mechanical) is latched per utterance.
|
|
15
21
|
*/
|
|
16
22
|
pushDelta(text: string): void;
|
|
17
23
|
/**
|
|
18
|
-
* Close the current input stream (call at message/turn end).
|
|
19
|
-
*
|
|
20
|
-
*
|
|
24
|
+
* Close the current input stream (call at message/turn end). Drains the
|
|
25
|
+
* trailing partial as final segments; in enhanced mode the session ends
|
|
26
|
+
* only after the last in-flight rewrite has pushed, while the next
|
|
27
|
+
* utterance may already be streaming.
|
|
21
28
|
*/
|
|
22
29
|
flush(): void;
|
|
23
30
|
/**
|
|
@@ -26,8 +33,8 @@ export declare class Vocalizer {
|
|
|
26
33
|
*/
|
|
27
34
|
speak(text: string): void;
|
|
28
35
|
/**
|
|
29
|
-
* Interrupt and drop
|
|
30
|
-
*
|
|
36
|
+
* Interrupt and drop every utterance, killing in-flight playback, synthesis,
|
|
37
|
+
* and rewrites (new turn / user message / Esc interrupt). Audio stops at once.
|
|
31
38
|
*/
|
|
32
39
|
clear(): void;
|
|
33
40
|
/** Lower the volume while the user is speaking (push-to-talk), so speech doesn't drown them out. */
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Create an abort signal that fires after a timeout and preserves caller cancellation. */
|
|
2
|
+
export declare function withTimeoutSignal(timeoutMs: number, signal?: AbortSignal): AbortSignal;
|
|
3
|
+
/** Detect a timeout raised by an abortable fetch. */
|
|
4
|
+
export declare function isTimeoutError(error: unknown): boolean;
|
|
@@ -385,6 +385,8 @@ export declare const ls: {
|
|
|
385
385
|
}): Promise<string[]>;
|
|
386
386
|
/** List untracked files (excludes ignored). */
|
|
387
387
|
untracked(cwd: string, signal?: AbortSignal): Promise<string[]>;
|
|
388
|
+
/** List paths present in a ref, optionally filtered to specific paths. */
|
|
389
|
+
tree(cwd: string, ref: string, files?: readonly string[], signal?: AbortSignal): Promise<string[]>;
|
|
388
390
|
/** List submodule paths (recursive). */
|
|
389
391
|
submodules(cwd: string, signal?: AbortSignal): Promise<string[]>;
|
|
390
392
|
};
|
|
@@ -32,6 +32,7 @@ export interface GeminiSearchParams extends GeminiToolParams {
|
|
|
32
32
|
sessionId?: string;
|
|
33
33
|
fetch?: FetchImpl;
|
|
34
34
|
antigravityEndpointMode?: "auto" | "production" | "sandbox";
|
|
35
|
+
geminiModel?: string;
|
|
35
36
|
}
|
|
36
37
|
export declare function buildGeminiRequestTools(params: GeminiToolParams): Array<Record<string, Record<string, unknown>>>;
|
|
37
38
|
/** First configured Gemini OAuth provider plus its pre-resolved access. */
|
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.3.
|
|
4
|
+
"version": "16.3.3",
|
|
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",
|
|
@@ -56,17 +56,17 @@
|
|
|
56
56
|
"@agentclientprotocol/sdk": "0.25.0",
|
|
57
57
|
"@babel/parser": "^7.29.7",
|
|
58
58
|
"@mozilla/readability": "^0.6.0",
|
|
59
|
-
"@oh-my-pi/hashline": "16.3.
|
|
60
|
-
"@oh-my-pi/omp-stats": "16.3.
|
|
61
|
-
"@oh-my-pi/pi-agent-core": "16.3.
|
|
62
|
-
"@oh-my-pi/pi-ai": "16.3.
|
|
63
|
-
"@oh-my-pi/pi-catalog": "16.3.
|
|
64
|
-
"@oh-my-pi/pi-mnemopi": "16.3.
|
|
65
|
-
"@oh-my-pi/pi-natives": "16.3.
|
|
66
|
-
"@oh-my-pi/pi-tui": "16.3.
|
|
67
|
-
"@oh-my-pi/pi-utils": "16.3.
|
|
68
|
-
"@oh-my-pi/pi-wire": "16.3.
|
|
69
|
-
"@oh-my-pi/snapcompact": "16.3.
|
|
59
|
+
"@oh-my-pi/hashline": "16.3.3",
|
|
60
|
+
"@oh-my-pi/omp-stats": "16.3.3",
|
|
61
|
+
"@oh-my-pi/pi-agent-core": "16.3.3",
|
|
62
|
+
"@oh-my-pi/pi-ai": "16.3.3",
|
|
63
|
+
"@oh-my-pi/pi-catalog": "16.3.3",
|
|
64
|
+
"@oh-my-pi/pi-mnemopi": "16.3.3",
|
|
65
|
+
"@oh-my-pi/pi-natives": "16.3.3",
|
|
66
|
+
"@oh-my-pi/pi-tui": "16.3.3",
|
|
67
|
+
"@oh-my-pi/pi-utils": "16.3.3",
|
|
68
|
+
"@oh-my-pi/pi-wire": "16.3.3",
|
|
69
|
+
"@oh-my-pi/snapcompact": "16.3.3",
|
|
70
70
|
"@opentelemetry/api": "^1.9.1",
|
|
71
71
|
"@opentelemetry/context-async-hooks": "^2.7.1",
|
|
72
72
|
"@opentelemetry/exporter-trace-otlp-proto": "^0.218.0",
|
|
@@ -332,9 +332,15 @@ async function formatInPlace(targets: readonly string[]): Promise<void> {
|
|
|
332
332
|
stdout: "pipe",
|
|
333
333
|
stderr: "pipe",
|
|
334
334
|
});
|
|
335
|
-
|
|
335
|
+
// Drain both pipes concurrently with proc.exited to avoid a pipe-buffer
|
|
336
|
+
// deadlock — biome check can emit thousands of lines when it rewrites the
|
|
337
|
+
// generated registry, easily exceeding the ~64 KiB OS pipe buffer.
|
|
338
|
+
const [exit, , stderr] = await Promise.all([
|
|
339
|
+
proc.exited,
|
|
340
|
+
new Response(proc.stdout).text(),
|
|
341
|
+
new Response(proc.stderr).text(),
|
|
342
|
+
]);
|
|
336
343
|
if (exit !== 0) {
|
|
337
|
-
const stderr = await new Response(proc.stderr).text();
|
|
338
344
|
throw new Error(`biome check --write failed (exit ${exit}): ${stderr}`);
|
|
339
345
|
}
|
|
340
346
|
}
|
|
@@ -41,7 +41,7 @@ describe("advisor", () => {
|
|
|
41
41
|
type: "toolCall",
|
|
42
42
|
id: "search-timeout",
|
|
43
43
|
name: "grep",
|
|
44
|
-
arguments: { pattern: "needle",
|
|
44
|
+
arguments: { pattern: "needle", path: "packages/coding-agent/src" },
|
|
45
45
|
},
|
|
46
46
|
],
|
|
47
47
|
timestamp: 1,
|
|
@@ -182,8 +182,8 @@ export const fsFixtures: Record<string, GalleryFixture> = {
|
|
|
182
182
|
glob: {
|
|
183
183
|
label: "Glob",
|
|
184
184
|
// Streaming: glob half-typed, no limit yet.
|
|
185
|
-
streamingArgs: {
|
|
186
|
-
args: {
|
|
185
|
+
streamingArgs: { path: "packages/coding-agent/src/tools/*-render" },
|
|
186
|
+
args: { path: "packages/coding-agent/src/**/*.test.ts", limit: 50 },
|
|
187
187
|
result: {
|
|
188
188
|
content: [
|
|
189
189
|
{
|
|
@@ -9,7 +9,7 @@ export const searchFixtures: Record<string, GalleryFixture> = {
|
|
|
9
9
|
},
|
|
10
10
|
args: {
|
|
11
11
|
pattern: "useState",
|
|
12
|
-
|
|
12
|
+
path: "packages/tui/src",
|
|
13
13
|
},
|
|
14
14
|
result: {
|
|
15
15
|
content: [
|
|
@@ -160,7 +160,7 @@ export const searchFixtures: Record<string, GalleryFixture> = {
|
|
|
160
160
|
},
|
|
161
161
|
args: {
|
|
162
162
|
pat: "useState($A)",
|
|
163
|
-
|
|
163
|
+
path: "packages/tui/src/components",
|
|
164
164
|
},
|
|
165
165
|
result: {
|
|
166
166
|
content: [
|
package/src/cli/models-cli.ts
CHANGED
|
@@ -84,6 +84,14 @@ function writeLine(line = ""): void {
|
|
|
84
84
|
process.stdout.write(`${line}\n`);
|
|
85
85
|
}
|
|
86
86
|
|
|
87
|
+
function writeModelsConfigError(error: Error): void {
|
|
88
|
+
writeLine(chalk.yellow("Warning: models.yml validation failed — custom providers disabled"));
|
|
89
|
+
for (const line of error.message.split("\n")) {
|
|
90
|
+
writeLine(` ${line}`);
|
|
91
|
+
}
|
|
92
|
+
writeLine();
|
|
93
|
+
}
|
|
94
|
+
|
|
87
95
|
function formatLimit(n: number | null): string {
|
|
88
96
|
return n === null ? "-" : formatNumber(n);
|
|
89
97
|
}
|
|
@@ -187,12 +195,23 @@ function renderProviderModels(
|
|
|
187
195
|
}
|
|
188
196
|
}
|
|
189
197
|
|
|
198
|
+
const configError = modelRegistry.getError();
|
|
199
|
+
|
|
190
200
|
if (json) {
|
|
201
|
+
if (configError) {
|
|
202
|
+
process.stderr.write(
|
|
203
|
+
`Warning: models.yml validation failed — custom providers disabled\n${configError.message}\n`,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
191
206
|
const output: ModelsJson = { models: filtered.slice().sort(byProviderThenId).map(toModelJson) };
|
|
192
207
|
writeLine(JSON.stringify(output));
|
|
193
208
|
return;
|
|
194
209
|
}
|
|
195
210
|
|
|
211
|
+
if (configError) {
|
|
212
|
+
writeModelsConfigError(configError);
|
|
213
|
+
}
|
|
214
|
+
|
|
196
215
|
if (available.length === 0) {
|
|
197
216
|
writeLine("No models available. Set API keys in environment variables.");
|
|
198
217
|
return;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { afterEach, describe, expect, it, vi } from "bun:test";
|
|
2
|
+
import { runUpdateCommand } from "./update-cli";
|
|
3
|
+
|
|
4
|
+
type FetchInput = string | URL | Request;
|
|
5
|
+
type FetchInit = RequestInit | BunFetchRequestInit;
|
|
6
|
+
|
|
7
|
+
describe("runUpdateCommand fetch cancellation", () => {
|
|
8
|
+
afterEach(() => {
|
|
9
|
+
vi.restoreAllMocks();
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it("checks release metadata with a timeout signal", async () => {
|
|
13
|
+
let requestSignal: AbortSignal | undefined;
|
|
14
|
+
vi.spyOn(console, "log").mockImplementation(() => {});
|
|
15
|
+
const fetchStub = Object.assign(
|
|
16
|
+
async (_input: FetchInput, init?: FetchInit) => {
|
|
17
|
+
requestSignal = init?.signal ?? undefined;
|
|
18
|
+
return Response.json({ version: "999.0.0" });
|
|
19
|
+
},
|
|
20
|
+
{ preconnect: globalThis.fetch.preconnect },
|
|
21
|
+
);
|
|
22
|
+
vi.spyOn(globalThis, "fetch").mockImplementation(fetchStub);
|
|
23
|
+
|
|
24
|
+
await runUpdateCommand({ force: false, check: true });
|
|
25
|
+
|
|
26
|
+
expect(requestSignal).toBeInstanceOf(AbortSignal);
|
|
27
|
+
});
|
|
28
|
+
});
|