@oh-my-pi/pi-coding-agent 15.10.0 → 15.10.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 +75 -1
- package/dist/types/cli/dry-balance-cli.d.ts +15 -1
- package/dist/types/commit/analysis/conventional.d.ts +2 -2
- package/dist/types/commit/analysis/summary.d.ts +2 -2
- package/dist/types/commit/changelog/generate.d.ts +2 -2
- package/dist/types/commit/changelog/index.d.ts +2 -2
- package/dist/types/commit/map-reduce/index.d.ts +3 -3
- package/dist/types/commit/map-reduce/map-phase.d.ts +2 -2
- package/dist/types/commit/map-reduce/reduce-phase.d.ts +2 -2
- package/dist/types/commit/model-selection.d.ts +10 -4
- package/dist/types/config/api-key-resolver.d.ts +34 -0
- package/dist/types/config/model-registry.d.ts +17 -1
- package/dist/types/config/settings-schema.d.ts +9 -0
- package/dist/types/dap/config.d.ts +14 -1
- package/dist/types/dap/types.d.ts +10 -0
- package/dist/types/lsp/utils.d.ts +3 -2
- package/dist/types/modes/components/chat-block.d.ts +64 -0
- package/dist/types/modes/components/custom-editor.d.ts +3 -0
- package/dist/types/modes/components/overlay-box.d.ts +17 -0
- package/dist/types/modes/components/plan-review-overlay.d.ts +59 -0
- package/dist/types/modes/components/plan-toc.d.ts +41 -0
- package/dist/types/modes/components/read-tool-group.d.ts +2 -0
- package/dist/types/modes/components/transcript-container.d.ts +11 -0
- package/dist/types/modes/controllers/command-controller.d.ts +1 -0
- package/dist/types/modes/controllers/event-controller.d.ts +0 -1
- package/dist/types/modes/controllers/extension-ui-controller.d.ts +0 -1
- package/dist/types/modes/controllers/input-controller.d.ts +1 -1
- package/dist/types/modes/controllers/streaming-reveal.d.ts +22 -0
- package/dist/types/modes/controllers/tan-command-controller.d.ts +6 -0
- package/dist/types/modes/interactive-mode.d.ts +15 -5
- package/dist/types/modes/theme/theme.d.ts +1 -1
- package/dist/types/modes/types.d.ts +18 -5
- package/dist/types/modes/utils/copy-targets.d.ts +21 -1
- package/dist/types/plan-mode/approved-plan.d.ts +27 -8
- package/dist/types/plan-mode/plan-protection.d.ts +4 -4
- package/dist/types/sdk.d.ts +2 -0
- package/dist/types/session/agent-session.d.ts +21 -0
- package/dist/types/session/messages.d.ts +12 -0
- package/dist/types/session/session-manager.d.ts +3 -1
- package/dist/types/slash-commands/types.d.ts +4 -6
- package/dist/types/task/executor.d.ts +7 -0
- package/dist/types/task/index.d.ts +1 -0
- package/dist/types/task/render.d.ts +3 -2
- package/dist/types/tools/archive-reader.d.ts +5 -0
- package/dist/types/tools/ast-edit.d.ts +3 -0
- package/dist/types/tools/ast-grep.d.ts +3 -0
- package/dist/types/tools/bash.d.ts +1 -0
- package/dist/types/tools/find.d.ts +8 -4
- package/dist/types/tools/grouped-file-output.d.ts +95 -12
- package/dist/types/tools/memory-render.d.ts +4 -1
- package/dist/types/tools/plan-mode-guard.d.ts +8 -9
- package/dist/types/tools/render-utils.d.ts +5 -9
- package/dist/types/tools/search.d.ts +4 -0
- package/dist/types/tools/sqlite-reader.d.ts +1 -0
- package/dist/types/tools/todo.d.ts +3 -2
- package/dist/types/tools/write.d.ts +3 -0
- package/dist/types/tui/output-block.d.ts +16 -4
- package/dist/types/tui/status-line.d.ts +3 -0
- package/dist/types/utils/enhanced-paste.d.ts +20 -0
- package/dist/types/web/search/providers/kimi.d.ts +1 -1
- package/package.json +9 -9
- package/src/auto-thinking/classifier.ts +5 -1
- package/src/cli/dry-balance-cli.ts +52 -17
- package/src/cli/gallery-cli.ts +4 -1
- package/src/cli/gallery-fixtures/misc.ts +29 -0
- package/src/commit/analysis/conventional.ts +2 -2
- package/src/commit/analysis/summary.ts +2 -2
- package/src/commit/changelog/generate.ts +2 -2
- package/src/commit/changelog/index.ts +2 -2
- package/src/commit/map-reduce/index.ts +3 -3
- package/src/commit/map-reduce/map-phase.ts +2 -2
- package/src/commit/map-reduce/reduce-phase.ts +2 -2
- package/src/commit/model-selection.ts +33 -9
- package/src/commit/pipeline.ts +4 -4
- package/src/config/api-key-resolver.ts +58 -0
- package/src/config/model-registry.ts +25 -2
- package/src/config/settings-schema.ts +10 -0
- package/src/config/settings.ts +20 -2
- package/src/dap/config.ts +41 -2
- package/src/dap/defaults.json +1 -0
- package/src/dap/session.ts +1 -0
- package/src/dap/types.ts +10 -0
- package/src/debug/index.ts +40 -54
- package/src/edit/renderer.ts +82 -78
- package/src/eval/__tests__/llm-bridge.test.ts +90 -31
- package/src/eval/llm-bridge.ts +8 -3
- package/src/goals/tools/goal-tool.ts +36 -26
- package/src/internal-urls/docs-index.generated.ts +6 -6
- package/src/lsp/utils.ts +3 -2
- package/src/main.ts +9 -7
- package/src/memories/index.ts +12 -5
- package/src/mnemopi/backend.ts +5 -1
- package/src/modes/acp/acp-agent.ts +33 -26
- package/src/modes/components/assistant-message.ts +2 -9
- package/src/modes/components/chat-block.ts +111 -0
- package/src/modes/components/copy-selector.ts +1 -44
- package/src/modes/components/custom-editor.ts +23 -0
- package/src/modes/components/custom-message.ts +1 -3
- package/src/modes/components/execution-shared.ts +1 -2
- package/src/modes/components/hook-message.ts +1 -3
- package/src/modes/components/overlay-box.ts +108 -0
- package/src/modes/components/plan-review-overlay.ts +799 -0
- package/src/modes/components/plan-toc.ts +138 -0
- package/src/modes/components/read-tool-group.ts +20 -4
- package/src/modes/components/skill-message.ts +0 -1
- package/src/modes/components/tips.txt +1 -0
- package/src/modes/components/todo-reminder.ts +0 -2
- package/src/modes/components/tool-execution.ts +68 -88
- package/src/modes/components/transcript-container.ts +84 -24
- package/src/modes/components/user-message.ts +1 -2
- package/src/modes/controllers/command-controller-shared.ts +7 -6
- package/src/modes/controllers/command-controller.ts +57 -55
- package/src/modes/controllers/event-controller.ts +41 -40
- package/src/modes/controllers/extension-ui-controller.ts +10 -73
- package/src/modes/controllers/input-controller.ts +124 -119
- package/src/modes/controllers/mcp-command-controller.ts +69 -60
- package/src/modes/controllers/selector-controller.ts +23 -25
- package/src/modes/controllers/streaming-reveal.ts +212 -0
- package/src/modes/controllers/tan-command-controller.ts +173 -0
- package/src/modes/interactive-mode.ts +169 -94
- package/src/modes/setup-wizard/wizard-overlay.ts +1 -1
- package/src/modes/theme/theme-schema.json +1 -1
- package/src/modes/theme/theme.ts +8 -4
- package/src/modes/types.ts +18 -7
- package/src/modes/utils/copy-targets.ts +133 -27
- package/src/modes/utils/ui-helpers.ts +44 -46
- package/src/plan-mode/approved-plan.ts +66 -43
- package/src/plan-mode/plan-protection.ts +4 -4
- package/src/prompts/system/background-tan-dispatch.md +8 -0
- package/src/prompts/system/plan-mode-active.md +67 -58
- package/src/prompts/system/plan-mode-approved.md +1 -1
- package/src/sdk.ts +11 -37
- package/src/session/agent-session.ts +82 -6
- package/src/session/messages.ts +26 -0
- package/src/session/session-manager.ts +13 -5
- package/src/slash-commands/builtin-registry.ts +36 -9
- package/src/slash-commands/types.ts +4 -6
- package/src/task/executor.ts +5 -2
- package/src/task/index.ts +4 -0
- package/src/task/render.ts +212 -147
- package/src/tools/archive-reader.ts +64 -0
- package/src/tools/ask.ts +119 -164
- package/src/tools/ast-edit.ts +98 -71
- package/src/tools/ast-grep.ts +37 -43
- package/src/tools/bash.ts +50 -6
- package/src/tools/debug.ts +20 -8
- package/src/tools/fetch.ts +297 -7
- package/src/tools/find.ts +44 -30
- package/src/tools/gh-renderer.ts +81 -42
- package/src/tools/grouped-file-output.ts +272 -48
- package/src/tools/image-gen.ts +150 -103
- package/src/tools/inspect-image-renderer.ts +63 -41
- package/src/tools/inspect-image.ts +8 -1
- package/src/tools/job.ts +3 -4
- package/src/tools/memory-render.ts +4 -1
- package/src/tools/plan-mode-guard.ts +21 -39
- package/src/tools/read.ts +23 -16
- package/src/tools/render-utils.ts +21 -37
- package/src/tools/resolve.ts +14 -0
- package/src/tools/search-tool-bm25.ts +36 -23
- package/src/tools/search.ts +80 -78
- package/src/tools/sqlite-reader.ts +9 -12
- package/src/tools/todo.ts +118 -52
- package/src/tools/write.ts +81 -62
- package/src/tui/output-block.ts +60 -13
- package/src/tui/status-line.ts +5 -1
- package/src/utils/commit-message-generator.ts +9 -1
- package/src/utils/enhanced-paste.ts +202 -0
- package/src/utils/title-generator.ts +2 -1
- package/src/web/search/providers/anthropic.ts +25 -19
- package/src/web/search/providers/exa.ts +11 -3
- package/src/web/search/providers/kimi.ts +28 -17
- package/src/web/search/providers/parallel.ts +35 -24
- package/src/web/search/providers/synthetic.ts +8 -6
- package/src/web/search/providers/tavily.ts +9 -8
- package/src/web/search/providers/zai.ts +8 -6
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import type { Api, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { Api, ApiKey, AssistantMessage, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { completeSimple, validateToolCall } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import * as z from "zod/v4";
|
|
@@ -25,7 +25,7 @@ export const changelogTool = {
|
|
|
25
25
|
|
|
26
26
|
export interface ChangelogPromptInput {
|
|
27
27
|
model: Model<Api>;
|
|
28
|
-
apiKey:
|
|
28
|
+
apiKey: ApiKey;
|
|
29
29
|
thinkingLevel?: ThinkingLevel;
|
|
30
30
|
changelogPath: string;
|
|
31
31
|
isPackageChangelog: boolean;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
3
|
-
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import type { Api, ApiKey, Model } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { logger } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import { CHANGELOG_CATEGORIES } from "../../commit/types";
|
|
6
6
|
import * as git from "../../utils/git";
|
|
@@ -15,7 +15,7 @@ const DEFAULT_MAX_DIFF_CHARS = 120_000;
|
|
|
15
15
|
export interface ChangelogFlowInput {
|
|
16
16
|
cwd: string;
|
|
17
17
|
model: Model<Api>;
|
|
18
|
-
apiKey:
|
|
18
|
+
apiKey: ApiKey;
|
|
19
19
|
thinkingLevel?: ThinkingLevel;
|
|
20
20
|
stagedFiles: string[];
|
|
21
21
|
dryRun: boolean;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { Api, ApiKey, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { $env } from "@oh-my-pi/pi-utils";
|
|
4
4
|
import { parseFileDiffs } from "../../commit/git/diff";
|
|
5
5
|
import type { ConventionalAnalysis } from "../../commit/types";
|
|
@@ -21,10 +21,10 @@ export interface MapReduceSettings {
|
|
|
21
21
|
|
|
22
22
|
export interface MapReduceInput {
|
|
23
23
|
model: Model<Api>;
|
|
24
|
-
apiKey:
|
|
24
|
+
apiKey: ApiKey;
|
|
25
25
|
thinkingLevel?: ThinkingLevel;
|
|
26
26
|
smolModel: Model<Api>;
|
|
27
|
-
smolApiKey:
|
|
27
|
+
smolApiKey: ApiKey;
|
|
28
28
|
smolThinkingLevel?: ThinkingLevel;
|
|
29
29
|
diff: string;
|
|
30
30
|
stat: string;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import type { Api, AssistantMessage, Message, Model } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { Api, ApiKey, AssistantMessage, Message, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import fileObserverSystemPrompt from "../../commit/prompts/file-observer-system.md" with { type: "text" };
|
|
@@ -18,7 +18,7 @@ const RETRY_BACKOFF_MS = 1000;
|
|
|
18
18
|
|
|
19
19
|
export interface MapPhaseInput {
|
|
20
20
|
model: Model<Api>;
|
|
21
|
-
apiKey:
|
|
21
|
+
apiKey: ApiKey;
|
|
22
22
|
thinkingLevel?: ThinkingLevel;
|
|
23
23
|
files: FileDiff[];
|
|
24
24
|
config?: {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { Api, ApiKey, Model } from "@oh-my-pi/pi-ai";
|
|
3
3
|
import { completeSimple } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { prompt } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import reduceSystemPrompt from "../../commit/prompts/reduce-system.md" with { type: "text" };
|
|
@@ -12,7 +12,7 @@ const ReduceTool = createConventionalAnalysisTool("Synthesize file observations
|
|
|
12
12
|
|
|
13
13
|
export interface ReducePhaseInput {
|
|
14
14
|
model: Model<Api>;
|
|
15
|
-
apiKey:
|
|
15
|
+
apiKey: ApiKey;
|
|
16
16
|
thinkingLevel?: ThinkingLevel;
|
|
17
17
|
observations: FileObservation[];
|
|
18
18
|
stat: string;
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
2
|
-
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
2
|
+
import type { Api, ApiKey, Model } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import type { ApiKeyResolverRegistry } from "../config/api-key-resolver";
|
|
3
4
|
import { MODEL_ROLE_IDS } from "../config/model-registry";
|
|
4
5
|
import {
|
|
5
6
|
type ModelLookupRegistry,
|
|
@@ -12,13 +13,19 @@ import MODEL_PRIO from "../priority.json" with { type: "json" };
|
|
|
12
13
|
|
|
13
14
|
export interface ResolvedCommitModel {
|
|
14
15
|
model: Model<Api>;
|
|
15
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Resolver for the model's bearer: re-resolves on 401 / usage-limit so the
|
|
18
|
+
* whole commit pipeline (analysis, map/reduce, changelog) inherits the
|
|
19
|
+
* central force-refresh + account-rotation policy.
|
|
20
|
+
*/
|
|
21
|
+
apiKey: ApiKey;
|
|
16
22
|
thinkingLevel?: ThinkingLevel;
|
|
17
23
|
}
|
|
18
24
|
|
|
19
|
-
type CommitModelRegistry = ModelLookupRegistry &
|
|
20
|
-
|
|
21
|
-
|
|
25
|
+
type CommitModelRegistry = ModelLookupRegistry &
|
|
26
|
+
ApiKeyResolverRegistry & {
|
|
27
|
+
getApiKey: (model: Model<Api>) => Promise<string | undefined>;
|
|
28
|
+
};
|
|
22
29
|
|
|
23
30
|
export async function resolvePrimaryModel(
|
|
24
31
|
override: string | undefined,
|
|
@@ -38,20 +45,32 @@ export async function resolvePrimaryModel(
|
|
|
38
45
|
if (!apiKey) {
|
|
39
46
|
throw new Error(`No API key available for model ${model.provider}/${model.id}`);
|
|
40
47
|
}
|
|
41
|
-
return {
|
|
48
|
+
return {
|
|
49
|
+
model,
|
|
50
|
+
apiKey: modelRegistry.resolver(model.provider, { baseUrl: model.baseUrl }),
|
|
51
|
+
thinkingLevel: resolved?.thinkingLevel,
|
|
52
|
+
};
|
|
42
53
|
}
|
|
43
54
|
|
|
44
55
|
export async function resolveSmolModel(
|
|
45
56
|
settings: Settings,
|
|
46
57
|
modelRegistry: CommitModelRegistry,
|
|
47
58
|
fallbackModel: Model<Api>,
|
|
48
|
-
fallbackApiKey:
|
|
59
|
+
fallbackApiKey: ApiKey,
|
|
49
60
|
): Promise<ResolvedCommitModel> {
|
|
50
61
|
const available = modelRegistry.getAvailable();
|
|
51
62
|
const resolvedSmol = resolveRoleSelection(["smol"], settings, available, modelRegistry);
|
|
52
63
|
if (resolvedSmol?.model) {
|
|
53
64
|
const apiKey = await modelRegistry.getApiKey(resolvedSmol.model);
|
|
54
|
-
if (apiKey)
|
|
65
|
+
if (apiKey) {
|
|
66
|
+
return {
|
|
67
|
+
model: resolvedSmol.model,
|
|
68
|
+
apiKey: modelRegistry.resolver(resolvedSmol.model.provider, {
|
|
69
|
+
baseUrl: resolvedSmol.model.baseUrl,
|
|
70
|
+
}),
|
|
71
|
+
thinkingLevel: resolvedSmol.thinkingLevel,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
55
74
|
}
|
|
56
75
|
|
|
57
76
|
const matchPreferences = { usageOrder: settings.getStorage()?.getModelUsageOrder() };
|
|
@@ -59,7 +78,12 @@ export async function resolveSmolModel(
|
|
|
59
78
|
const candidate = parseModelPattern(pattern, available, matchPreferences, { modelRegistry }).model;
|
|
60
79
|
if (!candidate) continue;
|
|
61
80
|
const apiKey = await modelRegistry.getApiKey(candidate);
|
|
62
|
-
if (apiKey)
|
|
81
|
+
if (apiKey) {
|
|
82
|
+
return {
|
|
83
|
+
model: candidate,
|
|
84
|
+
apiKey: modelRegistry.resolver(candidate.provider, { baseUrl: candidate.baseUrl }),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
63
87
|
}
|
|
64
88
|
|
|
65
89
|
return { model: fallbackModel, apiKey: fallbackApiKey };
|
package/src/commit/pipeline.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import * as path from "node:path";
|
|
2
2
|
import type { ThinkingLevel } from "@oh-my-pi/pi-agent-core";
|
|
3
|
-
import type { Api, Model } from "@oh-my-pi/pi-ai";
|
|
3
|
+
import type { Api, ApiKey, Model } from "@oh-my-pi/pi-ai";
|
|
4
4
|
import { getProjectDir, logger, prompt } from "@oh-my-pi/pi-utils";
|
|
5
5
|
import { ModelRegistry } from "../config/model-registry";
|
|
6
6
|
import { Settings } from "../config/settings";
|
|
@@ -145,10 +145,10 @@ async function generateAnalysis(input: {
|
|
|
145
145
|
contextFiles: Array<{ path: string; content: string }>;
|
|
146
146
|
userContext?: string;
|
|
147
147
|
primaryModel: Model<Api>;
|
|
148
|
-
primaryApiKey:
|
|
148
|
+
primaryApiKey: ApiKey;
|
|
149
149
|
primaryThinkingLevel?: ThinkingLevel;
|
|
150
150
|
smolModel: Model<Api>;
|
|
151
|
-
smolApiKey:
|
|
151
|
+
smolApiKey: ApiKey;
|
|
152
152
|
smolThinkingLevel?: ThinkingLevel;
|
|
153
153
|
commitSettings: {
|
|
154
154
|
mapReduceEnabled: boolean;
|
|
@@ -206,7 +206,7 @@ async function generateSummaryWithRetry(input: {
|
|
|
206
206
|
analysis: ConventionalAnalysis;
|
|
207
207
|
stat: string;
|
|
208
208
|
model: Model<Api>;
|
|
209
|
-
apiKey:
|
|
209
|
+
apiKey: ApiKey;
|
|
210
210
|
thinkingLevel?: ThinkingLevel;
|
|
211
211
|
userContext?: string;
|
|
212
212
|
}): Promise<{ summary: string }> {
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import type { ApiKeyResolver, AuthStorage } from "@oh-my-pi/pi-ai";
|
|
2
|
+
|
|
3
|
+
export interface ApiKeyResolverOptions {
|
|
4
|
+
/** Session id for credential stickiness; read at resolve time by the caller. */
|
|
5
|
+
sessionId?: string;
|
|
6
|
+
/** Provider base URL hint forwarded to the auth-storage cascade. */
|
|
7
|
+
baseUrl?: string;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Minimal slice of `ModelRegistry` the resolver needs. Typed structurally so
|
|
12
|
+
* narrower registry shells (e.g. the commit pipeline's `CommitModelRegistry`)
|
|
13
|
+
* can build resolvers without depending on the full class.
|
|
14
|
+
*/
|
|
15
|
+
export interface ApiKeyResolverRegistry {
|
|
16
|
+
getApiKeyForProvider(
|
|
17
|
+
provider: string,
|
|
18
|
+
sessionId?: string,
|
|
19
|
+
options?: { baseUrl?: string; forceRefresh?: boolean; signal?: AbortSignal },
|
|
20
|
+
): Promise<string | undefined>;
|
|
21
|
+
authStorage: Pick<AuthStorage, "rotateSessionCredential">;
|
|
22
|
+
/**
|
|
23
|
+
* Build an {@link ApiKeyResolver} implementing the central a/b/c auth-retry
|
|
24
|
+
* policy: initial → resolve; step (b) → force-refresh same account; step (c)
|
|
25
|
+
* → rotate to a sibling credential, then re-resolve.
|
|
26
|
+
*
|
|
27
|
+
* The resolver is stateless (safe to reuse across requests). Callers that
|
|
28
|
+
* need the initial key for a guard can call `resolveApiKeyOnce(resolver)`.
|
|
29
|
+
*/
|
|
30
|
+
resolver(provider: string, options?: ApiKeyResolverOptions): ApiKeyResolver;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Default implementation of {@link ApiKeyResolverRegistry.resolver}.
|
|
35
|
+
* Also usable standalone for structural registries that don't carry the method.
|
|
36
|
+
*/
|
|
37
|
+
export function createApiKeyResolver(
|
|
38
|
+
registry: Pick<ApiKeyResolverRegistry, "getApiKeyForProvider" | "authStorage">,
|
|
39
|
+
provider: string,
|
|
40
|
+
options: ApiKeyResolverOptions = {},
|
|
41
|
+
): ApiKeyResolver {
|
|
42
|
+
const { sessionId, baseUrl } = options;
|
|
43
|
+
return async ({ lastChance, error, signal }) => {
|
|
44
|
+
if (error === undefined) {
|
|
45
|
+
return registry.getApiKeyForProvider(provider, sessionId, { baseUrl });
|
|
46
|
+
}
|
|
47
|
+
if (lastChance) {
|
|
48
|
+
// Account constraint (401 / usage / account-rate-limit): rotate to a
|
|
49
|
+
// sibling credential. We do NOT honor any retry-after here — if a
|
|
50
|
+
// sibling exists we switch immediately; the precise no-sibling backoff
|
|
51
|
+
// is owned by `markUsageLimitReached` (default + server usage-report
|
|
52
|
+
// reset) and the outer whole-turn retry layer.
|
|
53
|
+
await registry.authStorage.rotateSessionCredential(provider, sessionId, { error, signal });
|
|
54
|
+
return registry.getApiKeyForProvider(provider, sessionId, { baseUrl });
|
|
55
|
+
}
|
|
56
|
+
return registry.getApiKeyForProvider(provider, sessionId, { baseUrl, forceRefresh: true, signal });
|
|
57
|
+
};
|
|
58
|
+
}
|
|
@@ -95,12 +95,14 @@ const STARTUP_MODEL_CACHE_PROVIDER_IDS: readonly string[] = [
|
|
|
95
95
|
...SPECIAL_MODEL_MANAGER_PROVIDER_IDS,
|
|
96
96
|
];
|
|
97
97
|
|
|
98
|
+
import type { ApiKeyResolver } from "@oh-my-pi/pi-ai";
|
|
98
99
|
import { registerOAuthProvider, unregisterOAuthProviders } from "@oh-my-pi/pi-ai/utils/oauth";
|
|
99
100
|
import type { OAuthCredentials, OAuthLoginCallbacks } from "@oh-my-pi/pi-ai/utils/oauth/types";
|
|
100
101
|
import { isRecord, logger } from "@oh-my-pi/pi-utils";
|
|
101
102
|
import { parseModelString, resolveProviderModelReference } from "../config/model-resolver";
|
|
102
103
|
import { isValidThemeColor, type ThemeColor } from "../modes/theme/theme";
|
|
103
104
|
import type { AuthStorage, OAuthCredential } from "../session/auth-storage";
|
|
105
|
+
import { type ApiKeyResolverOptions, createApiKeyResolver } from "./api-key-resolver";
|
|
104
106
|
import { type ConfigError, ConfigFile } from "./config-file";
|
|
105
107
|
import {
|
|
106
108
|
buildCanonicalModelIndex,
|
|
@@ -2365,12 +2367,33 @@ export class ModelRegistry {
|
|
|
2365
2367
|
|
|
2366
2368
|
/**
|
|
2367
2369
|
* Get API key for a provider (e.g., "openai").
|
|
2370
|
+
*
|
|
2371
|
+
* `options.forceRefresh` powers step (b) of the auth-retry policy — it
|
|
2372
|
+
* re-mints the session-sticky OAuth token even when the cached copy still
|
|
2373
|
+
* looks valid. `options.signal` is threaded into any broker-bound refresh.
|
|
2368
2374
|
*/
|
|
2369
|
-
async getApiKeyForProvider(
|
|
2375
|
+
async getApiKeyForProvider(
|
|
2376
|
+
provider: string,
|
|
2377
|
+
sessionId?: string,
|
|
2378
|
+
options?: { baseUrl?: string; forceRefresh?: boolean; signal?: AbortSignal },
|
|
2379
|
+
): Promise<string | undefined> {
|
|
2370
2380
|
if (this.#keylessProviders.has(provider) && !this.authStorage.hasAuth(provider)) {
|
|
2371
2381
|
return kNoAuth;
|
|
2372
2382
|
}
|
|
2373
|
-
return this.authStorage.getApiKey(provider, sessionId, {
|
|
2383
|
+
return this.authStorage.getApiKey(provider, sessionId, {
|
|
2384
|
+
baseUrl: options?.baseUrl,
|
|
2385
|
+
forceRefresh: options?.forceRefresh,
|
|
2386
|
+
signal: options?.signal,
|
|
2387
|
+
});
|
|
2388
|
+
}
|
|
2389
|
+
|
|
2390
|
+
/**
|
|
2391
|
+
* Build an {@link ApiKeyResolver} for this provider, implementing the
|
|
2392
|
+
* central a/b/c auth-retry policy. Callers that need the initial key for
|
|
2393
|
+
* a guard can call `resolveApiKeyOnce(resolver)`.
|
|
2394
|
+
*/
|
|
2395
|
+
resolver(provider: string, options?: ApiKeyResolverOptions): ApiKeyResolver {
|
|
2396
|
+
return createApiKeyResolver(this, provider, options);
|
|
2374
2397
|
}
|
|
2375
2398
|
|
|
2376
2399
|
async #peekApiKeyForProvider(provider: string): Promise<string | undefined> {
|
|
@@ -660,6 +660,16 @@ export const SETTINGS_SCHEMA = {
|
|
|
660
660
|
},
|
|
661
661
|
},
|
|
662
662
|
|
|
663
|
+
"display.smoothStreaming": {
|
|
664
|
+
type: "boolean",
|
|
665
|
+
default: true,
|
|
666
|
+
ui: {
|
|
667
|
+
tab: "appearance",
|
|
668
|
+
label: "Smooth Streaming",
|
|
669
|
+
description: "Reveal assistant text smoothly while streamed chunks arrive",
|
|
670
|
+
},
|
|
671
|
+
},
|
|
672
|
+
|
|
663
673
|
"display.showTokenUsage": {
|
|
664
674
|
type: "boolean",
|
|
665
675
|
default: false,
|
package/src/config/settings.ts
CHANGED
|
@@ -240,11 +240,13 @@ export class Settings {
|
|
|
240
240
|
return promise.then(
|
|
241
241
|
instance => {
|
|
242
242
|
globalInstance = instance;
|
|
243
|
+
clearBoundSettingsMethods();
|
|
243
244
|
globalInstancePromise = Promise.resolve(instance);
|
|
244
245
|
return instance;
|
|
245
246
|
},
|
|
246
247
|
error => {
|
|
247
248
|
globalInstance = null;
|
|
249
|
+
clearBoundSettingsMethods();
|
|
248
250
|
throw error;
|
|
249
251
|
},
|
|
250
252
|
);
|
|
@@ -978,6 +980,13 @@ export function onHindsightScopeChanged(cb: () => void): () => void {
|
|
|
978
980
|
|
|
979
981
|
let globalInstance: Settings | null = null;
|
|
980
982
|
let globalInstancePromise: Promise<Settings> | null = null;
|
|
983
|
+
let boundSettingsInstance: Settings | null = null;
|
|
984
|
+
let boundSettingsMethods = new Map<PropertyKey, unknown>();
|
|
985
|
+
|
|
986
|
+
function clearBoundSettingsMethods(): void {
|
|
987
|
+
boundSettingsInstance = null;
|
|
988
|
+
boundSettingsMethods = new Map<PropertyKey, unknown>();
|
|
989
|
+
}
|
|
981
990
|
|
|
982
991
|
export function isSettingsInitialized(): boolean {
|
|
983
992
|
return globalInstance !== null;
|
|
@@ -990,6 +999,7 @@ export function isSettingsInitialized(): boolean {
|
|
|
990
999
|
export function resetSettingsForTest(): void {
|
|
991
1000
|
globalInstance = null;
|
|
992
1001
|
globalInstancePromise = null;
|
|
1002
|
+
clearBoundSettingsMethods();
|
|
993
1003
|
}
|
|
994
1004
|
|
|
995
1005
|
/**
|
|
@@ -1001,9 +1011,17 @@ export const settings = new Proxy({} as Settings, {
|
|
|
1001
1011
|
if (!globalInstance) {
|
|
1002
1012
|
throw new Error("Settings not initialized. Call Settings.init() first.");
|
|
1003
1013
|
}
|
|
1004
|
-
|
|
1014
|
+
if (boundSettingsInstance !== globalInstance) {
|
|
1015
|
+
clearBoundSettingsMethods();
|
|
1016
|
+
boundSettingsInstance = globalInstance;
|
|
1017
|
+
}
|
|
1018
|
+
const value = (globalInstance as unknown as Record<PropertyKey, unknown>)[prop];
|
|
1005
1019
|
if (typeof value === "function") {
|
|
1006
|
-
|
|
1020
|
+
const cached = boundSettingsMethods.get(prop);
|
|
1021
|
+
if (cached) return cached;
|
|
1022
|
+
const bound = value.bind(globalInstance);
|
|
1023
|
+
boundSettingsMethods.set(prop, bound);
|
|
1024
|
+
return bound;
|
|
1007
1025
|
}
|
|
1008
1026
|
return value;
|
|
1009
1027
|
},
|
package/src/dap/config.ts
CHANGED
|
@@ -27,6 +27,7 @@ function normalizeAdapterConfig(config: unknown): DapAdapterConfig | null {
|
|
|
27
27
|
rootMarkers: normalizeStringArray(config.rootMarkers),
|
|
28
28
|
launchDefaults: normalizeObject(config.launchDefaults),
|
|
29
29
|
attachDefaults: normalizeObject(config.attachDefaults),
|
|
30
|
+
acceptsDirectoryProgram: config.acceptsDirectoryProgram === true,
|
|
30
31
|
...(connectMode ? { connectMode } : {}),
|
|
31
32
|
};
|
|
32
33
|
}
|
|
@@ -64,6 +65,7 @@ export function resolveAdapter(adapterName: string, cwd: string): DapResolvedAda
|
|
|
64
65
|
launchDefaults: config.launchDefaults ?? {},
|
|
65
66
|
attachDefaults: config.attachDefaults ?? {},
|
|
66
67
|
connectMode: config.connectMode ?? "stdio",
|
|
68
|
+
acceptsDirectoryProgram: config.acceptsDirectoryProgram === true,
|
|
67
69
|
};
|
|
68
70
|
}
|
|
69
71
|
|
|
@@ -124,12 +126,19 @@ function sortAdaptersForLaunch(program: string, cwd: string, adapters: DapResolv
|
|
|
124
126
|
return rootAware.map(entry => entry.adapter);
|
|
125
127
|
}
|
|
126
128
|
|
|
127
|
-
export function selectLaunchAdapter(
|
|
129
|
+
export function selectLaunchAdapter(
|
|
130
|
+
program: string,
|
|
131
|
+
cwd: string,
|
|
132
|
+
adapterName?: string,
|
|
133
|
+
programKind: LaunchProgramKind = "file",
|
|
134
|
+
): DapResolvedAdapter | null {
|
|
128
135
|
if (adapterName) {
|
|
129
136
|
return resolveAdapter(adapterName, cwd);
|
|
130
137
|
}
|
|
131
138
|
const matches = getMatchingAdapters(program, cwd);
|
|
132
|
-
const
|
|
139
|
+
const candidates =
|
|
140
|
+
programKind === "directory" ? matches.filter(adapter => adapter.acceptsDirectoryProgram) : matches;
|
|
141
|
+
const sorted = sortAdaptersForLaunch(program, cwd, candidates.length > 0 ? candidates : matches);
|
|
133
142
|
return sorted[0] ?? null;
|
|
134
143
|
}
|
|
135
144
|
|
|
@@ -148,3 +157,33 @@ export function selectAttachAdapter(cwd: string, adapterName?: string, port?: nu
|
|
|
148
157
|
}
|
|
149
158
|
return available[0] ?? null;
|
|
150
159
|
}
|
|
160
|
+
|
|
161
|
+
/** How the launch `program` resolves on disk. `"missing"` is reserved for
|
|
162
|
+
* programs the adapter creates on demand (rare); we treat them like files. */
|
|
163
|
+
export type LaunchProgramKind = "file" | "directory" | "missing";
|
|
164
|
+
|
|
165
|
+
/** Compute adapter-specific launch arguments that depend on the resolved
|
|
166
|
+
* program. Returned values are spread over `adapter.launchDefaults` so they
|
|
167
|
+
* take precedence over the static defaults but can still be overridden by
|
|
168
|
+
* the fields `DapSessionManager.launch` sets explicitly (program, cwd, args).
|
|
169
|
+
*
|
|
170
|
+
* Currently scoped to dlv, where `mode` selects how the program path is
|
|
171
|
+
* interpreted: directories and `.go` source files debug as a Go package
|
|
172
|
+
* (`mode=debug`), anything else is treated as a compiled binary (`mode=exec`).
|
|
173
|
+
*/
|
|
174
|
+
export function resolveLaunchOverrides(
|
|
175
|
+
adapter: DapResolvedAdapter,
|
|
176
|
+
program: string,
|
|
177
|
+
programKind: LaunchProgramKind,
|
|
178
|
+
): Record<string, unknown> {
|
|
179
|
+
if (adapter.name === "dlv") {
|
|
180
|
+
const extension = path.extname(program).toLowerCase();
|
|
181
|
+
if (programKind === "directory" || extension === ".go") {
|
|
182
|
+
return { mode: "debug" };
|
|
183
|
+
}
|
|
184
|
+
if (programKind === "file") {
|
|
185
|
+
return { mode: "exec" };
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return {};
|
|
189
|
+
}
|
package/src/dap/defaults.json
CHANGED
package/src/dap/session.ts
CHANGED
|
@@ -259,6 +259,7 @@ export class DapSessionManager {
|
|
|
259
259
|
session.needsConfigurationDone = session.capabilities.supportsConfigurationDoneRequest === true;
|
|
260
260
|
const launchArguments: DapLaunchArguments = {
|
|
261
261
|
...options.adapter.launchDefaults,
|
|
262
|
+
...(options.extraLaunchArguments ?? {}),
|
|
262
263
|
program: options.program,
|
|
263
264
|
cwd: options.cwd,
|
|
264
265
|
args: options.args,
|
package/src/dap/types.ts
CHANGED
|
@@ -488,6 +488,10 @@ export interface DapAdapterConfig {
|
|
|
488
488
|
* On Linux, connects via a unix domain socket.
|
|
489
489
|
* On macOS, the adapter dials into a local TCP listener (--client-addr). */
|
|
490
490
|
connectMode?: "stdio" | "socket";
|
|
491
|
+
/** When true, the adapter accepts a directory as the launch `program`
|
|
492
|
+
* (e.g. dlv treats it as a Go package path). When false/undefined, the
|
|
493
|
+
* debug tool rejects directory programs upfront. */
|
|
494
|
+
acceptsDirectoryProgram?: boolean;
|
|
491
495
|
}
|
|
492
496
|
|
|
493
497
|
export interface DapResolvedAdapter {
|
|
@@ -501,6 +505,7 @@ export interface DapResolvedAdapter {
|
|
|
501
505
|
launchDefaults: Record<string, unknown>;
|
|
502
506
|
attachDefaults: Record<string, unknown>;
|
|
503
507
|
connectMode: "stdio" | "socket";
|
|
508
|
+
acceptsDirectoryProgram: boolean;
|
|
504
509
|
}
|
|
505
510
|
|
|
506
511
|
export interface DapBreakpointRecord {
|
|
@@ -589,6 +594,11 @@ export interface DapLaunchSessionOptions {
|
|
|
589
594
|
program: string;
|
|
590
595
|
args?: string[];
|
|
591
596
|
cwd: string;
|
|
597
|
+
/** Per-launch overrides merged over `adapter.launchDefaults`. Used to
|
|
598
|
+
* inject adapter-specific values that depend on the resolved program
|
|
599
|
+
* (e.g. dlv's `mode` switches between `debug` and `exec` based on
|
|
600
|
+
* whether `program` is a Go package path or a compiled binary). */
|
|
601
|
+
extraLaunchArguments?: Record<string, unknown>;
|
|
592
602
|
}
|
|
593
603
|
|
|
594
604
|
export interface DapAttachSessionOptions {
|