@ottocode/sdk 0.1.228 → 0.1.231
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/package.json +1 -1
- package/src/config/src/index.ts +2 -0
- package/src/config/src/manager.ts +1 -0
- package/src/core/src/index.ts +6 -0
- package/src/core/src/mcp/copilot-auth.ts +104 -0
- package/src/core/src/mcp/index.ts +9 -0
- package/src/core/src/mcp/server-manager.ts +44 -75
- package/src/core/src/terminals/rust-libs.ts +0 -5
- package/src/core/src/tools/builtin/bash.ts +154 -108
- package/src/index.ts +7 -0
- package/src/prompts/src/providers.ts +40 -12
- package/src/providers/src/catalog-manual.ts +1 -0
- package/src/providers/src/catalog.ts +249 -5
- package/src/providers/src/openrouter-client.ts +1 -0
- package/src/providers/src/utils.ts +1 -0
- package/src/tunnel/qr.ts +0 -1
- package/src/types/src/config.ts +9 -0
- package/src/types/src/index.ts +1 -0
- package/src/types/src/provider.ts +1 -0
package/package.json
CHANGED
package/src/config/src/index.ts
CHANGED
package/src/core/src/index.ts
CHANGED
|
@@ -115,9 +115,15 @@ export { isDebugEnabled, isTraceEnabled } from './utils/debug.ts';
|
|
|
115
115
|
export {
|
|
116
116
|
MCPClientWrapper,
|
|
117
117
|
MCPServerManager,
|
|
118
|
+
COPILOT_MCP_SCOPE,
|
|
118
119
|
convertMCPToolsToAISDK,
|
|
120
|
+
getCopilotMCPOAuthKey,
|
|
121
|
+
getStoredCopilotMCPToken,
|
|
119
122
|
getMCPManager,
|
|
123
|
+
hasCopilotMCPScopes,
|
|
120
124
|
initializeMCP,
|
|
125
|
+
isGitHubCopilotUrl,
|
|
126
|
+
isStoredCopilotMCPAuthenticated,
|
|
121
127
|
shutdownMCP,
|
|
122
128
|
loadMCPConfig,
|
|
123
129
|
addMCPServerToConfig,
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import type { MCPScope } from './types.ts';
|
|
3
|
+
import type { OAuthCredentialStore } from './oauth/store.ts';
|
|
4
|
+
|
|
5
|
+
export const GITHUB_COPILOT_HOSTS = [
|
|
6
|
+
'api.githubcopilot.com',
|
|
7
|
+
'copilot-proxy.githubusercontent.com',
|
|
8
|
+
];
|
|
9
|
+
|
|
10
|
+
export const COPILOT_MCP_REQUIRED_SCOPES = [
|
|
11
|
+
'repo',
|
|
12
|
+
'read:org',
|
|
13
|
+
'gist',
|
|
14
|
+
'notifications',
|
|
15
|
+
'read:project',
|
|
16
|
+
'security_events',
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
export const COPILOT_MCP_SCOPE =
|
|
20
|
+
'repo read:org read:packages gist notifications read:project security_events';
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Returns whether a URL points at a GitHub Copilot-backed MCP endpoint.
|
|
24
|
+
*/
|
|
25
|
+
export function isGitHubCopilotUrl(url?: string): boolean {
|
|
26
|
+
if (!url) return false;
|
|
27
|
+
try {
|
|
28
|
+
const parsed = new URL(url);
|
|
29
|
+
return GITHUB_COPILOT_HOSTS.some(
|
|
30
|
+
(host) =>
|
|
31
|
+
parsed.hostname === host || parsed.hostname.endsWith(`.${host}`),
|
|
32
|
+
);
|
|
33
|
+
} catch {
|
|
34
|
+
return false;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Returns whether the stored scope string satisfies the GitHub Copilot MCP requirements.
|
|
40
|
+
*/
|
|
41
|
+
export function hasCopilotMCPScopes(scopes?: string): boolean {
|
|
42
|
+
if (!scopes) return false;
|
|
43
|
+
const granted = scopes.split(/[\s,]+/).filter(Boolean);
|
|
44
|
+
return COPILOT_MCP_REQUIRED_SCOPES.every((scope) => granted.includes(scope));
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Builds the MCP OAuth store key for a GitHub Copilot MCP server.
|
|
49
|
+
*/
|
|
50
|
+
export function getCopilotMCPOAuthKey(
|
|
51
|
+
serverName: string,
|
|
52
|
+
scope: MCPScope = 'global',
|
|
53
|
+
projectRoot?: string,
|
|
54
|
+
): string {
|
|
55
|
+
if (scope === 'project' && projectRoot) {
|
|
56
|
+
const hash = createHash('sha256')
|
|
57
|
+
.update(projectRoot)
|
|
58
|
+
.digest('hex')
|
|
59
|
+
.slice(0, 8);
|
|
60
|
+
return `${serverName}_proj_${hash}`;
|
|
61
|
+
}
|
|
62
|
+
return serverName;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Loads the stored GitHub Copilot MCP bearer token for a server.
|
|
67
|
+
*/
|
|
68
|
+
export async function getStoredCopilotMCPToken(
|
|
69
|
+
store: OAuthCredentialStore,
|
|
70
|
+
serverName: string,
|
|
71
|
+
scope: MCPScope = 'global',
|
|
72
|
+
projectRoot?: string,
|
|
73
|
+
): Promise<{ token: string | null; needsReauth: boolean; scopes?: string }> {
|
|
74
|
+
const tokens = await store.loadTokens(
|
|
75
|
+
getCopilotMCPOAuthKey(serverName, scope, projectRoot),
|
|
76
|
+
);
|
|
77
|
+
const token = tokens?.access_token ?? null;
|
|
78
|
+
if (!token) {
|
|
79
|
+
return { token: null, needsReauth: true };
|
|
80
|
+
}
|
|
81
|
+
return {
|
|
82
|
+
token,
|
|
83
|
+
needsReauth: !hasCopilotMCPScopes(tokens?.scope),
|
|
84
|
+
scopes: tokens?.scope,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Returns whether the stored GitHub Copilot MCP credentials are usable.
|
|
90
|
+
*/
|
|
91
|
+
export async function isStoredCopilotMCPAuthenticated(
|
|
92
|
+
store: OAuthCredentialStore,
|
|
93
|
+
serverName: string,
|
|
94
|
+
scope: MCPScope = 'global',
|
|
95
|
+
projectRoot?: string,
|
|
96
|
+
): Promise<boolean> {
|
|
97
|
+
const { token, needsReauth } = await getStoredCopilotMCPToken(
|
|
98
|
+
store,
|
|
99
|
+
serverName,
|
|
100
|
+
scope,
|
|
101
|
+
projectRoot,
|
|
102
|
+
);
|
|
103
|
+
return !!token && !needsReauth;
|
|
104
|
+
}
|
|
@@ -13,6 +13,15 @@ export { MCPServerManager } from './server-manager.ts';
|
|
|
13
13
|
|
|
14
14
|
export { convertMCPToolsToAISDK } from './tools.ts';
|
|
15
15
|
|
|
16
|
+
export {
|
|
17
|
+
COPILOT_MCP_SCOPE,
|
|
18
|
+
getCopilotMCPOAuthKey,
|
|
19
|
+
getStoredCopilotMCPToken,
|
|
20
|
+
hasCopilotMCPScopes,
|
|
21
|
+
isGitHubCopilotUrl,
|
|
22
|
+
isStoredCopilotMCPAuthenticated,
|
|
23
|
+
} from './copilot-auth.ts';
|
|
24
|
+
|
|
16
25
|
export {
|
|
17
26
|
getMCPToolBriefs,
|
|
18
27
|
buildLoadMCPToolsTool,
|
|
@@ -2,66 +2,12 @@ import { MCPClientWrapper, type MCPToolInfo } from './client.ts';
|
|
|
2
2
|
import type { MCPServerConfig, MCPServerStatus } from './types.ts';
|
|
3
3
|
import { OAuthCredentialStore } from './oauth/store.ts';
|
|
4
4
|
import { OttoOAuthProvider } from './oauth/provider.ts';
|
|
5
|
-
import {
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
];
|
|
12
|
-
|
|
13
|
-
function isGitHubCopilotUrl(url?: string): boolean {
|
|
14
|
-
if (!url) return false;
|
|
15
|
-
try {
|
|
16
|
-
const parsed = new URL(url);
|
|
17
|
-
return GITHUB_COPILOT_HOSTS.some(
|
|
18
|
-
(h) => parsed.hostname === h || parsed.hostname.endsWith(`.${h}`),
|
|
19
|
-
);
|
|
20
|
-
} catch {
|
|
21
|
-
return false;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const COPILOT_MCP_REQUIRED_SCOPES = [
|
|
26
|
-
'repo',
|
|
27
|
-
'read:org',
|
|
28
|
-
'gist',
|
|
29
|
-
'notifications',
|
|
30
|
-
'read:project',
|
|
31
|
-
'security_events',
|
|
32
|
-
];
|
|
33
|
-
|
|
34
|
-
function hasMCPScopes(scopes?: string): boolean {
|
|
35
|
-
if (!scopes) return false;
|
|
36
|
-
const granted = scopes.split(/[\s,]+/).filter(Boolean);
|
|
37
|
-
return COPILOT_MCP_REQUIRED_SCOPES.every((s) => granted.includes(s));
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
async function getCopilotToken(): Promise<string | null> {
|
|
41
|
-
try {
|
|
42
|
-
const auth = await getAuth('copilot');
|
|
43
|
-
if (auth?.type === 'oauth' && auth.refresh) {
|
|
44
|
-
return auth.refresh;
|
|
45
|
-
}
|
|
46
|
-
} catch {}
|
|
47
|
-
return null;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
async function getCopilotMCPToken(): Promise<{
|
|
51
|
-
token: string | null;
|
|
52
|
-
needsReauth: boolean;
|
|
53
|
-
}> {
|
|
54
|
-
try {
|
|
55
|
-
const auth = await getAuth('copilot');
|
|
56
|
-
if (auth?.type === 'oauth' && auth.refresh) {
|
|
57
|
-
if (!hasMCPScopes(auth.scopes)) {
|
|
58
|
-
return { token: auth.refresh, needsReauth: true };
|
|
59
|
-
}
|
|
60
|
-
return { token: auth.refresh, needsReauth: false };
|
|
61
|
-
}
|
|
62
|
-
} catch {}
|
|
63
|
-
return { token: null, needsReauth: true };
|
|
64
|
-
}
|
|
5
|
+
import {
|
|
6
|
+
getCopilotMCPOAuthKey,
|
|
7
|
+
getStoredCopilotMCPToken,
|
|
8
|
+
isGitHubCopilotUrl,
|
|
9
|
+
isStoredCopilotMCPAuthenticated,
|
|
10
|
+
} from './copilot-auth.ts';
|
|
65
11
|
|
|
66
12
|
type IndexedTool = {
|
|
67
13
|
server: string;
|
|
@@ -88,14 +34,11 @@ export class MCPServerManager {
|
|
|
88
34
|
|
|
89
35
|
private oauthKey(serverName: string): string {
|
|
90
36
|
const scope = this.serverScopes.get(serverName);
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
return `${serverName}_proj_${hash}`;
|
|
97
|
-
}
|
|
98
|
-
return serverName;
|
|
37
|
+
return getCopilotMCPOAuthKey(
|
|
38
|
+
serverName,
|
|
39
|
+
scope,
|
|
40
|
+
this.projectRoot ?? undefined,
|
|
41
|
+
);
|
|
99
42
|
}
|
|
100
43
|
|
|
101
44
|
async startServers(configs: MCPServerConfig[]): Promise<void> {
|
|
@@ -115,7 +58,12 @@ export class MCPServerManager {
|
|
|
115
58
|
|
|
116
59
|
if (transport !== 'stdio') {
|
|
117
60
|
if (isGitHubCopilotUrl(config.url)) {
|
|
118
|
-
const { token, needsReauth } = await
|
|
61
|
+
const { token, needsReauth } = await getStoredCopilotMCPToken(
|
|
62
|
+
this.oauthStore,
|
|
63
|
+
config.name,
|
|
64
|
+
config.scope ?? 'global',
|
|
65
|
+
this.projectRoot ?? undefined,
|
|
66
|
+
);
|
|
119
67
|
if (token && !needsReauth) {
|
|
120
68
|
config = {
|
|
121
69
|
...config,
|
|
@@ -159,7 +107,7 @@ export class MCPServerManager {
|
|
|
159
107
|
return;
|
|
160
108
|
}
|
|
161
109
|
console.error(
|
|
162
|
-
`[mcp] GitHub Copilot MCP server "${config.name}" requires authentication. Run \`otto
|
|
110
|
+
`[mcp] GitHub Copilot MCP server "${config.name}" requires authentication. Run \`otto mcp auth ${config.name}\`.`,
|
|
163
111
|
);
|
|
164
112
|
this.clients.set(config.name, client);
|
|
165
113
|
return;
|
|
@@ -296,7 +244,12 @@ export class MCPServerManager {
|
|
|
296
244
|
const config = client.serverConfig;
|
|
297
245
|
let authenticated = false;
|
|
298
246
|
if (isGitHubCopilotUrl(config.url)) {
|
|
299
|
-
authenticated =
|
|
247
|
+
authenticated = await isStoredCopilotMCPAuthenticated(
|
|
248
|
+
this.oauthStore,
|
|
249
|
+
name,
|
|
250
|
+
config.scope ?? 'global',
|
|
251
|
+
this.projectRoot ?? undefined,
|
|
252
|
+
).catch(() => false);
|
|
300
253
|
} else {
|
|
301
254
|
const key = this.oauthKey(name);
|
|
302
255
|
authenticated = await this.oauthStore
|
|
@@ -334,8 +287,13 @@ export class MCPServerManager {
|
|
|
334
287
|
if (transport === 'stdio') return null;
|
|
335
288
|
|
|
336
289
|
if (isGitHubCopilotUrl(config.url)) {
|
|
337
|
-
const token = await
|
|
338
|
-
|
|
290
|
+
const { token, needsReauth } = await getStoredCopilotMCPToken(
|
|
291
|
+
this.oauthStore,
|
|
292
|
+
config.name,
|
|
293
|
+
config.scope ?? 'global',
|
|
294
|
+
this.projectRoot ?? undefined,
|
|
295
|
+
);
|
|
296
|
+
if (token && !needsReauth) {
|
|
339
297
|
const authedConfig = {
|
|
340
298
|
...config,
|
|
341
299
|
headers: {
|
|
@@ -472,8 +430,19 @@ export class MCPServerManager {
|
|
|
472
430
|
): Promise<{ authenticated: boolean; expiresAt?: number }> {
|
|
473
431
|
const client = this.clients.get(name);
|
|
474
432
|
if (client && isGitHubCopilotUrl(client.serverConfig.url)) {
|
|
475
|
-
const
|
|
476
|
-
|
|
433
|
+
const key = this.oauthKey(name);
|
|
434
|
+
const tokens = await this.oauthStore.loadTokens(key);
|
|
435
|
+
return {
|
|
436
|
+
authenticated:
|
|
437
|
+
!!tokens?.access_token &&
|
|
438
|
+
(await isStoredCopilotMCPAuthenticated(
|
|
439
|
+
this.oauthStore,
|
|
440
|
+
name,
|
|
441
|
+
client.serverConfig.scope ?? 'global',
|
|
442
|
+
this.projectRoot ?? undefined,
|
|
443
|
+
)),
|
|
444
|
+
expiresAt: tokens?.expires_at,
|
|
445
|
+
};
|
|
477
446
|
}
|
|
478
447
|
const key = this.oauthKey(name);
|
|
479
448
|
const tokens = await this.oauthStore.loadTokens(key);
|
|
@@ -1,20 +1,15 @@
|
|
|
1
|
-
// @ts-expect-error Bun file asset import
|
|
2
1
|
import darwinArm64 from 'bun-pty/rust-pty/target/release/librust_pty_arm64.dylib' with {
|
|
3
2
|
type: 'file',
|
|
4
3
|
};
|
|
5
|
-
// @ts-expect-error Bun file asset import
|
|
6
4
|
import darwinX64 from 'bun-pty/rust-pty/target/release/librust_pty.dylib' with {
|
|
7
5
|
type: 'file',
|
|
8
6
|
};
|
|
9
|
-
// @ts-expect-error Bun file asset import
|
|
10
7
|
import linuxArm64 from 'bun-pty/rust-pty/target/release/librust_pty_arm64.so' with {
|
|
11
8
|
type: 'file',
|
|
12
9
|
};
|
|
13
|
-
// @ts-expect-error Bun file asset import
|
|
14
10
|
import linuxX64 from 'bun-pty/rust-pty/target/release/librust_pty.so' with {
|
|
15
11
|
type: 'file',
|
|
16
12
|
};
|
|
17
|
-
// @ts-expect-error Bun file asset import
|
|
18
13
|
import windowsDll from 'bun-pty/rust-pty/target/release/rust_pty.dll' with {
|
|
19
14
|
type: 'file',
|
|
20
15
|
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import { tool, type Tool } from 'ai';
|
|
2
|
-
import { z } from 'zod/v3';
|
|
3
2
|
import { spawn } from 'node:child_process';
|
|
3
|
+
import { z } from 'zod/v3';
|
|
4
4
|
import DESCRIPTION from './bash.txt' with { type: 'text' };
|
|
5
|
-
import { createToolError, type ToolResponse } from '../error.ts';
|
|
6
5
|
import { getAugmentedPath } from '../bin-manager.ts';
|
|
6
|
+
import { createToolError, type ToolResponse } from '../error.ts';
|
|
7
7
|
import { injectCoAuthorIntoGitCommit } from './git-identity.ts';
|
|
8
8
|
|
|
9
9
|
function normalizePath(p: string) {
|
|
@@ -41,10 +41,26 @@ function killProcessTree(pid: number) {
|
|
|
41
41
|
}
|
|
42
42
|
}
|
|
43
43
|
|
|
44
|
+
type BashResult = ToolResponse<{
|
|
45
|
+
exitCode: number;
|
|
46
|
+
stdout: string;
|
|
47
|
+
stderr: string;
|
|
48
|
+
}>;
|
|
49
|
+
|
|
50
|
+
type BashStreamChunk =
|
|
51
|
+
| {
|
|
52
|
+
channel: 'output';
|
|
53
|
+
delta: string;
|
|
54
|
+
}
|
|
55
|
+
| {
|
|
56
|
+
result: BashResult;
|
|
57
|
+
};
|
|
58
|
+
|
|
44
59
|
export function buildBashTool(projectRoot: string): {
|
|
45
60
|
name: string;
|
|
46
61
|
tool: Tool;
|
|
47
62
|
} {
|
|
63
|
+
// biome-ignore lint/suspicious/noExplicitAny: AI SDK tool typings do not model async-iterable execute results yet.
|
|
48
64
|
const bash = tool({
|
|
49
65
|
description: DESCRIPTION,
|
|
50
66
|
inputSchema: z
|
|
@@ -66,7 +82,7 @@ export function buildBashTool(projectRoot: string): {
|
|
|
66
82
|
.describe('Timeout in milliseconds (default: 300000 = 5 minutes)'),
|
|
67
83
|
})
|
|
68
84
|
.strict(),
|
|
69
|
-
|
|
85
|
+
execute(
|
|
70
86
|
{
|
|
71
87
|
cmd,
|
|
72
88
|
cwd,
|
|
@@ -79,13 +95,7 @@ export function buildBashTool(projectRoot: string): {
|
|
|
79
95
|
timeout?: number;
|
|
80
96
|
},
|
|
81
97
|
options?: { abortSignal?: AbortSignal },
|
|
82
|
-
):
|
|
83
|
-
ToolResponse<{
|
|
84
|
-
exitCode: number;
|
|
85
|
-
stdout: string;
|
|
86
|
-
stderr: string;
|
|
87
|
-
}>
|
|
88
|
-
> {
|
|
98
|
+
): AsyncIterable<BashStreamChunk> | BashResult {
|
|
89
99
|
const abortSignal = options?.abortSignal;
|
|
90
100
|
|
|
91
101
|
if (abortSignal?.aborted) {
|
|
@@ -95,126 +105,162 @@ export function buildBashTool(projectRoot: string): {
|
|
|
95
105
|
}
|
|
96
106
|
|
|
97
107
|
const absCwd = resolveSafePath(projectRoot, cwd || '.');
|
|
98
|
-
|
|
99
108
|
const finalCmd = injectCoAuthorIntoGitCommit(cmd);
|
|
100
109
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
});
|
|
110
|
+
const proc = spawn(finalCmd, {
|
|
111
|
+
cwd: absCwd,
|
|
112
|
+
shell: true,
|
|
113
|
+
stdio: ['ignore', 'pipe', 'pipe'],
|
|
114
|
+
env: { ...process.env, PATH: getAugmentedPath() },
|
|
115
|
+
detached: true,
|
|
116
|
+
});
|
|
109
117
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
exitCode: number;
|
|
120
|
-
stdout: string;
|
|
121
|
-
stderr: string;
|
|
122
|
-
}>,
|
|
123
|
-
) => {
|
|
124
|
-
if (settled) return;
|
|
125
|
-
settled = true;
|
|
126
|
-
if (timeoutId) clearTimeout(timeoutId);
|
|
127
|
-
if (abortSignal) {
|
|
128
|
-
abortSignal.removeEventListener('abort', onAbort);
|
|
129
|
-
}
|
|
130
|
-
resolve(result);
|
|
131
|
-
};
|
|
118
|
+
let stdout = '';
|
|
119
|
+
let stderr = '';
|
|
120
|
+
let didTimeout = false;
|
|
121
|
+
let didAbort = false;
|
|
122
|
+
let settled = false;
|
|
123
|
+
let done = false;
|
|
124
|
+
let timeoutId: ReturnType<typeof setTimeout> | null = null;
|
|
125
|
+
const queue: BashStreamChunk[] = [];
|
|
126
|
+
let notify: (() => void) | null = null;
|
|
132
127
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
128
|
+
const wake = () => {
|
|
129
|
+
if (!notify) return;
|
|
130
|
+
notify();
|
|
131
|
+
notify = null;
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
const pushDelta = (text: string) => {
|
|
135
|
+
if (!text) return;
|
|
136
|
+
queue.push({ channel: 'output', delta: text });
|
|
137
|
+
wake();
|
|
138
|
+
};
|
|
139
139
|
|
|
140
|
+
const settle = (result: BashResult) => {
|
|
141
|
+
if (settled) return;
|
|
142
|
+
settled = true;
|
|
143
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
140
144
|
if (abortSignal) {
|
|
141
|
-
abortSignal.
|
|
145
|
+
abortSignal.removeEventListener('abort', onAbort);
|
|
142
146
|
}
|
|
147
|
+
queue.push({ result });
|
|
148
|
+
done = true;
|
|
149
|
+
wake();
|
|
150
|
+
};
|
|
143
151
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
152
|
+
const onAbort = () => {
|
|
153
|
+
if (settled) return;
|
|
154
|
+
didAbort = true;
|
|
155
|
+
if (proc.pid) killProcessTree(proc.pid);
|
|
156
|
+
else proc.kill('SIGTERM');
|
|
157
|
+
};
|
|
151
158
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
159
|
+
if (abortSignal) {
|
|
160
|
+
abortSignal.addEventListener('abort', onAbort, { once: true });
|
|
161
|
+
}
|
|
155
162
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
163
|
+
if (timeout > 0) {
|
|
164
|
+
timeoutId = setTimeout(() => {
|
|
165
|
+
didTimeout = true;
|
|
166
|
+
if (proc.pid) killProcessTree(proc.pid);
|
|
167
|
+
else proc.kill();
|
|
168
|
+
}, timeout);
|
|
169
|
+
}
|
|
159
170
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
suggestion: 'Increase timeout or optimize the command',
|
|
178
|
-
},
|
|
179
|
-
),
|
|
180
|
-
);
|
|
181
|
-
} else if (exitCode !== 0 && !allowNonZeroExit) {
|
|
182
|
-
const errorDetail = stderr.trim() || stdout.trim() || '';
|
|
183
|
-
const errorMsg = `Command failed with exit code ${exitCode}${errorDetail ? `\n\n${errorDetail}` : ''}`;
|
|
184
|
-
settle(
|
|
185
|
-
createToolError(errorMsg, 'execution', {
|
|
186
|
-
exitCode,
|
|
187
|
-
stdout,
|
|
188
|
-
stderr,
|
|
189
|
-
cmd,
|
|
190
|
-
suggestion:
|
|
191
|
-
'Check command syntax or use allowNonZeroExit: true',
|
|
192
|
-
}),
|
|
193
|
-
);
|
|
194
|
-
} else {
|
|
195
|
-
settle({
|
|
196
|
-
ok: true,
|
|
197
|
-
exitCode: exitCode ?? 0,
|
|
171
|
+
proc.stdout?.on('data', (chunk) => {
|
|
172
|
+
const text = chunk.toString();
|
|
173
|
+
stdout += text;
|
|
174
|
+
pushDelta(text);
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
proc.stderr?.on('data', (chunk) => {
|
|
178
|
+
const text = chunk.toString();
|
|
179
|
+
stderr += text;
|
|
180
|
+
pushDelta(text);
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
proc.on('close', (exitCode) => {
|
|
184
|
+
if (didAbort) {
|
|
185
|
+
settle(
|
|
186
|
+
createToolError(`Command aborted by user: ${cmd}`, 'abort', {
|
|
187
|
+
cmd,
|
|
198
188
|
stdout,
|
|
199
189
|
stderr,
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
|
|
190
|
+
}),
|
|
191
|
+
);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
203
194
|
|
|
204
|
-
|
|
195
|
+
if (didTimeout) {
|
|
205
196
|
settle(
|
|
206
197
|
createToolError(
|
|
207
|
-
`Command
|
|
208
|
-
'
|
|
198
|
+
`Command timed out after ${timeout}ms: ${cmd}`,
|
|
199
|
+
'timeout',
|
|
209
200
|
{
|
|
210
|
-
|
|
211
|
-
|
|
201
|
+
parameter: 'timeout',
|
|
202
|
+
value: timeout,
|
|
203
|
+
stdout,
|
|
204
|
+
stderr,
|
|
205
|
+
suggestion: 'Increase timeout or optimize the command',
|
|
212
206
|
},
|
|
213
207
|
),
|
|
214
208
|
);
|
|
209
|
+
return;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
if (exitCode !== 0 && !allowNonZeroExit) {
|
|
213
|
+
const errorDetail = stderr.trim() || stdout.trim() || '';
|
|
214
|
+
const errorMsg = `Command failed with exit code ${exitCode}${errorDetail ? `\n\n${errorDetail}` : ''}`;
|
|
215
|
+
settle(
|
|
216
|
+
createToolError(errorMsg, 'execution', {
|
|
217
|
+
exitCode,
|
|
218
|
+
stdout,
|
|
219
|
+
stderr,
|
|
220
|
+
cmd,
|
|
221
|
+
suggestion: 'Check command syntax or use allowNonZeroExit: true',
|
|
222
|
+
}),
|
|
223
|
+
);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
settle({
|
|
228
|
+
ok: true,
|
|
229
|
+
exitCode: exitCode ?? 0,
|
|
230
|
+
stdout,
|
|
231
|
+
stderr,
|
|
215
232
|
});
|
|
216
233
|
});
|
|
234
|
+
|
|
235
|
+
proc.on('error', (err) => {
|
|
236
|
+
settle(
|
|
237
|
+
createToolError(
|
|
238
|
+
`Command execution failed: ${err.message}`,
|
|
239
|
+
'execution',
|
|
240
|
+
{
|
|
241
|
+
cmd,
|
|
242
|
+
originalError: err.message,
|
|
243
|
+
},
|
|
244
|
+
),
|
|
245
|
+
);
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
const stream = async function* (): AsyncGenerator<BashStreamChunk> {
|
|
249
|
+
while (!done || queue.length > 0) {
|
|
250
|
+
if (queue.length === 0) {
|
|
251
|
+
await new Promise<void>((resolve) => {
|
|
252
|
+
notify = resolve;
|
|
253
|
+
});
|
|
254
|
+
}
|
|
255
|
+
while (queue.length > 0) {
|
|
256
|
+
const chunk = queue.shift();
|
|
257
|
+
if (chunk) yield chunk;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
return stream();
|
|
217
263
|
},
|
|
218
|
-
});
|
|
264
|
+
} as any);
|
|
219
265
|
return { name: 'bash', tool: bash };
|
|
220
266
|
}
|
package/src/index.ts
CHANGED
|
@@ -29,6 +29,7 @@ export type {
|
|
|
29
29
|
DefaultConfig,
|
|
30
30
|
PathConfig,
|
|
31
31
|
OttoConfig,
|
|
32
|
+
ReasoningLevel,
|
|
32
33
|
} from './types/src/index.ts';
|
|
33
34
|
|
|
34
35
|
// =======================
|
|
@@ -361,9 +362,15 @@ export type { TunnelConnection, TunnelEvents } from './tunnel/index.ts';
|
|
|
361
362
|
export {
|
|
362
363
|
MCPClientWrapper,
|
|
363
364
|
MCPServerManager,
|
|
365
|
+
COPILOT_MCP_SCOPE,
|
|
364
366
|
convertMCPToolsToAISDK,
|
|
367
|
+
getCopilotMCPOAuthKey,
|
|
368
|
+
getStoredCopilotMCPToken,
|
|
365
369
|
getMCPManager,
|
|
370
|
+
hasCopilotMCPScopes,
|
|
366
371
|
initializeMCP,
|
|
372
|
+
isGitHubCopilotUrl,
|
|
373
|
+
isStoredCopilotMCPAuthenticated,
|
|
367
374
|
shutdownMCP,
|
|
368
375
|
loadMCPConfig,
|
|
369
376
|
addMCPServerToConfig,
|
|
@@ -49,6 +49,25 @@ async function readIfExists(path: string): Promise<string | undefined> {
|
|
|
49
49
|
return undefined;
|
|
50
50
|
}
|
|
51
51
|
|
|
52
|
+
function getPromptOverridePaths(args: {
|
|
53
|
+
projectRoot: string;
|
|
54
|
+
provider: string;
|
|
55
|
+
modelId?: string;
|
|
56
|
+
}): { modelPaths: string[]; providerPaths: string[] } {
|
|
57
|
+
const { projectRoot, provider, modelId } = args;
|
|
58
|
+
const modelPaths: string[] = [];
|
|
59
|
+
const providerPaths: string[] = [];
|
|
60
|
+
|
|
61
|
+
if (modelId) {
|
|
62
|
+
const sanitized = sanitizeModelId(modelId);
|
|
63
|
+
modelPaths.push(`${projectRoot}/.otto/prompts/models/${sanitized}.txt`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
providerPaths.push(`${projectRoot}/.otto/prompts/providers/${provider}.txt`);
|
|
67
|
+
|
|
68
|
+
return { modelPaths, providerPaths };
|
|
69
|
+
}
|
|
70
|
+
|
|
52
71
|
export type ProviderPromptResult = {
|
|
53
72
|
prompt: string;
|
|
54
73
|
resolvedType: string;
|
|
@@ -57,21 +76,37 @@ export type ProviderPromptResult = {
|
|
|
57
76
|
export async function providerBasePrompt(
|
|
58
77
|
provider: string,
|
|
59
78
|
modelId: string | undefined,
|
|
60
|
-
|
|
79
|
+
projectRoot: string,
|
|
61
80
|
): Promise<ProviderPromptResult> {
|
|
62
81
|
const id = String(provider || '').toLowerCase();
|
|
82
|
+
const { modelPaths, providerPaths } = getPromptOverridePaths({
|
|
83
|
+
projectRoot,
|
|
84
|
+
provider: id,
|
|
85
|
+
modelId,
|
|
86
|
+
});
|
|
63
87
|
|
|
64
88
|
if (modelId) {
|
|
65
89
|
const sanitized = sanitizeModelId(modelId);
|
|
66
|
-
const modelPath
|
|
67
|
-
|
|
68
|
-
|
|
90
|
+
for (const modelPath of modelPaths) {
|
|
91
|
+
const modelText = await readIfExists(modelPath);
|
|
92
|
+
if (!modelText) continue;
|
|
69
93
|
const promptType = `model:${sanitized}`;
|
|
70
|
-
debugLog(
|
|
94
|
+
debugLog(
|
|
95
|
+
`[provider] prompt: ${promptType} (${modelText.length} chars) from ${modelPath}`,
|
|
96
|
+
);
|
|
71
97
|
return { prompt: modelText, resolvedType: promptType };
|
|
72
98
|
}
|
|
73
99
|
}
|
|
74
100
|
|
|
101
|
+
for (const providerPath of providerPaths) {
|
|
102
|
+
const providerText = await readIfExists(providerPath);
|
|
103
|
+
if (!providerText) continue;
|
|
104
|
+
debugLog(
|
|
105
|
+
`[provider] prompt: custom:${id} (${providerText.length} chars) from ${providerPath}`,
|
|
106
|
+
);
|
|
107
|
+
return { prompt: providerText, resolvedType: `custom:${id}` };
|
|
108
|
+
}
|
|
109
|
+
|
|
75
110
|
if (isProviderId(id) && modelId) {
|
|
76
111
|
const info = getModelInfo(id, modelId);
|
|
77
112
|
if (info?.ownedBy) {
|
|
@@ -119,13 +154,6 @@ export async function providerBasePrompt(
|
|
|
119
154
|
return { prompt: result, resolvedType: 'glm' };
|
|
120
155
|
}
|
|
121
156
|
|
|
122
|
-
const providerPath = `src/prompts/providers/${id}.txt`;
|
|
123
|
-
const providerText = await readIfExists(providerPath);
|
|
124
|
-
if (providerText) {
|
|
125
|
-
debugLog(`[provider] prompt: custom:${id} (${providerText.length} chars)`);
|
|
126
|
-
return { prompt: providerText, resolvedType: `custom:${id}` };
|
|
127
|
-
}
|
|
128
|
-
|
|
129
157
|
const result = PROVIDER_DEFAULT.trim();
|
|
130
158
|
debugLog(`[provider] prompt: default (${result.length} chars)`);
|
|
131
159
|
return { prompt: result, resolvedType: 'default' };
|
|
@@ -14,6 +14,7 @@ const OWNER_NPM: Record<ModelOwner, string> = {
|
|
|
14
14
|
openai: '@ai-sdk/openai',
|
|
15
15
|
anthropic: '@ai-sdk/anthropic',
|
|
16
16
|
google: '@ai-sdk/google',
|
|
17
|
+
openrouter: '@openrouter/ai-sdk-provider',
|
|
17
18
|
xai: '@ai-sdk/xai',
|
|
18
19
|
moonshot: '@ai-sdk/openai-compatible',
|
|
19
20
|
zai: '@ai-sdk/openai-compatible',
|
|
@@ -762,7 +762,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
762
762
|
ownedBy: 'openai',
|
|
763
763
|
label: 'GPT-5.4',
|
|
764
764
|
modalities: {
|
|
765
|
-
input: ['text', 'image'],
|
|
765
|
+
input: ['text', 'image', 'pdf'],
|
|
766
766
|
output: ['text'],
|
|
767
767
|
},
|
|
768
768
|
toolCall: true,
|
|
@@ -2323,6 +2323,57 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
2323
2323
|
output: 64000,
|
|
2324
2324
|
},
|
|
2325
2325
|
},
|
|
2326
|
+
{
|
|
2327
|
+
id: 'gemini-3.1-flash-image-preview',
|
|
2328
|
+
ownedBy: 'google',
|
|
2329
|
+
label: 'Gemini 3.1 Flash Image (Preview)',
|
|
2330
|
+
modalities: {
|
|
2331
|
+
input: ['text', 'image', 'pdf'],
|
|
2332
|
+
output: ['text', 'image'],
|
|
2333
|
+
},
|
|
2334
|
+
toolCall: false,
|
|
2335
|
+
reasoningText: true,
|
|
2336
|
+
attachment: true,
|
|
2337
|
+
temperature: true,
|
|
2338
|
+
knowledge: '2025-01',
|
|
2339
|
+
releaseDate: '2026-02-26',
|
|
2340
|
+
lastUpdated: '2026-02-26',
|
|
2341
|
+
openWeights: false,
|
|
2342
|
+
cost: {
|
|
2343
|
+
input: 0.25,
|
|
2344
|
+
output: 60,
|
|
2345
|
+
},
|
|
2346
|
+
limit: {
|
|
2347
|
+
context: 131072,
|
|
2348
|
+
output: 32768,
|
|
2349
|
+
},
|
|
2350
|
+
},
|
|
2351
|
+
{
|
|
2352
|
+
id: 'gemini-3.1-flash-lite-preview',
|
|
2353
|
+
ownedBy: 'google',
|
|
2354
|
+
label: 'Gemini 3.1 Flash Lite Preview',
|
|
2355
|
+
modalities: {
|
|
2356
|
+
input: ['text', 'image', 'video', 'audio', 'pdf'],
|
|
2357
|
+
output: ['text'],
|
|
2358
|
+
},
|
|
2359
|
+
toolCall: true,
|
|
2360
|
+
reasoningText: true,
|
|
2361
|
+
attachment: true,
|
|
2362
|
+
temperature: true,
|
|
2363
|
+
knowledge: '2025-01',
|
|
2364
|
+
releaseDate: '2026-03-03',
|
|
2365
|
+
lastUpdated: '2026-03-03',
|
|
2366
|
+
openWeights: false,
|
|
2367
|
+
cost: {
|
|
2368
|
+
input: 0.5,
|
|
2369
|
+
output: 3,
|
|
2370
|
+
cacheRead: 0.05,
|
|
2371
|
+
},
|
|
2372
|
+
limit: {
|
|
2373
|
+
context: 1048576,
|
|
2374
|
+
output: 65536,
|
|
2375
|
+
},
|
|
2376
|
+
},
|
|
2326
2377
|
{
|
|
2327
2378
|
id: 'gemini-3.1-pro-preview',
|
|
2328
2379
|
ownedBy: 'google',
|
|
@@ -3940,6 +3991,78 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
3940
3991
|
output: 2000,
|
|
3941
3992
|
},
|
|
3942
3993
|
},
|
|
3994
|
+
{
|
|
3995
|
+
id: 'inception/mercury',
|
|
3996
|
+
label: 'Mercury',
|
|
3997
|
+
modalities: {
|
|
3998
|
+
input: ['text'],
|
|
3999
|
+
output: ['text'],
|
|
4000
|
+
},
|
|
4001
|
+
toolCall: true,
|
|
4002
|
+
reasoningText: false,
|
|
4003
|
+
attachment: false,
|
|
4004
|
+
temperature: true,
|
|
4005
|
+
releaseDate: '2025-06-26',
|
|
4006
|
+
lastUpdated: '2025-06-26',
|
|
4007
|
+
openWeights: false,
|
|
4008
|
+
cost: {
|
|
4009
|
+
input: 0.25,
|
|
4010
|
+
output: 0.75,
|
|
4011
|
+
cacheRead: 0.025,
|
|
4012
|
+
},
|
|
4013
|
+
limit: {
|
|
4014
|
+
context: 128000,
|
|
4015
|
+
output: 32000,
|
|
4016
|
+
},
|
|
4017
|
+
},
|
|
4018
|
+
{
|
|
4019
|
+
id: 'inception/mercury-2',
|
|
4020
|
+
label: 'Mercury 2',
|
|
4021
|
+
modalities: {
|
|
4022
|
+
input: ['text'],
|
|
4023
|
+
output: ['text'],
|
|
4024
|
+
},
|
|
4025
|
+
toolCall: true,
|
|
4026
|
+
reasoningText: true,
|
|
4027
|
+
attachment: false,
|
|
4028
|
+
temperature: true,
|
|
4029
|
+
releaseDate: '2026-03-04',
|
|
4030
|
+
lastUpdated: '2026-03-04',
|
|
4031
|
+
openWeights: false,
|
|
4032
|
+
cost: {
|
|
4033
|
+
input: 0.25,
|
|
4034
|
+
output: 0.75,
|
|
4035
|
+
cacheRead: 0.025,
|
|
4036
|
+
},
|
|
4037
|
+
limit: {
|
|
4038
|
+
context: 128000,
|
|
4039
|
+
output: 50000,
|
|
4040
|
+
},
|
|
4041
|
+
},
|
|
4042
|
+
{
|
|
4043
|
+
id: 'inception/mercury-coder',
|
|
4044
|
+
label: 'Mercury Coder',
|
|
4045
|
+
modalities: {
|
|
4046
|
+
input: ['text'],
|
|
4047
|
+
output: ['text'],
|
|
4048
|
+
},
|
|
4049
|
+
toolCall: true,
|
|
4050
|
+
reasoningText: false,
|
|
4051
|
+
attachment: false,
|
|
4052
|
+
temperature: true,
|
|
4053
|
+
releaseDate: '2025-04-30',
|
|
4054
|
+
lastUpdated: '2025-04-30',
|
|
4055
|
+
openWeights: false,
|
|
4056
|
+
cost: {
|
|
4057
|
+
input: 0.25,
|
|
4058
|
+
output: 0.75,
|
|
4059
|
+
cacheRead: 0.025,
|
|
4060
|
+
},
|
|
4061
|
+
limit: {
|
|
4062
|
+
context: 128000,
|
|
4063
|
+
output: 32000,
|
|
4064
|
+
},
|
|
4065
|
+
},
|
|
3943
4066
|
{
|
|
3944
4067
|
id: 'kwaipilot/kat-coder-pro:free',
|
|
3945
4068
|
label: 'Kat Coder Pro (free)',
|
|
@@ -5743,6 +5866,77 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
5743
5866
|
output: 50000,
|
|
5744
5867
|
},
|
|
5745
5868
|
},
|
|
5869
|
+
{
|
|
5870
|
+
id: 'openrouter/free',
|
|
5871
|
+
label: 'Free Models Router',
|
|
5872
|
+
modalities: {
|
|
5873
|
+
input: ['text', 'image'],
|
|
5874
|
+
output: ['text'],
|
|
5875
|
+
},
|
|
5876
|
+
toolCall: true,
|
|
5877
|
+
reasoningText: true,
|
|
5878
|
+
attachment: true,
|
|
5879
|
+
temperature: true,
|
|
5880
|
+
releaseDate: '2026-02-01',
|
|
5881
|
+
lastUpdated: '2026-02-01',
|
|
5882
|
+
openWeights: false,
|
|
5883
|
+
cost: {
|
|
5884
|
+
input: 0,
|
|
5885
|
+
output: 0,
|
|
5886
|
+
},
|
|
5887
|
+
limit: {
|
|
5888
|
+
context: 200000,
|
|
5889
|
+
output: 8000,
|
|
5890
|
+
},
|
|
5891
|
+
},
|
|
5892
|
+
{
|
|
5893
|
+
id: 'openrouter/healer-alpha',
|
|
5894
|
+
label: 'Healer Alpha',
|
|
5895
|
+
modalities: {
|
|
5896
|
+
input: ['text', 'image', 'audio', 'pdf'],
|
|
5897
|
+
output: ['text'],
|
|
5898
|
+
},
|
|
5899
|
+
toolCall: true,
|
|
5900
|
+
reasoningText: true,
|
|
5901
|
+
attachment: true,
|
|
5902
|
+
temperature: true,
|
|
5903
|
+
knowledge: '2026-03-11',
|
|
5904
|
+
releaseDate: '2026-03-11',
|
|
5905
|
+
lastUpdated: '2026-03-11',
|
|
5906
|
+
openWeights: false,
|
|
5907
|
+
cost: {
|
|
5908
|
+
input: 0,
|
|
5909
|
+
output: 0,
|
|
5910
|
+
},
|
|
5911
|
+
limit: {
|
|
5912
|
+
context: 262144,
|
|
5913
|
+
output: 64000,
|
|
5914
|
+
},
|
|
5915
|
+
},
|
|
5916
|
+
{
|
|
5917
|
+
id: 'openrouter/hunter-alpha',
|
|
5918
|
+
label: 'Hunter Alpha',
|
|
5919
|
+
modalities: {
|
|
5920
|
+
input: ['text', 'image', 'pdf'],
|
|
5921
|
+
output: ['text'],
|
|
5922
|
+
},
|
|
5923
|
+
toolCall: true,
|
|
5924
|
+
reasoningText: true,
|
|
5925
|
+
attachment: true,
|
|
5926
|
+
temperature: true,
|
|
5927
|
+
knowledge: '2026-03-11',
|
|
5928
|
+
releaseDate: '2026-03-11',
|
|
5929
|
+
lastUpdated: '2026-03-11',
|
|
5930
|
+
openWeights: false,
|
|
5931
|
+
cost: {
|
|
5932
|
+
input: 0,
|
|
5933
|
+
output: 0,
|
|
5934
|
+
},
|
|
5935
|
+
limit: {
|
|
5936
|
+
context: 1048576,
|
|
5937
|
+
output: 64000,
|
|
5938
|
+
},
|
|
5939
|
+
},
|
|
5746
5940
|
{
|
|
5747
5941
|
id: 'openrouter/sherlock-dash-alpha',
|
|
5748
5942
|
label: 'Sherlock Dash Alpha',
|
|
@@ -8199,6 +8393,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
8199
8393
|
output: 262144,
|
|
8200
8394
|
},
|
|
8201
8395
|
},
|
|
8396
|
+
{
|
|
8397
|
+
id: 'mimo-v2-flash-free',
|
|
8398
|
+
label: 'MiMo V2 Flash Free',
|
|
8399
|
+
modalities: {
|
|
8400
|
+
input: ['text'],
|
|
8401
|
+
output: ['text'],
|
|
8402
|
+
},
|
|
8403
|
+
toolCall: true,
|
|
8404
|
+
reasoningText: true,
|
|
8405
|
+
attachment: false,
|
|
8406
|
+
temperature: true,
|
|
8407
|
+
knowledge: '2024-12',
|
|
8408
|
+
releaseDate: '2025-12-16',
|
|
8409
|
+
lastUpdated: '2025-12-16',
|
|
8410
|
+
openWeights: true,
|
|
8411
|
+
cost: {
|
|
8412
|
+
input: 0,
|
|
8413
|
+
output: 0,
|
|
8414
|
+
cacheRead: 0,
|
|
8415
|
+
},
|
|
8416
|
+
limit: {
|
|
8417
|
+
context: 262144,
|
|
8418
|
+
output: 65536,
|
|
8419
|
+
},
|
|
8420
|
+
},
|
|
8202
8421
|
{
|
|
8203
8422
|
id: 'minimax-m2.1',
|
|
8204
8423
|
ownedBy: 'minimax',
|
|
@@ -8309,6 +8528,31 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
8309
8528
|
npm: '@ai-sdk/anthropic',
|
|
8310
8529
|
},
|
|
8311
8530
|
},
|
|
8531
|
+
{
|
|
8532
|
+
id: 'nemotron-3-super-free',
|
|
8533
|
+
label: 'Nemotron 3 Super Free',
|
|
8534
|
+
modalities: {
|
|
8535
|
+
input: ['text'],
|
|
8536
|
+
output: ['text'],
|
|
8537
|
+
},
|
|
8538
|
+
toolCall: true,
|
|
8539
|
+
reasoningText: true,
|
|
8540
|
+
attachment: false,
|
|
8541
|
+
temperature: true,
|
|
8542
|
+
knowledge: '2026-02',
|
|
8543
|
+
releaseDate: '2026-03-11',
|
|
8544
|
+
lastUpdated: '2026-03-11',
|
|
8545
|
+
openWeights: true,
|
|
8546
|
+
cost: {
|
|
8547
|
+
input: 0,
|
|
8548
|
+
output: 0,
|
|
8549
|
+
cacheRead: 0,
|
|
8550
|
+
},
|
|
8551
|
+
limit: {
|
|
8552
|
+
context: 1000000,
|
|
8553
|
+
output: 128000,
|
|
8554
|
+
},
|
|
8555
|
+
},
|
|
8312
8556
|
{
|
|
8313
8557
|
id: 'qwen3-coder',
|
|
8314
8558
|
label: 'Qwen3 Coder',
|
|
@@ -8593,7 +8837,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
8593
8837
|
temperature: true,
|
|
8594
8838
|
releaseDate: '2026-02-11',
|
|
8595
8839
|
lastUpdated: '2026-02-11',
|
|
8596
|
-
openWeights:
|
|
8840
|
+
openWeights: true,
|
|
8597
8841
|
cost: {
|
|
8598
8842
|
input: 1,
|
|
8599
8843
|
output: 3.2,
|
|
@@ -8868,7 +9112,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
8868
9112
|
temperature: true,
|
|
8869
9113
|
releaseDate: '2026-02-11',
|
|
8870
9114
|
lastUpdated: '2026-02-11',
|
|
8871
|
-
openWeights:
|
|
9115
|
+
openWeights: true,
|
|
8872
9116
|
cost: {
|
|
8873
9117
|
input: 0,
|
|
8874
9118
|
output: 0,
|
|
@@ -9661,7 +9905,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
9661
9905
|
output: 0,
|
|
9662
9906
|
},
|
|
9663
9907
|
limit: {
|
|
9664
|
-
context:
|
|
9908
|
+
context: 264000,
|
|
9665
9909
|
output: 64000,
|
|
9666
9910
|
},
|
|
9667
9911
|
},
|
|
@@ -9686,7 +9930,7 @@ export const catalog: Partial<Record<ProviderId, ProviderCatalogEntry>> = {
|
|
|
9686
9930
|
output: 0,
|
|
9687
9931
|
},
|
|
9688
9932
|
limit: {
|
|
9689
|
-
context:
|
|
9933
|
+
context: 400000,
|
|
9690
9934
|
output: 128000,
|
|
9691
9935
|
},
|
|
9692
9936
|
},
|
package/src/tunnel/qr.ts
CHANGED
package/src/types/src/config.ts
CHANGED
|
@@ -9,6 +9,13 @@ export type Scope = 'global' | 'local';
|
|
|
9
9
|
* Default settings for the CLI
|
|
10
10
|
*/
|
|
11
11
|
export type ToolApprovalMode = 'auto' | 'dangerous' | 'all';
|
|
12
|
+
export type ReasoningLevel =
|
|
13
|
+
| 'minimal'
|
|
14
|
+
| 'low'
|
|
15
|
+
| 'medium'
|
|
16
|
+
| 'high'
|
|
17
|
+
| 'max'
|
|
18
|
+
| 'xhigh';
|
|
12
19
|
|
|
13
20
|
export type DefaultConfig = {
|
|
14
21
|
agent: string;
|
|
@@ -17,7 +24,9 @@ export type DefaultConfig = {
|
|
|
17
24
|
toolApproval?: ToolApprovalMode;
|
|
18
25
|
guidedMode?: boolean;
|
|
19
26
|
reasoningText?: boolean;
|
|
27
|
+
reasoningLevel?: ReasoningLevel;
|
|
20
28
|
theme?: string;
|
|
29
|
+
fullWidthContent?: boolean;
|
|
21
30
|
};
|
|
22
31
|
|
|
23
32
|
export type ProviderSettings = Record<
|
package/src/types/src/index.ts
CHANGED