@kenkaiiii/ggcoder 4.11.2 → 4.12.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/dist/app-sidecar.js +427 -62
- package/dist/app-sidecar.js.map +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +5 -3
- package/dist/cli.js.map +1 -1
- package/dist/config.d.ts +5 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -0
- package/dist/config.js.map +1 -1
- package/dist/core/agent-session.d.ts +66 -11
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +219 -39
- package/dist/core/agent-session.js.map +1 -1
- package/dist/core/api-benchmark.d.ts +64 -0
- package/dist/core/api-benchmark.d.ts.map +1 -0
- package/dist/core/api-benchmark.js +381 -0
- package/dist/core/api-benchmark.js.map +1 -0
- package/dist/core/event-bus.d.ts +1 -0
- package/dist/core/event-bus.d.ts.map +1 -1
- package/dist/core/mcp/client.d.ts +32 -0
- package/dist/core/mcp/client.d.ts.map +1 -1
- package/dist/core/mcp/client.js +232 -27
- package/dist/core/mcp/client.js.map +1 -1
- package/dist/core/mcp/index.d.ts +3 -1
- package/dist/core/mcp/index.d.ts.map +1 -1
- package/dist/core/mcp/index.js +2 -0
- package/dist/core/mcp/index.js.map +1 -1
- package/dist/core/mcp/loopback.d.ts +27 -0
- package/dist/core/mcp/loopback.d.ts.map +1 -0
- package/dist/core/mcp/loopback.js +66 -0
- package/dist/core/mcp/loopback.js.map +1 -0
- package/dist/core/mcp/loopback.test.d.ts +2 -0
- package/dist/core/mcp/loopback.test.d.ts.map +1 -0
- package/dist/core/mcp/loopback.test.js +87 -0
- package/dist/core/mcp/loopback.test.js.map +1 -0
- package/dist/core/mcp/oauth-provider.d.ts +51 -0
- package/dist/core/mcp/oauth-provider.d.ts.map +1 -0
- package/dist/core/mcp/oauth-provider.js +95 -0
- package/dist/core/mcp/oauth-provider.js.map +1 -0
- package/dist/core/mcp/oauth-store.d.ts +39 -0
- package/dist/core/mcp/oauth-store.d.ts.map +1 -0
- package/dist/core/mcp/oauth-store.js +63 -0
- package/dist/core/mcp/oauth-store.js.map +1 -0
- package/dist/core/mcp/oauth-store.test.d.ts +2 -0
- package/dist/core/mcp/oauth-store.test.d.ts.map +1 -0
- package/dist/core/mcp/oauth-store.test.js +94 -0
- package/dist/core/mcp/oauth-store.test.js.map +1 -0
- package/dist/core/mcp/parse-add-command.d.ts.map +1 -1
- package/dist/core/mcp/parse-add-command.js +1 -0
- package/dist/core/mcp/parse-add-command.js.map +1 -1
- package/dist/core/mcp/parse-add-command.test.js +8 -2
- package/dist/core/mcp/parse-add-command.test.js.map +1 -1
- package/dist/core/mcp/store.d.ts +4 -4
- package/dist/core/mcp/store.d.ts.map +1 -1
- package/dist/core/mcp/store.js +7 -1
- package/dist/core/mcp/store.js.map +1 -1
- package/dist/core/mcp/store.test.js +11 -2
- package/dist/core/mcp/store.test.js.map +1 -1
- package/dist/core/mcp/types.d.ts +5 -1
- package/dist/core/mcp/types.d.ts.map +1 -1
- package/dist/core/process-manager.d.ts.map +1 -1
- package/dist/core/process-manager.js +5 -1
- package/dist/core/process-manager.js.map +1 -1
- package/dist/core/settings-manager.d.ts +4 -0
- package/dist/core/settings-manager.d.ts.map +1 -1
- package/dist/core/settings-manager.js +5 -0
- package/dist/core/settings-manager.js.map +1 -1
- package/dist/core/shell.d.ts +51 -0
- package/dist/core/shell.d.ts.map +1 -0
- package/dist/core/shell.js +82 -0
- package/dist/core/shell.js.map +1 -0
- package/dist/core/shell.test.d.ts +2 -0
- package/dist/core/shell.test.d.ts.map +1 -0
- package/dist/core/shell.test.js +87 -0
- package/dist/core/shell.test.js.map +1 -0
- package/dist/core/speed-benchmark.d.ts +133 -0
- package/dist/core/speed-benchmark.d.ts.map +1 -0
- package/dist/core/speed-benchmark.js +410 -0
- package/dist/core/speed-benchmark.js.map +1 -0
- package/dist/core/speed-benchmark.test.d.ts +2 -0
- package/dist/core/speed-benchmark.test.d.ts.map +1 -0
- package/dist/core/speed-benchmark.test.js +97 -0
- package/dist/core/speed-benchmark.test.js.map +1 -0
- package/dist/interactive.d.ts.map +1 -1
- package/dist/interactive.js +4 -3
- package/dist/interactive.js.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +17 -1
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit-diff.d.ts.map +1 -1
- package/dist/tools/edit-diff.js +25 -8
- package/dist/tools/edit-diff.js.map +1 -1
- package/dist/tools/generate-image.d.ts +39 -0
- package/dist/tools/generate-image.d.ts.map +1 -0
- package/dist/tools/generate-image.js +301 -0
- package/dist/tools/generate-image.js.map +1 -0
- package/dist/tools/generate-image.test.d.ts +2 -0
- package/dist/tools/generate-image.test.d.ts.map +1 -0
- package/dist/tools/generate-image.test.js +223 -0
- package/dist/tools/generate-image.test.js.map +1 -0
- package/dist/tools/index.d.ts +12 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +16 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/tools/ls.d.ts.map +1 -1
- package/dist/tools/ls.js +7 -4
- package/dist/tools/ls.js.map +1 -1
- package/dist/tools/plan-mode.test.js +5 -5
- package/dist/tools/plan-mode.test.js.map +1 -1
- package/dist/tools/prompt-hints.d.ts.map +1 -1
- package/dist/tools/prompt-hints.js +2 -0
- package/dist/tools/prompt-hints.js.map +1 -1
- package/dist/tools/safe-env.d.ts.map +1 -1
- package/dist/tools/safe-env.js +27 -0
- package/dist/tools/safe-env.js.map +1 -1
- package/dist/ui/App.d.ts +1 -1
- package/dist/ui/App.d.ts.map +1 -1
- package/dist/ui/hooks/usePixelFixFlow.d.ts +1 -1
- package/dist/ui/hooks/usePixelFixFlow.d.ts.map +1 -1
- package/dist/ui/hooks/usePixelFixFlow.js +1 -1
- package/dist/ui/hooks/usePixelFixFlow.js.map +1 -1
- package/dist/ui/render.d.ts +1 -1
- package/dist/ui/render.d.ts.map +1 -1
- package/package.json +5 -5
|
@@ -3,6 +3,16 @@ import { EventBus } from "./event-bus.js";
|
|
|
3
3
|
import { SlashCommandRegistry } from "./slash-commands.js";
|
|
4
4
|
import { type BranchInfo } from "./session-manager.js";
|
|
5
5
|
import type { BackgroundProcess } from "./process-manager.js";
|
|
6
|
+
/** A chat attachment (image / video / other file) prepared for the model. The
|
|
7
|
+
* raw base64 `data` rides native blocks; `path` (when persisted to disk) lets
|
|
8
|
+
* the agent's tools open the file directly. */
|
|
9
|
+
export interface SessionAttachment {
|
|
10
|
+
kind: "image" | "video" | "file";
|
|
11
|
+
mediaType: string;
|
|
12
|
+
data: string;
|
|
13
|
+
name: string;
|
|
14
|
+
path?: string;
|
|
15
|
+
}
|
|
6
16
|
export interface AgentSessionOptions {
|
|
7
17
|
provider: Provider;
|
|
8
18
|
model: string;
|
|
@@ -31,6 +41,16 @@ export interface AgentSessionOptions {
|
|
|
31
41
|
* resumable identity.
|
|
32
42
|
*/
|
|
33
43
|
transient?: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* If true, `initialize()` returns WITHOUT waiting for MCP servers to connect —
|
|
46
|
+
* the connection runs in the background and tools are appended when ready.
|
|
47
|
+
* Hosts whose readiness is gated on `initialize()` (the gg-app sidecar, which
|
|
48
|
+
* can't emit its listening handshake until init resolves) set this so a slow
|
|
49
|
+
* or hanging stdio MCP server (e.g. a first-run `npx -y …` download) can't
|
|
50
|
+
* delay the session from becoming usable. Default (false) keeps the CLI's
|
|
51
|
+
* connect-before-ready behavior so MCP tools are present on the first turn.
|
|
52
|
+
*/
|
|
53
|
+
backgroundMcpConnect?: boolean;
|
|
34
54
|
/**
|
|
35
55
|
* Plan-mode callbacks. When provided, the `enter_plan`/`exit_plan` tools are
|
|
36
56
|
* registered and the session manages plan-mode restrictions + system-prompt
|
|
@@ -58,6 +78,10 @@ export declare class AgentSession {
|
|
|
58
78
|
private extensionLoader;
|
|
59
79
|
private messages;
|
|
60
80
|
private tools;
|
|
81
|
+
/** Rebuilds the read tool for a new model (video byte cap is baked in at
|
|
82
|
+
* creation). Called from switchModel so video-capable models get the
|
|
83
|
+
* read-tool's native-video path after a mid-session model change. */
|
|
84
|
+
private rebuildReadTool;
|
|
61
85
|
private skills;
|
|
62
86
|
private cacheKeyLogged;
|
|
63
87
|
private hookStats;
|
|
@@ -73,6 +97,9 @@ export declare class AgentSession {
|
|
|
73
97
|
private regroundingInjected;
|
|
74
98
|
private compactionOccurred;
|
|
75
99
|
private originalRequest;
|
|
100
|
+
/** True after the cache has been pre-warmed for this session. Ensures we only
|
|
101
|
+
* fire the warm-up call once (before the first real turn). */
|
|
102
|
+
private cachePrewarmed;
|
|
76
103
|
private userQueue;
|
|
77
104
|
private processManager?;
|
|
78
105
|
private lspManager?;
|
|
@@ -97,7 +124,26 @@ export declare class AgentSession {
|
|
|
97
124
|
private currentLeafId;
|
|
98
125
|
private opts;
|
|
99
126
|
constructor(options: AgentSessionOptions);
|
|
127
|
+
/**
|
|
128
|
+
* Derive the output-token cap for a model. Follows the active model's
|
|
129
|
+
* `maxOutputTokens` so a session booted on a large-output model (e.g. Kimi's
|
|
130
|
+
* 256K) doesn't carry that cap to a smaller one (e.g. Opus's 128K) after a
|
|
131
|
+
* model switch — that mismatch surfaces from the provider as
|
|
132
|
+
* `max_tokens: 262144 > 128000, which is the maximum allowed …`. An explicit
|
|
133
|
+
* `maxTokens` override is honored but clamped to the model's ceiling.
|
|
134
|
+
*/
|
|
135
|
+
private resolveMaxTokens;
|
|
100
136
|
initialize(): Promise<void>;
|
|
137
|
+
/**
|
|
138
|
+
* Connect all configured MCP servers and append their tools to `this.tools`.
|
|
139
|
+
* Resolves the GLM api key first (Z.AI's bundled servers need it). Never
|
|
140
|
+
* throws — a failed connect is logged and skipped — so it is safe to either
|
|
141
|
+
* `await` (CLI: tools ready before the first turn) or fire-and-forget
|
|
142
|
+
* (sidecar: `backgroundMcpConnect`, so a slow stdio server can't stall
|
|
143
|
+
* startup). Tools are pushed onto the live array the agent loop reads each
|
|
144
|
+
* turn, so background-connected servers become available on the next prompt.
|
|
145
|
+
*/
|
|
146
|
+
private connectMcpServers;
|
|
101
147
|
/**
|
|
102
148
|
* Process user input. Handles slash commands or runs agent loop.
|
|
103
149
|
*/
|
|
@@ -109,13 +155,13 @@ export declare class AgentSession {
|
|
|
109
155
|
* agent can open them with its tools. Slash-command parsing is skipped —
|
|
110
156
|
* attachments are always a direct conversational turn.
|
|
111
157
|
*/
|
|
112
|
-
promptWithAttachments(text: string, attachments:
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
158
|
+
promptWithAttachments(text: string, attachments: SessionAttachment[]): Promise<void>;
|
|
159
|
+
/**
|
|
160
|
+
* Build the native content blocks (text + image/video notes + file notes) for
|
|
161
|
+
* a user message with attachments. Shared by {@link promptWithAttachments} and
|
|
162
|
+
* the mid-run steering drain so queued media is delivered identically.
|
|
163
|
+
*/
|
|
164
|
+
private buildAttachmentParts;
|
|
119
165
|
/**
|
|
120
166
|
* Reset per-run self-correction hook state. Mirrors the TUI's run_start
|
|
121
167
|
* resets so each run evaluates the hooks from a clean slate. `originalRequest`
|
|
@@ -168,12 +214,14 @@ export declare class AgentSession {
|
|
|
168
214
|
listBranches(): Promise<BranchInfo[]>;
|
|
169
215
|
getState(): AgentSessionState;
|
|
170
216
|
getPlanMode(): boolean;
|
|
171
|
-
/** Queue a user message to be injected mid-run
|
|
172
|
-
* queue length. No-op semantics are the caller's
|
|
173
|
-
|
|
217
|
+
/** Queue a user message (optionally with attachments) to be injected mid-run
|
|
218
|
+
* as steering. Returns the new queue length. No-op semantics are the caller's
|
|
219
|
+
* concern. */
|
|
220
|
+
queueMessage(text: string, attachments?: SessionAttachment[]): number;
|
|
174
221
|
/** Number of messages currently queued. */
|
|
175
222
|
getQueuedCount(): number;
|
|
176
|
-
/** Clear the queue, returning the combined text (to restore to the composer).
|
|
223
|
+
/** Clear the queue, returning the combined text (to restore to the composer).
|
|
224
|
+
* Queued attachments are dropped on cancel — the composer only restores text. */
|
|
177
225
|
drainQueue(): string;
|
|
178
226
|
/** Snapshot of background processes (bash run_in_background), newest-state. */
|
|
179
227
|
listBackgroundProcesses(): BackgroundProcess[];
|
|
@@ -209,6 +257,13 @@ export declare class AgentSession {
|
|
|
209
257
|
setThinkingLevel(level: ThinkingLevel | undefined): void;
|
|
210
258
|
/** Replace the abort signal (e.g. after cancellation). */
|
|
211
259
|
setSignal(signal: AbortSignal): void;
|
|
260
|
+
/** True when speedProfile is "optimized" (1-h cache TTL + pre-warm). */
|
|
261
|
+
private isSpeedOptimized;
|
|
262
|
+
/** Fire a cache pre-warm request for Anthropic so the first real turn is a
|
|
263
|
+
* cache read instead of a cold write. No-op for other providers and when
|
|
264
|
+
* speedProfile is not "optimized". Entirely best-effort — any failure is
|
|
265
|
+
* swallowed so prewarm never blocks or aborts the real prompt. */
|
|
266
|
+
private maybePrewarmCache;
|
|
212
267
|
private getPromptCacheKey;
|
|
213
268
|
/** Stable cache-routing key for downstream sub-agent processes. */
|
|
214
269
|
getCurrentCacheKey(): string | undefined;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-session.d.ts","sourceRoot":"","sources":["../../src/core/agent-session.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"agent-session.d.ts","sourceRoot":"","sources":["../../src/core/agent-session.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { agentLoop, isAbortError } from "@kenkaiiii/gg-agent";
|
|
2
|
-
import { ProviderError, } from "@kenkaiiii/gg-ai";
|
|
2
|
+
import { ProviderError, prewarmAnthropicCache, } from "@kenkaiiii/gg-ai";
|
|
3
3
|
import { EventBus } from "./event-bus.js";
|
|
4
4
|
import { SlashCommandRegistry, createBuiltinCommands, } from "./slash-commands.js";
|
|
5
5
|
import { PROMPT_COMMANDS, getPromptCommand } from "./prompt-commands.js";
|
|
@@ -16,7 +16,7 @@ import { discoverSkills } from "./skills.js";
|
|
|
16
16
|
import { ensureAppDirs } from "../config.js";
|
|
17
17
|
import { buildSystemPrompt } from "../system-prompt.js";
|
|
18
18
|
import { createTools, createWebSearchTool, } from "../tools/index.js";
|
|
19
|
-
import { MCPClientManager,
|
|
19
|
+
import { MCPClientManager, getAllMcpServers } from "./mcp/index.js";
|
|
20
20
|
import { log } from "./logger.js";
|
|
21
21
|
import { setEstimatorModel } from "./compaction/token-estimator.js";
|
|
22
22
|
import { discoverAgents } from "./agents.js";
|
|
@@ -37,6 +37,10 @@ export class AgentSession {
|
|
|
37
37
|
extensionLoader = new ExtensionLoader();
|
|
38
38
|
messages = [];
|
|
39
39
|
tools = [];
|
|
40
|
+
/** Rebuilds the read tool for a new model (video byte cap is baked in at
|
|
41
|
+
* creation). Called from switchModel so video-capable models get the
|
|
42
|
+
* read-tool's native-video path after a mid-session model change. */
|
|
43
|
+
rebuildReadTool;
|
|
40
44
|
skills = [];
|
|
41
45
|
cacheKeyLogged = false;
|
|
42
46
|
// ── Self-correction hook state (mirrors the TUI's useAgentLoop refs) ──
|
|
@@ -63,9 +67,13 @@ export class AgentSession {
|
|
|
63
67
|
regroundingInjected = false;
|
|
64
68
|
compactionOccurred = false;
|
|
65
69
|
originalRequest = "";
|
|
70
|
+
/** True after the cache has been pre-warmed for this session. Ensures we only
|
|
71
|
+
* fire the warm-up call once (before the first real turn). */
|
|
72
|
+
cachePrewarmed = false;
|
|
66
73
|
// Messages queued by the user while a run is in flight. Drained at the
|
|
67
74
|
// mid-loop steering boundary (user steering wins over the hooks), mirroring
|
|
68
|
-
// the TUI's getSteeringMessages.
|
|
75
|
+
// the TUI's getSteeringMessages. Each entry carries its own attachments so a
|
|
76
|
+
// user can queue media (images/video/files) mid-run, not just plain text.
|
|
69
77
|
userQueue = [];
|
|
70
78
|
processManager;
|
|
71
79
|
lspManager;
|
|
@@ -95,10 +103,27 @@ export class AgentSession {
|
|
|
95
103
|
this.model = options.model;
|
|
96
104
|
this.cwd = options.cwd;
|
|
97
105
|
this.baseUrl = options.baseUrl;
|
|
98
|
-
this.maxTokens =
|
|
106
|
+
this.maxTokens = this.resolveMaxTokens(options.model);
|
|
99
107
|
this.thinkingLevel = options.thinkingLevel;
|
|
100
108
|
this.customSystemPrompt = options.systemPrompt;
|
|
101
109
|
}
|
|
110
|
+
/**
|
|
111
|
+
* Derive the output-token cap for a model. Follows the active model's
|
|
112
|
+
* `maxOutputTokens` so a session booted on a large-output model (e.g. Kimi's
|
|
113
|
+
* 256K) doesn't carry that cap to a smaller one (e.g. Opus's 128K) after a
|
|
114
|
+
* model switch — that mismatch surfaces from the provider as
|
|
115
|
+
* `max_tokens: 262144 > 128000, which is the maximum allowed …`. An explicit
|
|
116
|
+
* `maxTokens` override is honored but clamped to the model's ceiling.
|
|
117
|
+
*/
|
|
118
|
+
resolveMaxTokens(modelId) {
|
|
119
|
+
const modelInfo = getModel(modelId);
|
|
120
|
+
if (this.opts.maxTokens) {
|
|
121
|
+
return modelInfo
|
|
122
|
+
? Math.min(this.opts.maxTokens, modelInfo.maxOutputTokens)
|
|
123
|
+
: this.opts.maxTokens;
|
|
124
|
+
}
|
|
125
|
+
return modelInfo?.maxOutputTokens ?? 16384;
|
|
126
|
+
}
|
|
102
127
|
async initialize() {
|
|
103
128
|
// Set model for accurate token estimation
|
|
104
129
|
setEstimatorModel(this.model);
|
|
@@ -125,12 +150,13 @@ export class AgentSession {
|
|
|
125
150
|
globalAgentsDir: paths.agentsDir,
|
|
126
151
|
projectDir: this.cwd,
|
|
127
152
|
});
|
|
128
|
-
const { tools, processManager, lspManager } = createTools(this.cwd, {
|
|
153
|
+
const { tools, processManager, rebuildReadTool, lspManager } = await createTools(this.cwd, {
|
|
129
154
|
agents,
|
|
130
155
|
skills: this.skills,
|
|
131
156
|
provider: this.provider,
|
|
132
157
|
model: this.model,
|
|
133
158
|
lspDiagnostics: this.settingsManager.get("lspDiagnostics"),
|
|
159
|
+
authStorage: this.authStorage,
|
|
134
160
|
// Lazy — sessionId/model/provider can change after createTools() runs, so
|
|
135
161
|
// sub-agent spawns read the current parent state at execution time.
|
|
136
162
|
getProvider: () => this.provider,
|
|
@@ -147,26 +173,22 @@ export class AgentSession {
|
|
|
147
173
|
: {}),
|
|
148
174
|
});
|
|
149
175
|
this.tools = tools;
|
|
176
|
+
this.rebuildReadTool = rebuildReadTool;
|
|
150
177
|
this.processManager = processManager;
|
|
151
178
|
this.lspManager = lspManager;
|
|
152
|
-
// Connect MCP servers
|
|
179
|
+
// Connect MCP servers. The connect attempt itself can block for up to the
|
|
180
|
+
// per-server connect timeout (~30s) — a slow stdio server such as a
|
|
181
|
+
// first-run `npx -y @playwright/mcp` download stalls here. When the host
|
|
182
|
+
// gates its own readiness on initialize() (the gg-app sidecar can't emit
|
|
183
|
+
// its listening handshake until this resolves), `backgroundMcpConnect`
|
|
184
|
+
// moves the connect off the critical path so the session becomes usable
|
|
185
|
+
// immediately and tools are appended whenever the servers come up.
|
|
153
186
|
this.mcpManager = new MCPClientManager();
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
if (this.provider === "glm") {
|
|
157
|
-
try {
|
|
158
|
-
const glmCreds = await this.authStorage.resolveCredentials("glm");
|
|
159
|
-
apiKey = glmCreds.accessToken;
|
|
160
|
-
}
|
|
161
|
-
catch {
|
|
162
|
-
// GLM not configured — skip Z.AI MCP servers
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
const mcpTools = await this.mcpManager.connectAll(getMCPServers(this.provider, apiKey));
|
|
166
|
-
this.tools.push(...mcpTools);
|
|
187
|
+
if (this.opts.backgroundMcpConnect) {
|
|
188
|
+
void this.connectMcpServers();
|
|
167
189
|
}
|
|
168
|
-
|
|
169
|
-
|
|
190
|
+
else {
|
|
191
|
+
await this.connectMcpServers();
|
|
170
192
|
}
|
|
171
193
|
const basePrompt = this.customSystemPrompt ??
|
|
172
194
|
(await buildSystemPrompt(this.cwd, this.skills, false, undefined, this.tools.map((tool) => tool.name), undefined, this.provider));
|
|
@@ -236,6 +258,47 @@ export class AgentSession {
|
|
|
236
258
|
await this.extensionLoader.loadAll(paths.extensionsDir, extContext);
|
|
237
259
|
this.eventBus.emit("session_start", { sessionId: this.sessionId });
|
|
238
260
|
}
|
|
261
|
+
/**
|
|
262
|
+
* Connect all configured MCP servers and append their tools to `this.tools`.
|
|
263
|
+
* Resolves the GLM api key first (Z.AI's bundled servers need it). Never
|
|
264
|
+
* throws — a failed connect is logged and skipped — so it is safe to either
|
|
265
|
+
* `await` (CLI: tools ready before the first turn) or fire-and-forget
|
|
266
|
+
* (sidecar: `backgroundMcpConnect`, so a slow stdio server can't stall
|
|
267
|
+
* startup). Tools are pushed onto the live array the agent loop reads each
|
|
268
|
+
* turn, so background-connected servers become available on the next prompt.
|
|
269
|
+
*/
|
|
270
|
+
async connectMcpServers() {
|
|
271
|
+
if (!this.mcpManager)
|
|
272
|
+
return;
|
|
273
|
+
try {
|
|
274
|
+
let apiKey;
|
|
275
|
+
if (this.provider === "glm") {
|
|
276
|
+
try {
|
|
277
|
+
const glmCreds = await this.authStorage.resolveCredentials("glm");
|
|
278
|
+
apiKey = glmCreds.accessToken;
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
// GLM not configured — skip Z.AI MCP servers
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
const mcpTools = await this.mcpManager.connectAll(await getAllMcpServers(this.provider, apiKey, this.cwd));
|
|
285
|
+
this.tools.push(...mcpTools);
|
|
286
|
+
// Background connect resolves AFTER initialize() has already built the
|
|
287
|
+
// system prompt (the default path awaits this before buildSystemPrompt,
|
|
288
|
+
// so its prompt already lists the tools). Refresh messages[0] so the
|
|
289
|
+
// model is also told about the MCP tools by name on its next turn —
|
|
290
|
+
// mirrors the TUI's replaceSystemPrompt after connectInitialMcpTools.
|
|
291
|
+
// Safe ordering: this method's first await yields before initialize()
|
|
292
|
+
// sets `messages`, and connectAll (process spawn / network) always
|
|
293
|
+
// resolves long after the local-only remainder of init has finished.
|
|
294
|
+
if (this.opts.backgroundMcpConnect && mcpTools.length > 0) {
|
|
295
|
+
await this.rebuildSystemPromptInPlace();
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch (err) {
|
|
299
|
+
log("WARN", "mcp", `MCP initialization failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
300
|
+
}
|
|
301
|
+
}
|
|
239
302
|
/**
|
|
240
303
|
* Process user input. Handles slash commands or runs agent loop.
|
|
241
304
|
*/
|
|
@@ -285,14 +348,59 @@ export class AgentSession {
|
|
|
285
348
|
* attachments are always a direct conversational turn.
|
|
286
349
|
*/
|
|
287
350
|
async promptWithAttachments(text, attachments) {
|
|
351
|
+
const parts = this.buildAttachmentParts(text, attachments);
|
|
352
|
+
if (parts.length === 0)
|
|
353
|
+
return;
|
|
354
|
+
const userMessage = { role: "user", content: parts };
|
|
355
|
+
this.messages.push(userMessage);
|
|
356
|
+
await this.persistMessage(userMessage);
|
|
357
|
+
this.lastPersistedIndex = this.messages.length;
|
|
358
|
+
await this.runLoop();
|
|
359
|
+
}
|
|
360
|
+
/**
|
|
361
|
+
* Build the native content blocks (text + image/video notes + file notes) for
|
|
362
|
+
* a user message with attachments. Shared by {@link promptWithAttachments} and
|
|
363
|
+
* the mid-run steering drain so queued media is delivered identically.
|
|
364
|
+
*/
|
|
365
|
+
buildAttachmentParts(text, attachments) {
|
|
288
366
|
const parts = [];
|
|
289
367
|
const fileNotes = [];
|
|
368
|
+
const modelSupportsVideo = getModel(this.model)?.supportsVideo ?? false;
|
|
290
369
|
for (const a of attachments) {
|
|
291
370
|
if (a.kind === "image") {
|
|
292
371
|
parts.push({ type: "image", mediaType: a.mediaType, data: a.data });
|
|
372
|
+
if (a.path) {
|
|
373
|
+
parts.push({ type: "text", text: `[Image saved at ${a.path}]` });
|
|
374
|
+
}
|
|
293
375
|
}
|
|
294
376
|
else if (a.kind === "video") {
|
|
295
|
-
|
|
377
|
+
// Mirror the CLI's buildUserContentWithAttachments: never send inline
|
|
378
|
+
// VideoContent in the user message. Video-capable models (Kimi/Gemini/
|
|
379
|
+
// MiniMax) watch video via the read tool, which auto-compresses to the
|
|
380
|
+
// model's byte cap and delivers it in the provider's required shape.
|
|
381
|
+
// Non-video models get a plain note so they know to use ffmpeg. The file
|
|
382
|
+
// was already saved to disk by prepareAttachments in the sidecar.
|
|
383
|
+
if (modelSupportsVideo && a.path) {
|
|
384
|
+
parts.push({
|
|
385
|
+
type: "text",
|
|
386
|
+
text: `The user attached a video at ${a.path}. You CAN watch it: call the read tool ` +
|
|
387
|
+
`on this exact path now, then answer based on what you see. Do not say you ` +
|
|
388
|
+
`cannot watch video — reading the file lets you analyze it.`,
|
|
389
|
+
});
|
|
390
|
+
}
|
|
391
|
+
else if (a.path) {
|
|
392
|
+
parts.push({
|
|
393
|
+
type: "text",
|
|
394
|
+
text: `[User attached a video file at ${a.path}. You cannot watch video directly; ` +
|
|
395
|
+
`if needed, use ffmpeg to extract frames or audio.]`,
|
|
396
|
+
});
|
|
397
|
+
}
|
|
398
|
+
else {
|
|
399
|
+
parts.push({
|
|
400
|
+
type: "text",
|
|
401
|
+
text: `[User attached a video file but it could not be saved for analysis.]`,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
296
404
|
}
|
|
297
405
|
else if (a.path) {
|
|
298
406
|
fileNotes.push(`- ${a.name} (saved at ${a.path})`);
|
|
@@ -306,13 +414,7 @@ export class AgentSession {
|
|
|
306
414
|
}
|
|
307
415
|
if (textParts.length > 0)
|
|
308
416
|
parts.unshift({ type: "text", text: textParts.join("\n\n") });
|
|
309
|
-
|
|
310
|
-
return;
|
|
311
|
-
const userMessage = { role: "user", content: parts };
|
|
312
|
-
this.messages.push(userMessage);
|
|
313
|
-
await this.persistMessage(userMessage);
|
|
314
|
-
this.lastPersistedIndex = this.messages.length;
|
|
315
|
-
await this.runLoop();
|
|
417
|
+
return parts;
|
|
316
418
|
}
|
|
317
419
|
/**
|
|
318
420
|
* Reset per-run self-correction hook state. Mirrors the TUI's run_start
|
|
@@ -406,8 +508,18 @@ export class AgentSession {
|
|
|
406
508
|
// User steering wins: drain any messages queued during this run first so the
|
|
407
509
|
// agent sees them mid-loop instead of after it stops.
|
|
408
510
|
if (this.userQueue.length > 0) {
|
|
409
|
-
const
|
|
410
|
-
|
|
511
|
+
const queued = this.userQueue.splice(0);
|
|
512
|
+
// Plain-text-only queue: keep the simple merged-string message.
|
|
513
|
+
if (queued.every((m) => m.attachments.length === 0)) {
|
|
514
|
+
const merged = queued.map((m) => m.text).join("\n\n");
|
|
515
|
+
return [{ role: "user", content: merged }];
|
|
516
|
+
}
|
|
517
|
+
// Any queued attachments → deliver one user message with text + media
|
|
518
|
+
// blocks built the same way as a non-queued attachment prompt.
|
|
519
|
+
const parts = [];
|
|
520
|
+
for (const m of queued)
|
|
521
|
+
parts.push(...this.buildAttachmentParts(m.text, m.attachments));
|
|
522
|
+
return [{ role: "user", content: parts }];
|
|
411
523
|
}
|
|
412
524
|
if (!this.settingsManager.get("idealReviewEnabled"))
|
|
413
525
|
return null;
|
|
@@ -507,7 +619,9 @@ export class AgentSession {
|
|
|
507
619
|
defaultHeaders: this.provider === "moonshot" && isKimiCodingEndpoint(effectiveBaseUrl)
|
|
508
620
|
? kimiCodingHeaders()
|
|
509
621
|
: undefined,
|
|
510
|
-
|
|
622
|
+
// speedProfile "optimized": 1-h cache TTL (survives turns >5 min apart)
|
|
623
|
+
// + pre-warm before the first turn. "baseline": current 5-min default.
|
|
624
|
+
cacheRetention: this.isSpeedOptimized() ? "long" : "short",
|
|
511
625
|
promptCacheKey: this.getPromptCacheKey(),
|
|
512
626
|
supportsImages: modelInfo?.supportsImages,
|
|
513
627
|
supportsVideo: modelInfo?.supportsVideo,
|
|
@@ -526,6 +640,11 @@ export class AgentSession {
|
|
|
526
640
|
}
|
|
527
641
|
};
|
|
528
642
|
try {
|
|
643
|
+
// Fire cache pre-warm before the first turn (Anthropic + speedProfile optimized).
|
|
644
|
+
// Runs concurrently with nothing — it must complete before runAgentLoop so
|
|
645
|
+
// the cache is warm when the real request arrives. Best-effort: swallowed
|
|
646
|
+
// inside maybePrewarmCache/prewarmAnthropicCache.
|
|
647
|
+
await this.maybePrewarmCache(creds);
|
|
529
648
|
await runAgentLoop(creds.accessToken, creds.accountId, creds.projectId);
|
|
530
649
|
}
|
|
531
650
|
catch (err) {
|
|
@@ -565,7 +684,25 @@ export class AgentSession {
|
|
|
565
684
|
this.provider = provider;
|
|
566
685
|
this.model = model;
|
|
567
686
|
setEstimatorModel(model);
|
|
568
|
-
|
|
687
|
+
// maxTokens must follow the active model — it was frozen at the boot
|
|
688
|
+
// model's `maxOutputTokens` in the constructor, so without this a session
|
|
689
|
+
// booted on e.g. Kimi (256K) keeps sending that cap after switching to a
|
|
690
|
+
// smaller model (Opus 128K), which the provider rejects.
|
|
691
|
+
this.maxTokens = this.resolveMaxTokens(model);
|
|
692
|
+
this.eventBus.emit("model_change", {
|
|
693
|
+
provider: this.provider,
|
|
694
|
+
model: this.model,
|
|
695
|
+
supportsVideo: getModel(this.model)?.supportsVideo ?? false,
|
|
696
|
+
});
|
|
697
|
+
// Rebuild the read tool for the new model's video byte cap. The tool's
|
|
698
|
+
// video capability (description + native-video execute path) is baked in
|
|
699
|
+
// at creation from the model's maxVideoBytes, so switching to/from a
|
|
700
|
+
// video-capable model mid-session needs a fresh tool object — mirrors
|
|
701
|
+
// the TUI's rebuildReadTool call on model switch.
|
|
702
|
+
if (this.rebuildReadTool) {
|
|
703
|
+
const newReadTool = this.rebuildReadTool(model);
|
|
704
|
+
this.tools = this.tools.map((t) => (t.name === "read" ? newReadTool : t));
|
|
705
|
+
}
|
|
569
706
|
// Update provider-specific tools when provider changes
|
|
570
707
|
if (provider && provider !== prevProvider) {
|
|
571
708
|
// Add/remove client-side web_search tool based on provider.
|
|
@@ -718,19 +855,24 @@ export class AgentSession {
|
|
|
718
855
|
getPlanMode() {
|
|
719
856
|
return this.planModeRef.current;
|
|
720
857
|
}
|
|
721
|
-
/** Queue a user message to be injected mid-run
|
|
722
|
-
* queue length. No-op semantics are the caller's
|
|
723
|
-
|
|
724
|
-
|
|
858
|
+
/** Queue a user message (optionally with attachments) to be injected mid-run
|
|
859
|
+
* as steering. Returns the new queue length. No-op semantics are the caller's
|
|
860
|
+
* concern. */
|
|
861
|
+
queueMessage(text, attachments = []) {
|
|
862
|
+
this.userQueue.push({ text, attachments });
|
|
725
863
|
return this.userQueue.length;
|
|
726
864
|
}
|
|
727
865
|
/** Number of messages currently queued. */
|
|
728
866
|
getQueuedCount() {
|
|
729
867
|
return this.userQueue.length;
|
|
730
868
|
}
|
|
731
|
-
/** Clear the queue, returning the combined text (to restore to the composer).
|
|
869
|
+
/** Clear the queue, returning the combined text (to restore to the composer).
|
|
870
|
+
* Queued attachments are dropped on cancel — the composer only restores text. */
|
|
732
871
|
drainQueue() {
|
|
733
|
-
return this.userQueue
|
|
872
|
+
return this.userQueue
|
|
873
|
+
.splice(0)
|
|
874
|
+
.map((m) => m.text)
|
|
875
|
+
.join("\n\n");
|
|
734
876
|
}
|
|
735
877
|
/** Snapshot of background processes (bash run_in_background), newest-state. */
|
|
736
878
|
listBackgroundProcesses() {
|
|
@@ -826,6 +968,44 @@ export class AgentSession {
|
|
|
826
968
|
setSignal(signal) {
|
|
827
969
|
this.opts = { ...this.opts, signal };
|
|
828
970
|
}
|
|
971
|
+
/** True when speedProfile is "optimized" (1-h cache TTL + pre-warm). */
|
|
972
|
+
isSpeedOptimized() {
|
|
973
|
+
return this.settingsManager?.get("speedProfile") === "optimized";
|
|
974
|
+
}
|
|
975
|
+
/** Fire a cache pre-warm request for Anthropic so the first real turn is a
|
|
976
|
+
* cache read instead of a cold write. No-op for other providers and when
|
|
977
|
+
* speedProfile is not "optimized". Entirely best-effort — any failure is
|
|
978
|
+
* swallowed so prewarm never blocks or aborts the real prompt. */
|
|
979
|
+
async maybePrewarmCache(creds) {
|
|
980
|
+
if (this.cachePrewarmed || !this.isSpeedOptimized() || this.provider !== "anthropic") {
|
|
981
|
+
return;
|
|
982
|
+
}
|
|
983
|
+
this.cachePrewarmed = true;
|
|
984
|
+
try {
|
|
985
|
+
const userAgent = await getClaudeCliUserAgent();
|
|
986
|
+
const systemText = typeof this.messages[0]?.content === "string" ? this.messages[0].content : "";
|
|
987
|
+
if (!systemText)
|
|
988
|
+
return;
|
|
989
|
+
await prewarmAnthropicCache({
|
|
990
|
+
apiKey: creds.accessToken,
|
|
991
|
+
model: this.model,
|
|
992
|
+
system: systemText,
|
|
993
|
+
tools: this.tools.map((t) => ({
|
|
994
|
+
name: t.name,
|
|
995
|
+
description: t.description,
|
|
996
|
+
parameters: t.parameters,
|
|
997
|
+
...(t.rawInputSchema ? { rawInputSchema: t.rawInputSchema } : {}),
|
|
998
|
+
})),
|
|
999
|
+
baseUrl: this.baseUrl ?? creds.baseUrl,
|
|
1000
|
+
userAgent,
|
|
1001
|
+
cacheRetention: "long",
|
|
1002
|
+
signal: this.opts.signal,
|
|
1003
|
+
});
|
|
1004
|
+
}
|
|
1005
|
+
catch {
|
|
1006
|
+
// Best-effort — prewarm failure must never block the session.
|
|
1007
|
+
}
|
|
1008
|
+
}
|
|
829
1009
|
getPromptCacheKey() {
|
|
830
1010
|
if (this.opts.promptCacheKey)
|
|
831
1011
|
return this.opts.promptCacheKey;
|