@oh-my-pi/pi-coding-agent 14.9.9 → 15.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +123 -0
- package/examples/extensions/plan-mode.ts +0 -1
- package/package.json +9 -9
- package/scripts/build-binary.ts +5 -0
- package/scripts/format-prompts.ts +1 -1
- package/src/autoresearch/helpers.ts +17 -0
- package/src/autoresearch/tools/log-experiment.ts +9 -17
- package/src/autoresearch/tools/run-experiment.ts +2 -17
- package/src/capability/skill.ts +7 -0
- package/src/cli/args.ts +2 -2
- package/src/cli/list-models.ts +1 -1
- package/src/cli/shell-cli.ts +3 -13
- package/src/cli/update-cli.ts +1 -1
- package/src/cli.ts +11 -29
- package/src/commands/acp.ts +24 -0
- package/src/commands/launch.ts +6 -4
- package/src/commit/agentic/prompts/system.md +1 -1
- package/src/commit/agentic/tools/propose-changelog.ts +8 -1
- package/src/commit/analysis/conventional.ts +8 -66
- package/src/commit/map-reduce/reduce-phase.ts +6 -65
- package/src/commit/pipeline.ts +2 -2
- package/src/commit/shared-llm.ts +89 -0
- package/src/config/config-file.ts +210 -0
- package/src/config/model-equivalence.ts +8 -11
- package/src/config/model-registry.ts +13 -2
- package/src/config/model-resolver.ts +31 -4
- package/src/config/settings-schema.ts +102 -1
- package/src/config/settings.ts +1 -1
- package/src/config.ts +3 -219
- package/src/edit/index.ts +22 -1
- package/src/edit/modes/patch.ts +10 -0
- package/src/edit/modes/replace.ts +3 -0
- package/src/edit/renderer.ts +17 -1
- package/src/eval/js/context-manager.ts +1 -1
- package/src/eval/js/executor.ts +3 -0
- package/src/eval/js/shared/rewrite-imports.ts +122 -50
- package/src/eval/js/shared/runtime.ts +31 -4
- package/src/eval/js/tool-bridge.ts +43 -21
- package/src/eval/py/executor.ts +5 -0
- package/src/exa/factory.ts +2 -2
- package/src/exa/mcp-client.ts +74 -1
- package/src/exec/bash-executor.ts +5 -1
- package/src/export/html/template.generated.ts +1 -1
- package/src/export/html/template.js +0 -11
- package/src/extensibility/extensions/runner.ts +55 -2
- package/src/extensibility/extensions/types.ts +98 -221
- package/src/extensibility/hooks/types.ts +89 -314
- package/src/extensibility/shared-events.ts +343 -0
- package/src/extensibility/skills.ts +42 -1
- package/src/goals/index.ts +3 -0
- package/src/goals/runtime.ts +500 -0
- package/src/goals/state.ts +37 -0
- package/src/goals/tools/goal-tool.ts +237 -0
- package/src/hashline/anchors.ts +2 -2
- package/src/hindsight/mental-models.ts +1 -1
- package/src/internal-urls/agent-protocol.ts +1 -20
- package/src/internal-urls/artifact-protocol.ts +1 -19
- package/src/internal-urls/docs-index.generated.ts +9 -10
- package/src/internal-urls/index.ts +1 -0
- package/src/internal-urls/issue-pr-protocol.ts +577 -0
- package/src/internal-urls/registry-helpers.ts +25 -0
- package/src/internal-urls/router.ts +6 -3
- package/src/internal-urls/types.ts +22 -1
- package/src/main.ts +24 -11
- package/src/mcp/oauth-flow.ts +20 -0
- package/src/modes/acp/acp-agent.ts +412 -71
- package/src/modes/acp/acp-client-bridge.ts +152 -0
- package/src/modes/acp/acp-event-mapper.ts +180 -15
- package/src/modes/acp/terminal-auth.ts +37 -0
- package/src/modes/components/assistant-message.ts +14 -8
- package/src/modes/components/bash-execution.ts +24 -63
- package/src/modes/components/custom-message.ts +14 -40
- package/src/modes/components/eval-execution.ts +27 -57
- package/src/modes/components/execution-shared.ts +102 -0
- package/src/modes/components/hook-message.ts +17 -49
- package/src/modes/components/mcp-add-wizard.ts +26 -5
- package/src/modes/components/message-frame.ts +88 -0
- package/src/modes/components/model-selector.ts +1 -1
- package/src/modes/components/read-tool-group.ts +29 -1
- package/src/modes/components/session-observer-overlay.ts +6 -2
- package/src/modes/components/session-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +55 -4
- package/src/modes/components/status-line/types.ts +4 -0
- package/src/modes/components/status-line.ts +28 -10
- package/src/modes/components/tool-execution.ts +7 -8
- package/src/modes/controllers/command-controller-shared.ts +108 -0
- package/src/modes/controllers/command-controller.ts +27 -10
- package/src/modes/controllers/event-controller.ts +60 -18
- package/src/modes/controllers/extension-ui-controller.ts +8 -2
- package/src/modes/controllers/input-controller.ts +85 -39
- package/src/modes/controllers/mcp-command-controller.ts +56 -61
- package/src/modes/controllers/ssh-command-controller.ts +18 -57
- package/src/modes/interactive-mode.ts +675 -39
- package/src/modes/print-mode.ts +16 -86
- package/src/modes/rpc/rpc-mode.ts +30 -88
- package/src/modes/runtime-init.ts +115 -0
- package/src/modes/theme/defaults/dark-poimandres.json +2 -0
- package/src/modes/theme/defaults/light-poimandres.json +2 -0
- package/src/modes/theme/theme.ts +18 -6
- package/src/modes/types.ts +20 -5
- package/src/modes/utils/context-usage.ts +13 -13
- package/src/modes/utils/ui-helpers.ts +25 -6
- package/src/plan-mode/approved-plan.ts +35 -1
- package/src/prompts/agents/designer.md +5 -5
- package/src/prompts/agents/explore.md +7 -7
- package/src/prompts/agents/init.md +9 -9
- package/src/prompts/agents/librarian.md +14 -14
- package/src/prompts/agents/plan.md +4 -4
- package/src/prompts/agents/reviewer.md +5 -5
- package/src/prompts/agents/task.md +10 -10
- package/src/prompts/commands/orchestrate.md +2 -2
- package/src/prompts/compaction/branch-summary.md +3 -3
- package/src/prompts/compaction/compaction-short-summary.md +7 -7
- package/src/prompts/compaction/compaction-summary-context.md +1 -1
- package/src/prompts/compaction/compaction-summary.md +5 -5
- package/src/prompts/compaction/compaction-turn-prefix.md +3 -3
- package/src/prompts/compaction/compaction-update-summary.md +11 -11
- package/src/prompts/goals/goal-budget-limit.md +16 -0
- package/src/prompts/goals/goal-continuation.md +28 -0
- package/src/prompts/goals/goal-mode-active.md +23 -0
- package/src/prompts/memories/consolidation.md +2 -2
- package/src/prompts/memories/read-path.md +1 -1
- package/src/prompts/memories/stage_one_input.md +1 -1
- package/src/prompts/memories/stage_one_system.md +5 -5
- package/src/prompts/review-request.md +4 -4
- package/src/prompts/system/agent-creation-architect.md +17 -17
- package/src/prompts/system/agent-creation-user.md +2 -2
- package/src/prompts/system/commit-message-system.md +2 -2
- package/src/prompts/system/custom-system-prompt.md +2 -2
- package/src/prompts/system/eager-todo.md +6 -6
- package/src/prompts/system/handoff-document.md +1 -1
- package/src/prompts/system/plan-mode-active.md +25 -24
- package/src/prompts/system/plan-mode-approved.md +4 -4
- package/src/prompts/system/plan-mode-compact-instructions.md +16 -0
- package/src/prompts/system/plan-mode-reference.md +2 -2
- package/src/prompts/system/plan-mode-subagent.md +8 -8
- package/src/prompts/system/plan-mode-tool-decision-reminder.md +3 -3
- package/src/prompts/system/project-prompt.md +4 -4
- package/src/prompts/system/subagent-system-prompt.md +7 -7
- package/src/prompts/system/subagent-yield-reminder.md +4 -4
- package/src/prompts/system/system-prompt.md +72 -71
- package/src/prompts/system/ttsr-interrupt.md +1 -1
- package/src/prompts/tools/apply-patch.md +1 -1
- package/src/prompts/tools/ast-edit.md +3 -3
- package/src/prompts/tools/ast-grep.md +3 -3
- package/src/prompts/tools/bash.md +6 -0
- package/src/prompts/tools/browser.md +3 -3
- package/src/prompts/tools/checkpoint.md +3 -3
- package/src/prompts/tools/find.md +3 -3
- package/src/prompts/tools/github.md +2 -5
- package/src/prompts/tools/goal.md +13 -0
- package/src/prompts/tools/hashline.md +104 -116
- package/src/prompts/tools/image-gen.md +3 -3
- package/src/prompts/tools/irc.md +1 -1
- package/src/prompts/tools/lsp.md +2 -2
- package/src/prompts/tools/patch.md +6 -6
- package/src/prompts/tools/read.md +8 -7
- package/src/prompts/tools/replace.md +5 -5
- package/src/prompts/tools/resolve.md +6 -5
- package/src/prompts/tools/retain.md +1 -1
- package/src/prompts/tools/rewind.md +2 -2
- package/src/prompts/tools/search.md +2 -2
- package/src/prompts/tools/ssh.md +2 -2
- package/src/prompts/tools/task.md +12 -6
- package/src/prompts/tools/web-search.md +2 -2
- package/src/prompts/tools/write.md +3 -3
- package/src/sdk.ts +81 -17
- package/src/session/agent-session.ts +656 -125
- package/src/session/blob-store.ts +36 -3
- package/src/session/client-bridge.ts +81 -0
- package/src/session/compaction/errors.ts +31 -0
- package/src/session/compaction/index.ts +1 -0
- package/src/session/messages.ts +67 -2
- package/src/session/session-manager.ts +131 -12
- package/src/session/session-storage.ts +33 -15
- package/src/session/streaming-output.ts +309 -13
- package/src/slash-commands/acp-builtins.ts +46 -0
- package/src/slash-commands/builtin-registry.ts +717 -116
- package/src/slash-commands/helpers/context-report.ts +39 -0
- package/src/slash-commands/helpers/format.ts +23 -0
- package/src/slash-commands/helpers/marketplace-manager.ts +25 -0
- package/src/slash-commands/helpers/mcp.ts +532 -0
- package/src/slash-commands/helpers/parse.ts +85 -0
- package/src/slash-commands/helpers/ssh.ts +193 -0
- package/src/slash-commands/helpers/todo.ts +279 -0
- package/src/slash-commands/helpers/usage-report.ts +91 -0
- package/src/slash-commands/types.ts +126 -0
- package/src/ssh/ssh-executor.ts +5 -0
- package/src/system-prompt.ts +4 -2
- package/src/task/executor.ts +27 -10
- package/src/task/index.ts +20 -1
- package/src/task/render.ts +27 -18
- package/src/task/types.ts +4 -0
- package/src/tools/ast-edit.ts +21 -120
- package/src/tools/ast-grep.ts +21 -119
- package/src/tools/bash-interactive.ts +9 -1
- package/src/tools/bash.ts +203 -6
- package/src/tools/browser/attach.ts +3 -3
- package/src/tools/browser/launch.ts +81 -18
- package/src/tools/browser/registry.ts +1 -5
- package/src/tools/browser/tab-supervisor.ts +51 -14
- package/src/tools/conflict-detect.ts +21 -10
- package/src/tools/eval.ts +3 -1
- package/src/tools/fetch.ts +15 -4
- package/src/tools/find.ts +39 -39
- package/src/tools/gh-renderer.ts +0 -12
- package/src/tools/gh.ts +689 -182
- package/src/tools/github-cache.ts +548 -0
- package/src/tools/index.ts +25 -11
- package/src/tools/inspect-image.ts +3 -10
- package/src/tools/output-meta.ts +176 -37
- package/src/tools/path-utils.ts +125 -2
- package/src/tools/read.ts +605 -239
- package/src/tools/render-utils.ts +92 -0
- package/src/tools/renderers.ts +2 -0
- package/src/tools/resolve.ts +72 -44
- package/src/tools/search.ts +120 -186
- package/src/tools/write.ts +67 -10
- package/src/tui/code-cell.ts +70 -2
- package/src/utils/file-mentions.ts +1 -1
- package/src/utils/image-loading.ts +7 -3
- package/src/utils/image-resize.ts +32 -43
- package/src/vim/parser.ts +0 -17
- package/src/vim/render.ts +1 -1
- package/src/vim/types.ts +1 -1
- package/src/web/search/providers/gemini.ts +35 -95
- package/src/prompts/tools/exit-plan-mode.md +0 -6
- package/src/tools/exit-plan-mode.ts +0 -97
- package/src/utils/fuzzy.ts +0 -108
- package/src/utils/image-convert.ts +0 -27
|
@@ -1,52 +1,16 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import type { Api,
|
|
3
|
-
import { completeSimple
|
|
2
|
+
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import { Type } from "@sinclair/typebox";
|
|
6
5
|
import analysisSystemPrompt from "../../commit/prompts/analysis-system.md" with { type: "text" };
|
|
7
6
|
import analysisUserPrompt from "../../commit/prompts/analysis-user.md" with { type: "text" };
|
|
8
|
-
import type {
|
|
7
|
+
import type { ConventionalAnalysis } from "../../commit/types";
|
|
9
8
|
import { toReasoningEffort } from "../../thinking";
|
|
10
|
-
import {
|
|
9
|
+
import { createConventionalAnalysisTool, parseConventionalAnalysisResponse } from "../shared-llm";
|
|
11
10
|
|
|
12
|
-
const ConventionalAnalysisTool =
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
parameters: Type.Object({
|
|
16
|
-
type: Type.Union([
|
|
17
|
-
Type.Literal("feat"),
|
|
18
|
-
Type.Literal("fix"),
|
|
19
|
-
Type.Literal("refactor"),
|
|
20
|
-
Type.Literal("docs"),
|
|
21
|
-
Type.Literal("test"),
|
|
22
|
-
Type.Literal("chore"),
|
|
23
|
-
Type.Literal("style"),
|
|
24
|
-
Type.Literal("perf"),
|
|
25
|
-
Type.Literal("build"),
|
|
26
|
-
Type.Literal("ci"),
|
|
27
|
-
Type.Literal("revert"),
|
|
28
|
-
]),
|
|
29
|
-
scope: Type.Union([Type.String(), Type.Null()]),
|
|
30
|
-
details: Type.Array(
|
|
31
|
-
Type.Object({
|
|
32
|
-
text: Type.String(),
|
|
33
|
-
changelog_category: Type.Optional(
|
|
34
|
-
Type.Union([
|
|
35
|
-
Type.Literal("Added"),
|
|
36
|
-
Type.Literal("Changed"),
|
|
37
|
-
Type.Literal("Fixed"),
|
|
38
|
-
Type.Literal("Deprecated"),
|
|
39
|
-
Type.Literal("Removed"),
|
|
40
|
-
Type.Literal("Security"),
|
|
41
|
-
Type.Literal("Breaking Changes"),
|
|
42
|
-
]),
|
|
43
|
-
),
|
|
44
|
-
user_visible: Type.Optional(Type.Boolean()),
|
|
45
|
-
}),
|
|
46
|
-
),
|
|
47
|
-
issue_refs: Type.Array(Type.String()),
|
|
48
|
-
}),
|
|
49
|
-
};
|
|
11
|
+
const ConventionalAnalysisTool = createConventionalAnalysisTool(
|
|
12
|
+
"Analyze a diff and return conventional commit classification.",
|
|
13
|
+
);
|
|
50
14
|
|
|
51
15
|
export interface ConventionalAnalysisInput {
|
|
52
16
|
model: Model<Api>;
|
|
@@ -96,27 +60,5 @@ export async function generateConventionalAnalysis({
|
|
|
96
60
|
{ apiKey, maxTokens: 2400, reasoning: toReasoningEffort(thinkingLevel) },
|
|
97
61
|
);
|
|
98
62
|
|
|
99
|
-
return
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
function parseAnalysisFromResponse(message: AssistantMessage): ConventionalAnalysis {
|
|
103
|
-
const toolCall = extractToolCall(message, "create_conventional_analysis");
|
|
104
|
-
if (toolCall) {
|
|
105
|
-
const parsed = validateToolCall([ConventionalAnalysisTool], toolCall) as {
|
|
106
|
-
type: ConventionalAnalysis["type"];
|
|
107
|
-
scope: string | null;
|
|
108
|
-
details: Array<{ text: string; changelog_category?: ChangelogCategory; user_visible?: boolean }>;
|
|
109
|
-
issue_refs: string[];
|
|
110
|
-
};
|
|
111
|
-
return normalizeAnalysis(parsed);
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const text = extractTextContent(message);
|
|
115
|
-
const parsed = parseJsonPayload(text) as {
|
|
116
|
-
type: ConventionalAnalysis["type"];
|
|
117
|
-
scope: string | null;
|
|
118
|
-
details: Array<{ text: string; changelog_category?: ChangelogCategory; user_visible?: boolean }>;
|
|
119
|
-
issue_refs: string[];
|
|
120
|
-
};
|
|
121
|
-
return normalizeAnalysis(parsed);
|
|
63
|
+
return parseConventionalAnalysisResponse(response, ConventionalAnalysisTool);
|
|
122
64
|
}
|
|
@@ -1,52 +1,14 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import type { Api,
|
|
3
|
-
import { completeSimple
|
|
2
|
+
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
|
-
import { Type } from "@sinclair/typebox";
|
|
6
5
|
import reduceSystemPrompt from "../../commit/prompts/reduce-system.md" with { type: "text" };
|
|
7
6
|
import reduceUserPrompt from "../../commit/prompts/reduce-user.md" with { type: "text" };
|
|
8
|
-
import type {
|
|
7
|
+
import type { ConventionalAnalysis, FileObservation } from "../../commit/types";
|
|
9
8
|
import { toReasoningEffort } from "../../thinking";
|
|
10
|
-
import {
|
|
9
|
+
import { createConventionalAnalysisTool, parseConventionalAnalysisResponse } from "../shared-llm";
|
|
11
10
|
|
|
12
|
-
const ReduceTool =
|
|
13
|
-
name: "create_conventional_analysis",
|
|
14
|
-
description: "Synthesize file observations into a conventional commit analysis.",
|
|
15
|
-
parameters: Type.Object({
|
|
16
|
-
type: Type.Union([
|
|
17
|
-
Type.Literal("feat"),
|
|
18
|
-
Type.Literal("fix"),
|
|
19
|
-
Type.Literal("refactor"),
|
|
20
|
-
Type.Literal("docs"),
|
|
21
|
-
Type.Literal("test"),
|
|
22
|
-
Type.Literal("chore"),
|
|
23
|
-
Type.Literal("style"),
|
|
24
|
-
Type.Literal("perf"),
|
|
25
|
-
Type.Literal("build"),
|
|
26
|
-
Type.Literal("ci"),
|
|
27
|
-
Type.Literal("revert"),
|
|
28
|
-
]),
|
|
29
|
-
scope: Type.Union([Type.String(), Type.Null()]),
|
|
30
|
-
details: Type.Array(
|
|
31
|
-
Type.Object({
|
|
32
|
-
text: Type.String(),
|
|
33
|
-
changelog_category: Type.Optional(
|
|
34
|
-
Type.Union([
|
|
35
|
-
Type.Literal("Added"),
|
|
36
|
-
Type.Literal("Changed"),
|
|
37
|
-
Type.Literal("Fixed"),
|
|
38
|
-
Type.Literal("Deprecated"),
|
|
39
|
-
Type.Literal("Removed"),
|
|
40
|
-
Type.Literal("Security"),
|
|
41
|
-
Type.Literal("Breaking Changes"),
|
|
42
|
-
]),
|
|
43
|
-
),
|
|
44
|
-
user_visible: Type.Optional(Type.Boolean()),
|
|
45
|
-
}),
|
|
46
|
-
),
|
|
47
|
-
issue_refs: Type.Array(Type.String()),
|
|
48
|
-
}),
|
|
49
|
-
};
|
|
11
|
+
const ReduceTool = createConventionalAnalysisTool("Synthesize file observations into a conventional commit analysis.");
|
|
50
12
|
|
|
51
13
|
export interface ReducePhaseInput {
|
|
52
14
|
model: Model<Api>;
|
|
@@ -83,26 +45,5 @@ export async function runReducePhase({
|
|
|
83
45
|
{ apiKey, maxTokens: 2400, reasoning: toReasoningEffort(thinkingLevel) },
|
|
84
46
|
);
|
|
85
47
|
|
|
86
|
-
return
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
function parseAnalysisResponse(message: AssistantMessage): ConventionalAnalysis {
|
|
90
|
-
const toolCall = extractToolCall(message, "create_conventional_analysis");
|
|
91
|
-
if (toolCall) {
|
|
92
|
-
const parsed = validateToolCall([ReduceTool], toolCall) as {
|
|
93
|
-
type: ConventionalAnalysis["type"];
|
|
94
|
-
scope: string | null;
|
|
95
|
-
details: Array<{ text: string; changelog_category?: ChangelogCategory; user_visible?: boolean }>;
|
|
96
|
-
issue_refs: string[];
|
|
97
|
-
};
|
|
98
|
-
return normalizeAnalysis(parsed);
|
|
99
|
-
}
|
|
100
|
-
const text = extractTextContent(message);
|
|
101
|
-
const parsed = parseJsonPayload(text) as {
|
|
102
|
-
type: ConventionalAnalysis["type"];
|
|
103
|
-
scope: string | null;
|
|
104
|
-
details: Array<{ text: string; changelog_category?: ChangelogCategory; user_visible?: boolean }>;
|
|
105
|
-
issue_refs: string[];
|
|
106
|
-
};
|
|
107
|
-
return normalizeAnalysis(parsed);
|
|
48
|
+
return parseConventionalAnalysisResponse(response, ReduceTool);
|
|
108
49
|
}
|
package/src/commit/pipeline.ts
CHANGED
|
@@ -25,8 +25,8 @@ import type { CommitCommandArgs, ConventionalAnalysis } from "./types";
|
|
|
25
25
|
|
|
26
26
|
const SUMMARY_MAX_CHARS = 72;
|
|
27
27
|
const RECENT_COMMITS_COUNT = 8;
|
|
28
|
-
let
|
|
29
|
-
const TYPES_DESCRIPTION = (): string => (
|
|
28
|
+
let typesDescription: string | undefined;
|
|
29
|
+
const TYPES_DESCRIPTION = (): string => (typesDescription ??= prompt.render(typesDescriptionPrompt));
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Execute the omp commit pipeline for staged changes.
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import type { AssistantMessage } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import { validateToolCall } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import { Type } from "@sinclair/typebox";
|
|
4
|
+
import type { ChangelogCategory, ConventionalAnalysis } from "./types";
|
|
5
|
+
import { extractTextContent, extractToolCall, normalizeAnalysis, parseJsonPayload } from "./utils";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Shared TypeBox schema for the `create_conventional_analysis` tool used by
|
|
9
|
+
* both the single-pass analysis call and the map-reduce reduce phase. Schemas
|
|
10
|
+
* are identical across phases — only the surrounding tool `description`
|
|
11
|
+
* differs to reflect the input the phase is summarizing.
|
|
12
|
+
*/
|
|
13
|
+
export const conventionalAnalysisParameters = Type.Object({
|
|
14
|
+
type: Type.Union([
|
|
15
|
+
Type.Literal("feat"),
|
|
16
|
+
Type.Literal("fix"),
|
|
17
|
+
Type.Literal("refactor"),
|
|
18
|
+
Type.Literal("docs"),
|
|
19
|
+
Type.Literal("test"),
|
|
20
|
+
Type.Literal("chore"),
|
|
21
|
+
Type.Literal("style"),
|
|
22
|
+
Type.Literal("perf"),
|
|
23
|
+
Type.Literal("build"),
|
|
24
|
+
Type.Literal("ci"),
|
|
25
|
+
Type.Literal("revert"),
|
|
26
|
+
]),
|
|
27
|
+
scope: Type.Union([Type.String(), Type.Null()]),
|
|
28
|
+
details: Type.Array(
|
|
29
|
+
Type.Object({
|
|
30
|
+
text: Type.String(),
|
|
31
|
+
changelog_category: Type.Optional(
|
|
32
|
+
Type.Union([
|
|
33
|
+
Type.Literal("Added"),
|
|
34
|
+
Type.Literal("Changed"),
|
|
35
|
+
Type.Literal("Fixed"),
|
|
36
|
+
Type.Literal("Deprecated"),
|
|
37
|
+
Type.Literal("Removed"),
|
|
38
|
+
Type.Literal("Security"),
|
|
39
|
+
Type.Literal("Breaking Changes"),
|
|
40
|
+
]),
|
|
41
|
+
),
|
|
42
|
+
user_visible: Type.Optional(Type.Boolean()),
|
|
43
|
+
}),
|
|
44
|
+
),
|
|
45
|
+
issue_refs: Type.Array(Type.String()),
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
export interface ConventionalAnalysisTool {
|
|
49
|
+
name: "create_conventional_analysis";
|
|
50
|
+
description: string;
|
|
51
|
+
parameters: typeof conventionalAnalysisParameters;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Build a `create_conventional_analysis` tool descriptor. Phase-specific
|
|
56
|
+
* `description` text is the only thing that varies between callers.
|
|
57
|
+
*/
|
|
58
|
+
export function createConventionalAnalysisTool(description: string): ConventionalAnalysisTool {
|
|
59
|
+
return {
|
|
60
|
+
name: "create_conventional_analysis",
|
|
61
|
+
description,
|
|
62
|
+
parameters: conventionalAnalysisParameters,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
interface ParsedConventionalAnalysis {
|
|
67
|
+
type: ConventionalAnalysis["type"];
|
|
68
|
+
scope: string | null;
|
|
69
|
+
details: Array<{ text: string; changelog_category?: ChangelogCategory; user_visible?: boolean }>;
|
|
70
|
+
issue_refs: string[];
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Extract a {@link ConventionalAnalysis} from an assistant response, preferring
|
|
75
|
+
* a structured tool call and falling back to JSON embedded in text content.
|
|
76
|
+
*/
|
|
77
|
+
export function parseConventionalAnalysisResponse(
|
|
78
|
+
message: AssistantMessage,
|
|
79
|
+
tool: ConventionalAnalysisTool,
|
|
80
|
+
): ConventionalAnalysis {
|
|
81
|
+
const toolCall = extractToolCall(message, tool.name);
|
|
82
|
+
if (toolCall) {
|
|
83
|
+
const parsed = validateToolCall([tool], toolCall) as ParsedConventionalAnalysis;
|
|
84
|
+
return normalizeAnalysis(parsed);
|
|
85
|
+
}
|
|
86
|
+
const text = extractTextContent(message);
|
|
87
|
+
const parsed = parseJsonPayload(text) as ParsedConventionalAnalysis;
|
|
88
|
+
return normalizeAnalysis(parsed);
|
|
89
|
+
}
|
|
@@ -0,0 +1,210 @@
|
|
|
1
|
+
import * as fs from "node:fs";
|
|
2
|
+
import * as path from "node:path";
|
|
3
|
+
import { getAgentDir, isEnoent, logger } from "@oh-my-pi/pi-utils";
|
|
4
|
+
import type { TSchema } from "@sinclair/typebox";
|
|
5
|
+
import { Value } from "@sinclair/typebox/value";
|
|
6
|
+
import type { ErrorObject } from "ajv";
|
|
7
|
+
import { JSONC, YAML } from "bun";
|
|
8
|
+
|
|
9
|
+
function migrateJsonToYml(jsonPath: string, ymlPath: string) {
|
|
10
|
+
try {
|
|
11
|
+
if (fs.existsSync(ymlPath)) return;
|
|
12
|
+
if (!fs.existsSync(jsonPath)) return;
|
|
13
|
+
|
|
14
|
+
const content = fs.readFileSync(jsonPath, "utf-8");
|
|
15
|
+
const parsed = JSON.parse(content);
|
|
16
|
+
if (!parsed) {
|
|
17
|
+
logger.warn("migrateJsonToYml: invalid json structure", { path: jsonPath });
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
fs.writeFileSync(ymlPath, YAML.stringify(parsed, null, 2));
|
|
21
|
+
} catch (error) {
|
|
22
|
+
logger.warn("migrateJsonToYml: migration failed", { error: String(error) });
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IConfigFile<T> {
|
|
27
|
+
readonly id: string;
|
|
28
|
+
readonly schema: TSchema;
|
|
29
|
+
path?(): string;
|
|
30
|
+
load(): T | null;
|
|
31
|
+
invalidate?(): void;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export class ConfigError extends Error {
|
|
35
|
+
readonly #message: string;
|
|
36
|
+
constructor(
|
|
37
|
+
public readonly id: string,
|
|
38
|
+
public readonly schemaErrors: ErrorObject[] | null | undefined,
|
|
39
|
+
public readonly other?: { err: unknown; stage: string },
|
|
40
|
+
) {
|
|
41
|
+
let messages: string[] | undefined;
|
|
42
|
+
let cause: Error | undefined;
|
|
43
|
+
let klass: string;
|
|
44
|
+
|
|
45
|
+
if (schemaErrors) {
|
|
46
|
+
klass = "Schema";
|
|
47
|
+
messages = schemaErrors.map(e => `${e.instancePath || "root"}: ${e.message}`);
|
|
48
|
+
} else if (other) {
|
|
49
|
+
klass = other.stage;
|
|
50
|
+
if (other.err instanceof Error) {
|
|
51
|
+
messages = [other.err.message];
|
|
52
|
+
cause = other.err;
|
|
53
|
+
} else {
|
|
54
|
+
messages = [String(other.err)];
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
klass = "Unknown";
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const title = `Failed to load config file ${id}, ${klass} error:`;
|
|
61
|
+
let message: string;
|
|
62
|
+
switch (messages?.length ?? 0) {
|
|
63
|
+
case 0:
|
|
64
|
+
message = title.slice(0, -1);
|
|
65
|
+
break;
|
|
66
|
+
case 1:
|
|
67
|
+
message = `${title} ${messages![0]}`;
|
|
68
|
+
break;
|
|
69
|
+
default:
|
|
70
|
+
message = `${title}\n${messages!.map(m => ` - ${m}`).join("\n")}`;
|
|
71
|
+
break;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
super(message, { cause });
|
|
75
|
+
this.name = "LoadError";
|
|
76
|
+
this.#message = message;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
get message(): string {
|
|
80
|
+
return this.#message;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
toString(): string {
|
|
84
|
+
return this.message;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export type LoadStatus = "ok" | "error" | "not-found";
|
|
89
|
+
|
|
90
|
+
export type LoadResult<T> =
|
|
91
|
+
| { value?: null; error: ConfigError; status: "error" }
|
|
92
|
+
| { value: T; error?: undefined; status: "ok" }
|
|
93
|
+
| { value?: null; error?: unknown; status: "not-found" };
|
|
94
|
+
|
|
95
|
+
export class ConfigFile<T> implements IConfigFile<T> {
|
|
96
|
+
readonly #basePath: string;
|
|
97
|
+
#cache?: LoadResult<T>;
|
|
98
|
+
#auxValidate?: (value: T) => void;
|
|
99
|
+
|
|
100
|
+
constructor(
|
|
101
|
+
readonly id: string,
|
|
102
|
+
readonly schema: TSchema,
|
|
103
|
+
configPath: string = path.join(getAgentDir(), `${id}.yml`),
|
|
104
|
+
) {
|
|
105
|
+
this.#basePath = configPath;
|
|
106
|
+
if (configPath.endsWith(".yml")) {
|
|
107
|
+
const jsonPath = `${configPath.slice(0, -4)}.json`;
|
|
108
|
+
migrateJsonToYml(jsonPath, configPath);
|
|
109
|
+
} else if (configPath.endsWith(".yaml")) {
|
|
110
|
+
const jsonPath = `${configPath.slice(0, -5)}.json`;
|
|
111
|
+
migrateJsonToYml(jsonPath, configPath);
|
|
112
|
+
} else if (configPath.endsWith(".json") || configPath.endsWith(".jsonc")) {
|
|
113
|
+
// JSON configs are still supported without migration.
|
|
114
|
+
} else {
|
|
115
|
+
throw new Error(`Invalid config file path: ${configPath}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
relocate(configPath?: string): ConfigFile<T> {
|
|
120
|
+
if (!configPath || configPath === this.#basePath) return this;
|
|
121
|
+
const result = new ConfigFile<T>(this.id, this.schema, configPath);
|
|
122
|
+
result.#auxValidate = this.#auxValidate;
|
|
123
|
+
return result;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
getMtimeMs(): number | null {
|
|
127
|
+
try {
|
|
128
|
+
return fs.statSync(this.path()).mtimeMs;
|
|
129
|
+
} catch (err) {
|
|
130
|
+
if (isEnoent(err)) return null;
|
|
131
|
+
throw err;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
withValidation(name: string, validate: (value: T) => void): this {
|
|
136
|
+
const prev = this.#auxValidate;
|
|
137
|
+
this.#auxValidate = (value: T) => {
|
|
138
|
+
prev?.(value);
|
|
139
|
+
try {
|
|
140
|
+
validate(value);
|
|
141
|
+
} catch (error) {
|
|
142
|
+
throw new ConfigError(this.id, undefined, { err: error, stage: `Validate(${name})` });
|
|
143
|
+
}
|
|
144
|
+
};
|
|
145
|
+
return this;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
createDefault(): T {
|
|
149
|
+
return Value.Default(this.schema, [], undefined) as T;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
#storeCache(result: LoadResult<T>): LoadResult<T> {
|
|
153
|
+
this.#cache = result;
|
|
154
|
+
return result;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
tryLoad(): LoadResult<T> {
|
|
158
|
+
if (this.#cache) return this.#cache;
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
const content = fs.readFileSync(this.path(), "utf-8").trim();
|
|
162
|
+
|
|
163
|
+
let parsed: unknown;
|
|
164
|
+
if (this.#basePath.endsWith(".json") || this.#basePath.endsWith(".jsonc")) {
|
|
165
|
+
parsed = JSONC.parse(content);
|
|
166
|
+
} else if (this.#basePath.endsWith(".yml") || this.#basePath.endsWith(".yaml")) {
|
|
167
|
+
parsed = YAML.parse(content);
|
|
168
|
+
} else {
|
|
169
|
+
throw new Error(`Invalid config file path: ${this.#basePath}`);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
if (!Value.Check(this.schema, parsed)) {
|
|
173
|
+
const schemaErrors: ErrorObject[] = [];
|
|
174
|
+
for (const err of Value.Errors(this.schema, parsed)) {
|
|
175
|
+
schemaErrors.push({ instancePath: err.path, message: err.message } as ErrorObject);
|
|
176
|
+
if (schemaErrors.length >= 50) break;
|
|
177
|
+
}
|
|
178
|
+
const error = new ConfigError(this.id, schemaErrors);
|
|
179
|
+
logger.warn("Failed to parse config file", { path: this.path(), error });
|
|
180
|
+
return this.#storeCache({ error, status: "error" });
|
|
181
|
+
}
|
|
182
|
+
return this.#storeCache({ value: parsed as T, status: "ok" });
|
|
183
|
+
} catch (error) {
|
|
184
|
+
if (isEnoent(error)) {
|
|
185
|
+
return this.#storeCache({ status: "not-found" });
|
|
186
|
+
}
|
|
187
|
+
logger.warn("Failed to parse config file", { path: this.path(), error });
|
|
188
|
+
return this.#storeCache({
|
|
189
|
+
error: new ConfigError(this.id, undefined, { err: error, stage: "Unexpected" }),
|
|
190
|
+
status: "error",
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
load(): T | null {
|
|
196
|
+
return this.tryLoad().value ?? null;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
loadOrDefault(): T {
|
|
200
|
+
return this.tryLoad().value ?? this.createDefault();
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
path(): string {
|
|
204
|
+
return this.#basePath;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
invalidate() {
|
|
208
|
+
this.#cache = undefined;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
@@ -72,15 +72,12 @@ const TRAILING_MARKER_SUFFIXES: readonly string[] = (() => {
|
|
|
72
72
|
})();
|
|
73
73
|
const WRAPPER_PREFIXES = ["duo-chat-"] as const;
|
|
74
74
|
|
|
75
|
-
let
|
|
75
|
+
let referenceDataCache: CanonicalReferenceData | undefined;
|
|
76
76
|
const EMPTY_COMPILED_EQUIVALENCE: CompiledEquivalenceConfig = {
|
|
77
77
|
overrides: new Map<string, string>(),
|
|
78
78
|
exclude: new Set<string>(),
|
|
79
79
|
};
|
|
80
|
-
const
|
|
81
|
-
CompiledEquivalenceConfig,
|
|
82
|
-
WeakMap<Model<Api>, ResolvedCanonicalModel>
|
|
83
|
-
> = new WeakMap();
|
|
80
|
+
const resolutionCache: WeakMap<CompiledEquivalenceConfig, WeakMap<Model<Api>, ResolvedCanonicalModel>> = new WeakMap();
|
|
84
81
|
const FAMILY_EXTRACTION_PATTERNS = [
|
|
85
82
|
/(?:^|[/:._-])((?:claude|gemini|gpt|grok|glm|qwen|minimax|kimi|deepseek|llama|gemma|nova|mistral|ministral|pixtral|codestral|devstral|magistral|ernie|doubao|seed|aion|olmo|molmo|nemotron|palmyra|command|codex|coder|o[1345])[-a-z0-9.]+)(?::|$)/i,
|
|
86
83
|
/(?:^|[/:._-])((?:claude|gemini|gpt|grok|glm|qwen|minimax|kimi|deepseek|llama|gemma|nova|mistral|ministral|pixtral|codestral|devstral|magistral|ernie|doubao|seed|aion|olmo|molmo|nemotron|palmyra|command|codex|coder|o[1345])[-a-z0-9.]+(?:[-_/][a-z0-9.]+)*)(?::|$)/i,
|
|
@@ -98,8 +95,8 @@ function shouldReplaceReference(existing: Model<Api> | undefined, candidate: Mod
|
|
|
98
95
|
}
|
|
99
96
|
|
|
100
97
|
function createCanonicalReferenceData(): CanonicalReferenceData {
|
|
101
|
-
if (
|
|
102
|
-
return
|
|
98
|
+
if (referenceDataCache) {
|
|
99
|
+
return referenceDataCache;
|
|
103
100
|
}
|
|
104
101
|
const references = new Map<string, Model<Api>>();
|
|
105
102
|
for (const provider of getBundledProviders()) {
|
|
@@ -112,11 +109,11 @@ function createCanonicalReferenceData(): CanonicalReferenceData {
|
|
|
112
109
|
}
|
|
113
110
|
}
|
|
114
111
|
const officialIds = new Set(references.keys());
|
|
115
|
-
|
|
112
|
+
referenceDataCache = {
|
|
116
113
|
references: Object.freeze(references) as Map<string, Model<Api>>,
|
|
117
114
|
officialIds: Object.freeze(officialIds) as Set<string>,
|
|
118
115
|
};
|
|
119
|
-
return
|
|
116
|
+
return referenceDataCache;
|
|
120
117
|
}
|
|
121
118
|
|
|
122
119
|
function normalizeSelectorKey(selector: string): string {
|
|
@@ -668,10 +665,10 @@ export function buildCanonicalModelIndex(
|
|
|
668
665
|
const byId = new Map<string, CanonicalModelRecord>();
|
|
669
666
|
const bySelector = new Map<string, string>();
|
|
670
667
|
|
|
671
|
-
let modelCache =
|
|
668
|
+
let modelCache = resolutionCache.get(compiledEquivalence);
|
|
672
669
|
if (!modelCache) {
|
|
673
670
|
modelCache = new WeakMap<Model<Api>, ResolvedCanonicalModel>();
|
|
674
|
-
|
|
671
|
+
resolutionCache.set(compiledEquivalence, modelCache);
|
|
675
672
|
}
|
|
676
673
|
|
|
677
674
|
for (const model of models) {
|
|
@@ -18,6 +18,8 @@ import {
|
|
|
18
18
|
registerCustomApi,
|
|
19
19
|
type SimpleStreamOptions,
|
|
20
20
|
type ThinkingConfig,
|
|
21
|
+
UNK_CONTEXT_WINDOW,
|
|
22
|
+
UNK_MAX_TOKENS,
|
|
21
23
|
unregisterCustomApis,
|
|
22
24
|
} from "@oh-my-pi/pi-ai";
|
|
23
25
|
|
|
@@ -29,10 +31,10 @@ import { registerOAuthProvider, unregisterOAuthProviders } from "@oh-my-pi/pi-ai
|
|
|
29
31
|
import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/utils/oauth/types";
|
|
30
32
|
import { isRecord, logger } from "@oh-my-pi/pi-utils";
|
|
31
33
|
import { type Static, Type } from "@sinclair/typebox";
|
|
32
|
-
import { type ConfigError, ConfigFile } from "../config";
|
|
33
34
|
import { parseModelString, resolveProviderModelReference } from "../config/model-resolver";
|
|
34
35
|
import { isValidThemeColor, type ThemeColor } from "../modes/theme/theme";
|
|
35
36
|
import type { AuthStorage, OAuthCredential } from "../session/auth-storage";
|
|
37
|
+
import { type ConfigError, ConfigFile } from "./config-file";
|
|
36
38
|
import {
|
|
37
39
|
buildCanonicalModelIndex,
|
|
38
40
|
type CanonicalModelIndex,
|
|
@@ -1053,7 +1055,16 @@ export class ModelRegistry {
|
|
|
1053
1055
|
const key = `${replacementModel.provider}\u0000${replacementModel.id}`;
|
|
1054
1056
|
const existingIndex = indexByKey.get(key);
|
|
1055
1057
|
if (existingIndex !== undefined) {
|
|
1056
|
-
merged[existingIndex]
|
|
1058
|
+
const existing = merged[existingIndex];
|
|
1059
|
+
merged[existingIndex] = {
|
|
1060
|
+
...replacementModel,
|
|
1061
|
+
contextWindow:
|
|
1062
|
+
replacementModel.contextWindow === UNK_CONTEXT_WINDOW
|
|
1063
|
+
? existing.contextWindow
|
|
1064
|
+
: replacementModel.contextWindow,
|
|
1065
|
+
maxTokens:
|
|
1066
|
+
replacementModel.maxTokens === UNK_MAX_TOKENS ? existing.maxTokens : replacementModel.maxTokens,
|
|
1067
|
+
};
|
|
1057
1068
|
} else {
|
|
1058
1069
|
merged.push(replacementModel);
|
|
1059
1070
|
indexByKey.set(key, merged.length - 1);
|
|
@@ -12,10 +12,10 @@ import {
|
|
|
12
12
|
type Model,
|
|
13
13
|
modelsAreEqual,
|
|
14
14
|
} from "@oh-my-pi/pi-ai";
|
|
15
|
+
import { fuzzyMatch } from "@oh-my-pi/pi-tui";
|
|
15
16
|
import chalk from "chalk";
|
|
16
17
|
import MODEL_PRIO from "../priority.json" with { type: "json" };
|
|
17
18
|
import { parseThinkingLevel, resolveThinkingLevelForModel } from "../thinking";
|
|
18
|
-
import { fuzzyMatch } from "../utils/fuzzy";
|
|
19
19
|
import { isAuthenticated, kNoAuth, MODEL_ROLE_IDS, type ModelRegistry, type ModelRole } from "./model-registry";
|
|
20
20
|
import type { Settings } from "./settings";
|
|
21
21
|
|
|
@@ -607,9 +607,6 @@ export function resolveModelRoleValue(
|
|
|
607
607
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
608
608
|
}
|
|
609
609
|
|
|
610
|
-
const lastColonIndex = normalized.lastIndexOf(":");
|
|
611
|
-
const _thinkingSelector =
|
|
612
|
-
lastColonIndex > PREFIX_MODEL_ROLE.length ? parseThinkingLevel(normalized.slice(lastColonIndex + 1)) : undefined;
|
|
613
610
|
const effectivePatterns = resolveConfiguredRolePattern(normalized, options?.settings);
|
|
614
611
|
if (!effectivePatterns || effectivePatterns.length === 0) {
|
|
615
612
|
return { model: undefined, thinkingLevel: undefined, explicitThinkingLevel: false, warning: undefined };
|
|
@@ -956,6 +953,36 @@ export async function resolveModelScope(
|
|
|
956
953
|
return scopedModels;
|
|
957
954
|
}
|
|
958
955
|
|
|
956
|
+
/**
|
|
957
|
+
* Resolve the set of models a session is allowed to use, given the active
|
|
958
|
+
* settings. Starts from `modelRegistry.getAvailable()` (so disabled providers
|
|
959
|
+
* and providers without credentials are already filtered out) and, when
|
|
960
|
+
* `enabledModels` is configured for the current path scope, further restricts
|
|
961
|
+
* the result to models matching those patterns.
|
|
962
|
+
*
|
|
963
|
+
* Returns the unfiltered available list when `enabledModels` is empty.
|
|
964
|
+
* Returns an empty list when `enabledModels` is configured but no available
|
|
965
|
+
* model matches any pattern — callers MUST treat this as "no usable model"
|
|
966
|
+
* rather than falling back to the global default (see issue #1022).
|
|
967
|
+
*/
|
|
968
|
+
export async function resolveAllowedModels(
|
|
969
|
+
modelRegistry: Pick<ModelRegistry, "getAvailable" | "getCanonicalVariants">,
|
|
970
|
+
settings: Settings | undefined,
|
|
971
|
+
preferences?: ModelMatchPreferences,
|
|
972
|
+
): Promise<Model<Api>[]> {
|
|
973
|
+
const available = modelRegistry.getAvailable();
|
|
974
|
+
const patterns = settings?.get("enabledModels");
|
|
975
|
+
if (!patterns || patterns.length === 0) {
|
|
976
|
+
return available;
|
|
977
|
+
}
|
|
978
|
+
const scoped = await resolveModelScope(patterns, modelRegistry, preferences);
|
|
979
|
+
if (scoped.length === 0) {
|
|
980
|
+
return [];
|
|
981
|
+
}
|
|
982
|
+
const allowed = new Set(scoped.map(entry => `${entry.model.provider}/${entry.model.id}`));
|
|
983
|
+
return available.filter(model => allowed.has(`${model.provider}/${model.id}`));
|
|
984
|
+
}
|
|
985
|
+
|
|
959
986
|
export interface ResolveCliModelResult {
|
|
960
987
|
model: Model<Api> | undefined;
|
|
961
988
|
selector?: string;
|