@bubblebrain-ai/bubble 0.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/README.md +70 -0
- package/dist/agent/evidence-tracker.d.ts +15 -0
- package/dist/agent/evidence-tracker.js +93 -0
- package/dist/agent/execution-governor.d.ts +30 -0
- package/dist/agent/execution-governor.js +169 -0
- package/dist/agent/subtask-policy.d.ts +14 -0
- package/dist/agent/subtask-policy.js +60 -0
- package/dist/agent/task-classifier.d.ts +3 -0
- package/dist/agent/task-classifier.js +36 -0
- package/dist/agent/tool-arbiter.d.ts +7 -0
- package/dist/agent/tool-arbiter.js +33 -0
- package/dist/agent/tool-intent.d.ts +20 -0
- package/dist/agent/tool-intent.js +176 -0
- package/dist/agent.d.ts +95 -0
- package/dist/agent.js +672 -0
- package/dist/approval/controller.d.ts +48 -0
- package/dist/approval/controller.js +78 -0
- package/dist/approval/danger.d.ts +13 -0
- package/dist/approval/danger.js +55 -0
- package/dist/approval/diff-hunks.d.ts +12 -0
- package/dist/approval/diff-hunks.js +32 -0
- package/dist/approval/session-cache.d.ts +35 -0
- package/dist/approval/session-cache.js +68 -0
- package/dist/approval/tool-helper.d.ts +14 -0
- package/dist/approval/tool-helper.js +32 -0
- package/dist/approval/types.d.ts +56 -0
- package/dist/approval/types.js +8 -0
- package/dist/bubble-home.d.ts +8 -0
- package/dist/bubble-home.js +19 -0
- package/dist/cli.d.ts +19 -0
- package/dist/cli.js +82 -0
- package/dist/config.d.ts +41 -0
- package/dist/config.js +144 -0
- package/dist/context/budget.d.ts +21 -0
- package/dist/context/budget.js +72 -0
- package/dist/context/compact-llm.d.ts +16 -0
- package/dist/context/compact-llm.js +132 -0
- package/dist/context/compact.d.ts +15 -0
- package/dist/context/compact.js +251 -0
- package/dist/context/overflow.d.ts +9 -0
- package/dist/context/overflow.js +46 -0
- package/dist/context/projector.d.ts +26 -0
- package/dist/context/projector.js +150 -0
- package/dist/context/prune.d.ts +9 -0
- package/dist/context/prune.js +111 -0
- package/dist/lsp/config.d.ts +18 -0
- package/dist/lsp/config.js +58 -0
- package/dist/lsp/diagnostics.d.ts +24 -0
- package/dist/lsp/diagnostics.js +103 -0
- package/dist/lsp/index.d.ts +3 -0
- package/dist/lsp/index.js +3 -0
- package/dist/lsp/service.d.ts +85 -0
- package/dist/lsp/service.js +695 -0
- package/dist/main.d.ts +5 -0
- package/dist/main.js +352 -0
- package/dist/mcp/client.d.ts +68 -0
- package/dist/mcp/client.js +163 -0
- package/dist/mcp/config.d.ts +26 -0
- package/dist/mcp/config.js +127 -0
- package/dist/mcp/manager.d.ts +55 -0
- package/dist/mcp/manager.js +296 -0
- package/dist/mcp/name.d.ts +26 -0
- package/dist/mcp/name.js +40 -0
- package/dist/mcp/transports.d.ts +53 -0
- package/dist/mcp/transports.js +248 -0
- package/dist/mcp/types.d.ts +111 -0
- package/dist/mcp/types.js +14 -0
- package/dist/memory/db.d.ts +62 -0
- package/dist/memory/db.js +313 -0
- package/dist/memory/index.d.ts +9 -0
- package/dist/memory/index.js +9 -0
- package/dist/memory/paths.d.ts +18 -0
- package/dist/memory/paths.js +38 -0
- package/dist/memory/phase1.d.ts +23 -0
- package/dist/memory/phase1.js +172 -0
- package/dist/memory/phase2.d.ts +19 -0
- package/dist/memory/phase2.js +100 -0
- package/dist/memory/prompts.d.ts +19 -0
- package/dist/memory/prompts.js +99 -0
- package/dist/memory/reset.d.ts +1 -0
- package/dist/memory/reset.js +13 -0
- package/dist/memory/start.d.ts +24 -0
- package/dist/memory/start.js +50 -0
- package/dist/memory/storage.d.ts +10 -0
- package/dist/memory/storage.js +82 -0
- package/dist/memory/store.d.ts +43 -0
- package/dist/memory/store.js +193 -0
- package/dist/memory/usage.d.ts +1 -0
- package/dist/memory/usage.js +38 -0
- package/dist/model-catalog.d.ts +20 -0
- package/dist/model-catalog.js +99 -0
- package/dist/model-config.d.ts +32 -0
- package/dist/model-config.js +59 -0
- package/dist/model-pricing.d.ts +23 -0
- package/dist/model-pricing.js +46 -0
- package/dist/oauth/index.d.ts +3 -0
- package/dist/oauth/index.js +2 -0
- package/dist/oauth/openai-codex.d.ts +9 -0
- package/dist/oauth/openai-codex.js +173 -0
- package/dist/oauth/storage.d.ts +18 -0
- package/dist/oauth/storage.js +60 -0
- package/dist/oauth/types.d.ts +15 -0
- package/dist/oauth/types.js +1 -0
- package/dist/orchestrator/default-hooks.d.ts +2 -0
- package/dist/orchestrator/default-hooks.js +96 -0
- package/dist/orchestrator/hooks.d.ts +78 -0
- package/dist/orchestrator/hooks.js +52 -0
- package/dist/orchestrator/workflow.d.ts +10 -0
- package/dist/orchestrator/workflow.js +22 -0
- package/dist/permission/mode.d.ts +23 -0
- package/dist/permission/mode.js +20 -0
- package/dist/permissions/rule.d.ts +39 -0
- package/dist/permissions/rule.js +234 -0
- package/dist/permissions/settings.d.ts +71 -0
- package/dist/permissions/settings.js +202 -0
- package/dist/permissions/types.d.ts +61 -0
- package/dist/permissions/types.js +14 -0
- package/dist/prompt/compose.d.ts +12 -0
- package/dist/prompt/compose.js +67 -0
- package/dist/prompt/environment.d.ts +12 -0
- package/dist/prompt/environment.js +38 -0
- package/dist/prompt/provider-prompts/anthropic.d.ts +1 -0
- package/dist/prompt/provider-prompts/anthropic.js +5 -0
- package/dist/prompt/provider-prompts/codex.d.ts +1 -0
- package/dist/prompt/provider-prompts/codex.js +5 -0
- package/dist/prompt/provider-prompts/default.d.ts +1 -0
- package/dist/prompt/provider-prompts/default.js +6 -0
- package/dist/prompt/provider-prompts/gemini.d.ts +1 -0
- package/dist/prompt/provider-prompts/gemini.js +5 -0
- package/dist/prompt/provider-prompts/gpt.d.ts +1 -0
- package/dist/prompt/provider-prompts/gpt.js +5 -0
- package/dist/prompt/reminders.d.ts +30 -0
- package/dist/prompt/reminders.js +164 -0
- package/dist/prompt/runtime.d.ts +12 -0
- package/dist/prompt/runtime.js +31 -0
- package/dist/prompt/skills.d.ts +2 -0
- package/dist/prompt/skills.js +4 -0
- package/dist/provider-openai-codex.d.ts +14 -0
- package/dist/provider-openai-codex.js +409 -0
- package/dist/provider-registry.d.ts +56 -0
- package/dist/provider-registry.js +244 -0
- package/dist/provider-transform.d.ts +10 -0
- package/dist/provider-transform.js +69 -0
- package/dist/provider.d.ts +31 -0
- package/dist/provider.js +269 -0
- package/dist/question/controller.d.ts +22 -0
- package/dist/question/controller.js +97 -0
- package/dist/question/index.d.ts +2 -0
- package/dist/question/index.js +2 -0
- package/dist/question/types.d.ts +42 -0
- package/dist/question/types.js +6 -0
- package/dist/session-log.d.ts +16 -0
- package/dist/session-log.js +267 -0
- package/dist/session-types.d.ts +55 -0
- package/dist/session-types.js +1 -0
- package/dist/session.d.ts +32 -0
- package/dist/session.js +135 -0
- package/dist/skills/discovery.d.ts +12 -0
- package/dist/skills/discovery.js +148 -0
- package/dist/skills/format.d.ts +2 -0
- package/dist/skills/format.js +47 -0
- package/dist/skills/frontmatter.d.ts +5 -0
- package/dist/skills/frontmatter.js +60 -0
- package/dist/skills/invocation.d.ts +8 -0
- package/dist/skills/invocation.js +51 -0
- package/dist/skills/registry.d.ts +17 -0
- package/dist/skills/registry.js +42 -0
- package/dist/skills/types.d.ts +32 -0
- package/dist/skills/types.js +1 -0
- package/dist/slash-commands/commands.d.ts +7 -0
- package/dist/slash-commands/commands.js +779 -0
- package/dist/slash-commands/index.d.ts +4 -0
- package/dist/slash-commands/index.js +8 -0
- package/dist/slash-commands/registry.d.ts +31 -0
- package/dist/slash-commands/registry.js +70 -0
- package/dist/slash-commands/types.d.ts +44 -0
- package/dist/slash-commands/types.js +1 -0
- package/dist/slash-commands/unified.d.ts +38 -0
- package/dist/slash-commands/unified.js +38 -0
- package/dist/system-prompt.d.ts +34 -0
- package/dist/system-prompt.js +7 -0
- package/dist/tools/bash.d.ts +6 -0
- package/dist/tools/bash.js +135 -0
- package/dist/tools/edit.d.ts +16 -0
- package/dist/tools/edit.js +95 -0
- package/dist/tools/exa-mcp.d.ts +3 -0
- package/dist/tools/exa-mcp.js +74 -0
- package/dist/tools/exit-plan-mode.d.ts +17 -0
- package/dist/tools/exit-plan-mode.js +68 -0
- package/dist/tools/glob.d.ts +5 -0
- package/dist/tools/glob.js +129 -0
- package/dist/tools/grep.d.ts +5 -0
- package/dist/tools/grep.js +111 -0
- package/dist/tools/index.d.ts +36 -0
- package/dist/tools/index.js +59 -0
- package/dist/tools/lsp.d.ts +4 -0
- package/dist/tools/lsp.js +92 -0
- package/dist/tools/memory.d.ts +3 -0
- package/dist/tools/memory.js +90 -0
- package/dist/tools/question.d.ts +3 -0
- package/dist/tools/question.js +174 -0
- package/dist/tools/read.d.ts +7 -0
- package/dist/tools/read.js +83 -0
- package/dist/tools/sensitive-paths.d.ts +3 -0
- package/dist/tools/sensitive-paths.js +24 -0
- package/dist/tools/skill.d.ts +5 -0
- package/dist/tools/skill.js +51 -0
- package/dist/tools/task.d.ts +2 -0
- package/dist/tools/task.js +57 -0
- package/dist/tools/todo.d.ts +12 -0
- package/dist/tools/todo.js +151 -0
- package/dist/tools/tool-search.d.ts +23 -0
- package/dist/tools/tool-search.js +124 -0
- package/dist/tools/web-fetch.d.ts +6 -0
- package/dist/tools/web-fetch.js +75 -0
- package/dist/tools/web-search.d.ts +5 -0
- package/dist/tools/web-search.js +49 -0
- package/dist/tools/write.d.ts +11 -0
- package/dist/tools/write.js +77 -0
- package/dist/tui/display-history.d.ts +35 -0
- package/dist/tui/display-history.js +243 -0
- package/dist/tui/file-mentions.d.ts +29 -0
- package/dist/tui/file-mentions.js +174 -0
- package/dist/tui/image-paste.d.ts +54 -0
- package/dist/tui/image-paste.js +288 -0
- package/dist/tui/markdown-theme-rules.d.ts +23 -0
- package/dist/tui/markdown-theme-rules.js +164 -0
- package/dist/tui/markdown-theme.d.ts +5 -0
- package/dist/tui/markdown-theme.js +27 -0
- package/dist/tui/opencode-spinner.d.ts +21 -0
- package/dist/tui/opencode-spinner.js +216 -0
- package/dist/tui/prompt-keybindings.d.ts +41 -0
- package/dist/tui/prompt-keybindings.js +28 -0
- package/dist/tui/recent-activity.d.ts +8 -0
- package/dist/tui/recent-activity.js +71 -0
- package/dist/tui/run.d.ts +39 -0
- package/dist/tui/run.js +5696 -0
- package/dist/tui/sidebar-mcp.d.ts +31 -0
- package/dist/tui/sidebar-mcp.js +62 -0
- package/dist/tui/sidebar-state.d.ts +12 -0
- package/dist/tui/sidebar-state.js +69 -0
- package/dist/types.d.ts +219 -0
- package/dist/types.js +4 -0
- package/dist/variant/thinking-level.d.ts +5 -0
- package/dist/variant/thinking-level.js +25 -0
- package/dist/variant/variant-resolver.d.ts +4 -0
- package/dist/variant/variant-resolver.js +12 -0
- package/package.json +47 -0
|
@@ -0,0 +1,244 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Multi-provider registry.
|
|
3
|
+
*
|
|
4
|
+
* Supports OpenAI-compatible providers with dynamic or static model lists.
|
|
5
|
+
* Reads provider configuration from models.json first, then falls back to config.json.
|
|
6
|
+
*/
|
|
7
|
+
import { BUILTIN_PROVIDERS as CATALOG_PROVIDERS, getBuiltinProvider, listBuiltinModels, } from "./model-catalog.js";
|
|
8
|
+
import { ModelConfig } from "./model-config.js";
|
|
9
|
+
import { AuthStorage } from "./oauth/index.js";
|
|
10
|
+
import { fetchOpenAICodexModels } from "./provider-openai-codex.js";
|
|
11
|
+
import { refreshOpenAICodex } from "./oauth/openai-codex.js";
|
|
12
|
+
export const BUILTIN_PROVIDERS = CATALOG_PROVIDERS;
|
|
13
|
+
export const USER_VISIBLE_PROVIDER_IDS = BUILTIN_PROVIDERS
|
|
14
|
+
.filter((provider) => provider.id !== "openrouter" && provider.id !== "openai-codex")
|
|
15
|
+
.map((provider) => provider.id);
|
|
16
|
+
export function isUserVisibleProvider(providerId) {
|
|
17
|
+
return USER_VISIBLE_PROVIDER_IDS.includes(providerId);
|
|
18
|
+
}
|
|
19
|
+
export class ProviderRegistry {
|
|
20
|
+
config;
|
|
21
|
+
modelConfig;
|
|
22
|
+
authStorage;
|
|
23
|
+
constructor(config) {
|
|
24
|
+
this.config = config;
|
|
25
|
+
this.modelConfig = new ModelConfig();
|
|
26
|
+
this.authStorage = new AuthStorage();
|
|
27
|
+
}
|
|
28
|
+
getModelConfig() {
|
|
29
|
+
return this.modelConfig;
|
|
30
|
+
}
|
|
31
|
+
getAuthStorage() {
|
|
32
|
+
return this.authStorage;
|
|
33
|
+
}
|
|
34
|
+
supportsOAuth(providerId) {
|
|
35
|
+
return !!getBuiltinProvider(providerId)?.supportsOAuth;
|
|
36
|
+
}
|
|
37
|
+
resolveOAuthAuthKey(providerId) {
|
|
38
|
+
if (providerId === "openai" && !this.authStorage.has("openai") && this.authStorage.has("openai-codex")) {
|
|
39
|
+
return "openai-codex";
|
|
40
|
+
}
|
|
41
|
+
return providerId;
|
|
42
|
+
}
|
|
43
|
+
getDefaultModel(providerId, authType = "api") {
|
|
44
|
+
const customModels = this.modelConfig.getCustomModels(providerId);
|
|
45
|
+
if (customModels.length > 0) {
|
|
46
|
+
return customModels[0].id;
|
|
47
|
+
}
|
|
48
|
+
if (providerId === "openai" && authType === "oauth") {
|
|
49
|
+
return listBuiltinModels("openai-codex")[0]?.id;
|
|
50
|
+
}
|
|
51
|
+
return listBuiltinModels(providerId)[0]?.id;
|
|
52
|
+
}
|
|
53
|
+
async prepareProvider(providerId) {
|
|
54
|
+
const authKey = this.resolveOAuthAuthKey(providerId);
|
|
55
|
+
if (providerId === "openai" && this.authStorage.isExpired(authKey)) {
|
|
56
|
+
const creds = this.authStorage.get(authKey);
|
|
57
|
+
if (creds?.refreshToken) {
|
|
58
|
+
const refreshed = await refreshOpenAICodex(creds.refreshToken);
|
|
59
|
+
this.authStorage.set("openai", {
|
|
60
|
+
type: "oauth",
|
|
61
|
+
accessToken: refreshed.accessToken,
|
|
62
|
+
refreshToken: refreshed.refreshToken,
|
|
63
|
+
expiresAt: refreshed.expiresAt,
|
|
64
|
+
idToken: refreshed.idToken,
|
|
65
|
+
accountId: refreshed.accountId,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
getConfigured() {
|
|
71
|
+
// 1. Try models.json first
|
|
72
|
+
const modelsJsonProviders = this.modelConfig.getAllProviders();
|
|
73
|
+
const keys = Object.keys(modelsJsonProviders);
|
|
74
|
+
let providers = [];
|
|
75
|
+
if (keys.length > 0) {
|
|
76
|
+
providers = keys.map((id) => {
|
|
77
|
+
const builtin = getBuiltinProvider(id);
|
|
78
|
+
const cfg = modelsJsonProviders[id];
|
|
79
|
+
return {
|
|
80
|
+
id,
|
|
81
|
+
name: builtin?.name || id,
|
|
82
|
+
baseURL: cfg.baseURL || builtin?.baseURL || "",
|
|
83
|
+
apiKey: cfg.apiKey || "",
|
|
84
|
+
enabled: true,
|
|
85
|
+
authType: "api",
|
|
86
|
+
};
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
// 2. Fall back to config.json providers (interactive TUI style)
|
|
91
|
+
providers = this.config.getProviders();
|
|
92
|
+
}
|
|
93
|
+
// 3. Inject OAuth access tokens
|
|
94
|
+
for (const p of providers) {
|
|
95
|
+
const authKey = this.resolveOAuthAuthKey(p.id);
|
|
96
|
+
if (this.authStorage.has(authKey)) {
|
|
97
|
+
const token = this.authStorage.getAccessToken(authKey);
|
|
98
|
+
if (token) {
|
|
99
|
+
p.apiKey = token;
|
|
100
|
+
p.authType = "oauth";
|
|
101
|
+
if (p.id === "openai") {
|
|
102
|
+
p.baseURL = "https://chatgpt.com/backend-api";
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
// 4. Auto-include built-in OAuth providers that have credentials
|
|
108
|
+
const configuredIds = new Set(providers.map((p) => p.id));
|
|
109
|
+
for (const builtin of BUILTIN_PROVIDERS) {
|
|
110
|
+
if (builtin.id === "openai-codex")
|
|
111
|
+
continue;
|
|
112
|
+
if (configuredIds.has(builtin.id))
|
|
113
|
+
continue;
|
|
114
|
+
const authKey = this.resolveOAuthAuthKey(builtin.id);
|
|
115
|
+
if (this.authStorage.has(authKey)) {
|
|
116
|
+
const token = this.authStorage.getAccessToken(authKey);
|
|
117
|
+
providers.push({
|
|
118
|
+
...builtin,
|
|
119
|
+
apiKey: token || "",
|
|
120
|
+
enabled: !!token,
|
|
121
|
+
authType: "oauth",
|
|
122
|
+
...(builtin.id === "openai" ? { baseURL: "https://chatgpt.com/backend-api" } : {}),
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return providers;
|
|
127
|
+
}
|
|
128
|
+
getEnabled() {
|
|
129
|
+
return this.getConfigured().filter((p) => p.enabled && p.apiKey);
|
|
130
|
+
}
|
|
131
|
+
getDefault() {
|
|
132
|
+
const enabled = this.getEnabled();
|
|
133
|
+
if (enabled.length === 0)
|
|
134
|
+
return undefined;
|
|
135
|
+
const defaultId = this.config.getDefaultProvider();
|
|
136
|
+
const preferred = enabled.filter((provider) => isUserVisibleProvider(provider.id));
|
|
137
|
+
return preferred.find((p) => p.id === defaultId)
|
|
138
|
+
|| preferred[0]
|
|
139
|
+
|| enabled.find((p) => p.id === defaultId)
|
|
140
|
+
|| enabled[0];
|
|
141
|
+
}
|
|
142
|
+
setDefault(id) {
|
|
143
|
+
this.config.setDefaultProvider(id);
|
|
144
|
+
}
|
|
145
|
+
addProvider(id, apiKey) {
|
|
146
|
+
const builtin = getBuiltinProvider(id);
|
|
147
|
+
if (!builtin)
|
|
148
|
+
return false;
|
|
149
|
+
const providers = this.config.getProviders();
|
|
150
|
+
const idx = providers.findIndex((p) => p.id === id);
|
|
151
|
+
const profile = { ...builtin, apiKey, enabled: true };
|
|
152
|
+
if (idx >= 0) {
|
|
153
|
+
providers[idx] = profile;
|
|
154
|
+
}
|
|
155
|
+
else {
|
|
156
|
+
providers.push(profile);
|
|
157
|
+
}
|
|
158
|
+
this.config.setProviders(providers);
|
|
159
|
+
return true;
|
|
160
|
+
}
|
|
161
|
+
removeProvider(id) {
|
|
162
|
+
const providers = this.config.getProviders().filter((p) => p.id !== id);
|
|
163
|
+
this.config.setProviders(providers);
|
|
164
|
+
}
|
|
165
|
+
updateProviderKey(id, apiKey) {
|
|
166
|
+
const providers = this.config.getProviders();
|
|
167
|
+
const p = providers.find((x) => x.id === id);
|
|
168
|
+
if (p) {
|
|
169
|
+
p.apiKey = apiKey;
|
|
170
|
+
this.config.setProviders(providers);
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
async listModels(provider) {
|
|
174
|
+
// 1. Custom models from models.json always take precedence
|
|
175
|
+
const customModels = this.modelConfig.getCustomModels(provider.id);
|
|
176
|
+
if (customModels.length > 0) {
|
|
177
|
+
return customModels;
|
|
178
|
+
}
|
|
179
|
+
// 2. Built-in provider dynamic/static lists
|
|
180
|
+
if (provider.id === "openrouter") {
|
|
181
|
+
try {
|
|
182
|
+
const res = await fetch("https://openrouter.ai/api/v1/models");
|
|
183
|
+
const data = (await res.json());
|
|
184
|
+
const models = data.data || [];
|
|
185
|
+
return models.map((m) => ({
|
|
186
|
+
id: m.id,
|
|
187
|
+
name: m.name || m.id,
|
|
188
|
+
providerId: provider.id,
|
|
189
|
+
}));
|
|
190
|
+
}
|
|
191
|
+
catch {
|
|
192
|
+
// fall through to static
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
if (provider.id === "openai" && provider.authType === "oauth" && provider.apiKey) {
|
|
196
|
+
try {
|
|
197
|
+
const models = await fetchOpenAICodexModels({
|
|
198
|
+
baseURL: provider.baseURL,
|
|
199
|
+
accessToken: provider.apiKey,
|
|
200
|
+
});
|
|
201
|
+
if (models.length > 0) {
|
|
202
|
+
return models.map((id) => ({ id, name: id, providerId: provider.id }));
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
// fall through to static
|
|
207
|
+
}
|
|
208
|
+
return listBuiltinModels("openai-codex").map((model) => ({
|
|
209
|
+
id: model.id,
|
|
210
|
+
name: model.name,
|
|
211
|
+
providerId: provider.id,
|
|
212
|
+
}));
|
|
213
|
+
}
|
|
214
|
+
return listBuiltinModels(provider.id).map((model) => ({
|
|
215
|
+
id: model.id,
|
|
216
|
+
name: model.name,
|
|
217
|
+
providerId: provider.id,
|
|
218
|
+
}));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
/** Encode a model selection as "providerId:modelId". */
|
|
222
|
+
export function encodeModel(providerId, modelId) {
|
|
223
|
+
return `${providerId}:${modelId}`;
|
|
224
|
+
}
|
|
225
|
+
/** Decode "providerId:modelId" or legacy plain modelId. */
|
|
226
|
+
export function decodeModel(value) {
|
|
227
|
+
if (value.includes(":")) {
|
|
228
|
+
const [providerId, ...rest] = value.split(":");
|
|
229
|
+
return { providerId, modelId: rest.join(":") };
|
|
230
|
+
}
|
|
231
|
+
return { modelId: value };
|
|
232
|
+
}
|
|
233
|
+
/** Strip provider prefix for concise display. */
|
|
234
|
+
export function displayModel(model) {
|
|
235
|
+
const { modelId } = decodeModel(model);
|
|
236
|
+
return modelId;
|
|
237
|
+
}
|
|
238
|
+
/** Normalize user input to provider:model format when possible. */
|
|
239
|
+
export function normalizeModel(model, defaultProvider = "openai") {
|
|
240
|
+
const { providerId, modelId } = decodeModel(model);
|
|
241
|
+
if (providerId)
|
|
242
|
+
return model;
|
|
243
|
+
return encodeModel(defaultProvider, modelId);
|
|
244
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ThinkingLevel } from "./types.js";
|
|
2
|
+
export { getAvailableThinkingLevels, getDefaultThinkingLevel, normalizeThinkingLevel } from "./variant/variant-resolver.js";
|
|
3
|
+
export interface ProviderRequestConfig {
|
|
4
|
+
effectiveThinkingLevel: ThinkingLevel;
|
|
5
|
+
reasoningEffort?: ThinkingLevel;
|
|
6
|
+
reasoningContentEcho?: "tool_calls" | "all";
|
|
7
|
+
extraBody?: Record<string, unknown>;
|
|
8
|
+
omitTemperature?: boolean;
|
|
9
|
+
}
|
|
10
|
+
export declare function resolveProviderRequestConfig(providerId: string, modelId: string, requestedLevel: ThinkingLevel): ProviderRequestConfig;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { getAvailableThinkingLevels, normalizeThinkingLevel } from "./variant/variant-resolver.js";
|
|
2
|
+
export { getAvailableThinkingLevels, getDefaultThinkingLevel, normalizeThinkingLevel } from "./variant/variant-resolver.js";
|
|
3
|
+
const MOONSHOT_PROVIDER_IDS = new Set(["moonshot-cn", "moonshot-intl", "kimi-for-coding"]);
|
|
4
|
+
const KIMI_K25_FAMILY = new Set(["kimi-k2.5", "k2.6-code-preview", "kimi-k2.6"]);
|
|
5
|
+
const KIMI_THINKING_FAMILY = new Set(["kimi-k2-thinking", "kimi-k2-thinking-turbo"]);
|
|
6
|
+
export function resolveProviderRequestConfig(providerId, modelId, requestedLevel) {
|
|
7
|
+
const supportedLevels = getAvailableThinkingLevels(providerId, modelId);
|
|
8
|
+
const effectiveThinkingLevel = normalizeThinkingLevel(requestedLevel, supportedLevels);
|
|
9
|
+
// ChatGPT OAuth via openai-codex currently rejects explicit reasoning params for this account path.
|
|
10
|
+
// Keep the session/UI state, but don't send reasoning flags on this provider until the protocol is clearer.
|
|
11
|
+
if (providerId === "openai-codex") {
|
|
12
|
+
return { effectiveThinkingLevel };
|
|
13
|
+
}
|
|
14
|
+
if (providerId === "deepseek" && (modelId === "deepseek-v4-flash" || modelId === "deepseek-v4-pro")) {
|
|
15
|
+
return {
|
|
16
|
+
effectiveThinkingLevel,
|
|
17
|
+
reasoningContentEcho: "all",
|
|
18
|
+
extraBody: {
|
|
19
|
+
thinking: { type: "enabled" },
|
|
20
|
+
reasoning_effort: effectiveThinkingLevel,
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
// Zhipu/Z.AI OpenAI-compatible endpoints expose reasoning via a provider-specific
|
|
25
|
+
// `thinking` block rather than OpenAI's `reasoning_effort` shape.
|
|
26
|
+
if (["zhipuai", "zhipuai-coding-plan", "zai", "zai-coding-plan"].includes(providerId)) {
|
|
27
|
+
return {
|
|
28
|
+
effectiveThinkingLevel,
|
|
29
|
+
extraBody: effectiveThinkingLevel === "off"
|
|
30
|
+
? undefined
|
|
31
|
+
: {
|
|
32
|
+
thinking: {
|
|
33
|
+
type: "enabled",
|
|
34
|
+
clear_thinking: false,
|
|
35
|
+
},
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
// Moonshot / Kimi: kimi-k2.5 family (incl. k2.6-code-preview, kimi-k2.6) locks
|
|
40
|
+
// temperature/top_p/n/penalties and exposes thinking via extra_body.thinking;
|
|
41
|
+
// kimi-k2-thinking family locks temperature=1.
|
|
42
|
+
if (MOONSHOT_PROVIDER_IDS.has(providerId)) {
|
|
43
|
+
if (KIMI_K25_FAMILY.has(modelId)) {
|
|
44
|
+
return {
|
|
45
|
+
effectiveThinkingLevel,
|
|
46
|
+
omitTemperature: true,
|
|
47
|
+
reasoningContentEcho: "tool_calls",
|
|
48
|
+
extraBody: {
|
|
49
|
+
thinking: { type: effectiveThinkingLevel === "off" ? "disabled" : "enabled" },
|
|
50
|
+
},
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
if (KIMI_THINKING_FAMILY.has(modelId)) {
|
|
54
|
+
return { effectiveThinkingLevel, omitTemperature: true };
|
|
55
|
+
}
|
|
56
|
+
return { effectiveThinkingLevel };
|
|
57
|
+
}
|
|
58
|
+
if (providerId === "openai"
|
|
59
|
+
|| providerId === "openrouter"
|
|
60
|
+
|| providerId === "google"
|
|
61
|
+
|| providerId === "azure"
|
|
62
|
+
|| providerId === "openai-compatible") {
|
|
63
|
+
return {
|
|
64
|
+
effectiveThinkingLevel,
|
|
65
|
+
reasoningEffort: effectiveThinkingLevel === "off" ? undefined : effectiveThinkingLevel,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
return { effectiveThinkingLevel };
|
|
69
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-compatible Provider implementation.
|
|
3
|
+
*
|
|
4
|
+
* Works with OpenRouter, OpenAI, DeepSeek, Google, Groq, Together, and local OpenAI-compatible endpoints.
|
|
5
|
+
*/
|
|
6
|
+
import type { Message, Provider, StreamChunk, ThinkingLevel } from "./types.js";
|
|
7
|
+
type ReasoningContentEcho = "tool_calls" | "all";
|
|
8
|
+
export declare function toChatCompletionsMessage(message: Message, options?: {
|
|
9
|
+
reasoningContentEcho?: ReasoningContentEcho;
|
|
10
|
+
}): Record<string, unknown>;
|
|
11
|
+
export interface ProviderInstanceOptions {
|
|
12
|
+
providerId?: string;
|
|
13
|
+
apiKey: string;
|
|
14
|
+
baseURL: string;
|
|
15
|
+
/** Requested thinking level */
|
|
16
|
+
thinkingLevel?: ThinkingLevel;
|
|
17
|
+
}
|
|
18
|
+
export declare function createUnavailableProvider(message: string): Provider;
|
|
19
|
+
export declare function createProviderInstance(options: ProviderInstanceOptions): Provider;
|
|
20
|
+
export declare function normalizeToolArgs(raw: string): string;
|
|
21
|
+
/**
|
|
22
|
+
* Convert an OpenAI-compatible chat-completions stream into our internal StreamChunk events.
|
|
23
|
+
*
|
|
24
|
+
* Multi-tool-call streams are buffered by `index` and emitted in index order at
|
|
25
|
+
* `finish_reason === "tool_calls"`, so the agent layer always sees a clean
|
|
26
|
+
* (isStart -> args -> isEnd) sequence per call. This matters for providers like
|
|
27
|
+
* Kimi K2.5 that emit several parallel tool calls per assistant turn -- the
|
|
28
|
+
* previous single-slot implementation silently dropped every call but the last.
|
|
29
|
+
*/
|
|
30
|
+
export declare function translateOpenAIStream(stream: AsyncIterable<any>): AsyncIterable<StreamChunk>;
|
|
31
|
+
export {};
|
package/dist/provider.js
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* OpenAI-compatible Provider implementation.
|
|
3
|
+
*
|
|
4
|
+
* Works with OpenRouter, OpenAI, DeepSeek, Google, Groq, Together, and local OpenAI-compatible endpoints.
|
|
5
|
+
*/
|
|
6
|
+
import OpenAI from "openai";
|
|
7
|
+
import { createOpenAICodexProvider, isOpenAICodexBaseUrl } from "./provider-openai-codex.js";
|
|
8
|
+
import { resolveProviderRequestConfig } from "./provider-transform.js";
|
|
9
|
+
export function toChatCompletionsMessage(message, options = {}) {
|
|
10
|
+
const reasoningContentEcho = options.reasoningContentEcho ?? "tool_calls";
|
|
11
|
+
if (message.role === "assistant") {
|
|
12
|
+
const out = {
|
|
13
|
+
role: "assistant",
|
|
14
|
+
content: message.content || null,
|
|
15
|
+
};
|
|
16
|
+
if (reasoningContentEcho === "all") {
|
|
17
|
+
// DeepSeek thinking mode requires every assistant message to echo the
|
|
18
|
+
// provider field, even when the original value is an empty string.
|
|
19
|
+
out.reasoning_content = message.reasoning ?? "";
|
|
20
|
+
}
|
|
21
|
+
if (message.toolCalls && message.toolCalls.length > 0) {
|
|
22
|
+
out.tool_calls = message.toolCalls.map((tc) => ({
|
|
23
|
+
id: tc.id,
|
|
24
|
+
type: "function",
|
|
25
|
+
function: { name: tc.name, arguments: tc.arguments || "{}" },
|
|
26
|
+
}));
|
|
27
|
+
// Kimi-k2.5 with thinking enabled requires reasoning_content to be echoed
|
|
28
|
+
// back on assistant messages that carry tool_calls.
|
|
29
|
+
if (message.reasoning && reasoningContentEcho === "tool_calls") {
|
|
30
|
+
out.reasoning_content = message.reasoning;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
35
|
+
if (message.role === "tool") {
|
|
36
|
+
return { role: "tool", tool_call_id: message.toolCallId, content: message.content };
|
|
37
|
+
}
|
|
38
|
+
return { role: message.role, content: message.content };
|
|
39
|
+
}
|
|
40
|
+
export function createUnavailableProvider(message) {
|
|
41
|
+
async function* streamChat() {
|
|
42
|
+
throw new Error(message);
|
|
43
|
+
}
|
|
44
|
+
async function complete() {
|
|
45
|
+
throw new Error(message);
|
|
46
|
+
}
|
|
47
|
+
return { streamChat, complete };
|
|
48
|
+
}
|
|
49
|
+
export function createProviderInstance(options) {
|
|
50
|
+
if (isOpenAICodexBaseUrl(options.baseURL)) {
|
|
51
|
+
return createOpenAICodexProvider({ ...options, providerId: options.providerId || "openai-codex" });
|
|
52
|
+
}
|
|
53
|
+
const client = new OpenAI({
|
|
54
|
+
apiKey: options.apiKey,
|
|
55
|
+
baseURL: options.baseURL,
|
|
56
|
+
});
|
|
57
|
+
const fallbackModel = "gpt-4o";
|
|
58
|
+
async function* streamChat(messages, chatOptions) {
|
|
59
|
+
const requestConfig = resolveProviderRequestConfig(options.providerId || "", chatOptions.model, chatOptions.thinkingLevel ?? options.thinkingLevel ?? "off");
|
|
60
|
+
const tools = chatOptions.tools?.map((t) => ({
|
|
61
|
+
type: "function",
|
|
62
|
+
function: {
|
|
63
|
+
name: t.name,
|
|
64
|
+
description: t.description,
|
|
65
|
+
parameters: t.parameters,
|
|
66
|
+
},
|
|
67
|
+
}));
|
|
68
|
+
const body = {
|
|
69
|
+
model: chatOptions.model,
|
|
70
|
+
messages: messages.map((message) => toChatCompletionsMessage(message, {
|
|
71
|
+
reasoningContentEcho: requestConfig.reasoningContentEcho ?? "tool_calls",
|
|
72
|
+
})),
|
|
73
|
+
tools: tools && tools.length > 0 ? tools : undefined,
|
|
74
|
+
tool_choice: tools && tools.length > 0 ? "auto" : undefined,
|
|
75
|
+
stream: true,
|
|
76
|
+
};
|
|
77
|
+
// DeepSeek only emits final usage in streaming mode when this flag is set.
|
|
78
|
+
if (options.providerId === "deepseek") {
|
|
79
|
+
body.stream_options = { include_usage: true };
|
|
80
|
+
}
|
|
81
|
+
if (!requestConfig.omitTemperature) {
|
|
82
|
+
body.temperature = chatOptions.temperature ?? 0.2;
|
|
83
|
+
}
|
|
84
|
+
if (requestConfig.extraBody) {
|
|
85
|
+
Object.assign(body, requestConfig.extraBody);
|
|
86
|
+
}
|
|
87
|
+
if (requestConfig.reasoningEffort && requestConfig.reasoningEffort !== "off") {
|
|
88
|
+
body.reasoning = { enabled: true };
|
|
89
|
+
}
|
|
90
|
+
const stream = (await client.chat.completions.create(body));
|
|
91
|
+
yield* translateOpenAIStream(stream);
|
|
92
|
+
yield { type: "done" };
|
|
93
|
+
}
|
|
94
|
+
async function complete(messages, chatOptions) {
|
|
95
|
+
const requestConfig = resolveProviderRequestConfig(options.providerId || "", chatOptions?.model ?? fallbackModel, chatOptions?.thinkingLevel ?? options.thinkingLevel ?? "off");
|
|
96
|
+
const body = {
|
|
97
|
+
model: chatOptions?.model ?? fallbackModel,
|
|
98
|
+
messages: messages.map((message) => toChatCompletionsMessage(message, {
|
|
99
|
+
reasoningContentEcho: requestConfig.reasoningContentEcho ?? "tool_calls",
|
|
100
|
+
})),
|
|
101
|
+
};
|
|
102
|
+
if (!requestConfig.omitTemperature) {
|
|
103
|
+
body.temperature = chatOptions?.temperature ?? 0.2;
|
|
104
|
+
}
|
|
105
|
+
if (requestConfig.extraBody) {
|
|
106
|
+
Object.assign(body, requestConfig.extraBody);
|
|
107
|
+
}
|
|
108
|
+
if (requestConfig.reasoningEffort && requestConfig.reasoningEffort !== "off") {
|
|
109
|
+
body.reasoning = { enabled: true };
|
|
110
|
+
}
|
|
111
|
+
const response = await client.chat.completions.create(body);
|
|
112
|
+
return response.choices[0]?.message?.content ?? "";
|
|
113
|
+
}
|
|
114
|
+
return { streamChat, complete };
|
|
115
|
+
}
|
|
116
|
+
// Some providers (notably Fireworks-hosted Kimi) stream tool-call arguments
|
|
117
|
+
// as repeated full snapshots in each delta instead of incremental chunks, so
|
|
118
|
+
// a naive `+=` produces `{"x":1}{"x":1}` — not valid JSON. Parse the raw
|
|
119
|
+
// stream; if it doesn't parse but contains a balanced `{…}` prefix or suffix
|
|
120
|
+
// that does, use that. Empty or unsalvageable input becomes `"{}"` so the
|
|
121
|
+
// downstream echo to the model is always valid JSON.
|
|
122
|
+
export function normalizeToolArgs(raw) {
|
|
123
|
+
const s = (raw ?? "").trim();
|
|
124
|
+
if (!s)
|
|
125
|
+
return "{}";
|
|
126
|
+
try {
|
|
127
|
+
JSON.parse(s);
|
|
128
|
+
return s;
|
|
129
|
+
}
|
|
130
|
+
catch { }
|
|
131
|
+
const firstBrace = extractBalancedJson(s, 0);
|
|
132
|
+
if (firstBrace) {
|
|
133
|
+
try {
|
|
134
|
+
JSON.parse(firstBrace);
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
return "{}";
|
|
138
|
+
}
|
|
139
|
+
// If the content after the first balanced object is another valid object
|
|
140
|
+
// with the same parse, we've got a snapshot duplication — keep one copy.
|
|
141
|
+
const rest = s.slice(firstBrace.length).trim();
|
|
142
|
+
if (!rest)
|
|
143
|
+
return firstBrace;
|
|
144
|
+
try {
|
|
145
|
+
JSON.parse(rest);
|
|
146
|
+
return firstBrace;
|
|
147
|
+
}
|
|
148
|
+
catch { }
|
|
149
|
+
return firstBrace;
|
|
150
|
+
}
|
|
151
|
+
return "{}";
|
|
152
|
+
}
|
|
153
|
+
function extractBalancedJson(s, start) {
|
|
154
|
+
if (s[start] !== "{")
|
|
155
|
+
return null;
|
|
156
|
+
let depth = 0;
|
|
157
|
+
let inStr = false;
|
|
158
|
+
let escape = false;
|
|
159
|
+
for (let i = start; i < s.length; i++) {
|
|
160
|
+
const c = s[i];
|
|
161
|
+
if (escape) {
|
|
162
|
+
escape = false;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (c === "\\" && inStr) {
|
|
166
|
+
escape = true;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
if (c === '"') {
|
|
170
|
+
inStr = !inStr;
|
|
171
|
+
continue;
|
|
172
|
+
}
|
|
173
|
+
if (inStr)
|
|
174
|
+
continue;
|
|
175
|
+
if (c === "{")
|
|
176
|
+
depth++;
|
|
177
|
+
else if (c === "}") {
|
|
178
|
+
depth--;
|
|
179
|
+
if (depth === 0)
|
|
180
|
+
return s.slice(start, i + 1);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Convert an OpenAI-compatible chat-completions stream into our internal StreamChunk events.
|
|
187
|
+
*
|
|
188
|
+
* Multi-tool-call streams are buffered by `index` and emitted in index order at
|
|
189
|
+
* `finish_reason === "tool_calls"`, so the agent layer always sees a clean
|
|
190
|
+
* (isStart -> args -> isEnd) sequence per call. This matters for providers like
|
|
191
|
+
* Kimi K2.5 that emit several parallel tool calls per assistant turn -- the
|
|
192
|
+
* previous single-slot implementation silently dropped every call but the last.
|
|
193
|
+
*/
|
|
194
|
+
export async function* translateOpenAIStream(stream) {
|
|
195
|
+
const toolCalls = new Map();
|
|
196
|
+
function* flushToolCalls() {
|
|
197
|
+
if (toolCalls.size === 0)
|
|
198
|
+
return;
|
|
199
|
+
const sorted = [...toolCalls.entries()].sort(([a], [b]) => a - b);
|
|
200
|
+
for (const [, entry] of sorted) {
|
|
201
|
+
if (!entry.id || !entry.name)
|
|
202
|
+
continue;
|
|
203
|
+
const args = normalizeToolArgs(entry.args);
|
|
204
|
+
yield { type: "tool_call", id: entry.id, name: entry.name, arguments: "", isStart: true, isEnd: false };
|
|
205
|
+
yield { type: "tool_call", id: entry.id, name: entry.name, arguments: args, isStart: false, isEnd: false };
|
|
206
|
+
yield { type: "tool_call", id: entry.id, name: entry.name, arguments: "", isStart: false, isEnd: true };
|
|
207
|
+
}
|
|
208
|
+
toolCalls.clear();
|
|
209
|
+
}
|
|
210
|
+
for await (const chunk of stream) {
|
|
211
|
+
const delta = chunk.choices?.[0]?.delta;
|
|
212
|
+
const usage = chunk.usage;
|
|
213
|
+
if (usage) {
|
|
214
|
+
yield {
|
|
215
|
+
type: "usage",
|
|
216
|
+
usage: {
|
|
217
|
+
promptTokens: typeof usage.prompt_tokens === "number" ? usage.prompt_tokens : 0,
|
|
218
|
+
completionTokens: typeof usage.completion_tokens === "number" ? usage.completion_tokens : 0,
|
|
219
|
+
promptCacheHitTokens: typeof usage.prompt_cache_hit_tokens === "number" ? usage.prompt_cache_hit_tokens : undefined,
|
|
220
|
+
promptCacheMissTokens: typeof usage.prompt_cache_miss_tokens === "number" ? usage.prompt_cache_miss_tokens : undefined,
|
|
221
|
+
reasoningTokens: typeof usage.completion_tokens_details?.reasoning_tokens === "number"
|
|
222
|
+
? usage.completion_tokens_details.reasoning_tokens
|
|
223
|
+
: undefined,
|
|
224
|
+
totalTokens: typeof usage.total_tokens === "number" ? usage.total_tokens : undefined,
|
|
225
|
+
},
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
const reasoning = delta?.reasoning ?? delta?.thinking ?? delta?.reasoning_content;
|
|
229
|
+
if (reasoning) {
|
|
230
|
+
yield { type: "reasoning_delta", content: reasoning };
|
|
231
|
+
}
|
|
232
|
+
if (delta?.content) {
|
|
233
|
+
const thinkMatch = delta.content.match(/<think>([\s\S]*?)<\/think>/);
|
|
234
|
+
if (thinkMatch) {
|
|
235
|
+
if (thinkMatch[1]) {
|
|
236
|
+
yield { type: "reasoning_delta", content: thinkMatch[1] };
|
|
237
|
+
}
|
|
238
|
+
const remaining = delta.content.replace(/<think>[\s\S]*?<\/think>/, "");
|
|
239
|
+
if (remaining) {
|
|
240
|
+
yield { type: "text", content: remaining };
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
yield { type: "text", content: delta.content };
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
if (delta?.tool_calls) {
|
|
248
|
+
for (const tc of delta.tool_calls) {
|
|
249
|
+
const idx = typeof tc.index === "number" ? tc.index : 0;
|
|
250
|
+
let entry = toolCalls.get(idx);
|
|
251
|
+
if (!entry) {
|
|
252
|
+
entry = { id: "", name: "", args: "" };
|
|
253
|
+
toolCalls.set(idx, entry);
|
|
254
|
+
}
|
|
255
|
+
if (tc.id)
|
|
256
|
+
entry.id = tc.id;
|
|
257
|
+
if (tc.function?.name)
|
|
258
|
+
entry.name = tc.function.name;
|
|
259
|
+
if (typeof tc.function?.arguments === "string")
|
|
260
|
+
entry.args += tc.function.arguments;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
const finishReason = chunk.choices?.[0]?.finish_reason;
|
|
264
|
+
if (finishReason === "tool_calls") {
|
|
265
|
+
yield* flushToolCalls();
|
|
266
|
+
}
|
|
267
|
+
}
|
|
268
|
+
yield* flushToolCalls();
|
|
269
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { QuestionAnswer, QuestionEvent, QuestionPrompt, QuestionRequest, QuestionToolRef } from "./types.js";
|
|
2
|
+
import { QuestionRejectedError } from "./types.js";
|
|
3
|
+
export interface QuestionAskInput {
|
|
4
|
+
sessionID?: string;
|
|
5
|
+
questions: QuestionPrompt[];
|
|
6
|
+
tool?: QuestionToolRef;
|
|
7
|
+
}
|
|
8
|
+
export declare class QuestionController {
|
|
9
|
+
private pending;
|
|
10
|
+
private listeners;
|
|
11
|
+
private counter;
|
|
12
|
+
ask(input: QuestionAskInput): Promise<QuestionAnswer[]>;
|
|
13
|
+
reply(requestID: string, answers: QuestionAnswer[]): boolean;
|
|
14
|
+
reject(requestID: string): boolean;
|
|
15
|
+
list(sessionID?: string): QuestionRequest[];
|
|
16
|
+
subscribe(listener: (event: QuestionEvent) => void): () => void;
|
|
17
|
+
rejectAll(): void;
|
|
18
|
+
private emit;
|
|
19
|
+
private nextID;
|
|
20
|
+
}
|
|
21
|
+
export type { QuestionAnswer, QuestionEvent, QuestionPrompt, QuestionRequest };
|
|
22
|
+
export { QuestionRejectedError };
|