@co0ontty/wand 3.1.1 → 4.0.0
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/auth.d.ts +19 -5
- package/dist/auth.js +83 -45
- package/dist/build-info.json +3 -3
- package/dist/cert.d.ts +1 -1
- package/dist/cert.js +124 -74
- package/dist/config.js +25 -8
- package/dist/express-async.d.ts +6 -0
- package/dist/express-async.js +28 -0
- package/dist/git-quick-commit.d.ts +2 -0
- package/dist/git-quick-commit.js +215 -76
- package/dist/git-utils.d.ts +4 -0
- package/dist/git-utils.js +60 -11
- package/dist/git-worktree.d.ts +8 -1
- package/dist/git-worktree.js +406 -41
- package/dist/models.d.ts +34 -4
- package/dist/models.js +334 -48
- package/dist/process-manager.d.ts +22 -30
- package/dist/process-manager.js +374 -441
- package/dist/provider-history-scanner.d.ts +54 -0
- package/dist/provider-history-scanner.js +354 -0
- package/dist/request-limits.d.ts +1 -0
- package/dist/request-limits.js +8 -0
- package/dist/resume-policy.d.ts +2 -0
- package/dist/resume-policy.js +5 -0
- package/dist/runtime-config.d.ts +16 -0
- package/dist/runtime-config.js +49 -0
- package/dist/server-file-routes.d.ts +17 -0
- package/dist/server-file-routes.js +653 -0
- package/dist/server-session-routes.d.ts +16 -3
- package/dist/server-session-routes.js +170 -149
- package/dist/server-settings-routes.d.ts +43 -0
- package/dist/server-settings-routes.js +225 -0
- package/dist/server-update-routes.d.ts +61 -0
- package/dist/server-update-routes.js +215 -0
- package/dist/server.d.ts +6 -4
- package/dist/server.js +350 -1313
- package/dist/session-logger.d.ts +32 -2
- package/dist/session-logger.js +145 -15
- package/dist/session-registry.d.ts +27 -0
- package/dist/session-registry.js +153 -0
- package/dist/session-transport.d.ts +31 -0
- package/dist/session-transport.js +82 -0
- package/dist/storage.d.ts +24 -6
- package/dist/storage.js +291 -44
- package/dist/structured-claude-adapter.d.ts +19 -0
- package/dist/structured-claude-adapter.js +117 -0
- package/dist/structured-codex-adapter.d.ts +3 -0
- package/dist/structured-codex-adapter.js +29 -0
- package/dist/structured-opencode-adapter.d.ts +11 -0
- package/dist/structured-opencode-adapter.js +115 -0
- package/dist/structured-provider-common.d.ts +11 -0
- package/dist/structured-provider-common.js +77 -0
- package/dist/structured-session-manager.d.ts +32 -35
- package/dist/structured-session-manager.js +551 -605
- package/dist/types.d.ts +10 -0
- package/dist/update-helper.js +5 -1
- package/dist/web-ui/content/scripts.js +32 -32
- package/dist/web-ui/embedded-assets.d.ts +1 -1
- package/dist/web-ui/embedded-assets.js +2 -2
- package/dist/ws-broadcast.d.ts +16 -1
- package/dist/ws-broadcast.js +124 -58
- package/package.json +2 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import path from "node:path";
|
|
4
|
+
import { isRunningAsRoot } from "./env-utils.js";
|
|
5
|
+
import { buildLanguageDirective, buildManagedAutonomyDirective } from "./language-prompt.js";
|
|
6
|
+
import { thinkingEffortToClaudeCliEffort, thinkingEffortToSdkBudget } from "./structured-provider-common.js";
|
|
7
|
+
const ROOT_FALLBACK_ALLOWED_TOOLS = [
|
|
8
|
+
"Bash", "Edit", "Write", "Read", "Glob", "Grep", "NotebookEdit", "WebFetch", "WebSearch",
|
|
9
|
+
];
|
|
10
|
+
const mcpServerCache = new Map();
|
|
11
|
+
function readJsonSafe(filePath) {
|
|
12
|
+
try {
|
|
13
|
+
const parsed = JSON.parse(readFileSync(filePath, "utf-8"));
|
|
14
|
+
return parsed && typeof parsed === "object" ? parsed : null;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function mtimeOf(filePath) {
|
|
21
|
+
try {
|
|
22
|
+
return statSync(filePath).mtimeMs;
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return 0;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function extractMcpServerKeys(node) {
|
|
29
|
+
if (!node || typeof node !== "object")
|
|
30
|
+
return [];
|
|
31
|
+
const mcpServers = node.mcpServers;
|
|
32
|
+
return mcpServers && typeof mcpServers === "object"
|
|
33
|
+
? Object.keys(mcpServers)
|
|
34
|
+
: [];
|
|
35
|
+
}
|
|
36
|
+
function collectMcpServerNames(cwd) {
|
|
37
|
+
const userConfigPath = path.join(homedir(), ".claude.json");
|
|
38
|
+
const projectMcpPath = path.join(cwd, ".mcp.json");
|
|
39
|
+
const fingerprint = `${mtimeOf(userConfigPath)}:${mtimeOf(projectMcpPath)}`;
|
|
40
|
+
const cached = mcpServerCache.get(cwd);
|
|
41
|
+
if (cached?.mtimeFingerprint === fingerprint)
|
|
42
|
+
return cached.names;
|
|
43
|
+
const names = new Set();
|
|
44
|
+
const userConfig = readJsonSafe(userConfigPath);
|
|
45
|
+
if (userConfig) {
|
|
46
|
+
for (const name of extractMcpServerKeys(userConfig))
|
|
47
|
+
names.add(name);
|
|
48
|
+
const projects = userConfig.projects;
|
|
49
|
+
if (projects && typeof projects === "object") {
|
|
50
|
+
for (const name of extractMcpServerKeys(projects[cwd]))
|
|
51
|
+
names.add(name);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
for (const name of extractMcpServerKeys(readJsonSafe(projectMcpPath)))
|
|
55
|
+
names.add(name);
|
|
56
|
+
const result = Array.from(names);
|
|
57
|
+
mcpServerCache.set(cwd, { mtimeFingerprint: fingerprint, names: result });
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
export function derivePermissionPolicy(mode, autoApprove, cwd) {
|
|
61
|
+
const shouldBypass = autoApprove || mode === "full-access" || mode === "managed";
|
|
62
|
+
const shouldAcceptEdits = mode === "auto-edit";
|
|
63
|
+
const mcpAllow = shouldBypass ? [] : collectMcpServerNames(cwd).map((name) => `mcp__${name}`);
|
|
64
|
+
const withMcp = (base) => {
|
|
65
|
+
if (!mcpAllow.length)
|
|
66
|
+
return base;
|
|
67
|
+
return base ? [...base, ...mcpAllow] : [...mcpAllow];
|
|
68
|
+
};
|
|
69
|
+
if (!isRunningAsRoot()) {
|
|
70
|
+
if (shouldBypass)
|
|
71
|
+
return { permissionMode: "bypassPermissions", allowedTools: undefined };
|
|
72
|
+
if (shouldAcceptEdits)
|
|
73
|
+
return { permissionMode: "acceptEdits", allowedTools: withMcp(undefined) };
|
|
74
|
+
return { permissionMode: "default", allowedTools: withMcp(undefined) };
|
|
75
|
+
}
|
|
76
|
+
if (shouldBypass || shouldAcceptEdits) {
|
|
77
|
+
return { permissionMode: "acceptEdits", allowedTools: withMcp(ROOT_FALLBACK_ALLOWED_TOOLS) };
|
|
78
|
+
}
|
|
79
|
+
return { permissionMode: "default", allowedTools: withMcp(undefined) };
|
|
80
|
+
}
|
|
81
|
+
export function buildAppendSystemPromptParts(language, mode) {
|
|
82
|
+
const trimmedLanguage = language?.trim();
|
|
83
|
+
const parts = [];
|
|
84
|
+
if (mode === "managed")
|
|
85
|
+
parts.push(buildManagedAutonomyDirective(trimmedLanguage === "中文"));
|
|
86
|
+
if (trimmedLanguage) {
|
|
87
|
+
const directive = buildLanguageDirective(trimmedLanguage);
|
|
88
|
+
if (directive)
|
|
89
|
+
parts.push(directive);
|
|
90
|
+
}
|
|
91
|
+
return parts;
|
|
92
|
+
}
|
|
93
|
+
export function buildClaudeCliArgs(session, options) {
|
|
94
|
+
const args = ["-p", "--verbose", "--output-format", "stream-json"];
|
|
95
|
+
if (options.permissionPolicy.permissionMode !== "default") {
|
|
96
|
+
args.push("--permission-mode", options.permissionPolicy.permissionMode);
|
|
97
|
+
}
|
|
98
|
+
if (options.permissionPolicy.allowedTools)
|
|
99
|
+
args.push("--allowedTools", ...options.permissionPolicy.allowedTools);
|
|
100
|
+
for (const part of options.systemPromptParts ?? [])
|
|
101
|
+
args.push("--append-system-prompt", part);
|
|
102
|
+
const modelChoice = session.selectedModel?.trim();
|
|
103
|
+
if (modelChoice && modelChoice !== "default")
|
|
104
|
+
args.push("--model", modelChoice);
|
|
105
|
+
const effort = thinkingEffortToClaudeCliEffort(session.thinkingEffort);
|
|
106
|
+
if (effort)
|
|
107
|
+
args.push("--effort", effort);
|
|
108
|
+
if (session.mode === "managed")
|
|
109
|
+
args.push("--disallowedTools", "AskUserQuestion");
|
|
110
|
+
if (session.claudeSessionId)
|
|
111
|
+
args.push("--resume", session.claudeSessionId);
|
|
112
|
+
return args;
|
|
113
|
+
}
|
|
114
|
+
export function buildClaudeSdkThinking(effort) {
|
|
115
|
+
const budgetTokens = thinkingEffortToSdkBudget(effort);
|
|
116
|
+
return budgetTokens > 0 ? { type: "enabled", budgetTokens } : { type: "disabled" };
|
|
117
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { thinkingEffortToCodexReasoningEffort } from "./structured-provider-common.js";
|
|
2
|
+
/** Build the stable CLI contract for a structured Codex turn. */
|
|
3
|
+
export function buildCodexArgs(session) {
|
|
4
|
+
const args = ["exec", "--json", "--color", "never"];
|
|
5
|
+
const shouldBypass = session.autoApprovePermissions === true
|
|
6
|
+
|| session.mode === "full-access"
|
|
7
|
+
|| session.mode === "managed";
|
|
8
|
+
if (shouldBypass) {
|
|
9
|
+
args.push("--dangerously-bypass-approvals-and-sandbox");
|
|
10
|
+
}
|
|
11
|
+
else if (session.mode === "auto-edit" || session.mode === "agent" || session.mode === "agent-max") {
|
|
12
|
+
args.push("--sandbox", "workspace-write");
|
|
13
|
+
}
|
|
14
|
+
else {
|
|
15
|
+
args.push("--sandbox", "read-only");
|
|
16
|
+
}
|
|
17
|
+
args.push("--skip-git-repo-check");
|
|
18
|
+
const modelChoice = session.selectedModel?.trim();
|
|
19
|
+
if (modelChoice && modelChoice !== "default")
|
|
20
|
+
args.push("--model", modelChoice);
|
|
21
|
+
const reasoningEffort = thinkingEffortToCodexReasoningEffort(session.thinkingEffort);
|
|
22
|
+
if (reasoningEffort)
|
|
23
|
+
args.push("-c", `model_reasoning_effort=${reasoningEffort}`);
|
|
24
|
+
if (session.claudeSessionId)
|
|
25
|
+
args.push("resume", session.claudeSessionId, "-");
|
|
26
|
+
else
|
|
27
|
+
args.push("-");
|
|
28
|
+
return args;
|
|
29
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { ContentBlock, ConversationTurn, SessionSnapshot } from "./types.js";
|
|
2
|
+
export interface OpenCodeTurnState {
|
|
3
|
+
blocks: ContentBlock[];
|
|
4
|
+
result: string;
|
|
5
|
+
sessionId: string | null;
|
|
6
|
+
usage?: ConversationTurn["usage"];
|
|
7
|
+
}
|
|
8
|
+
export declare function buildOpenCodeArgs(session: SessionSnapshot): string[];
|
|
9
|
+
export declare function openCodeToolName(name: string): string;
|
|
10
|
+
/** Apply one OpenCode NDJSON event to the current transport-neutral turn state. */
|
|
11
|
+
export declare function applyOpenCodeEvent(turnState: OpenCodeTurnState, event: Record<string, unknown>, createId?: () => string): string | null;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { thinkingEffortToOpenCodeVariant } from "./structured-provider-common.js";
|
|
3
|
+
function asRecord(value) {
|
|
4
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
5
|
+
? value
|
|
6
|
+
: null;
|
|
7
|
+
}
|
|
8
|
+
function extractStructuredText(value) {
|
|
9
|
+
if (typeof value === "string")
|
|
10
|
+
return value;
|
|
11
|
+
if (Array.isArray(value))
|
|
12
|
+
return value.map(extractStructuredText).filter(Boolean).join("\n");
|
|
13
|
+
const record = asRecord(value);
|
|
14
|
+
if (!record)
|
|
15
|
+
return "";
|
|
16
|
+
for (const key of ["text", "output_text", "message", "content", "summary"]) {
|
|
17
|
+
const text = extractStructuredText(record[key]);
|
|
18
|
+
if (text)
|
|
19
|
+
return text;
|
|
20
|
+
}
|
|
21
|
+
return "";
|
|
22
|
+
}
|
|
23
|
+
export function buildOpenCodeArgs(session) {
|
|
24
|
+
const args = ["run", "--format", "json", "--thinking"];
|
|
25
|
+
const modelChoice = session.selectedModel?.trim();
|
|
26
|
+
if (modelChoice && modelChoice !== "default")
|
|
27
|
+
args.push("--model", modelChoice);
|
|
28
|
+
const variant = thinkingEffortToOpenCodeVariant(session.thinkingEffort);
|
|
29
|
+
if (variant)
|
|
30
|
+
args.push("--variant", variant);
|
|
31
|
+
if (session.autoApprovePermissions === true
|
|
32
|
+
|| session.mode === "full-access"
|
|
33
|
+
|| session.mode === "managed"
|
|
34
|
+
|| session.mode === "auto-edit") {
|
|
35
|
+
args.push("--dangerously-skip-permissions");
|
|
36
|
+
}
|
|
37
|
+
if (session.claudeSessionId)
|
|
38
|
+
args.push("--session", session.claudeSessionId);
|
|
39
|
+
return args;
|
|
40
|
+
}
|
|
41
|
+
export function openCodeToolName(name) {
|
|
42
|
+
const mapped = {
|
|
43
|
+
bash: "Bash",
|
|
44
|
+
shell: "Bash",
|
|
45
|
+
read: "Read",
|
|
46
|
+
edit: "Edit",
|
|
47
|
+
write: "Write",
|
|
48
|
+
glob: "Glob",
|
|
49
|
+
grep: "Grep",
|
|
50
|
+
webfetch: "WebFetch",
|
|
51
|
+
websearch: "WebSearch",
|
|
52
|
+
todowrite: "TodoWrite",
|
|
53
|
+
task: "Task",
|
|
54
|
+
skill: "Skill",
|
|
55
|
+
};
|
|
56
|
+
return mapped[name.toLowerCase()] ?? `OpenCode/${name}`;
|
|
57
|
+
}
|
|
58
|
+
/** Apply one OpenCode NDJSON event to the current transport-neutral turn state. */
|
|
59
|
+
export function applyOpenCodeEvent(turnState, event, createId = randomUUID) {
|
|
60
|
+
if (typeof event.sessionID === "string" && event.sessionID)
|
|
61
|
+
turnState.sessionId = event.sessionID;
|
|
62
|
+
const type = typeof event.type === "string" ? event.type : "";
|
|
63
|
+
const part = asRecord(event.part);
|
|
64
|
+
if (!part) {
|
|
65
|
+
if (type === "error")
|
|
66
|
+
return extractStructuredText(event.error) || "OpenCode run failed";
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
if (type === "text" && typeof part.text === "string" && part.text.trim()) {
|
|
70
|
+
turnState.blocks.push({ type: "text", text: part.text });
|
|
71
|
+
turnState.result += (turnState.result ? "\n" : "") + part.text;
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
if (type === "reasoning" && typeof part.text === "string" && part.text.trim()) {
|
|
75
|
+
turnState.blocks.push({ type: "thinking", thinking: part.text });
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
if (type === "tool_use") {
|
|
79
|
+
const state = asRecord(part.state) ?? {};
|
|
80
|
+
const tool = typeof part.tool === "string" && part.tool ? part.tool : "tool";
|
|
81
|
+
const toolId = typeof part.callID === "string" && part.callID
|
|
82
|
+
? part.callID
|
|
83
|
+
: typeof part.id === "string" && part.id
|
|
84
|
+
? part.id
|
|
85
|
+
: createId();
|
|
86
|
+
const input = asRecord(state.input) ?? {};
|
|
87
|
+
turnState.blocks.push({
|
|
88
|
+
type: "tool_use",
|
|
89
|
+
id: toolId,
|
|
90
|
+
name: openCodeToolName(tool),
|
|
91
|
+
description: typeof state.title === "string" ? state.title : undefined,
|
|
92
|
+
input,
|
|
93
|
+
});
|
|
94
|
+
const failed = state.status === "error";
|
|
95
|
+
const content = failed
|
|
96
|
+
? (typeof state.error === "string" ? state.error : "OpenCode tool failed")
|
|
97
|
+
: (typeof state.output === "string" ? state.output : "");
|
|
98
|
+
turnState.blocks.push({ type: "tool_result", tool_use_id: toolId, content, is_error: failed });
|
|
99
|
+
return null;
|
|
100
|
+
}
|
|
101
|
+
if (type === "step_finish") {
|
|
102
|
+
const tokens = asRecord(part.tokens);
|
|
103
|
+
const cache = asRecord(tokens?.cache);
|
|
104
|
+
const previous = turnState.usage ?? {};
|
|
105
|
+
turnState.usage = {
|
|
106
|
+
inputTokens: (previous.inputTokens ?? 0) + (typeof tokens?.input === "number" ? tokens.input : 0),
|
|
107
|
+
outputTokens: (previous.outputTokens ?? 0) + (typeof tokens?.output === "number" ? tokens.output : 0),
|
|
108
|
+
reasoningOutputTokens: (previous.reasoningOutputTokens ?? 0) + (typeof tokens?.reasoning === "number" ? tokens.reasoning : 0),
|
|
109
|
+
cacheReadInputTokens: (previous.cacheReadInputTokens ?? 0) + (typeof cache?.read === "number" ? cache.read : 0),
|
|
110
|
+
cacheCreationInputTokens: (previous.cacheCreationInputTokens ?? 0) + (typeof cache?.write === "number" ? cache.write : 0),
|
|
111
|
+
totalCostUsd: (previous.totalCostUsd ?? 0) + (typeof part.cost === "number" ? part.cost : 0),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { SessionProvider, SessionRunner, SessionSnapshot, StructuredSessionState, WandConfig } from "./types.js";
|
|
2
|
+
export declare function isStructuredRunnerForProvider(provider: SessionProvider, runner: unknown): runner is SessionRunner;
|
|
3
|
+
export declare function defaultStructuredRunner(provider: SessionProvider, configuredClaudeRunner?: WandConfig["structuredRunner"]): SessionRunner;
|
|
4
|
+
export declare function resolveStructuredRunner(provider: SessionProvider, requestedRunner: unknown, configuredClaudeRunner?: WandConfig["structuredRunner"]): SessionRunner;
|
|
5
|
+
export declare function defaultStructuredState(provider: SessionProvider, runner?: SessionRunner): StructuredSessionState;
|
|
6
|
+
export declare function normalizeThinkingEffort(value: unknown): SessionSnapshot["thinkingEffort"];
|
|
7
|
+
export declare function thinkingEffortToSdkBudget(effort: SessionSnapshot["thinkingEffort"]): number;
|
|
8
|
+
export declare function thinkingEffortToClaudeCliEffort(effort: SessionSnapshot["thinkingEffort"]): string | null;
|
|
9
|
+
export declare function thinkingEffortToClaudeSlashEffort(effort: SessionSnapshot["thinkingEffort"]): string;
|
|
10
|
+
export declare function thinkingEffortToCodexReasoningEffort(effort: SessionSnapshot["thinkingEffort"]): string | null;
|
|
11
|
+
export declare function thinkingEffortToOpenCodeVariant(effort: SessionSnapshot["thinkingEffort"]): string | null;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
export function isStructuredRunnerForProvider(provider, runner) {
|
|
2
|
+
if (provider === "claude")
|
|
3
|
+
return runner === "claude-sdk" || runner === "claude-cli-print";
|
|
4
|
+
if (provider === "codex")
|
|
5
|
+
return runner === "codex-cli-exec";
|
|
6
|
+
return runner === "opencode-cli-run";
|
|
7
|
+
}
|
|
8
|
+
export function defaultStructuredRunner(provider, configuredClaudeRunner = "cli") {
|
|
9
|
+
if (provider === "codex")
|
|
10
|
+
return "codex-cli-exec";
|
|
11
|
+
if (provider === "opencode")
|
|
12
|
+
return "opencode-cli-run";
|
|
13
|
+
return configuredClaudeRunner === "sdk" ? "claude-sdk" : "claude-cli-print";
|
|
14
|
+
}
|
|
15
|
+
export function resolveStructuredRunner(provider, requestedRunner, configuredClaudeRunner = "cli") {
|
|
16
|
+
const runner = requestedRunner ?? defaultStructuredRunner(provider, configuredClaudeRunner);
|
|
17
|
+
if (!isStructuredRunnerForProvider(provider, runner)) {
|
|
18
|
+
throw new Error(`runner ${String(runner)} 不支持 provider ${provider}。`);
|
|
19
|
+
}
|
|
20
|
+
return runner;
|
|
21
|
+
}
|
|
22
|
+
export function defaultStructuredState(provider, runner = defaultStructuredRunner(provider)) {
|
|
23
|
+
return { provider, runner, lastError: null, inFlight: false, activeRequestId: null };
|
|
24
|
+
}
|
|
25
|
+
export function normalizeThinkingEffort(value) {
|
|
26
|
+
if (typeof value !== "string")
|
|
27
|
+
return null;
|
|
28
|
+
const normalized = value.trim().toLowerCase();
|
|
29
|
+
if (normalized === "off" || normalized === "standard" || normalized === "deep" || normalized === "max")
|
|
30
|
+
return normalized;
|
|
31
|
+
if (/^codex:[a-z0-9][a-z0-9_-]{0,31}$/.test(normalized))
|
|
32
|
+
return normalized;
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
export function thinkingEffortToSdkBudget(effort) {
|
|
36
|
+
if (effort === "standard")
|
|
37
|
+
return 4096;
|
|
38
|
+
if (effort === "deep")
|
|
39
|
+
return 16000;
|
|
40
|
+
if (effort === "max")
|
|
41
|
+
return 31999;
|
|
42
|
+
return 0;
|
|
43
|
+
}
|
|
44
|
+
export function thinkingEffortToClaudeCliEffort(effort) {
|
|
45
|
+
if (effort === "standard")
|
|
46
|
+
return "low";
|
|
47
|
+
if (effort === "deep")
|
|
48
|
+
return "medium";
|
|
49
|
+
if (effort === "max")
|
|
50
|
+
return "max";
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
export function thinkingEffortToClaudeSlashEffort(effort) {
|
|
54
|
+
return thinkingEffortToClaudeCliEffort(effort) ?? "auto";
|
|
55
|
+
}
|
|
56
|
+
export function thinkingEffortToCodexReasoningEffort(effort) {
|
|
57
|
+
if (typeof effort === "string" && effort.startsWith("codex:"))
|
|
58
|
+
return effort.slice("codex:".length) || null;
|
|
59
|
+
if (effort === "standard")
|
|
60
|
+
return "low";
|
|
61
|
+
if (effort === "deep")
|
|
62
|
+
return "medium";
|
|
63
|
+
if (effort === "max")
|
|
64
|
+
return "xhigh";
|
|
65
|
+
return null;
|
|
66
|
+
}
|
|
67
|
+
export function thinkingEffortToOpenCodeVariant(effort) {
|
|
68
|
+
if (!effort || effort === "off")
|
|
69
|
+
return null;
|
|
70
|
+
if (effort === "standard")
|
|
71
|
+
return "low";
|
|
72
|
+
if (effort === "deep")
|
|
73
|
+
return "high";
|
|
74
|
+
if (effort === "max")
|
|
75
|
+
return "max";
|
|
76
|
+
return effort.startsWith("codex:") ? effort.slice("codex:".length) || null : null;
|
|
77
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import { query as sdkQuery } from "@anthropic-ai/claude-agent-sdk";
|
|
1
2
|
import { SessionLogger } from "./session-logger.js";
|
|
2
3
|
import { WandStorage } from "./storage.js";
|
|
3
4
|
import { ContentBlock, ExecutionMode, ProcessEvent, SessionProvider, SessionRunner, SessionSnapshot, SessionSource, WandConfig } from "./types.js";
|
|
5
|
+
export { isStructuredRunnerForProvider, normalizeThinkingEffort, resolveStructuredRunner, thinkingEffortToClaudeCliEffort, thinkingEffortToCodexReasoningEffort, thinkingEffortToOpenCodeVariant, thinkingEffortToSdkBudget, } from "./structured-provider-common.js";
|
|
4
6
|
interface CreateStructuredSessionOptions {
|
|
5
7
|
cwd: string;
|
|
6
8
|
mode: ExecutionMode;
|
|
@@ -21,21 +23,6 @@ interface CreateStructuredSessionOptions {
|
|
|
21
23
|
*/
|
|
22
24
|
claudeSessionId?: string;
|
|
23
25
|
}
|
|
24
|
-
/**
|
|
25
|
-
* 把任意外部输入收敛到合法的 thinkingEffort 枚举值。`null` / 非法值都视为
|
|
26
|
-
* "未设置"——上层调用方再根据 provider 决定是否填默认值。
|
|
27
|
-
*/
|
|
28
|
-
export declare function normalizeThinkingEffort(value: unknown): SessionSnapshot["thinkingEffort"];
|
|
29
|
-
/** Claude SDK 用:把 thinkingEffort 映射成 `thinking.budget_tokens`。off / 空 → 0(不启用)。 */
|
|
30
|
-
export declare function thinkingEffortToSdkBudget(effort: SessionSnapshot["thinkingEffort"]): number;
|
|
31
|
-
/** Claude CLI 用:把 thinkingEffort 映射到 `--effort` / `/effort` 支持的等级。off → 不覆盖默认值。 */
|
|
32
|
-
export declare function thinkingEffortToClaudeCliEffort(effort: SessionSnapshot["thinkingEffort"]): string | null;
|
|
33
|
-
/** Claude PTY slash-command 用:off 表示恢复模型默认 effort。 */
|
|
34
|
-
export declare function thinkingEffortToClaudeSlashEffort(effort: SessionSnapshot["thinkingEffort"]): string;
|
|
35
|
-
/** Codex CLI 用:把 thinkingEffort 映射到 model_reasoning_effort 配置。off → 不覆盖 Codex 默认。 */
|
|
36
|
-
export declare function thinkingEffortToCodexReasoningEffort(effort: SessionSnapshot["thinkingEffort"]): string | null;
|
|
37
|
-
/** OpenCode exposes provider-specific reasoning presets through `--variant`. */
|
|
38
|
-
export declare function thinkingEffortToOpenCodeVariant(effort: SessionSnapshot["thinkingEffort"]): string | null;
|
|
39
26
|
/**
|
|
40
27
|
* Preserve both Responses content-part arrays and arbitrary structured tool output.
|
|
41
28
|
* Arrays without a `type` discriminator (for example Codex tool_search results)
|
|
@@ -73,6 +60,7 @@ export declare class StructuredSessionManager {
|
|
|
73
60
|
private readonly storage;
|
|
74
61
|
private readonly config;
|
|
75
62
|
private readonly logger;
|
|
63
|
+
private readonly sdkQueryFactory;
|
|
76
64
|
private readonly sessions;
|
|
77
65
|
private readonly pendingChildren;
|
|
78
66
|
private readonly pendingSdkAbort;
|
|
@@ -83,12 +71,6 @@ export declare class StructuredSessionManager {
|
|
|
83
71
|
*/
|
|
84
72
|
private readonly pendingSdkQueries;
|
|
85
73
|
private readonly interruptedWith;
|
|
86
|
-
/**
|
|
87
|
-
* 用户主动点了「停止」的会话。异步收尾(claude -p / codex 的 close、SDK 的 abort)
|
|
88
|
-
* 据此跳过"结构化会话执行失败"路径——主动停止不是失败,按正常 idle 收尾、保留历史内容。
|
|
89
|
-
* 用 Set.delete 消费:读取的同时清除,下一轮真失败不会被旧标记误抑制。
|
|
90
|
-
*/
|
|
91
|
-
private readonly userStopped;
|
|
92
74
|
/**
|
|
93
75
|
* Sessions where the current interrupt is a "queue promote" (用户从排队条点了「立即」
|
|
94
76
|
* 把队首插队到 now)。退出处理三个分支默认会把 queuedMessages 清空——因为常规的
|
|
@@ -97,8 +79,10 @@ export declare class StructuredSessionManager {
|
|
|
97
79
|
* 收到后必须 delete 掉,避免下一次普通 interrupt 误带 flag。
|
|
98
80
|
*/
|
|
99
81
|
private readonly preserveQueueOnInterrupt;
|
|
100
|
-
/** Last wall-clock time (ms)
|
|
82
|
+
/** Last wall-clock time (ms) a streaming checkpoint reached SQLite. */
|
|
101
83
|
private readonly lastStreamSaveAt;
|
|
84
|
+
private readonly streamCheckpointTimers;
|
|
85
|
+
private readonly streamCheckpointDirty;
|
|
102
86
|
/**
|
|
103
87
|
* Idempotency keys we've already accepted, mapped to their wall-clock timestamp.
|
|
104
88
|
* Android WebView 在进程恢复时偶尔会重发上一个未收到响应的 POST(HTTP/2 stream
|
|
@@ -110,21 +94,32 @@ export declare class StructuredSessionManager {
|
|
|
110
94
|
private emitEvent;
|
|
111
95
|
private archiveTimer;
|
|
112
96
|
private readonly topicRequests;
|
|
113
|
-
|
|
97
|
+
private readonly streamEmitTimers;
|
|
98
|
+
private disposed;
|
|
99
|
+
constructor(storage: WandStorage, config: WandConfig, logger?: SessionLogger | null, sdkQueryFactory?: typeof sdkQuery);
|
|
114
100
|
private archiveExpiredSessions;
|
|
115
101
|
setEventEmitter(emitEvent: (event: ProcessEvent) => void): void;
|
|
116
|
-
/**
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
*/
|
|
102
|
+
/** Stop every runner and flush terminal state before storage is closed. */
|
|
103
|
+
dispose(): void;
|
|
104
|
+
private trackStreamEmitTimer;
|
|
105
|
+
private clearStreamEmitTimer;
|
|
106
|
+
/** Mark streaming payload dirty and enforce both leading and trailing checkpoints. */
|
|
122
107
|
private saveStreamingSnapshot;
|
|
108
|
+
private flushStreamingCheckpoint;
|
|
109
|
+
private clearStreamingCheckpoint;
|
|
110
|
+
private cancelStreamingCheckpointTimer;
|
|
111
|
+
private saveAuthoritativeSession;
|
|
112
|
+
private checkpointSessionMessages;
|
|
123
113
|
list(): SessionSnapshot[];
|
|
124
114
|
/** Return lightweight snapshots for the session list (no output/messages). */
|
|
125
115
|
listSlim(): SessionSnapshot[];
|
|
126
116
|
get(id: string): SessionSnapshot | null;
|
|
127
117
|
setSessionTopic(id: string, title: string, description: string): SessionSnapshot;
|
|
118
|
+
/**
|
|
119
|
+
* Update worktree merge progress on the canonical in-memory snapshot before
|
|
120
|
+
* persisting it. A null result means this manager does not own the session.
|
|
121
|
+
*/
|
|
122
|
+
setWorktreeMergeState(id: string, status: SessionSnapshot["worktreeMergeStatus"], info: SessionSnapshot["worktreeMergeInfo"]): SessionSnapshot | null;
|
|
128
123
|
private maybeGenerateSessionTopic;
|
|
129
124
|
createSession(options: CreateStructuredSessionOptions): SessionSnapshot;
|
|
130
125
|
sendMessage(id: string, input: string, opts?: {
|
|
@@ -170,19 +165,22 @@ export declare class StructuredSessionManager {
|
|
|
170
165
|
/** Toggle auto-approve for the session. */
|
|
171
166
|
toggleAutoApprove(sessionId: string): SessionSnapshot;
|
|
172
167
|
/** Resolve a specific escalation by requestId. */
|
|
173
|
-
resolveEscalation(sessionId: string, requestId: string, resolution
|
|
168
|
+
resolveEscalation(sessionId: string, requestId: string, resolution: unknown): SessionSnapshot;
|
|
174
169
|
stop(id: string): SessionSnapshot;
|
|
175
170
|
delete(id: string): void;
|
|
176
171
|
private requireSession;
|
|
172
|
+
/** True only while this exact turn still owns the session's mutable state. */
|
|
173
|
+
private isCurrentRequest;
|
|
174
|
+
private currentSessionForRequest;
|
|
175
|
+
/** Delete a handle only if it still belongs to the execution doing cleanup. */
|
|
176
|
+
private releasePendingChild;
|
|
177
|
+
private releasePendingSdkAbort;
|
|
178
|
+
private releasePendingSdkQuery;
|
|
177
179
|
private emitStructuredSnapshot;
|
|
178
180
|
private flushNextQueuedMessage;
|
|
179
181
|
private emit;
|
|
180
182
|
private incrementApprovalStats;
|
|
181
|
-
private buildCodexArgs;
|
|
182
183
|
private runCodexStreaming;
|
|
183
|
-
private buildOpenCodeArgs;
|
|
184
|
-
private openCodeToolName;
|
|
185
|
-
private applyOpenCodeEvent;
|
|
186
184
|
private runOpenCodeStreaming;
|
|
187
185
|
/**
|
|
188
186
|
* Spawn `claude -p --output-format stream-json` and parse NDJSON lines as
|
|
@@ -277,4 +275,3 @@ export declare class StructuredSessionManager {
|
|
|
277
275
|
private extractSdkUsage;
|
|
278
276
|
private extractCodexUsage;
|
|
279
277
|
}
|
|
280
|
-
export {};
|