@kenkaiiii/ggcoder 4.11.3 → 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 +368 -53
- 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 +57 -11
- package/dist/core/agent-session.d.ts.map +1 -1
- package/dist/core/agent-session.js +196 -38
- 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 +4 -4
|
@@ -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?;
|
|
@@ -107,6 +134,16 @@ export declare class AgentSession {
|
|
|
107
134
|
*/
|
|
108
135
|
private resolveMaxTokens;
|
|
109
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;
|
|
110
147
|
/**
|
|
111
148
|
* Process user input. Handles slash commands or runs agent loop.
|
|
112
149
|
*/
|
|
@@ -118,13 +155,13 @@ export declare class AgentSession {
|
|
|
118
155
|
* agent can open them with its tools. Slash-command parsing is skipped —
|
|
119
156
|
* attachments are always a direct conversational turn.
|
|
120
157
|
*/
|
|
121
|
-
promptWithAttachments(text: string, attachments:
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
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;
|
|
128
165
|
/**
|
|
129
166
|
* Reset per-run self-correction hook state. Mirrors the TUI's run_start
|
|
130
167
|
* resets so each run evaluates the hooks from a clean slate. `originalRequest`
|
|
@@ -177,12 +214,14 @@ export declare class AgentSession {
|
|
|
177
214
|
listBranches(): Promise<BranchInfo[]>;
|
|
178
215
|
getState(): AgentSessionState;
|
|
179
216
|
getPlanMode(): boolean;
|
|
180
|
-
/** Queue a user message to be injected mid-run
|
|
181
|
-
* queue length. No-op semantics are the caller's
|
|
182
|
-
|
|
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;
|
|
183
221
|
/** Number of messages currently queued. */
|
|
184
222
|
getQueuedCount(): number;
|
|
185
|
-
/** 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. */
|
|
186
225
|
drainQueue(): string;
|
|
187
226
|
/** Snapshot of background processes (bash run_in_background), newest-state. */
|
|
188
227
|
listBackgroundProcesses(): BackgroundProcess[];
|
|
@@ -218,6 +257,13 @@ export declare class AgentSession {
|
|
|
218
257
|
setThinkingLevel(level: ThinkingLevel | undefined): void;
|
|
219
258
|
/** Replace the abort signal (e.g. after cancellation). */
|
|
220
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;
|
|
221
267
|
private getPromptCacheKey;
|
|
222
268
|
/** Stable cache-routing key for downstream sub-agent processes. */
|
|
223
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;
|
|
@@ -142,12 +150,13 @@ export class AgentSession {
|
|
|
142
150
|
globalAgentsDir: paths.agentsDir,
|
|
143
151
|
projectDir: this.cwd,
|
|
144
152
|
});
|
|
145
|
-
const { tools, processManager, lspManager } = createTools(this.cwd, {
|
|
153
|
+
const { tools, processManager, rebuildReadTool, lspManager } = await createTools(this.cwd, {
|
|
146
154
|
agents,
|
|
147
155
|
skills: this.skills,
|
|
148
156
|
provider: this.provider,
|
|
149
157
|
model: this.model,
|
|
150
158
|
lspDiagnostics: this.settingsManager.get("lspDiagnostics"),
|
|
159
|
+
authStorage: this.authStorage,
|
|
151
160
|
// Lazy — sessionId/model/provider can change after createTools() runs, so
|
|
152
161
|
// sub-agent spawns read the current parent state at execution time.
|
|
153
162
|
getProvider: () => this.provider,
|
|
@@ -164,26 +173,22 @@ export class AgentSession {
|
|
|
164
173
|
: {}),
|
|
165
174
|
});
|
|
166
175
|
this.tools = tools;
|
|
176
|
+
this.rebuildReadTool = rebuildReadTool;
|
|
167
177
|
this.processManager = processManager;
|
|
168
178
|
this.lspManager = lspManager;
|
|
169
|
-
// 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.
|
|
170
186
|
this.mcpManager = new MCPClientManager();
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if (this.provider === "glm") {
|
|
174
|
-
try {
|
|
175
|
-
const glmCreds = await this.authStorage.resolveCredentials("glm");
|
|
176
|
-
apiKey = glmCreds.accessToken;
|
|
177
|
-
}
|
|
178
|
-
catch {
|
|
179
|
-
// GLM not configured — skip Z.AI MCP servers
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
const mcpTools = await this.mcpManager.connectAll(getMCPServers(this.provider, apiKey));
|
|
183
|
-
this.tools.push(...mcpTools);
|
|
187
|
+
if (this.opts.backgroundMcpConnect) {
|
|
188
|
+
void this.connectMcpServers();
|
|
184
189
|
}
|
|
185
|
-
|
|
186
|
-
|
|
190
|
+
else {
|
|
191
|
+
await this.connectMcpServers();
|
|
187
192
|
}
|
|
188
193
|
const basePrompt = this.customSystemPrompt ??
|
|
189
194
|
(await buildSystemPrompt(this.cwd, this.skills, false, undefined, this.tools.map((tool) => tool.name), undefined, this.provider));
|
|
@@ -253,6 +258,47 @@ export class AgentSession {
|
|
|
253
258
|
await this.extensionLoader.loadAll(paths.extensionsDir, extContext);
|
|
254
259
|
this.eventBus.emit("session_start", { sessionId: this.sessionId });
|
|
255
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
|
+
}
|
|
256
302
|
/**
|
|
257
303
|
* Process user input. Handles slash commands or runs agent loop.
|
|
258
304
|
*/
|
|
@@ -302,14 +348,59 @@ export class AgentSession {
|
|
|
302
348
|
* attachments are always a direct conversational turn.
|
|
303
349
|
*/
|
|
304
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) {
|
|
305
366
|
const parts = [];
|
|
306
367
|
const fileNotes = [];
|
|
368
|
+
const modelSupportsVideo = getModel(this.model)?.supportsVideo ?? false;
|
|
307
369
|
for (const a of attachments) {
|
|
308
370
|
if (a.kind === "image") {
|
|
309
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
|
+
}
|
|
310
375
|
}
|
|
311
376
|
else if (a.kind === "video") {
|
|
312
|
-
|
|
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
|
+
}
|
|
313
404
|
}
|
|
314
405
|
else if (a.path) {
|
|
315
406
|
fileNotes.push(`- ${a.name} (saved at ${a.path})`);
|
|
@@ -323,13 +414,7 @@ export class AgentSession {
|
|
|
323
414
|
}
|
|
324
415
|
if (textParts.length > 0)
|
|
325
416
|
parts.unshift({ type: "text", text: textParts.join("\n\n") });
|
|
326
|
-
|
|
327
|
-
return;
|
|
328
|
-
const userMessage = { role: "user", content: parts };
|
|
329
|
-
this.messages.push(userMessage);
|
|
330
|
-
await this.persistMessage(userMessage);
|
|
331
|
-
this.lastPersistedIndex = this.messages.length;
|
|
332
|
-
await this.runLoop();
|
|
417
|
+
return parts;
|
|
333
418
|
}
|
|
334
419
|
/**
|
|
335
420
|
* Reset per-run self-correction hook state. Mirrors the TUI's run_start
|
|
@@ -423,8 +508,18 @@ export class AgentSession {
|
|
|
423
508
|
// User steering wins: drain any messages queued during this run first so the
|
|
424
509
|
// agent sees them mid-loop instead of after it stops.
|
|
425
510
|
if (this.userQueue.length > 0) {
|
|
426
|
-
const
|
|
427
|
-
|
|
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 }];
|
|
428
523
|
}
|
|
429
524
|
if (!this.settingsManager.get("idealReviewEnabled"))
|
|
430
525
|
return null;
|
|
@@ -524,7 +619,9 @@ export class AgentSession {
|
|
|
524
619
|
defaultHeaders: this.provider === "moonshot" && isKimiCodingEndpoint(effectiveBaseUrl)
|
|
525
620
|
? kimiCodingHeaders()
|
|
526
621
|
: undefined,
|
|
527
|
-
|
|
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",
|
|
528
625
|
promptCacheKey: this.getPromptCacheKey(),
|
|
529
626
|
supportsImages: modelInfo?.supportsImages,
|
|
530
627
|
supportsVideo: modelInfo?.supportsVideo,
|
|
@@ -543,6 +640,11 @@ export class AgentSession {
|
|
|
543
640
|
}
|
|
544
641
|
};
|
|
545
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);
|
|
546
648
|
await runAgentLoop(creds.accessToken, creds.accountId, creds.projectId);
|
|
547
649
|
}
|
|
548
650
|
catch (err) {
|
|
@@ -587,7 +689,20 @@ export class AgentSession {
|
|
|
587
689
|
// booted on e.g. Kimi (256K) keeps sending that cap after switching to a
|
|
588
690
|
// smaller model (Opus 128K), which the provider rejects.
|
|
589
691
|
this.maxTokens = this.resolveMaxTokens(model);
|
|
590
|
-
this.eventBus.emit("model_change", {
|
|
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
|
+
}
|
|
591
706
|
// Update provider-specific tools when provider changes
|
|
592
707
|
if (provider && provider !== prevProvider) {
|
|
593
708
|
// Add/remove client-side web_search tool based on provider.
|
|
@@ -740,19 +855,24 @@ export class AgentSession {
|
|
|
740
855
|
getPlanMode() {
|
|
741
856
|
return this.planModeRef.current;
|
|
742
857
|
}
|
|
743
|
-
/** Queue a user message to be injected mid-run
|
|
744
|
-
* queue length. No-op semantics are the caller's
|
|
745
|
-
|
|
746
|
-
|
|
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 });
|
|
747
863
|
return this.userQueue.length;
|
|
748
864
|
}
|
|
749
865
|
/** Number of messages currently queued. */
|
|
750
866
|
getQueuedCount() {
|
|
751
867
|
return this.userQueue.length;
|
|
752
868
|
}
|
|
753
|
-
/** 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. */
|
|
754
871
|
drainQueue() {
|
|
755
|
-
return this.userQueue
|
|
872
|
+
return this.userQueue
|
|
873
|
+
.splice(0)
|
|
874
|
+
.map((m) => m.text)
|
|
875
|
+
.join("\n\n");
|
|
756
876
|
}
|
|
757
877
|
/** Snapshot of background processes (bash run_in_background), newest-state. */
|
|
758
878
|
listBackgroundProcesses() {
|
|
@@ -848,6 +968,44 @@ export class AgentSession {
|
|
|
848
968
|
setSignal(signal) {
|
|
849
969
|
this.opts = { ...this.opts, signal };
|
|
850
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
|
+
}
|
|
851
1009
|
getPromptCacheKey() {
|
|
852
1010
|
if (this.opts.promptCacheKey)
|
|
853
1011
|
return this.opts.promptCacheKey;
|