@agi-cli/server 0.1.120 → 0.1.121

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.
Files changed (70) hide show
  1. package/package.json +3 -3
  2. package/src/index.ts +5 -5
  3. package/src/openapi/paths/git.ts +4 -0
  4. package/src/routes/ask.ts +13 -14
  5. package/src/routes/branch.ts +2 -2
  6. package/src/routes/config/agents.ts +1 -1
  7. package/src/routes/config/cwd.ts +1 -1
  8. package/src/routes/config/main.ts +1 -1
  9. package/src/routes/config/models.ts +32 -4
  10. package/src/routes/config/providers.ts +1 -1
  11. package/src/routes/config/utils.ts +14 -1
  12. package/src/routes/files.ts +1 -1
  13. package/src/routes/git/commit.ts +23 -6
  14. package/src/routes/git/schemas.ts +1 -0
  15. package/src/routes/session-files.ts +1 -1
  16. package/src/routes/session-messages.ts +2 -2
  17. package/src/routes/sessions.ts +8 -6
  18. package/src/runtime/agent/registry.ts +333 -0
  19. package/src/runtime/agent/runner-reasoning.ts +108 -0
  20. package/src/runtime/agent/runner-setup.ts +265 -0
  21. package/src/runtime/agent/runner.ts +356 -0
  22. package/src/runtime/agent-registry.ts +6 -333
  23. package/src/runtime/{ask-service.ts → ask/service.ts} +5 -5
  24. package/src/runtime/{debug.ts → debug/index.ts} +1 -1
  25. package/src/runtime/{api-error.ts → errors/api-error.ts} +2 -2
  26. package/src/runtime/message/compaction-auto.ts +137 -0
  27. package/src/runtime/message/compaction-context.ts +64 -0
  28. package/src/runtime/message/compaction-detect.ts +19 -0
  29. package/src/runtime/message/compaction-limits.ts +58 -0
  30. package/src/runtime/message/compaction-mark.ts +115 -0
  31. package/src/runtime/message/compaction-prune.ts +75 -0
  32. package/src/runtime/message/compaction.ts +23 -0
  33. package/src/runtime/{history-builder.ts → message/history-builder.ts} +2 -2
  34. package/src/runtime/{message-service.ts → message/service.ts} +8 -14
  35. package/src/runtime/{history → message}/tool-history-tracker.ts +1 -1
  36. package/src/runtime/{prompt.ts → prompt/builder.ts} +1 -1
  37. package/src/runtime/{provider.ts → provider/anthropic.ts} +4 -219
  38. package/src/runtime/provider/google.ts +12 -0
  39. package/src/runtime/provider/index.ts +44 -0
  40. package/src/runtime/provider/openai.ts +26 -0
  41. package/src/runtime/provider/opencode.ts +61 -0
  42. package/src/runtime/provider/openrouter.ts +11 -0
  43. package/src/runtime/provider/solforge.ts +22 -0
  44. package/src/runtime/provider/zai.ts +53 -0
  45. package/src/runtime/{branch.ts → session/branch.ts} +1 -1
  46. package/src/runtime/{db-operations.ts → session/db-operations.ts} +1 -1
  47. package/src/runtime/{session-manager.ts → session/manager.ts} +1 -1
  48. package/src/runtime/{session-queue.ts → session/queue.ts} +2 -2
  49. package/src/runtime/stream/abort-handler.ts +65 -0
  50. package/src/runtime/stream/error-handler.ts +200 -0
  51. package/src/runtime/stream/finish-handler.ts +123 -0
  52. package/src/runtime/stream/handlers.ts +5 -0
  53. package/src/runtime/stream/step-finish.ts +93 -0
  54. package/src/runtime/stream/types.ts +17 -0
  55. package/src/runtime/{tool-context.ts → tools/context.ts} +1 -1
  56. package/src/runtime/{tool-context-setup.ts → tools/setup.ts} +3 -3
  57. package/src/runtime/{token-utils.ts → utils/token.ts} +2 -2
  58. package/src/tools/adapter.ts +4 -4
  59. package/src/runtime/compaction.ts +0 -536
  60. package/src/runtime/runner.ts +0 -654
  61. package/src/runtime/stream-handlers.ts +0 -508
  62. /package/src/runtime/{cache-optimizer.ts → context/cache-optimizer.ts} +0 -0
  63. /package/src/runtime/{environment.ts → context/environment.ts} +0 -0
  64. /package/src/runtime/{context-optimizer.ts → context/optimizer.ts} +0 -0
  65. /package/src/runtime/{debug-state.ts → debug/state.ts} +0 -0
  66. /package/src/runtime/{error-handling.ts → errors/handling.ts} +0 -0
  67. /package/src/runtime/{history-truncator.ts → message/history-truncator.ts} +0 -0
  68. /package/src/runtime/{provider-selection.ts → provider/selection.ts} +0 -0
  69. /package/src/runtime/{tool-mapping.ts → tools/mapping.ts} +0 -0
  70. /package/src/runtime/{cwd.ts → utils/cwd.ts} +0 -0
