@agi-cli/server 0.1.119 → 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 +9 -5
  3. package/src/openapi/paths/git.ts +4 -0
  4. package/src/routes/ask.ts +13 -14
  5. package/src/routes/branch.ts +106 -0
  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/session/branch.ts +277 -0
  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
@@ -5,13 +5,13 @@ import {
5
5
  createSession,
6
6
  getLastSession,
7
7
  getSessionById,
8
- } from './session-manager.ts';
8
+ } from '../session/manager.ts';
9
9
  import {
10
10
  selectProviderAndModel,
11
11
  type ProviderSelection,
12
- } from './provider-selection.ts';
13
- import { resolveAgentConfig } from './agent-registry.ts';
14
- import { dispatchAssistantMessage } from './message-service.ts';
12
+ } from '../provider/selection.ts';
13
+ import { resolveAgentConfig } from '../agent/registry.ts';
14
+ import { dispatchAssistantMessage } from '../message/service.ts';
15
15
  import {
16
16
  validateProviderModel,
17
17
  isProviderAuthorized,
@@ -21,7 +21,7 @@ import {
21
21
  type ProviderId,
22
22
  } from '@agi-cli/sdk';
23
23
  import { sessions } from '@agi-cli/database/schema';
24
- import { time } from './debug.ts';
24
+ import { time } from '../debug/index.ts';
25
25
 
26
26
  export class AskServiceError extends Error {
27
27
  constructor(
@@ -5,7 +5,7 @@
5
5
  * centralized debug-state and logger modules.
6
6
  */
7
7
 
8
- import { isDebugEnabled as isDebugEnabledNew } from './debug-state';
8
+ import { isDebugEnabled as isDebugEnabledNew } from './state.ts';
9
9
  import { time as timeNew, debug as debugNew } from '@agi-cli/sdk';
10
10
 
11
11
  const TRUTHY = new Set(['1', 'true', 'yes', 'on']);
@@ -5,8 +5,8 @@
5
5
  * across all API endpoints.
6
6
  */
7
7
 
8
- import { isDebugEnabled } from './debug-state';
9
- import { toErrorPayload } from './error-handling';
8
+ import { isDebugEnabled } from '../debug/state.ts';
9
+ import { toErrorPayload } from './handling.ts';
10
10
 
11
11
  /**
12
12
  * Standard API error response format
@@ -0,0 +1,137 @@
1
+ import type { getDb } from '@agi-cli/database';
2
+ import { messageParts } from '@agi-cli/database/schema';
3
+ import { eq } from 'drizzle-orm';
4
+ import { streamText } from 'ai';
5
+ import { resolveModel } from '../provider/index.ts';
6
+ import { loadConfig } from '@agi-cli/sdk';
7
+ import { debugLog } from '../debug/index.ts';
8
+ import { getModelLimits } from './compaction-limits.ts';
9
+ import { buildCompactionContext } from './compaction-context.ts';
10
+ import { getCompactionSystemPrompt } from './compaction-detect.ts';
11
+ import { markSessionCompacted } from './compaction-mark.ts';
12
+
13
+ export async function performAutoCompaction(
14
+ db: Awaited<ReturnType<typeof getDb>>,
15
+ sessionId: string,
16
+ assistantMessageId: string,
17
+ publishFn: (event: {
18
+ type: string;
19
+ sessionId: string;
20
+ payload: Record<string, unknown>;
21
+ }) => void,
22
+ provider: string,
23
+ modelId: string,
24
+ ): Promise<{
25
+ success: boolean;
26
+ summary?: string;
27
+ error?: string;
28
+ compactMessageId?: string;
29
+ }> {
30
+ debugLog(`[compaction] Starting auto-compaction for session ${sessionId}`);
31
+
32
+ try {
33
+ const limits = getModelLimits(provider, modelId);
34
+ const contextTokenLimit = limits
35
+ ? Math.max(Math.floor(limits.context * 0.5), 15000)
36
+ : 15000;
37
+ debugLog(
38
+ `[compaction] Model ${modelId} context limit: ${limits?.context ?? 'unknown'}, using ${contextTokenLimit} tokens for compaction`,
39
+ );
40
+
41
+ const context = await buildCompactionContext(
42
+ db,
43
+ sessionId,
44
+ contextTokenLimit,
45
+ );
46
+ if (!context || context.length < 100) {
47
+ debugLog('[compaction] Not enough context to compact');
48
+ return { success: false, error: 'Not enough context to compact' };
49
+ }
50
+
51
+ const cfg = await loadConfig();
52
+ debugLog(
53
+ `[compaction] Using session model ${provider}/${modelId} for auto-compaction`,
54
+ );
55
+ const model = await resolveModel(
56
+ provider as Parameters<typeof resolveModel>[0],
57
+ modelId,
58
+ cfg,
59
+ );
60
+
61
+ const compactPartId = crypto.randomUUID();
62
+ const now = Date.now();
63
+
64
+ await db.insert(messageParts).values({
65
+ id: compactPartId,
66
+ messageId: assistantMessageId,
67
+ index: 0,
68
+ stepIndex: 0,
69
+ type: 'text',
70
+ content: JSON.stringify({ text: '' }),
71
+ agent: 'system',
72
+ provider: provider,
73
+ model: modelId,
74
+ startedAt: now,
75
+ });
76
+
77
+ const prompt = getCompactionSystemPrompt();
78
+ const result = streamText({
79
+ model,
80
+ system: `${prompt}\n\nIMPORTANT: Generate a comprehensive summary. This will replace the detailed conversation history.`,
81
+ messages: [
82
+ {
83
+ role: 'user',
84
+ content: `Please summarize this conversation:\n\n<conversation-to-summarize>\n${context}\n</conversation-to-summarize>`,
85
+ },
86
+ ],
87
+ maxOutputTokens: 2000,
88
+ });
89
+
90
+ let summary = '';
91
+ for await (const chunk of result.textStream) {
92
+ summary += chunk;
93
+
94
+ publishFn({
95
+ type: 'message.part.delta',
96
+ sessionId,
97
+ payload: {
98
+ messageId: assistantMessageId,
99
+ partId: compactPartId,
100
+ stepIndex: 0,
101
+ type: 'text',
102
+ delta: chunk,
103
+ },
104
+ });
105
+ }
106
+
107
+ await db
108
+ .update(messageParts)
109
+ .set({
110
+ content: JSON.stringify({ text: summary }),
111
+ completedAt: Date.now(),
112
+ })
113
+ .where(eq(messageParts.id, compactPartId));
114
+
115
+ if (!summary || summary.length < 50) {
116
+ debugLog('[compaction] Failed to generate summary');
117
+ return { success: false, error: 'Failed to generate summary' };
118
+ }
119
+
120
+ debugLog(`[compaction] Generated summary: ${summary.slice(0, 100)}...`);
121
+
122
+ const compactResult = await markSessionCompacted(
123
+ db,
124
+ sessionId,
125
+ assistantMessageId,
126
+ );
127
+ debugLog(
128
+ `[compaction] Marked ${compactResult.compacted} parts as compacted, saved ~${compactResult.saved} tokens`,
129
+ );
130
+
131
+ return { success: true, summary, compactMessageId: assistantMessageId };
132
+ } catch (err) {
133
+ const errorMsg = err instanceof Error ? err.message : String(err);
134
+ debugLog(`[compaction] Auto-compaction failed: ${errorMsg}`);
135
+ return { success: false, error: errorMsg };
136
+ }
137
+ }
@@ -0,0 +1,64 @@
1
+ import type { getDb } from '@agi-cli/database';
2
+ import { messages, messageParts } from '@agi-cli/database/schema';
3
+ import { eq, asc } from 'drizzle-orm';
4
+
5
+ export async function buildCompactionContext(
6
+ db: Awaited<ReturnType<typeof getDb>>,
7
+ sessionId: string,
8
+ contextTokenLimit?: number,
9
+ ): Promise<string> {
10
+ const allMessages = await db
11
+ .select()
12
+ .from(messages)
13
+ .where(eq(messages.sessionId, sessionId))
14
+ .orderBy(asc(messages.createdAt));
15
+
16
+ const lines: string[] = [];
17
+ let totalChars = 0;
18
+ const maxChars = contextTokenLimit ? contextTokenLimit * 4 : 60000;
19
+
20
+ for (const msg of allMessages) {
21
+ if (totalChars > maxChars) {
22
+ lines.unshift('[...earlier content truncated...]');
23
+ break;
24
+ }
25
+
26
+ const parts = await db
27
+ .select()
28
+ .from(messageParts)
29
+ .where(eq(messageParts.messageId, msg.id))
30
+ .orderBy(asc(messageParts.index));
31
+
32
+ for (const part of parts) {
33
+ if (part.compactedAt) continue;
34
+
35
+ try {
36
+ const content = JSON.parse(part.content ?? '{}');
37
+
38
+ if (part.type === 'text' && content.text) {
39
+ const text = `[${msg.role.toUpperCase()}]: ${content.text}`;
40
+ lines.push(text.slice(0, 3000));
41
+ totalChars += text.length;
42
+ } else if (part.type === 'tool_call' && content.name) {
43
+ const argsStr =
44
+ typeof content.args === 'object'
45
+ ? JSON.stringify(content.args).slice(0, 500)
46
+ : '';
47
+ const text = `[TOOL ${content.name}]: ${argsStr}`;
48
+ lines.push(text);
49
+ totalChars += text.length;
50
+ } else if (part.type === 'tool_result' && content.result !== null) {
51
+ const resultStr =
52
+ typeof content.result === 'string'
53
+ ? content.result.slice(0, 1500)
54
+ : JSON.stringify(content.result ?? '').slice(0, 1500);
55
+ const text = `[RESULT]: ${resultStr}`;
56
+ lines.push(text);
57
+ totalChars += text.length;
58
+ }
59
+ } catch {}
60
+ }
61
+ }
62
+
63
+ return lines.join('\n');
64
+ }
@@ -0,0 +1,19 @@
1
+ export function isCompactCommand(content: string): boolean {
2
+ const trimmed = content.trim().toLowerCase();
3
+ return trimmed === '/compact';
4
+ }
5
+
6
+ export function getCompactionSystemPrompt(): string {
7
+ return `
8
+ The user has requested to compact the conversation. Generate a comprehensive summary that captures:
9
+
10
+ 1. **Main Goals**: What was the user trying to accomplish?
11
+ 2. **Key Actions**: What files were created, modified, or deleted?
12
+ 3. **Important Decisions**: What approaches or solutions were chosen and why?
13
+ 4. **Current State**: What is done and what might be pending?
14
+ 5. **Critical Context**: Any gotchas, errors encountered, or important details for continuing.
15
+
16
+ Format your response as a clear, structured summary. Start with "📦 **Context Compacted**" header.
17
+ Keep under 2000 characters but be thorough. This summary will replace detailed tool history.
18
+ `;
19
+ }
@@ -0,0 +1,58 @@
1
+ export const PRUNE_PROTECT = 40_000;
2
+
3
+ export function estimateTokens(text: string): number {
4
+ return Math.max(0, Math.round((text || '').length / 4));
5
+ }
6
+
7
+ export interface TokenUsage {
8
+ input: number;
9
+ output: number;
10
+ cacheRead?: number;
11
+ cacheWrite?: number;
12
+ reasoning?: number;
13
+ }
14
+
15
+ export interface ModelLimits {
16
+ context: number;
17
+ output: number;
18
+ }
19
+
20
+ export function isOverflow(tokens: TokenUsage, limits: ModelLimits): boolean {
21
+ if (limits.context === 0) return false;
22
+
23
+ const count = tokens.input + (tokens.cacheRead ?? 0) + tokens.output;
24
+ const usableContext = limits.context - limits.output;
25
+
26
+ return count > usableContext;
27
+ }
28
+
29
+ export function getModelLimits(
30
+ _provider: string,
31
+ model: string,
32
+ ): ModelLimits | null {
33
+ const defaults: Record<string, ModelLimits> = {
34
+ 'claude-sonnet-4-20250514': { context: 200000, output: 16000 },
35
+ 'claude-3-5-sonnet-20241022': { context: 200000, output: 8192 },
36
+ 'claude-3-5-haiku-20241022': { context: 200000, output: 8192 },
37
+ 'gpt-4o': { context: 128000, output: 16384 },
38
+ 'gpt-4o-mini': { context: 128000, output: 16384 },
39
+ o1: { context: 200000, output: 100000 },
40
+ 'o3-mini': { context: 200000, output: 100000 },
41
+ 'gemini-2.0-flash': { context: 1000000, output: 8192 },
42
+ 'gemini-1.5-pro': { context: 2000000, output: 8192 },
43
+ };
44
+
45
+ if (defaults[model]) return defaults[model];
46
+
47
+ for (const [key, limits] of Object.entries(defaults)) {
48
+ if (model.includes(key) || key.includes(model)) return limits;
49
+ }
50
+
51
+ return null;
52
+ }
53
+
54
+ export function isCompacted(part: { compactedAt?: number | null }): boolean {
55
+ return !!part.compactedAt;
56
+ }
57
+
58
+ export const COMPACTED_PLACEHOLDER = '[Compacted]';
@@ -0,0 +1,115 @@
1
+ import type { getDb } from '@agi-cli/database';
2
+ import { messages, messageParts } from '@agi-cli/database/schema';
3
+ import { eq, desc, and, lt } from 'drizzle-orm';
4
+ import { debugLog } from '../debug/index.ts';
5
+ import { estimateTokens, PRUNE_PROTECT } from './compaction-limits.ts';
6
+
7
+ const PROTECTED_TOOLS = ['skill'];
8
+
9
+ export async function markSessionCompacted(
10
+ db: Awaited<ReturnType<typeof getDb>>,
11
+ sessionId: string,
12
+ compactMessageId: string,
13
+ ): Promise<{ compacted: number; saved: number }> {
14
+ debugLog(`[compaction] Marking session ${sessionId} as compacted`);
15
+
16
+ const compactMsg = await db
17
+ .select()
18
+ .from(messages)
19
+ .where(eq(messages.id, compactMessageId))
20
+ .limit(1);
21
+
22
+ if (!compactMsg.length) {
23
+ debugLog('[compaction] Compact message not found');
24
+ return { compacted: 0, saved: 0 };
25
+ }
26
+
27
+ const cutoffTime = compactMsg[0].createdAt;
28
+
29
+ const oldMessages = await db
30
+ .select()
31
+ .from(messages)
32
+ .where(
33
+ and(
34
+ eq(messages.sessionId, sessionId),
35
+ lt(messages.createdAt, cutoffTime),
36
+ ),
37
+ )
38
+ .orderBy(desc(messages.createdAt));
39
+
40
+ let totalTokens = 0;
41
+ let compactedTokens = 0;
42
+ const toCompact: Array<{ id: string; content: string }> = [];
43
+ let turns = 0;
44
+
45
+ for (const msg of oldMessages) {
46
+ if (msg.role === 'user') {
47
+ turns++;
48
+ }
49
+
50
+ if (turns < 2) continue;
51
+
52
+ const parts = await db
53
+ .select()
54
+ .from(messageParts)
55
+ .where(eq(messageParts.messageId, msg.id))
56
+ .orderBy(desc(messageParts.index));
57
+
58
+ for (const part of parts) {
59
+ if (part.type !== 'tool_call' && part.type !== 'tool_result') continue;
60
+
61
+ if (part.toolName && PROTECTED_TOOLS.includes(part.toolName)) {
62
+ continue;
63
+ }
64
+
65
+ if (part.compactedAt) continue;
66
+
67
+ let content: { result?: unknown; args?: unknown };
68
+ try {
69
+ content = JSON.parse(part.content ?? '{}');
70
+ } catch {
71
+ continue;
72
+ }
73
+
74
+ const contentStr =
75
+ part.type === 'tool_result'
76
+ ? typeof content.result === 'string'
77
+ ? content.result
78
+ : JSON.stringify(content.result ?? '')
79
+ : JSON.stringify(content.args ?? '');
80
+
81
+ const estimate = estimateTokens(contentStr);
82
+ totalTokens += estimate;
83
+
84
+ if (totalTokens > PRUNE_PROTECT) {
85
+ compactedTokens += estimate;
86
+ toCompact.push({ id: part.id, content: part.content ?? '{}' });
87
+ }
88
+ }
89
+ }
90
+
91
+ debugLog(
92
+ `[compaction] Found ${toCompact.length} parts to compact, saving ~${compactedTokens} tokens`,
93
+ );
94
+
95
+ if (toCompact.length > 0) {
96
+ const compactedAt = Date.now();
97
+
98
+ for (const part of toCompact) {
99
+ try {
100
+ await db
101
+ .update(messageParts)
102
+ .set({ compactedAt })
103
+ .where(eq(messageParts.id, part.id));
104
+ } catch (err) {
105
+ debugLog(
106
+ `[compaction] Failed to mark part ${part.id}: ${err instanceof Error ? err.message : String(err)}`,
107
+ );
108
+ }
109
+ }
110
+
111
+ debugLog(`[compaction] Marked ${toCompact.length} parts as compacted`);
112
+ }
113
+
114
+ return { compacted: toCompact.length, saved: compactedTokens };
115
+ }
@@ -0,0 +1,75 @@
1
+ import type { getDb } from '@agi-cli/database';
2
+ import { messages, messageParts } from '@agi-cli/database/schema';
3
+ import { eq, desc } from 'drizzle-orm';
4
+ import { debugLog } from '../debug/index.ts';
5
+ import { estimateTokens, PRUNE_PROTECT } from './compaction-limits.ts';
6
+
7
+ const PROTECTED_TOOLS = ['skill'];
8
+
9
+ export async function pruneSession(
10
+ db: Awaited<ReturnType<typeof getDb>>,
11
+ sessionId: string,
12
+ ): Promise<{ pruned: number; saved: number }> {
13
+ debugLog(`[compaction] Auto-pruning session ${sessionId}`);
14
+
15
+ const allMessages = await db
16
+ .select()
17
+ .from(messages)
18
+ .where(eq(messages.sessionId, sessionId))
19
+ .orderBy(desc(messages.createdAt));
20
+
21
+ let totalTokens = 0;
22
+ let prunedTokens = 0;
23
+ const toPrune: Array<{ id: string }> = [];
24
+ let turns = 0;
25
+
26
+ for (const msg of allMessages) {
27
+ if (msg.role === 'user') turns++;
28
+ if (turns < 2) continue;
29
+
30
+ const parts = await db
31
+ .select()
32
+ .from(messageParts)
33
+ .where(eq(messageParts.messageId, msg.id))
34
+ .orderBy(desc(messageParts.index));
35
+
36
+ for (const part of parts) {
37
+ if (part.type !== 'tool_result') continue;
38
+ if (part.toolName && PROTECTED_TOOLS.includes(part.toolName)) continue;
39
+ if (part.compactedAt) continue;
40
+
41
+ let content: { result?: unknown };
42
+ try {
43
+ content = JSON.parse(part.content ?? '{}');
44
+ } catch {
45
+ continue;
46
+ }
47
+
48
+ const estimate = estimateTokens(
49
+ typeof content.result === 'string'
50
+ ? content.result
51
+ : JSON.stringify(content.result ?? ''),
52
+ );
53
+ totalTokens += estimate;
54
+
55
+ if (totalTokens > PRUNE_PROTECT) {
56
+ prunedTokens += estimate;
57
+ toPrune.push({ id: part.id });
58
+ }
59
+ }
60
+ }
61
+
62
+ if (toPrune.length > 0) {
63
+ const compactedAt = Date.now();
64
+ for (const part of toPrune) {
65
+ try {
66
+ await db
67
+ .update(messageParts)
68
+ .set({ compactedAt })
69
+ .where(eq(messageParts.id, part.id));
70
+ } catch {}
71
+ }
72
+ }
73
+
74
+ return { pruned: toPrune.length, saved: prunedTokens };
75
+ }
@@ -0,0 +1,23 @@
1
+ export {
2
+ PRUNE_PROTECT,
3
+ estimateTokens,
4
+ type TokenUsage,
5
+ type ModelLimits,
6
+ isOverflow,
7
+ getModelLimits,
8
+ isCompacted,
9
+ COMPACTED_PLACEHOLDER,
10
+ } from './compaction-limits.ts';
11
+
12
+ export {
13
+ isCompactCommand,
14
+ getCompactionSystemPrompt,
15
+ } from './compaction-detect.ts';
16
+
17
+ export { buildCompactionContext } from './compaction-context.ts';
18
+
19
+ export { markSessionCompacted } from './compaction-mark.ts';
20
+
21
+ export { pruneSession } from './compaction-prune.ts';
22
+
23
+ export { performAutoCompaction } from './compaction-auto.ts';
@@ -2,8 +2,8 @@ import { convertToModelMessages, type ModelMessage, type UIMessage } from 'ai';
2
2
  import type { getDb } from '@agi-cli/database';
3
3
  import { messages, messageParts } from '@agi-cli/database/schema';
4
4
  import { eq, asc } from 'drizzle-orm';
5
- import { debugLog } from './debug.ts';
6
- import { ToolHistoryTracker } from './history/tool-history-tracker.ts';
5
+ import { debugLog } from '../debug/index.ts';
6
+ import { ToolHistoryTracker } from './tool-history-tracker.ts';
7
7
 
8
8
  /**
9
9
  * Builds the conversation history for a session from the database,
@@ -3,12 +3,12 @@ import { eq } from 'drizzle-orm';
3
3
  import type { AGIConfig } from '@agi-cli/sdk';
4
4
  import type { DB } from '@agi-cli/database';
5
5
  import { messages, messageParts, sessions } from '@agi-cli/database/schema';
6
- import { publish } from '../events/bus.ts';
7
- import { enqueueAssistantRun } from './session-queue.ts';
8
- import { runSessionLoop } from './runner.ts';
9
- import { resolveModel } from './provider.ts';
10
- import { getFastModel, type ProviderId } from '@agi-cli/sdk';
11
- import { debugLog } from './debug.ts';
6
+ import { publish } from '../../events/bus.ts';
7
+ import { enqueueAssistantRun } from '../session/queue.ts';
8
+ import { runSessionLoop } from '../agent/runner.ts';
9
+ import { resolveModel } from '../provider/index.ts';
10
+ import { getFastModelForAuth, type ProviderId } from '@agi-cli/sdk';
11
+ import { debugLog } from '../debug/index.ts';
12
12
  import { isCompactCommand, buildCompactionContext } from './compaction.ts';
13
13
 
14
14
  type SessionRow = typeof sessions.$inferSelect;
@@ -287,20 +287,14 @@ async function generateSessionTitle(args: {
287
287
  debugLog(`[TITLE_GEN] Provider: ${provider}, Model: ${modelName}`);
288
288
 
289
289
  const { getAuth } = await import('@agi-cli/sdk');
290
- const { getProviderSpoofPrompt } = await import('./prompt.ts');
290
+ const { getProviderSpoofPrompt } = await import('../prompt/builder.ts');
291
291
  const auth = await getAuth(provider, cfg.projectRoot);
292
292
  const needsSpoof = auth?.type === 'oauth';
293
293
  const spoofPrompt = needsSpoof
294
294
  ? getProviderSpoofPrompt(provider)
295
295
  : undefined;
296
296
 
297
- // Use a smaller, faster model for title generation
298
- // Look up the cheapest/fastest model from the catalog for this provider
299
- // For OpenAI OAuth, use codex-mini as it works with ChatGPT backend
300
- const titleModel =
301
- needsSpoof && provider === 'openai'
302
- ? 'gpt-5.1-codex-mini'
303
- : (getFastModel(provider) ?? modelName);
297
+ const titleModel = getFastModelForAuth(provider, auth?.type) ?? modelName;
304
298
  debugLog(`[TITLE_GEN] Using title model: ${titleModel}`);
305
299
  const model = await resolveModel(provider, titleModel, cfg);
306
300
 
@@ -1,4 +1,4 @@
1
- import { debugLog } from '../debug.ts';
1
+ import { debugLog } from '../debug/index.ts';
2
2
 
3
3
  type ToolResultPart = {
4
4
  type: string;
@@ -1,5 +1,5 @@
1
1
  import { providerBasePrompt } from '@agi-cli/sdk';
2
- import { composeEnvironmentAndInstructions } from './environment.ts';
2
+ import { composeEnvironmentAndInstructions } from '../context/environment.ts';
3
3
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports
4
4
  import BASE_PROMPT from '@agi-cli/sdk/prompts/base.txt' with { type: 'text' };
5
5
  // eslint-disable-next-line @typescript-eslint/consistent-type-imports