@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.
Files changed (62) hide show
  1. package/dist/auth.d.ts +19 -5
  2. package/dist/auth.js +83 -45
  3. package/dist/build-info.json +3 -3
  4. package/dist/cert.d.ts +1 -1
  5. package/dist/cert.js +124 -74
  6. package/dist/config.js +25 -8
  7. package/dist/express-async.d.ts +6 -0
  8. package/dist/express-async.js +28 -0
  9. package/dist/git-quick-commit.d.ts +2 -0
  10. package/dist/git-quick-commit.js +215 -76
  11. package/dist/git-utils.d.ts +4 -0
  12. package/dist/git-utils.js +60 -11
  13. package/dist/git-worktree.d.ts +8 -1
  14. package/dist/git-worktree.js +406 -41
  15. package/dist/models.d.ts +34 -4
  16. package/dist/models.js +334 -48
  17. package/dist/process-manager.d.ts +22 -30
  18. package/dist/process-manager.js +374 -441
  19. package/dist/provider-history-scanner.d.ts +54 -0
  20. package/dist/provider-history-scanner.js +354 -0
  21. package/dist/request-limits.d.ts +1 -0
  22. package/dist/request-limits.js +8 -0
  23. package/dist/resume-policy.d.ts +2 -0
  24. package/dist/resume-policy.js +5 -0
  25. package/dist/runtime-config.d.ts +16 -0
  26. package/dist/runtime-config.js +49 -0
  27. package/dist/server-file-routes.d.ts +17 -0
  28. package/dist/server-file-routes.js +653 -0
  29. package/dist/server-session-routes.d.ts +16 -3
  30. package/dist/server-session-routes.js +170 -149
  31. package/dist/server-settings-routes.d.ts +43 -0
  32. package/dist/server-settings-routes.js +225 -0
  33. package/dist/server-update-routes.d.ts +61 -0
  34. package/dist/server-update-routes.js +215 -0
  35. package/dist/server.d.ts +6 -4
  36. package/dist/server.js +350 -1313
  37. package/dist/session-logger.d.ts +32 -2
  38. package/dist/session-logger.js +145 -15
  39. package/dist/session-registry.d.ts +27 -0
  40. package/dist/session-registry.js +153 -0
  41. package/dist/session-transport.d.ts +31 -0
  42. package/dist/session-transport.js +82 -0
  43. package/dist/storage.d.ts +24 -6
  44. package/dist/storage.js +291 -44
  45. package/dist/structured-claude-adapter.d.ts +19 -0
  46. package/dist/structured-claude-adapter.js +117 -0
  47. package/dist/structured-codex-adapter.d.ts +3 -0
  48. package/dist/structured-codex-adapter.js +29 -0
  49. package/dist/structured-opencode-adapter.d.ts +11 -0
  50. package/dist/structured-opencode-adapter.js +115 -0
  51. package/dist/structured-provider-common.d.ts +11 -0
  52. package/dist/structured-provider-common.js +77 -0
  53. package/dist/structured-session-manager.d.ts +32 -35
  54. package/dist/structured-session-manager.js +551 -605
  55. package/dist/types.d.ts +10 -0
  56. package/dist/update-helper.js +5 -1
  57. package/dist/web-ui/content/scripts.js +32 -32
  58. package/dist/web-ui/embedded-assets.d.ts +1 -1
  59. package/dist/web-ui/embedded-assets.js +2 -2
  60. package/dist/ws-broadcast.d.ts +16 -1
  61. package/dist/ws-broadcast.js +124 -58
  62. 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,3 @@
1
+ import type { SessionSnapshot } from "./types.js";
2
+ /** Build the stable CLI contract for a structured Codex turn. */
3
+ export declare function buildCodexArgs(session: SessionSnapshot): string[];
@@ -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) we did a full saveSession for a streaming session. */
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
- constructor(storage: WandStorage, config: WandConfig, logger?: SessionLogger | null);
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
- * In-memory snapshot is updated unconditionally; the SQLite write is rate-
118
- * limited to once per STREAM_SAVE_THROTTLE_MS. Caller must still invoke
119
- * `storage.saveSession` directly at terminal events (close / failure) so the
120
- * final state is durable.
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?: "approve_once" | "approve_turn" | "deny"): SessionSnapshot;
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 {};