@@ -1,80 +1,11 @@
1
- import type { AGIConfig, ProviderId } from '@agi-cli/sdk';
2
- import {
3
- catalog,
4
- createSolforgeModel,
5
- createOpenAIOAuthModel,
6
- getAuth,
7
- refreshToken,
8
- setAuth,
9
- } from '@agi-cli/sdk';
10
- import { openai, createOpenAI } from '@ai-sdk/openai';
1
+ import type { AGIConfig } from '@agi-cli/sdk';
2
+ import { getAuth, refreshToken, setAuth } from '@agi-cli/sdk';
11
3
  import { createAnthropic } from '@ai-sdk/anthropic';
12
- import { google, createGoogleGenerativeAI } from '@ai-sdk/google';
13
- import { createOpenRouter } from '@openrouter/ai-sdk-provider';
14
- import { toClaudeCodeName } from './tool-mapping.ts';
4
+ import { toClaudeCodeName } from '../tools/mapping.ts';
15
5
 
16
- // Version to report in user-agent for Claude Code compatibility
17
6
  const CLAUDE_CLI_VERSION = '1.0.61';
18
- import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
19
7
 
20
- export type ProviderName = ProviderId;
21
-
22
- async function getZaiInstance(cfg: AGIConfig, model: string) {
23
- const auth = await getAuth('zai', cfg.projectRoot);
24
- const entry = catalog.zai;
25
-
26
- let apiKey = '';
27
- const baseURL = entry?.api || 'https://api.z.ai/api/paas/v4';
28
-
29
- if (auth?.type === 'api' && auth.key) {
30
- apiKey = auth.key;
31
- } else {
32
- apiKey = process.env.ZAI_API_KEY || process.env.ZHIPU_API_KEY || '';
33
- }
34
-
35
- const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
36
-
37
- const instance = createOpenAICompatible({
38
- name: entry?.label ?? 'Z.AI',
39
- baseURL,
40
- headers,
41
- });
42
-
43
- return instance(model);
44
- }
45
-
46
- async function getZaiCodingInstance(cfg: AGIConfig, model: string) {
47
- const auth =
48
- (await getAuth('zai', cfg.projectRoot)) ||
49
- (await getAuth('zai-coding', cfg.projectRoot));
50
- const entry = catalog['zai-coding'];
51
-
52
- let apiKey = '';
53
- const baseURL = entry?.api || 'https://api.z.ai/api/coding/paas/v4';
54
-
55
- if (auth?.type === 'api' && auth.key) {
56
- apiKey = auth.key;
57
- } else {
58
- apiKey = process.env.ZAI_API_KEY || process.env.ZHIPU_API_KEY || '';
59
- }
60
-
61
- const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
62
-
63
- const instance = createOpenAICompatible({
64
- name: entry?.label ?? 'Z.AI Coding',
65
- baseURL,
66
- headers,
67
- });
68
-
69
- return instance(model);
70
- }
71
-
72
- function getOpenRouterInstance() {
73
- const apiKey = process.env.OPENROUTER_API_KEY ?? '';
74
- return createOpenRouter({ apiKey });
75
- }
76
-
77
- async function getAnthropicInstance(cfg: AGIConfig) {
8
+ export async function getAnthropicInstance(cfg: AGIConfig) {
78
9
  const auth = await getAuth('anthropic', cfg.projectRoot);
79
10
 
80
11
  if (auth?.type === 'oauth') {
@@ -137,7 +68,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
137
68
  }
138
69
  }
139
70
 
140
- // Required Claude Code headers
141
71
  headers.authorization = `Bearer ${currentAuth.access}`;
142
72
  headers['anthropic-beta'] =
143
73
  'claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14';
@@ -149,7 +79,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
149
79
  headers['content-type'] = 'application/json';
150
80
  headers.accept = 'application/json';
151
81
 
152
- // Stainless headers (fingerprinting)
153
82
  headers['x-stainless-arch'] = process.arch === 'arm64' ? 'arm64' : 'x64';
154
83
  headers['x-stainless-helper-method'] = 'stream';
155
84
  headers['x-stainless-lang'] = 'js';
@@ -165,19 +94,16 @@ async function getAnthropicInstance(cfg: AGIConfig) {
165
94
  headers['x-stainless-runtime-version'] = process.version;
166
95
  headers['x-stainless-timeout'] = '600';
167
96
 
168
- // Add ?beta=true to URL
169
97
  let url = typeof input === 'string' ? input : input.toString();
170
98
  if (url.includes('/v1/messages') && !url.includes('beta=true')) {
171
99
  url += url.includes('?') ? '&beta=true' : '?beta=true';
172
100
  }
173
101
 
174
- // Transform request body: tool names to PascalCase + apply caching
175
102
  let body = init?.body;
176
103
  if (body && typeof body === 'string') {
177
104
  try {
178
105
  const parsed = JSON.parse(body);
179
106
 
180
- // Transform tool names
181
107
  if (parsed.tools && Array.isArray(parsed.tools)) {
182
108
  parsed.tools = parsed.tools.map(
183
109
  (tool: { name: string; [key: string]: unknown }) => ({
@@ -187,16 +113,11 @@ async function getAnthropicInstance(cfg: AGIConfig) {
187
113
  );
188
114
  }
189
115
 
190
- // Apply ephemeral caching (max 4 cache breakpoints total)
191
- // Adapter adds 2 tool cache blocks, so we can add 2 more:
192
- // - 1 system block (the first one with tools description)
193
- // - 1 message block (the last user message)
194
116
  const MAX_SYSTEM_CACHE = 1;
195
117
  const MAX_MESSAGE_CACHE = 1;
196
118
  let systemCacheUsed = 0;
197
119
  let messageCacheUsed = 0;
198
120
 
199
- // Cache first system message only (contains agent instructions)
200
121
  if (parsed.system && Array.isArray(parsed.system)) {
201
122
  parsed.system = parsed.system.map(
202
123
  (
@@ -217,7 +138,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
217
138
  );
218
139
  }
219
140
 
220
- // Transform tool names in messages and apply caching to last message only
221
141
  if (parsed.messages && Array.isArray(parsed.messages)) {
222
142
  const messageCount = parsed.messages.length;
223
143
 
@@ -230,7 +150,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
230
150
  },
231
151
  msgIndex: number,
232
152
  ) => {
233
- // Only cache the very last message
234
153
  const isLast = msgIndex === messageCount - 1;
235
154
 
236
155
  if (Array.isArray(msg.content)) {
@@ -245,7 +164,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
245
164
  ) => {
246
165
  let transformedBlock = block;
247
166
 
248
- // Transform tool names
249
167
  if (block.type === 'tool_use' && block.name) {
250
168
  transformedBlock = {
251
169
  ...block,
@@ -259,7 +177,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
259
177
  };
260
178
  }
261
179
 
262
- // Add cache_control to last block of last message
263
180
  if (
264
181
  isLast &&
265
182
  !transformedBlock.cache_control &&
@@ -279,7 +196,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
279
196
  return { ...msg, content };
280
197
  }
281
198
 
282
- // For string content, wrap in array with cache_control if last message
283
199
  if (
284
200
  isLast &&
285
201
  messageCacheUsed < MAX_MESSAGE_CACHE &&
@@ -321,8 +237,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
321
237
  });
322
238
  }
323
239
 
324
- // For API key auth, also apply caching via customFetch
325
- // This optimizes token usage even without OAuth
326
240
  const customFetch = async (
327
241
  input: string | URL | Request,
328
242
  init?: RequestInit,
@@ -332,15 +246,11 @@ async function getAnthropicInstance(cfg: AGIConfig) {
332
246
  try {
333
247
  const parsed = JSON.parse(body);
334
248
 
335
- // Apply ephemeral caching (max 4 cache breakpoints total)
336
- // Adapter adds 2 tool cache blocks, so we can add 2 more:
337
- // - 1 system block + 1 message block = 2
338
249
  const MAX_SYSTEM_CACHE = 1;
339
250
  const MAX_MESSAGE_CACHE = 1;
340
251
  let systemCacheUsed = 0;
341
252
  let messageCacheUsed = 0;
342
253
 
343
- // Cache first system message
344
254
  if (parsed.system && Array.isArray(parsed.system)) {
345
255
  parsed.system = parsed.system.map(
346
256
  (
@@ -361,7 +271,6 @@ async function getAnthropicInstance(cfg: AGIConfig) {
361
271
  );
362
272
  }
363
273
 
364
- // Cache last message only
365
274
  if (parsed.messages && Array.isArray(parsed.messages)) {
366
275
  const messageCount = parsed.messages.length;
367
276
  parsed.messages = parsed.messages.map(
@@ -432,127 +341,3 @@ async function getAnthropicInstance(cfg: AGIConfig) {
432
341
  fetch: customFetch as typeof fetch,
433
342
  });
434
343
  }
435
-
436
- export async function resolveModel(
437
- provider: ProviderName,
438
- model: string,
439
- cfg: AGIConfig,
440
- options?: { systemPrompt?: string },
441
- ) {
442
- if (provider === 'openai') {
443
- const auth = await getAuth('openai', cfg.projectRoot);
444
- if (auth?.type === 'oauth') {
445
- const isCodexModel = model.toLowerCase().includes('codex');
446
- return createOpenAIOAuthModel(model, {
447
- oauth: auth,
448
- projectRoot: cfg.projectRoot,
449
- reasoningEffort: isCodexModel ? 'high' : 'medium',
450
- reasoningSummary: 'auto',
451
- instructions: options?.systemPrompt,
452
- });
453
- }
454
- if (auth?.type === 'api' && auth.key) {
455
- const instance = createOpenAI({ apiKey: auth.key });
456
- return instance(model);
457
- }
458
- return openai(model);
459
- }
460
- if (provider === 'anthropic') {
461
- const instance = await getAnthropicInstance(cfg);
462
- return instance(model);
463
- }
464
- if (provider === 'google') {
465
- const auth = await getAuth('google', cfg.projectRoot);
466
- if (auth?.type === 'api' && auth.key) {
467
- const instance = createGoogleGenerativeAI({ apiKey: auth.key });
468
- return instance(model);
469
- }
470
- return google(model);
471
- }
472
- if (provider === 'openrouter') {
473
- const openrouter = getOpenRouterInstance();
474
- return openrouter.chat(model);
475
- }
476
- if (provider === 'opencode') {
477
- const entry = catalog[provider];
478
- const normalizedModel = normalizeModelIdentifier(provider, model);
479
- const modelInfo =
480
- entry?.models.find((m) => m.id === normalizedModel) ??
481
- entry?.models.find((m) => m.id === model);
482
- const resolvedModelId = modelInfo?.id ?? normalizedModel ?? model;
483
- const binding = modelInfo?.provider?.npm ?? entry?.npm;
484
- const apiKey = process.env.OPENCODE_API_KEY ?? '';
485
- const baseURL =
486
- modelInfo?.provider?.baseURL ||
487
- modelInfo?.provider?.api ||
488
- entry?.api ||
489
- 'https://opencode.ai/zen/v1';
490
- const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
491
- if (binding === '@ai-sdk/openai') {
492
- const instance = createOpenAI({ apiKey, baseURL });
493
- return instance(resolvedModelId);
494
- }
495
- if (binding === '@ai-sdk/anthropic') {
496
- const instance = createAnthropic({ apiKey, baseURL });
497
- return instance(resolvedModelId);
498
- }
499
- if (binding === '@ai-sdk/openai-compatible') {
500
- const instance = createOpenAICompatible({
501
- name: entry?.label ?? 'opencode',
502
- baseURL,
503
- headers,
504
- });
505
- return instance(resolvedModelId);
506
- }
507
-
508
- const ocOpenAI = createOpenAI({ apiKey, baseURL });
509
- const ocAnthropic = createAnthropic({ apiKey, baseURL });
510
- const ocCompat = createOpenAICompatible({
511
- name: entry?.label ?? 'opencode',
512
- baseURL,
513
- headers,
514
- });
515
-
516
- const id = resolvedModelId.toLowerCase();
517
- if (id.includes('claude')) return ocAnthropic(resolvedModelId);
518
- if (
519
- id.includes('qwen3-coder') ||
520
- id.includes('grok-code') ||
521
- id.includes('kimi-k2')
522
- )
523
- return ocCompat(resolvedModelId);
524
- return ocOpenAI(resolvedModelId);
525
- }
526
- if (provider === 'solforge') {
527
- const privateKey = process.env.SOLFORGE_PRIVATE_KEY ?? '';
528
- if (!privateKey) {
529
- throw new Error(
530
- 'Solforge provider requires SOLFORGE_PRIVATE_KEY (base58 Solana secret).',
531
- );
532
- }
533
- const baseURL = process.env.SOLFORGE_BASE_URL;
534
- const rpcURL = process.env.SOLFORGE_SOLANA_RPC_URL;
535
- const topupAmount = process.env.SOLFORGE_TOPUP_MICRO_USDC;
536
- return createSolforgeModel(
537
- model,
538
- { privateKey },
539
- {
540
- baseURL,
541
- rpcURL,
542
- topupAmountMicroUsdc: topupAmount,
543
- },
544
- );
545
- }
546
- if (provider === 'zai') {
547
- return getZaiInstance(cfg, model);
548
- }
549
- if (provider === 'zai-coding') {
550
- return getZaiCodingInstance(cfg, model);
551
- }
552
- throw new Error(`Unsupported provider: ${provider}`);
553
- }
554
-
555
- function normalizeModelIdentifier(provider: ProviderId, model: string): string {
556
- const prefix = `${provider}/`;
557
- return model.startsWith(prefix) ? model.slice(prefix.length) : model;
558
- }
@@ -0,0 +1,12 @@
1
+ import type { AGIConfig } from '@agi-cli/sdk';
2
+ import { getAuth } from '@agi-cli/sdk';
3
+ import { google, createGoogleGenerativeAI } from '@ai-sdk/google';
4
+
5
+ export async function resolveGoogleModel(model: string, cfg: AGIConfig) {
6
+ const auth = await getAuth('google', cfg.projectRoot);
7
+ if (auth?.type === 'api' && auth.key) {
8
+ const instance = createGoogleGenerativeAI({ apiKey: auth.key });
9
+ return instance(model);
10
+ }
11
+ return google(model);
12
+ }
@@ -0,0 +1,44 @@
1
+ import type { AGIConfig, ProviderId } from '@agi-cli/sdk';
2
+ import { getAnthropicInstance } from './anthropic.ts';
3
+ import { resolveOpenAIModel } from './openai.ts';
4
+ import { resolveGoogleModel } from './google.ts';
5
+ import { resolveOpenRouterModel } from './openrouter.ts';
6
+ import { resolveSolforgeModel } from './solforge.ts';
7
+ import { getZaiInstance, getZaiCodingInstance } from './zai.ts';
8
+ import { resolveOpencodeModel } from './opencode.ts';
9
+
10
+ export type ProviderName = ProviderId;
11
+
12
+ export async function resolveModel(
13
+ provider: ProviderName,
14
+ model: string,
15
+ cfg: AGIConfig,
16
+ options?: { systemPrompt?: string },
17
+ ) {
18
+ if (provider === 'openai') {
19
+ return resolveOpenAIModel(model, cfg, options);
20
+ }
21
+ if (provider === 'anthropic') {
22
+ const instance = await getAnthropicInstance(cfg);
23
+ return instance(model);
24
+ }
25
+ if (provider === 'google') {
26
+ return resolveGoogleModel(model, cfg);
27
+ }
28
+ if (provider === 'openrouter') {
29
+ return resolveOpenRouterModel(model);
30
+ }
31
+ if (provider === 'opencode') {
32
+ return resolveOpencodeModel(model, cfg);
33
+ }
34
+ if (provider === 'solforge') {
35
+ return resolveSolforgeModel(model);
36
+ }
37
+ if (provider === 'zai') {
38
+ return getZaiInstance(cfg, model);
39
+ }
40
+ if (provider === 'zai-coding') {
41
+ return getZaiCodingInstance(cfg, model);
42
+ }
43
+ throw new Error(`Unsupported provider: ${provider}`);
44
+ }
@@ -0,0 +1,26 @@
1
+ import type { AGIConfig } from '@agi-cli/sdk';
2
+ import { getAuth, createOpenAIOAuthModel } from '@agi-cli/sdk';
3
+ import { openai, createOpenAI } from '@ai-sdk/openai';
4
+
5
+ export async function resolveOpenAIModel(
6
+ model: string,
7
+ cfg: AGIConfig,
8
+ options?: { systemPrompt?: string },
9
+ ) {
10
+ const auth = await getAuth('openai', cfg.projectRoot);
11
+ if (auth?.type === 'oauth') {
12
+ const isCodexModel = model.toLowerCase().includes('codex');
13
+ return createOpenAIOAuthModel(model, {
14
+ oauth: auth,
15
+ projectRoot: cfg.projectRoot,
16
+ reasoningEffort: isCodexModel ? 'high' : 'medium',
17
+ reasoningSummary: 'auto',
18
+ instructions: options?.systemPrompt,
19
+ });
20
+ }
21
+ if (auth?.type === 'api' && auth.key) {
22
+ const instance = createOpenAI({ apiKey: auth.key });
23
+ return instance(model);
24
+ }
25
+ return openai(model);
26
+ }
@@ -0,0 +1,61 @@
1
+ import type { AGIConfig, ProviderId } from '@agi-cli/sdk';
2
+ import { catalog } from '@agi-cli/sdk';
3
+ import { createOpenAI } from '@ai-sdk/openai';
4
+ import { createAnthropic } from '@ai-sdk/anthropic';
5
+ import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
6
+
7
+ function normalizeModelIdentifier(provider: ProviderId, model: string): string {
8
+ const prefix = `${provider}/`;
9
+ return model.startsWith(prefix) ? model.slice(prefix.length) : model;
10
+ }
11
+
12
+ export function resolveOpencodeModel(model: string, _cfg: AGIConfig) {
13
+ const entry = catalog.opencode;
14
+ const normalizedModel = normalizeModelIdentifier('opencode', model);
15
+ const modelInfo =
16
+ entry?.models.find((m) => m.id === normalizedModel) ??
17
+ entry?.models.find((m) => m.id === model);
18
+ const resolvedModelId = modelInfo?.id ?? normalizedModel ?? model;
19
+ const binding = modelInfo?.provider?.npm ?? entry?.npm;
20
+ const apiKey = process.env.OPENCODE_API_KEY ?? '';
21
+ const baseURL =
22
+ modelInfo?.provider?.baseURL ||
23
+ modelInfo?.provider?.api ||
24
+ entry?.api ||
25
+ 'https://opencode.ai/zen/v1';
26
+ const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
27
+ if (binding === '@ai-sdk/openai') {
28
+ const instance = createOpenAI({ apiKey, baseURL });
29
+ return instance(resolvedModelId);
30
+ }
31
+ if (binding === '@ai-sdk/anthropic') {
32
+ const instance = createAnthropic({ apiKey, baseURL });
33
+ return instance(resolvedModelId);
34
+ }
35
+ if (binding === '@ai-sdk/openai-compatible') {
36
+ const instance = createOpenAICompatible({
37
+ name: entry?.label ?? 'opencode',
38
+ baseURL,
39
+ headers,
40
+ });
41
+ return instance(resolvedModelId);
42
+ }
43
+
44
+ const ocOpenAI = createOpenAI({ apiKey, baseURL });
45
+ const ocAnthropic = createAnthropic({ apiKey, baseURL });
46
+ const ocCompat = createOpenAICompatible({
47
+ name: entry?.label ?? 'opencode',
48
+ baseURL,
49
+ headers,
50
+ });
51
+
52
+ const id = resolvedModelId.toLowerCase();
53
+ if (id.includes('claude')) return ocAnthropic(resolvedModelId);
54
+ if (
55
+ id.includes('qwen3-coder') ||
56
+ id.includes('grok-code') ||
57
+ id.includes('kimi-k2')
58
+ )
59
+ return ocCompat(resolvedModelId);
60
+ return ocOpenAI(resolvedModelId);
61
+ }
@@ -0,0 +1,11 @@
1
+ import { createOpenRouter } from '@openrouter/ai-sdk-provider';
2
+
3
+ export function getOpenRouterInstance() {
4
+ const apiKey = process.env.OPENROUTER_API_KEY ?? '';
5
+ return createOpenRouter({ apiKey });
6
+ }
7
+
8
+ export function resolveOpenRouterModel(model: string) {
9
+ const openrouter = getOpenRouterInstance();
10
+ return openrouter.chat(model);
11
+ }
@@ -0,0 +1,22 @@
1
+ import { createSolforgeModel } from '@agi-cli/sdk';
2
+
3
+ export function resolveSolforgeModel(model: string) {
4
+ const privateKey = process.env.SOLFORGE_PRIVATE_KEY ?? '';
5
+ if (!privateKey) {
6
+ throw new Error(
7
+ 'Solforge provider requires SOLFORGE_PRIVATE_KEY (base58 Solana secret).',
8
+ );
9
+ }
10
+ const baseURL = process.env.SOLFORGE_BASE_URL;
11
+ const rpcURL = process.env.SOLFORGE_SOLANA_RPC_URL;
12
+ const topupAmount = process.env.SOLFORGE_TOPUP_MICRO_USDC;
13
+ return createSolforgeModel(
14
+ model,
15
+ { privateKey },
16
+ {
17
+ baseURL,
18
+ rpcURL,
19
+ topupAmountMicroUsdc: topupAmount,
20
+ },
21
+ );
22
+ }
@@ -0,0 +1,53 @@
1
+ import type { AGIConfig } from '@agi-cli/sdk';
2
+ import { catalog, getAuth } from '@agi-cli/sdk';
3
+ import { createOpenAICompatible } from '@ai-sdk/openai-compatible';
4
+
5
+ export async function getZaiInstance(cfg: AGIConfig, model: string) {
6
+ const auth = await getAuth('zai', cfg.projectRoot);
7
+ const entry = catalog.zai;
8
+
9
+ let apiKey = '';
10
+ const baseURL = entry?.api || 'https://api.z.ai/api/paas/v4';
11
+
12
+ if (auth?.type === 'api' && auth.key) {
13
+ apiKey = auth.key;
14
+ } else {
15
+ apiKey = process.env.ZAI_API_KEY || process.env.ZHIPU_API_KEY || '';
16
+ }
17
+
18
+ const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
19
+
20
+ const instance = createOpenAICompatible({
21
+ name: entry?.label ?? 'Z.AI',
22
+ baseURL,
23
+ headers,
24
+ });
25
+
26
+ return instance(model);
27
+ }
28
+
29
+ export async function getZaiCodingInstance(cfg: AGIConfig, model: string) {
30
+ const auth =
31
+ (await getAuth('zai', cfg.projectRoot)) ||
32
+ (await getAuth('zai-coding', cfg.projectRoot));
33
+ const entry = catalog['zai-coding'];
34
+
35
+ let apiKey = '';
36
+ const baseURL = entry?.api || 'https://api.z.ai/api/coding/paas/v4';
37
+
38
+ if (auth?.type === 'api' && auth.key) {
39
+ apiKey = auth.key;
40
+ } else {
41
+ apiKey = process.env.ZAI_API_KEY || process.env.ZHIPU_API_KEY || '';
42
+ }
43
+
44
+ const headers = apiKey ? { Authorization: `Bearer ${apiKey}` } : undefined;
45
+
46
+ const instance = createOpenAICompatible({
47
+ name: entry?.label ?? 'Z.AI Coding',
48
+ baseURL,
49
+ headers,
50
+ });
51
+
52
+ return instance(model);
53
+ }
@@ -1,7 +1,7 @@
1
1
  import { eq, asc } from 'drizzle-orm';
2
2
  import type { DB } from '@agi-cli/database';
3
3
  import { sessions, messages, messageParts } from '@agi-cli/database/schema';
4
- import { publish } from '../events/bus.ts';
4
+ import { publish } from '../../events/bus.ts';
5
5
  import type { ProviderId } from '@agi-cli/sdk';
6
6
 
7
7
  type SessionRow = typeof sessions.$inferSelect;
@@ -1,7 +1,7 @@
1
1
  import type { getDb } from '@agi-cli/database';
2
2
  import { messages, messageParts, sessions } from '@agi-cli/database/schema';
3
3
  import { eq } from 'drizzle-orm';
4
- import type { RunOpts } from './session-queue.ts';
4
+ import type { RunOpts } from './queue.ts';
5
5
 
6
6
  export type UsageData = {
7
7
  inputTokens?: number;
@@ -8,7 +8,7 @@ import {
8
8
  ensureProviderEnv,
9
9
  type ProviderId,
10
10
  } from '@agi-cli/sdk';
11
- import { publish } from '../events/bus.ts';
11
+ import { publish } from '../../events/bus.ts';
12
12
 
13
13
  type SessionRow = typeof sessions.$inferSelect;
14
14
 
@@ -1,5 +1,5 @@
1
- import type { ProviderName } from './provider.ts';
2
- import { publish } from '../events/bus.ts';
1
+ import type { ProviderName } from '../provider/index.ts';
2
+ import { publish } from '../../events/bus.ts';
3
3
 
4
4
  export type RunOpts = {
5
5
  sessionId: string;
@@ -0,0 +1,65 @@
1
+ import type { getDb } from '@agi-cli/database';
2
+ import { messages, messageParts } from '@agi-cli/database/schema';
3
+ import { eq } from 'drizzle-orm';
4
+ import { publish } from '../../events/bus.ts';
5
+ import type { RunOpts } from '../session/queue.ts';
6
+ import type { ToolAdapterContext } from '../../tools/adapter.ts';
7
+ import type { AbortEvent } from './types.ts';
8
+
9
+ export function createAbortHandler(
10
+ opts: RunOpts,
11
+ db: Awaited<ReturnType<typeof getDb>>,
12
+ getStepIndex: () => number,
13
+ sharedCtx: ToolAdapterContext,
14
+ ) {
15
+ return async ({ steps }: AbortEvent) => {
16
+ const stepIndex = getStepIndex();
17
+
18
+ const abortPartId = crypto.randomUUID();
19
+ await db.insert(messageParts).values({
20
+ id: abortPartId,
21
+ messageId: opts.assistantMessageId,
22
+ index: await sharedCtx.nextIndex(),
23
+ stepIndex,
24
+ type: 'error',
25
+ content: JSON.stringify({
26
+ message: 'Generation stopped by user',
27
+ type: 'abort',
28
+ isAborted: true,
29
+ stepsCompleted: steps.length,
30
+ }),
31
+ agent: opts.agent,
32
+ provider: opts.provider,
33
+ model: opts.model,
34
+ startedAt: Date.now(),
35
+ completedAt: Date.now(),
36
+ });
37
+
38
+ await db
39
+ .update(messages)
40
+ .set({
41
+ status: 'error',
42
+ error: 'Generation stopped by user',
43
+ errorType: 'abort',
44
+ errorDetails: JSON.stringify({
45
+ stepsCompleted: steps.length,
46
+ abortedAt: Date.now(),
47
+ }),
48
+ isAborted: true,
49
+ })
50
+ .where(eq(messages.id, opts.assistantMessageId));
51
+
52
+ publish({
53
+ type: 'error',
54
+ sessionId: opts.sessionId,
55
+ payload: {
56
+ messageId: opts.assistantMessageId,
57
+ partId: abortPartId,
58
+ error: 'Generation stopped by user',
59
+ errorType: 'abort',
60
+ isAborted: true,
61
+ stepsCompleted: steps.length,
62
+ },
63
+ });
64
+ };
65
+ }