@cjhyy/code-shell-core 0.6.0-rc.1 → 0.6.0-rc.10
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/context/compaction.d.ts +30 -0
- package/dist/context/compaction.js +93 -0
- package/dist/context/manager.d.ts +18 -0
- package/dist/context/manager.js +156 -44
- package/dist/context/token-counter.js +13 -0
- package/dist/engine/engine.d.ts +22 -12
- package/dist/engine/engine.js +263 -81
- package/dist/engine/model-connections-pool.js +1 -0
- package/dist/engine/model-facade.js +2 -12
- package/dist/engine/query.js +2 -0
- package/dist/engine/runtime.d.ts +2 -0
- package/dist/engine/runtime.js +25 -0
- package/dist/engine/session-usage.d.ts +12 -0
- package/dist/engine/session-usage.js +56 -0
- package/dist/engine/steer-queue.d.ts +2 -1
- package/dist/engine/steer-queue.js +2 -2
- package/dist/engine/turn-loop.d.ts +28 -2
- package/dist/engine/turn-loop.js +153 -26
- package/dist/git/utils.d.ts +12 -0
- package/dist/git/utils.js +33 -6
- package/dist/index.d.ts +4 -3
- package/dist/index.js +4 -3
- package/dist/llm/capabilities/rules.js +1 -1
- package/dist/llm/model-pool.d.ts +7 -0
- package/dist/llm/model-pool.js +8 -1
- package/dist/model-catalog/builtin.js +6 -1
- package/dist/preset/index.d.ts +5 -1
- package/dist/preset/index.js +21 -2
- package/dist/prompt/composer.d.ts +5 -0
- package/dist/prompt/composer.js +10 -2
- package/dist/prompt/sections/base.md +1 -0
- package/dist/protocol/chat-session-manager.d.ts +1 -0
- package/dist/protocol/chat-session-manager.js +2 -0
- package/dist/protocol/chat-session.d.ts +4 -1
- package/dist/protocol/chat-session.js +9 -3
- package/dist/protocol/client.d.ts +5 -1
- package/dist/protocol/client.js +8 -2
- package/dist/protocol/server.d.ts +13 -12
- package/dist/protocol/server.js +199 -67
- package/dist/protocol/types.d.ts +14 -0
- package/dist/runtime/background-shell.js +14 -0
- package/dist/runtime/safe-spawn.js +89 -11
- package/dist/runtime/spawn-common.d.ts +15 -4
- package/dist/runtime/spawn-common.js +113 -12
- package/dist/session/session-manager.js +7 -1
- package/dist/session/transcript.d.ts +4 -0
- package/dist/session/transcript.js +21 -0
- package/dist/tool-system/builtin/bash.js +3 -2
- package/dist/tool-system/builtin/cron.js +10 -2
- package/dist/tool-system/builtin/edit-model-catalog.js +15 -5
- package/dist/tool-system/builtin/generate-video.js +3 -0
- package/dist/tool-system/builtin/grep.d.ts +9 -0
- package/dist/tool-system/builtin/grep.js +100 -3
- package/dist/tool-system/builtin/index.d.ts +3 -1
- package/dist/tool-system/builtin/index.js +5 -5
- package/dist/tool-system/builtin/powershell.js +4 -1
- package/dist/tool-system/builtin/sleep.js +5 -0
- package/dist/tool-system/context.d.ts +10 -0
- package/dist/tool-system/executor.js +25 -2
- package/dist/tool-system/mcp-manager.js +17 -0
- package/dist/tool-system/mcp-stdio-diagnostics.d.ts +9 -0
- package/dist/tool-system/mcp-stdio-diagnostics.js +93 -0
- package/dist/tool-system/permission.d.ts +3 -1
- package/dist/tool-system/permission.js +2 -1
- package/dist/tool-system/sandbox/off.js +7 -1
- package/dist/types.d.ts +35 -1
- package/dist/utils/exec.d.ts +8 -0
- package/dist/utils/exec.js +10 -0
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Public API exports.
|
|
5
5
|
*/
|
|
6
|
-
export declare const VERSION = "0.
|
|
6
|
+
export declare const VERSION = "0.6.0-rc.8";
|
|
7
7
|
export type { Message, ContentBlock, ToolDefinition, ToolCall, ToolResult, RegisteredTool, TranscriptEvent, TranscriptEventType, SessionState, SessionStatus, TokenUsage, CompiledInput, PermissionDecision, PermissionMode, PermissionRule, TurnPhase, TurnResult, TerminalReason, StreamEvent, StreamCallback, LLMConfig, ClientDefaults, LLMResponse, Settings, MCPServerConfig, } from "./types.js";
|
|
8
8
|
export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, SandboxUnavailableError, } from "./exceptions.js";
|
|
9
9
|
export { Engine, loadAgentDefinitionsForCwd } from "./engine/engine.js";
|
|
@@ -116,7 +116,7 @@ export { getGraphemeSegmenter, firstGrapheme, lastGrapheme, getWordSegmenter, ge
|
|
|
116
116
|
export { env } from "./utils/env.js";
|
|
117
117
|
export { default as sliceAnsi } from "./utils/sliceAnsi.js";
|
|
118
118
|
export { execFileNoThrow } from "./utils/execFileNoThrow.js";
|
|
119
|
-
export { findExecutable, resolveExecutable, setGitPathOverride, resolveGit, isGitAvailable, } from "./utils/exec.js";
|
|
119
|
+
export { findExecutable, resolveExecutable, setGitPathOverride, resolveGit, isGitAvailable, resolveGitPath, } from "./utils/exec.js";
|
|
120
120
|
export { gte } from "./utils/semver.js";
|
|
121
121
|
export { lock, lockSync, unlock, check } from "./utils/lockfile.js";
|
|
122
122
|
export { logForDebugging } from "./utils/debug.js";
|
|
@@ -134,7 +134,7 @@ export { CostTracker, costTracker, installCostTracking } from "./cost-tracker.js
|
|
|
134
134
|
export { NOOP_COLORIZER, type Colorizer } from "./colorizer.js";
|
|
135
135
|
export { hasApiKey, resolveApiKey, appendOnboardingResult, detectEnvKeys, saveArenaSettingsByKeys, type OnboardingResult, } from "./onboarding.js";
|
|
136
136
|
export { getCurrentVersion, checkForUpdate, scheduleAutoInstallOnExit, getUpdateAvailable, getAutoUpdateDisabledReason, type UpdateInfo, } from "./updater.js";
|
|
137
|
-
export { isGitRepo, getCurrentBranch, getGitStatus, getGitDiff, getGitDiffStat, getGitLog, gitAdd, gitCommit, gitListBranches, gitCheckout, ghAvailable, ghPrComments, } from "./git/utils.js";
|
|
137
|
+
export { isGitRepo, resolveProjectRoot, getCurrentBranch, getGitStatus, getGitDiff, getGitDiffStat, getGitLog, gitAdd, gitCommit, gitListBranches, gitCheckout, ghAvailable, ghPrComments, } from "./git/utils.js";
|
|
138
138
|
export { buildReviewPrompt, parseDimensions, ALL_DIMENSIONS, type ReviewDimension, type ReviewPromptOptions, } from "./review/review-prompt.js";
|
|
139
139
|
/**
|
|
140
140
|
* @internal Exports below are consumed by the in-repo TUI/desktop hosts, not
|
|
@@ -153,6 +153,7 @@ export { resolveQuotaCredentials } from "./quota/credentials.js";
|
|
|
153
153
|
export type { QuotaResult, ProviderQuota, QuotaWindow, QuotaCredentials } from "./quota/types.js";
|
|
154
154
|
export { asyncAgentRegistry, type AsyncAgentEntry, } from "./tool-system/builtin/agent-registry.js";
|
|
155
155
|
export { backgroundShellManager, BackgroundShellManager, type BgShell, type BgShellStatus, } from "./runtime/background-shell.js";
|
|
156
|
+
export { ENV_DENY_REGEX, ENV_ALLOWLIST } from "./runtime/spawn-common.js";
|
|
156
157
|
export { getImageProvider, DEFAULT_IMAGE_MODEL, type ImageProvider, type ImageProviderCreds, type ImageGenerateRequest, type ImageGenerateResult, } from "./tool-system/builtin/image-providers.js";
|
|
157
158
|
export { transcribe, type TranscribeCreds, type TranscribeRequest, type TranscribeResult, } from "./stt/transcribe.js";
|
|
158
159
|
export { resolveTranscribeProvider, isTranscribeAvailable, describeTranscribe, type ResolvedTranscribeProvider, type TranscribeDescription, } from "./stt/resolve-transcribe.js";
|
package/dist/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Public API exports.
|
|
5
5
|
*/
|
|
6
|
-
export const VERSION = "0.
|
|
6
|
+
export const VERSION = "0.6.0-rc.8";
|
|
7
7
|
// ─── Exceptions ──────────────────────────────────────────────────
|
|
8
8
|
export { FrameworkError, LLMError, LLMRateLimitError, ContextLimitError, ToolError, ToolNotFoundError, ToolExecutionError, ToolTimeoutError, PermissionDeniedError, SessionError, TranscriptError, ConfigError, SandboxUnavailableError, } from "./exceptions.js";
|
|
9
9
|
// ─── Engine (primary API) ────────────────────────────────────────
|
|
@@ -136,7 +136,7 @@ export { getGraphemeSegmenter, firstGrapheme, lastGrapheme, getWordSegmenter, ge
|
|
|
136
136
|
export { env } from "./utils/env.js";
|
|
137
137
|
export { default as sliceAnsi } from "./utils/sliceAnsi.js";
|
|
138
138
|
export { execFileNoThrow } from "./utils/execFileNoThrow.js";
|
|
139
|
-
export { findExecutable, resolveExecutable, setGitPathOverride, resolveGit, isGitAvailable, } from "./utils/exec.js";
|
|
139
|
+
export { findExecutable, resolveExecutable, setGitPathOverride, resolveGit, isGitAvailable, resolveGitPath, } from "./utils/exec.js";
|
|
140
140
|
export { gte } from "./utils/semver.js";
|
|
141
141
|
// Cross-process file lock (proper-lockfile via createRequire, ESM-safe). Used by
|
|
142
142
|
// RunLock / CronStore internally; exported so other writers of shared files
|
|
@@ -163,7 +163,7 @@ export { hasApiKey, resolveApiKey, appendOnboardingResult, detectEnvKeys, saveAr
|
|
|
163
163
|
// ─── Updater ─────────────────────────────────────────────────────
|
|
164
164
|
export { getCurrentVersion, checkForUpdate, scheduleAutoInstallOnExit, getUpdateAvailable, getAutoUpdateDisabledReason, } from "./updater.js";
|
|
165
165
|
// ─── Git Utilities ───────────────────────────────────────────────
|
|
166
|
-
export { isGitRepo, getCurrentBranch, getGitStatus, getGitDiff, getGitDiffStat, getGitLog, gitAdd, gitCommit, gitListBranches, gitCheckout, ghAvailable, ghPrComments, } from "./git/utils.js";
|
|
166
|
+
export { isGitRepo, resolveProjectRoot, getCurrentBranch, getGitStatus, getGitDiff, getGitDiffStat, getGitLog, gitAdd, gitCommit, gitListBranches, gitCheckout, ghAvailable, ghPrComments, } from "./git/utils.js";
|
|
167
167
|
// ─── Code review (TODO 7.3) ─────────────────────────────────────
|
|
168
168
|
export { buildReviewPrompt, parseDimensions, ALL_DIMENSIONS, } from "./review/review-prompt.js";
|
|
169
169
|
// ─── Tool-system (extended for TUI) ─────────────────────────────
|
|
@@ -187,6 +187,7 @@ export { checkQuota, formatQuota } from "./quota/index.js";
|
|
|
187
187
|
export { resolveQuotaCredentials } from "./quota/credentials.js";
|
|
188
188
|
export { asyncAgentRegistry, } from "./tool-system/builtin/agent-registry.js";
|
|
189
189
|
export { backgroundShellManager, BackgroundShellManager, } from "./runtime/background-shell.js";
|
|
190
|
+
export { ENV_DENY_REGEX, ENV_ALLOWLIST } from "./runtime/spawn-common.js";
|
|
190
191
|
export { getImageProvider, DEFAULT_IMAGE_MODEL, } from "./tool-system/builtin/image-providers.js";
|
|
191
192
|
// ─── Speech-to-text (voice input / 听写) ────────────────────────
|
|
192
193
|
export { transcribe, } from "./stt/transcribe.js";
|
|
@@ -190,7 +190,7 @@ export const RULES = [
|
|
|
190
190
|
// Most modern frontier models on OpenRouter are multimodal; flag the
|
|
191
191
|
// big families explicitly. The catch-all below stays vision=false so
|
|
192
192
|
// older/text-only routes don't get a green checkmark they don't earn.
|
|
193
|
-
match:
|
|
193
|
+
match: /^~?(?:openai\/(?:gpt-4o|gpt-5|o[1-9])|anthropic\/claude-(?:3|4|opus|sonnet|haiku|fable)|google\/gemini-(?:[2-9]|pro)|x-ai\/grok-[4-9]|qwen\/qwen-?vl|meta\/llama-(?:3\.2|4))/i,
|
|
194
194
|
capability: {
|
|
195
195
|
supportsVision: true,
|
|
196
196
|
reasoning: { kind: "openrouter-reasoning" },
|
package/dist/llm/model-pool.d.ts
CHANGED
|
@@ -21,6 +21,11 @@ export interface ModelEntry {
|
|
|
21
21
|
provider: string;
|
|
22
22
|
/** Full model path: "anthropic/claude-opus-4-6" */
|
|
23
23
|
model: string;
|
|
24
|
+
/**
|
|
25
|
+
* Catalog/provider kind used by capability rules. This can differ from
|
|
26
|
+
* `provider`, which only selects the client protocol family.
|
|
27
|
+
*/
|
|
28
|
+
providerKind?: string;
|
|
24
29
|
baseUrl?: string;
|
|
25
30
|
apiKey?: string;
|
|
26
31
|
/** Whether the catalog template requires a key (default true; false = local/
|
|
@@ -79,6 +84,8 @@ export declare class ModelPool {
|
|
|
79
84
|
* The first entry (or the one matching `defaultKey`) becomes active.
|
|
80
85
|
*/
|
|
81
86
|
constructor(entries?: ModelEntry[], defaultKey?: string);
|
|
87
|
+
/** Clear all registered models and active selection. */
|
|
88
|
+
clear(): void;
|
|
82
89
|
/** Register a model at runtime. */
|
|
83
90
|
register(entry: ModelEntry): void;
|
|
84
91
|
/** Fill in known defaults (e.g. context window for DeepSeek V4) when the entry doesn't specify them. */
|
package/dist/llm/model-pool.js
CHANGED
|
@@ -142,6 +142,11 @@ export class ModelPool {
|
|
|
142
142
|
this.activeKey = entries[0].key;
|
|
143
143
|
}
|
|
144
144
|
}
|
|
145
|
+
/** Clear all registered models and active selection. */
|
|
146
|
+
clear() {
|
|
147
|
+
this.models.clear();
|
|
148
|
+
this.activeKey = undefined;
|
|
149
|
+
}
|
|
145
150
|
/** Register a model at runtime. */
|
|
146
151
|
register(entry) {
|
|
147
152
|
this.models.set(entry.key, this.withBuiltinDefaults(entry));
|
|
@@ -216,6 +221,7 @@ export class ModelPool {
|
|
|
216
221
|
return "anthropic";
|
|
217
222
|
return "openai";
|
|
218
223
|
};
|
|
224
|
+
const providerKind = entry.providerKind ?? fromCat?.kind;
|
|
219
225
|
return {
|
|
220
226
|
provider: entry.provider ||
|
|
221
227
|
kindToClientProvider(fromCat?.kind) ||
|
|
@@ -228,6 +234,7 @@ export class ModelPool {
|
|
|
228
234
|
// instead of fabricating 8192, which silently truncates long outputs and
|
|
229
235
|
// masks the real per-model cap.
|
|
230
236
|
maxTokens: entry.maxOutputTokens,
|
|
237
|
+
...(entry.maxContextTokens !== undefined ? { maxContextTokens: entry.maxContextTokens } : {}),
|
|
231
238
|
// reasoning: entry overrides catalog. No base fallback — see class doc.
|
|
232
239
|
...(entry.reasoning ?? fromCat?.reasoning
|
|
233
240
|
? { reasoning: entry.reasoning ?? fromCat?.reasoning }
|
|
@@ -244,7 +251,7 @@ export class ModelPool {
|
|
|
244
251
|
...(entry.reasoningSummary ? { reasoningSummary: entry.reasoningSummary } : {}),
|
|
245
252
|
// Carry the catalog kind through so the capability layer can pick
|
|
246
253
|
// per-(kind, model) request-shape rules.
|
|
247
|
-
...(
|
|
254
|
+
...(providerKind ? { providerKind } : {}),
|
|
248
255
|
...(entry.extraBody && Object.keys(entry.extraBody).length > 0
|
|
249
256
|
? { extraBody: entry.extraBody }
|
|
250
257
|
: {}),
|
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
* ~/.code-shell/model-catalog.user.json) — no UI / adapter changes, as long as
|
|
9
9
|
* its adapterKind points at an already-wired adapter.
|
|
10
10
|
*/
|
|
11
|
+
import { capabilitiesFor } from "../llm/capabilities/index.js";
|
|
11
12
|
import { paramSpecsFromCapability } from "../llm/capabilities/param-specs.js";
|
|
12
13
|
/**
|
|
13
14
|
* Build a text model preset, projecting its params from the capability layer
|
|
@@ -15,11 +16,13 @@ import { paramSpecsFromCapability } from "../llm/capabilities/param-specs.js";
|
|
|
15
16
|
* when the model has none, matching the "absent → no knobs" contract.
|
|
16
17
|
*/
|
|
17
18
|
function textPreset(kind, value, label, ctx) {
|
|
19
|
+
const capability = capabilitiesFor(kind, value);
|
|
18
20
|
const params = paramSpecsFromCapability(kind, value);
|
|
19
21
|
return {
|
|
20
22
|
value,
|
|
21
23
|
...(label ? { label } : {}),
|
|
22
24
|
...(ctx !== undefined ? { maxContextTokens: ctx } : {}),
|
|
25
|
+
supportsVision: capability.supportsVision,
|
|
23
26
|
...(params.length > 0 ? { params } : {}),
|
|
24
27
|
};
|
|
25
28
|
}
|
|
@@ -97,14 +100,16 @@ const ZHIPU_PARAMS = [
|
|
|
97
100
|
];
|
|
98
101
|
/** Zhipu GLM preset — shared ZHIPU_PARAMS, with per-model context window. */
|
|
99
102
|
function glmPreset(value, label, ctx) {
|
|
100
|
-
return { value, label, maxContextTokens: ctx, params: ZHIPU_PARAMS };
|
|
103
|
+
return { value, label, maxContextTokens: ctx, supportsVision: false, params: ZHIPU_PARAMS };
|
|
101
104
|
}
|
|
102
105
|
/** OpenRouter preset with explicit params (no capability-layer projection). */
|
|
103
106
|
function orPreset(value, label, params, ctx) {
|
|
107
|
+
const capability = capabilitiesFor("openrouter", value);
|
|
104
108
|
return {
|
|
105
109
|
value,
|
|
106
110
|
label,
|
|
107
111
|
...(ctx !== undefined ? { maxContextTokens: ctx } : {}),
|
|
112
|
+
supportsVision: capability.supportsVision,
|
|
108
113
|
...(params && params.length > 0 ? { params } : {}),
|
|
109
114
|
};
|
|
110
115
|
}
|
package/dist/preset/index.d.ts
CHANGED
|
@@ -49,6 +49,10 @@ export declare function registerPreset(preset: AgentPreset): void;
|
|
|
49
49
|
/** List all available preset names (built-in + custom). */
|
|
50
50
|
export declare function listPresetNames(): string[];
|
|
51
51
|
export declare function resolveAgentPreset(name?: string): AgentPreset;
|
|
52
|
+
export interface BuildPresetSystemPromptOptions {
|
|
53
|
+
activeToolNames?: readonly string[];
|
|
54
|
+
platform?: NodeJS.Platform;
|
|
55
|
+
}
|
|
52
56
|
/**
|
|
53
57
|
* Build the full system prompt for a preset by loading and joining its sections.
|
|
54
58
|
*
|
|
@@ -58,7 +62,7 @@ export declare function resolveAgentPreset(name?: string): AgentPreset;
|
|
|
58
62
|
* (tools + usage instructions disappear together). Omit `activeToolNames` to
|
|
59
63
|
* include all sections (e.g. when assembling a generic/preview prompt).
|
|
60
64
|
*/
|
|
61
|
-
export declare function buildPresetSystemPrompt(preset: AgentPreset,
|
|
65
|
+
export declare function buildPresetSystemPrompt(preset: AgentPreset, optionsOrActiveToolNames?: BuildPresetSystemPromptOptions | readonly string[]): string;
|
|
62
66
|
export declare function resolveBuiltinToolNames(options?: {
|
|
63
67
|
preset?: string;
|
|
64
68
|
enabledBuiltinTools?: string[];
|
package/dist/preset/index.js
CHANGED
|
@@ -232,6 +232,19 @@ const BROWSER_SECTION_TOOLS = ["browser_observe", "browser_act", "browser_naviga
|
|
|
232
232
|
const TOOL_GATED_SECTIONS = {
|
|
233
233
|
browser: BROWSER_SECTION_TOOLS,
|
|
234
234
|
};
|
|
235
|
+
function isActiveToolNamesList(value) {
|
|
236
|
+
return Array.isArray(value);
|
|
237
|
+
}
|
|
238
|
+
function platformShellGuidance(platform) {
|
|
239
|
+
if (platform !== "win32")
|
|
240
|
+
return "";
|
|
241
|
+
return [
|
|
242
|
+
"# Windows Shell Guidance",
|
|
243
|
+
"- On Windows, Bash uses Git Bash when it is available. Prefer Bash for ordinary shell commands, git operations, package-manager commands, tests, and POSIX-style command lines.",
|
|
244
|
+
"- Do not choose PowerShell merely because the OS is Windows. Use PowerShell only when the user explicitly asks for it or the task requires PowerShell-specific cmdlets, Windows APIs, registry access, or `.ps1` behavior.",
|
|
245
|
+
"- When writing Bash commands on Windows, use Git Bash paths such as `/d/github/project` instead of PowerShell-only syntax or raw `D:\\...` paths inside `cd` commands.",
|
|
246
|
+
].join("\n");
|
|
247
|
+
}
|
|
235
248
|
/**
|
|
236
249
|
* Build the full system prompt for a preset by loading and joining its sections.
|
|
237
250
|
*
|
|
@@ -241,8 +254,12 @@ const TOOL_GATED_SECTIONS = {
|
|
|
241
254
|
* (tools + usage instructions disappear together). Omit `activeToolNames` to
|
|
242
255
|
* include all sections (e.g. when assembling a generic/preview prompt).
|
|
243
256
|
*/
|
|
244
|
-
export function buildPresetSystemPrompt(preset,
|
|
257
|
+
export function buildPresetSystemPrompt(preset, optionsOrActiveToolNames) {
|
|
245
258
|
let sections = preset.promptSections;
|
|
259
|
+
const options = isActiveToolNamesList(optionsOrActiveToolNames)
|
|
260
|
+
? { activeToolNames: optionsOrActiveToolNames }
|
|
261
|
+
: (optionsOrActiveToolNames ?? {});
|
|
262
|
+
const activeToolNames = options.activeToolNames;
|
|
246
263
|
if (activeToolNames) {
|
|
247
264
|
const active = new Set(activeToolNames);
|
|
248
265
|
sections = sections.filter((s) => {
|
|
@@ -250,7 +267,9 @@ export function buildPresetSystemPrompt(preset, activeToolNames) {
|
|
|
250
267
|
return !gate || gate.some((t) => active.has(t));
|
|
251
268
|
});
|
|
252
269
|
}
|
|
253
|
-
return loadSections(sections)
|
|
270
|
+
return [loadSections(sections), platformShellGuidance(options.platform ?? process.platform)]
|
|
271
|
+
.filter(Boolean)
|
|
272
|
+
.join("\n\n");
|
|
254
273
|
}
|
|
255
274
|
export function resolveBuiltinToolNames(options) {
|
|
256
275
|
const preset = resolveAgentPreset(options?.preset);
|
|
@@ -41,6 +41,10 @@ export interface ComposerOptions {
|
|
|
41
41
|
* dispatch gate.
|
|
42
42
|
*/
|
|
43
43
|
skillAllowlist?: string[];
|
|
44
|
+
/** Volatile goal state used only in the trailing dynamic-context message. */
|
|
45
|
+
goalToolState?: {
|
|
46
|
+
hasGoal: boolean;
|
|
47
|
+
};
|
|
44
48
|
/**
|
|
45
49
|
* settings.memories.maxAge (days). When > 0, memories whose file mtime is
|
|
46
50
|
* older than this are dropped from the injected memory context (TODO 8.1).
|
|
@@ -79,6 +83,7 @@ export declare class PromptComposer {
|
|
|
79
83
|
* breakpoint — a change here never re-bills the history prefix.
|
|
80
84
|
*/
|
|
81
85
|
buildDynamicContextMessage(): Promise<Message | null>;
|
|
86
|
+
private buildGoalToolContext;
|
|
82
87
|
invalidateCache(sectionName?: string): void;
|
|
83
88
|
private getSections;
|
|
84
89
|
private getInstructions;
|
package/dist/prompt/composer.js
CHANGED
|
@@ -113,7 +113,8 @@ export class PromptComposer {
|
|
|
113
113
|
// prefix — so a memory change (extraction / recall usage++ / approve) never
|
|
114
114
|
// re-bills the cached prefix. See buildUserContextMessage for the rationale.
|
|
115
115
|
const memoryContext = this.getMemoryContext();
|
|
116
|
-
const
|
|
116
|
+
const goalToolContext = this.buildGoalToolContext();
|
|
117
|
+
const parts = [skillsListing, gitStatus, memoryContext, goalToolContext].filter(Boolean);
|
|
117
118
|
if (parts.length === 0)
|
|
118
119
|
return null;
|
|
119
120
|
return {
|
|
@@ -121,6 +122,13 @@ export class PromptComposer {
|
|
|
121
122
|
content: `<system-reminder>\n${parts.join("\n\n")}\n</system-reminder>`,
|
|
122
123
|
};
|
|
123
124
|
}
|
|
125
|
+
buildGoalToolContext() {
|
|
126
|
+
if (!this.options.goalToolState)
|
|
127
|
+
return "";
|
|
128
|
+
return this.options.goalToolState.hasGoal
|
|
129
|
+
? "当前存在 active goal。只有在目标完全完成时才可调用 complete_goal;只有用户明确要求取消/停止/放弃该目标时才可调用 cancel_goal。"
|
|
130
|
+
: "当前没有 active goal。不要调用 complete_goal/cancel_goal;如果误调用,系统会拒绝。";
|
|
131
|
+
}
|
|
124
132
|
invalidateCache(sectionName) {
|
|
125
133
|
this.sectionCache.invalidate(sectionName);
|
|
126
134
|
if (!sectionName) {
|
|
@@ -179,7 +187,7 @@ export class PromptComposer {
|
|
|
179
187
|
cacheBreak: true,
|
|
180
188
|
compute: () => {
|
|
181
189
|
const preset = this.options.preset ?? resolveAgentPreset();
|
|
182
|
-
return buildPresetSystemPrompt(preset, activeToolNames);
|
|
190
|
+
return buildPresetSystemPrompt(preset, { activeToolNames, platform: process.platform });
|
|
183
191
|
},
|
|
184
192
|
});
|
|
185
193
|
// Skills listing is intentionally NOT a system section: it changes when a
|
|
@@ -31,6 +31,7 @@ When you encounter an obstacle, do not use destructive actions as a shortcut to
|
|
|
31
31
|
- To search for files use Glob instead of find or ls
|
|
32
32
|
- To search the content of files, use Grep instead of grep or rg
|
|
33
33
|
- Reserve using the Bash exclusively for system commands and terminal operations that require shell execution.
|
|
34
|
+
- Shell choice: use Bash for ordinary shell commands, git operations, package-manager commands, test/build scripts, and POSIX-style command lines. Use PowerShell only when the user explicitly asks for it or the task requires PowerShell-specific cmdlets, Windows APIs, registry access, or `.ps1` behavior.
|
|
34
35
|
- For long-lived processes that don't exit on their own — a dev server (`npm run dev`, `vite`), a watcher, a tunnel — call Bash with `run_in_background: true`. It returns a `shell_id` immediately instead of blocking until a timeout. Then use `BashOutput(shell_id)` to read its logs (e.g. to confirm it started or to see an error), `ListShells()` to see what's running, and `KillShell(shell_id)` to stop it. Never run such a command in the foreground — it will just block until it's killed. Plain one-shot commands (build, test, git) stay foreground.
|
|
35
36
|
- Break down and manage multi-step work with the TodoWrite tool. Pass the complete todo list each call; rewrite it as items move pending → in_progress → completed.
|
|
36
37
|
- You can call multiple tools in a single response. If you intend to call multiple tools and there are no dependencies between them, make all independent tool calls in parallel.
|
|
@@ -3,11 +3,13 @@ import { backgroundShellManager } from "../runtime/background-shell.js";
|
|
|
3
3
|
import { clearAgentOutputFiles } from "../tool-system/builtin/agent-output-file.js";
|
|
4
4
|
export class ChatSessionManager {
|
|
5
5
|
sessions = new Map();
|
|
6
|
+
runtime;
|
|
6
7
|
factory;
|
|
7
8
|
maxSessions;
|
|
8
9
|
idleTtlMs;
|
|
9
10
|
sweeper = null;
|
|
10
11
|
constructor(opts) {
|
|
12
|
+
this.runtime = opts.runtime;
|
|
11
13
|
this.factory = opts.engineFactory;
|
|
12
14
|
this.maxSessions = opts.maxSessions ?? 16;
|
|
13
15
|
this.idleTtlMs = opts.idleTtlMs ?? 30 * 60 * 1000;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import type { Engine, EngineResult } from "../engine/engine.js";
|
|
2
|
+
import type { ModelEntry } from "../llm/model-pool.js";
|
|
2
3
|
import type { StreamEvent } from "../types.js";
|
|
3
4
|
export interface ChatSessionOptions {
|
|
4
5
|
id: string;
|
|
@@ -17,6 +18,8 @@ export interface TurnOpts {
|
|
|
17
18
|
* completion notification) rather than the user's own input — persisted so
|
|
18
19
|
* the disk reader skips it as a user bubble on replay. See Engine.run. */
|
|
19
20
|
injected?: boolean;
|
|
21
|
+
/** Stable id for this user-intent; forwarded to Engine.run for idempotency. */
|
|
22
|
+
clientMessageId?: string;
|
|
20
23
|
}
|
|
21
24
|
/**
|
|
22
25
|
* One ChatSession per UI chat tab. Owns a single Engine, an AbortController
|
|
@@ -116,7 +119,7 @@ export declare class ChatSession {
|
|
|
116
119
|
* turn is in flight so a hot switch never mutates the model under a
|
|
117
120
|
* running LLM client.
|
|
118
121
|
*/
|
|
119
|
-
requestModelSwitch(key: string):
|
|
122
|
+
requestModelSwitch(key: string): ModelEntry;
|
|
120
123
|
/**
|
|
121
124
|
* Perform the actual model switch for this session: rotate the engine's model
|
|
122
125
|
* and reset the session's cumulative usage on disk — a different model has a
|
|
@@ -126,10 +126,14 @@ export class ChatSession {
|
|
|
126
126
|
*/
|
|
127
127
|
requestModelSwitch(key) {
|
|
128
128
|
if (this.isBusy()) {
|
|
129
|
+
const pool = this.engine.getModelPool();
|
|
130
|
+
const entry = pool.get(key);
|
|
131
|
+
if (!entry)
|
|
132
|
+
throw new Error(`Model not found: ${key}`);
|
|
129
133
|
this.pendingModel = key;
|
|
130
|
-
return;
|
|
134
|
+
return entry;
|
|
131
135
|
}
|
|
132
|
-
this.applyModelSwitch(key);
|
|
136
|
+
return this.applyModelSwitch(key);
|
|
133
137
|
}
|
|
134
138
|
/**
|
|
135
139
|
* Perform the actual model switch for this session: rotate the engine's model
|
|
@@ -140,8 +144,9 @@ export class ChatSession {
|
|
|
140
144
|
* path and the deferred run-boundary path.
|
|
141
145
|
*/
|
|
142
146
|
applyModelSwitch(key) {
|
|
143
|
-
this.engine.switchModel(key);
|
|
147
|
+
const entry = this.engine.switchModel(key);
|
|
144
148
|
this.engine.resetSessionUsage(this.id);
|
|
149
|
+
return entry;
|
|
145
150
|
}
|
|
146
151
|
queueDepth() {
|
|
147
152
|
return this.queue.length;
|
|
@@ -164,6 +169,7 @@ export class ChatSession {
|
|
|
164
169
|
onStream,
|
|
165
170
|
goal: next.opts.goal,
|
|
166
171
|
injected: next.opts.injected,
|
|
172
|
+
clientMessageId: next.opts.clientMessageId,
|
|
167
173
|
});
|
|
168
174
|
this.lastActivityAt = Date.now();
|
|
169
175
|
next.resolve(result);
|
|
@@ -23,6 +23,7 @@ export interface AgentRunOptions {
|
|
|
23
23
|
cwd?: string;
|
|
24
24
|
sessionId?: string;
|
|
25
25
|
permissionMode?: PermissionMode;
|
|
26
|
+
model?: string;
|
|
26
27
|
planMode?: boolean;
|
|
27
28
|
}
|
|
28
29
|
export type BackgroundAgentCompletedHandler = (sessionId: string, event: BackgroundAgentCompletedEvent) => void;
|
|
@@ -84,7 +85,10 @@ export declare class AgentClient {
|
|
|
84
85
|
* the desktop preload's steer(); without it the SDK can't reach the method
|
|
85
86
|
* (request() is private) — the protocol was asymmetric after the steer merge.
|
|
86
87
|
*/
|
|
87
|
-
steer(sessionId: string, text: string, id?: string): Promise<
|
|
88
|
+
steer(sessionId: string, text: string, id?: string, clientMessageId?: string): Promise<{
|
|
89
|
+
accepted: boolean;
|
|
90
|
+
id?: string;
|
|
91
|
+
}>;
|
|
88
92
|
/**
|
|
89
93
|
* Revoke a still-pending steer entry by id (before the loop consumes it).
|
|
90
94
|
* Returns false when the loop already consumed it (not an error — it will
|
package/dist/protocol/client.js
CHANGED
|
@@ -125,8 +125,14 @@ export class AgentClient {
|
|
|
125
125
|
* the desktop preload's steer(); without it the SDK can't reach the method
|
|
126
126
|
* (request() is private) — the protocol was asymmetric after the steer merge.
|
|
127
127
|
*/
|
|
128
|
-
async steer(sessionId, text, id) {
|
|
129
|
-
await this.request(Methods.Steer, {
|
|
128
|
+
async steer(sessionId, text, id, clientMessageId) {
|
|
129
|
+
const res = (await this.request(Methods.Steer, {
|
|
130
|
+
sessionId,
|
|
131
|
+
text,
|
|
132
|
+
id,
|
|
133
|
+
clientMessageId,
|
|
134
|
+
}));
|
|
135
|
+
return { accepted: res?.accepted === true, id: res?.id };
|
|
130
136
|
}
|
|
131
137
|
/**
|
|
132
138
|
* Revoke a still-pending steer entry by id (before the loop consumes it).
|
|
@@ -180,14 +180,11 @@ export declare class AgentServer {
|
|
|
180
180
|
*/
|
|
181
181
|
private requestApprovalFromClient;
|
|
182
182
|
/**
|
|
183
|
-
*
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
*
|
|
187
|
-
*
|
|
188
|
-
* (the chatManager approve handler looks there, keyed by sessionId+requestId)
|
|
189
|
-
* and tags the notify with sessionId so the renderer routes the question to
|
|
190
|
-
* the right chat tab.
|
|
183
|
+
* Per-session AskUserQuestion for the chatManager path. Resolves via the
|
|
184
|
+
* SESSION's pendingApprovals (the chatManager approve handler looks there,
|
|
185
|
+
* keyed by sessionId+requestId) and tags the notify with sessionId so the
|
|
186
|
+
* renderer routes the question to the right chat tab. This intentionally has
|
|
187
|
+
* no wall-clock timeout; Stop/cancel drains the pending ask.
|
|
191
188
|
*/
|
|
192
189
|
private requestAskUserForSession;
|
|
193
190
|
/**
|
|
@@ -206,6 +203,11 @@ export declare class AgentServer {
|
|
|
206
203
|
* parse into { ok, count?, error? }. Degrades to ok:false on timeout/malformed.
|
|
207
204
|
*/
|
|
208
205
|
private requestCredentialInjectForSession;
|
|
206
|
+
/**
|
|
207
|
+
* Ask the client to answer a question from the agent (legacy single-engine
|
|
208
|
+
* path). This intentionally has no wall-clock timeout; Stop/cancel drains
|
|
209
|
+
* the pending ask.
|
|
210
|
+
*/
|
|
209
211
|
private requestAskUserFromClient;
|
|
210
212
|
private notify;
|
|
211
213
|
/**
|
|
@@ -220,11 +222,10 @@ export declare class AgentServer {
|
|
|
220
222
|
private clearApprovalTimer;
|
|
221
223
|
/**
|
|
222
224
|
* Resolve all of a chat session's pending approvals as cancelled and clear
|
|
223
|
-
*
|
|
225
|
+
* any matching server-side approval timers. Used by handleCancel's
|
|
224
226
|
* per-session path so a Stop while a tool is awaiting approval doesn't leave
|
|
225
|
-
* the tool hanging
|
|
226
|
-
*
|
|
227
|
-
* (see requestAskUserForSession / makeBrowserBridge).
|
|
227
|
+
* the tool hanging. Bounded request types have same-keyed timer entries;
|
|
228
|
+
* AskUserQuestion does not.
|
|
228
229
|
*/
|
|
229
230
|
private cancelSessionApprovals;
|
|
230
231
|
private clearAllApprovalTimers;
|