@whispering233/ai-editor-agent 0.0.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.
@@ -0,0 +1,96 @@
1
+ import type { LLMMessage, LLMUsage } from "@whispering233/ai-editor-llm";
2
+ import { type ToolDefinition } from "@whispering233/ai-editor-tools";
3
+ import { type SessionMessage } from "./session.js";
4
+ /** 分层 token 预算(决策 6:系统 ~500 / 聚焦 ~3000 / 历史 ~6000;可整体或单项覆盖)。
5
+ * 分层合计约 11.5K,远低于 DeepSeek 64K 窗口——各层独立校验、不设总预算校验(YAGNI:
6
+ * 分层裁剪已保证各层在其预算内,窗口余量充足) */
7
+ export interface ContextBudgets {
8
+ /** 基础 system 层:内核 + 项目提示词 + 临时指令(决策 6 ~500;超限仅记录不裁剪——用户内容不可裁) */
9
+ system: number;
10
+ /** 聚焦上下文层(决策 6 ~3000;超限截断并显式告知) */
11
+ focus: number;
12
+ /** 对话历史层(决策 6 ~6000;超限走 session 成对裁剪——同裁同留不拆对) */
13
+ history: number;
14
+ }
15
+ export declare const DEFAULT_CONTEXT_BUDGETS: ContextBudgets;
16
+ /** 工具清单注入所需的工具形态(仅消费 name/description,不依赖 registry 全量定义) */
17
+ export type ToolListEntry = Pick<ToolDefinition, "name" | "description">;
18
+ /** buildContext 输入:除 history 外全部可空(无聚焦 / 无项目提示词 / 无临时指令均为合法态) */
19
+ export interface BuildContextInput {
20
+ /**
21
+ * 会话历史(session 状态;本层负责超预算成对裁剪 + 末条约束收尾)。
22
+ * **前置条件**:必须含至少一条 user/tool 消息(决策 18 末条约束);空历史属调用方错误——
23
+ * S7.3 应在 history 为空时走引导分支(不发请求),勿传入空数组(buildPayload 空数组
24
+ * 语义同源:空历史 = 无有效上下文,两者互相印证)
25
+ */
26
+ history: SessionMessage[];
27
+ /** 内核提示词(默认 KERNEL_PROMPT;测试可覆盖) */
28
+ kernelPrompt?: string;
29
+ /** 项目提示词(决策 7 项目层,调用方从 ProjectConfig.prompt 传入;可空) */
30
+ projectPrompt?: string;
31
+ /** 临时指令(决策 7 临时层,即时输入不持久化;可空) */
32
+ instruction?: string;
33
+ /** 聚焦上下文文本(决策 6 聚焦层,调用方查询实体/大纲节点后拼好传入;空/缺省 = 无聚焦) */
34
+ focus?: string;
35
+ /** 工具清单(默认内部 listTools();测试可注入 mock) */
36
+ tools?: ToolListEntry[];
37
+ /**
38
+ * 最近一次成功响应的 usage 基线(决策 6;本层触发裁剪后重置为 null)。
39
+ * **口径**(ora S7.3 审核 S1,与 run.ts toHistoryBaseline / llm token.ts 三处等价表述):
40
+ * 调用方(S7.3)须换算为「历史段」真实分量传入——以 usage.prompt_tokens 为起点减
41
+ * system + toolList + focus 估算(completion 属输出、不占 prompt_tokens,无需扣除);
42
+ * 若以 total_tokens 为起点则再减 completion(两式等价)。meta.tokens 已提供
43
+ * system/toolList/focus 各层估算可供换算;直接传原始 total_tokens 会与非历史层重复计费
44
+ */
45
+ lastUsage?: LLMUsage | null;
46
+ /** 分层预算覆盖(缺省用决策 6 常量) */
47
+ budgets?: Partial<ContextBudgets>;
48
+ }
49
+ /** buildContext 输出:完整喂回 messages + 各层估算 + 裁剪/截断/基线记录 */
50
+ export interface AssembledContext {
51
+ /** 完整喂回数组:[system(基础+工具清单), (system(聚焦) 若有), ...历史];末条恒 user/tool 或为空 */
52
+ messages: LLMMessage[];
53
+ /** 各层估算 token(history 为裁剪后、无基线重置后的估算) */
54
+ tokens: {
55
+ /** 基础 system(内核+项目+临时) */
56
+ system: number;
57
+ /** 工具清单(独立于 system 层计,不参与裁剪——LLM 需完整工具表) */
58
+ toolList: number;
59
+ /** 聚焦层(截断后;无聚焦为 0) */
60
+ focus: number;
61
+ /** 历史层(裁剪后) */
62
+ history: number;
63
+ /** 四层合计 */
64
+ total: number;
65
+ };
66
+ meta: {
67
+ /** 基础 system 超限(仅记录不裁剪——用户内容不可裁) */
68
+ systemOverBudget: boolean;
69
+ /** 聚焦被截断(含截断提示) */
70
+ focusTruncated: boolean;
71
+ /** 历史被成对裁剪(同裁同留不拆对) */
72
+ historyTrimmed: boolean;
73
+ /** 裁剪后历史条数 */
74
+ historyMessageCount: number;
75
+ /** 基线被重置(触发裁剪/重排后旧 usage 不再可信——决策 6 防预算漂移) */
76
+ lastUsageReset: boolean;
77
+ /**
78
+ * 生效基线:未触发裁剪时保留换算后的 lastUsage;触发后为 null。
79
+ * **回写契约(S7.3 接线)**:本层消费的 lastUsage 是「历史段」口径(见
80
+ * BuildContextInput.lastUsage);effectiveLastUsage 仅回写「是否重置」这一决策,
81
+ * S7.3 下一轮组装时仍须按换算公式从最新 usage 重新计算历史段分量传入
82
+ */
83
+ effectiveLastUsage: LLMUsage | null;
84
+ };
85
+ }
86
+ /**
87
+ * 组装完整喂回上下文(决策 6/7):
88
+ * 1. 基础 system = 内核 + 项目提示词 + 临时指令 + 工具清单(超 system 预算仅记录不裁剪)
89
+ * 2. 聚焦注入 = 独立 system 消息(有 focus 时;超预算截断并显式告知)
90
+ * 3. 历史 = 超预算成对裁剪(同裁同留)+ 末条约束收尾(buildPayload——输出末条恒 user/tool)
91
+ * 4. usage 基线:未触发裁剪时保留 lastUsage(真实用量优先,决策 6);一旦触发裁剪即重置为
92
+ * null(旧 usage 描述裁剪前前缀,沿用会导致预算漂移)——meta.effectiveLastUsage 反映
93
+ * 生效基线,S7.3 应回写该值作为下一轮输入
94
+ */
95
+ export declare function buildContext(input: BuildContextInput): AssembledContext;
96
+ //# sourceMappingURL=context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.d.ts","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"AAeA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AAEzE,OAAO,EAAa,KAAK,cAAc,EAAE,MAAM,gCAAgC,CAAC;AAShF,OAAO,EAA6B,KAAK,cAAc,EAAE,MAAM,cAAc,CAAC;AAI9E;;4BAE4B;AAC5B,MAAM,WAAW,cAAc;IAC7B,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf,mCAAmC;IACnC,KAAK,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,eAAO,MAAM,uBAAuB,EAAE,cAIrC,CAAC;AAIF,4DAA4D;AAC5D,MAAM,MAAM,aAAa,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,GAAG,aAAa,CAAC,CAAC;AAEzE,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC;;;;;OAKG;IACH,OAAO,EAAE,cAAc,EAAE,CAAC;IAC1B,oCAAoC;IACpC,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,sDAAsD;IACtD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iCAAiC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,qDAAqD;IACrD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,wCAAwC;IACxC,KAAK,CAAC,EAAE,aAAa,EAAE,CAAC;IACxB;;;;;;;OAOG;IACH,SAAS,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC5B,yBAAyB;IACzB,OAAO,CAAC,EAAE,OAAO,CAAC,cAAc,CAAC,CAAC;CACnC;AAED,wDAAwD;AACxD,MAAM,WAAW,gBAAgB;IAC/B,yEAAyE;IACzE,QAAQ,EAAE,UAAU,EAAE,CAAC;IACvB,yCAAyC;IACzC,MAAM,EAAE;QACN,0BAA0B;QAC1B,MAAM,EAAE,MAAM,CAAC;QACf,4CAA4C;QAC5C,QAAQ,EAAE,MAAM,CAAC;QACjB,sBAAsB;QACtB,KAAK,EAAE,MAAM,CAAC;QACd,eAAe;QACf,OAAO,EAAE,MAAM,CAAC;QAChB,WAAW;QACX,KAAK,EAAE,MAAM,CAAC;KACf,CAAC;IACF,IAAI,EAAE;QACJ,oCAAoC;QACpC,gBAAgB,EAAE,OAAO,CAAC;QAC1B,mBAAmB;QACnB,cAAc,EAAE,OAAO,CAAC;QACxB,uBAAuB;QACvB,cAAc,EAAE,OAAO,CAAC;QACxB,cAAc;QACd,mBAAmB,EAAE,MAAM,CAAC;QAC5B,8CAA8C;QAC9C,cAAc,EAAE,OAAO,CAAC;QACxB;;;;;WAKG;QACH,kBAAkB,EAAE,QAAQ,GAAG,IAAI,CAAC;KACrC,CAAC;CACH;AAsED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,GAAG,gBAAgB,CA2DvE"}
@@ -0,0 +1,150 @@
1
+ // @whispering233/ai-editor-agent 上下文组装(S7.2)
2
+ //
3
+ // 契约来源:doc/design/decisions.md 决策 6(分层上下文策略:系统 ~500 / 聚焦 ~3000 /
4
+ // 历史 ~6000;usage 基线——优先最近真实 usage,裁剪历史后重置基线防预算漂移)、决策 7
5
+ // (提示词三层注入:内核 + 项目 + 临时)、doc/api/endpoints.md(POST /api/v1/chat 的
6
+ // context 字段 focus_entity_type/focus_entity_id/focus_node_id——聚焦数据由调用方查询后
7
+ // 以文本传入)、packages/llm/src/token.ts(estimateTokens / estimateMessagesTokens)、
8
+ // packages/tools/src/registry.ts(listTools 工具清单)。
9
+ //
10
+ // 架构边界:本模块**不依赖 db**——聚焦上下文文本与项目提示词均由调用方(S7.6 server 层)
11
+ // 查询后传入,context.ts 只做组装与预算控制;工具列表默认内部取 registry(agent 依赖 tools ✓)。
12
+ //
13
+ // 消费链(S7.3 主循环):SessionState → buildContext()(本层成对裁剪 + 基线重置)→
14
+ // 输出 LLMMessage[](system + 可选聚焦 system + 历史)直接喂给模型。
15
+ import { estimateMessagesTokens, estimateTokens } from "@whispering233/ai-editor-llm";
16
+ import { listTools } from "@whispering233/ai-editor-tools";
17
+ import { FOCUS_TITLE, FOCUS_TRUNCATION_NOTICE, INSTRUCTION_TITLE, KERNEL_PROMPT, PROJECT_PROMPT_TITLE, TOOL_LIST_TITLE, } from "./prompts.js";
18
+ import { buildPayload, trimSession } from "./session.js";
19
+ export const DEFAULT_CONTEXT_BUDGETS = {
20
+ system: 500,
21
+ focus: 3000,
22
+ history: 6000,
23
+ };
24
+ // ============ 内部组装辅助(纯函数) ============
25
+ /** 基础 system 文本:内核 + 项目提示词 + 临时指令(决策 7 三层注入,空层跳过) */
26
+ function buildSystemBase(kernel, project, instruction) {
27
+ const parts = [kernel];
28
+ if (project !== undefined && project.trim() !== "") {
29
+ parts.push(`${PROJECT_PROMPT_TITLE}\n${project}`);
30
+ }
31
+ if (instruction !== undefined && instruction.trim() !== "") {
32
+ parts.push(`${INSTRUCTION_TITLE}\n${instruction}`);
33
+ }
34
+ return parts.join("\n\n");
35
+ }
36
+ /** 工具清单文本:name + description 一行(registry listTools 形态;空清单返回空串) */
37
+ function buildToolListText(tools) {
38
+ if (tools.length === 0)
39
+ return "";
40
+ const lines = tools.map((t) => `- ${t.name}:${t.description}`);
41
+ return `${TOOL_LIST_TITLE}\n${lines.join("\n")}`;
42
+ }
43
+ /**
44
+ * 聚焦文本按预算截断(决策 15 精神:显式告知,不静默丢数据):
45
+ * 按 chars/4 反推可保留字符数(预留截断提示空间),保留前缀 + 截断提示。
46
+ */
47
+ function trimFocus(focus, maxTokens) {
48
+ if (estimateTokens(focus) <= maxTokens) {
49
+ return { text: focus, truncated: false };
50
+ }
51
+ const maxChars = Math.max(0, Math.floor(maxTokens * 4));
52
+ const keptChars = Math.max(0, maxChars - FOCUS_TRUNCATION_NOTICE.length);
53
+ return { text: focus.slice(0, keptChars) + FOCUS_TRUNCATION_NOTICE, truncated: true };
54
+ }
55
+ /**
56
+ * 历史按 token 预算成对裁剪(决策 6 + 决策 18):
57
+ * 用 trimSession 以「配对块」为单位裁剪(同裁同留不拆对),二分找预算内尽量多保留的
58
+ * 最大条数。裁剪后的估算**一律不带 usage 基线**——旧基线描述裁剪前前缀,沿用即预算漂移。
59
+ * 返回 { messages, trimmed }:trimmed=true 表示至少裁掉一条消息。
60
+ */
61
+ function trimHistoryToBudget(history, maxTokens) {
62
+ if (estimateMessagesTokens(history) <= maxTokens) {
63
+ return { messages: history, trimmed: false };
64
+ }
65
+ // 二分最大可行条数 n:f(n) = estimate(trimSession(history, n)),f(0)=0 ≤ 预算。
66
+ // 单调性论证:n 增大时原放不下的块可能变得可放(swap)——新纳入块的条数严格大于
67
+ // 被其挤出的更早块(否则此前不会被拒),保留**条数**单调不减;条数分布均匀时
68
+ // f(n) 即近似单调不减,二分求最大可行 n 正确。
69
+ // 兜底:即使极端大小构造破坏 token 单调性,lo 不变式(f(lo) ≤ 预算恒成立)保证
70
+ // 二分终止时输出不超预算——最坏只是保留条数略少于最优,绝无超预算风险。
71
+ let lo = 0;
72
+ let hi = history.length;
73
+ while (lo < hi) {
74
+ const mid = Math.ceil((lo + hi) / 2);
75
+ if (estimateMessagesTokens(trimSession(history, mid)) <= maxTokens) {
76
+ lo = mid;
77
+ }
78
+ else {
79
+ hi = mid - 1;
80
+ }
81
+ }
82
+ return { messages: trimSession(history, lo), trimmed: lo < history.length };
83
+ }
84
+ // ============ 主组装 ============
85
+ /**
86
+ * 组装完整喂回上下文(决策 6/7):
87
+ * 1. 基础 system = 内核 + 项目提示词 + 临时指令 + 工具清单(超 system 预算仅记录不裁剪)
88
+ * 2. 聚焦注入 = 独立 system 消息(有 focus 时;超预算截断并显式告知)
89
+ * 3. 历史 = 超预算成对裁剪(同裁同留)+ 末条约束收尾(buildPayload——输出末条恒 user/tool)
90
+ * 4. usage 基线:未触发裁剪时保留 lastUsage(真实用量优先,决策 6);一旦触发裁剪即重置为
91
+ * null(旧 usage 描述裁剪前前缀,沿用会导致预算漂移)——meta.effectiveLastUsage 反映
92
+ * 生效基线,S7.3 应回写该值作为下一轮输入
93
+ */
94
+ export function buildContext(input) {
95
+ const budgets = { ...DEFAULT_CONTEXT_BUDGETS, ...input.budgets };
96
+ const kernel = input.kernelPrompt ?? KERNEL_PROMPT;
97
+ const tools = input.tools ?? listTools();
98
+ // ---- 1. 基础 system 层(内核 + 项目 + 临时 + 工具清单) ----
99
+ const baseText = buildSystemBase(kernel, input.projectPrompt, input.instruction);
100
+ const toolListText = buildToolListText(tools);
101
+ const systemContent = toolListText === "" ? baseText : `${baseText}\n\n${toolListText}`;
102
+ const systemTokens = estimateTokens(baseText);
103
+ const toolListTokens = estimateTokens(toolListText);
104
+ const systemOverBudget = systemTokens > budgets.system;
105
+ // ---- 2. 聚焦层(独立 system 消息;超预算截断) ----
106
+ let focusMessage = null;
107
+ let focusTokens = 0;
108
+ let focusTruncated = false;
109
+ if (input.focus !== undefined && input.focus !== "") {
110
+ const trimmed = trimFocus(input.focus, budgets.focus);
111
+ focusTruncated = trimmed.truncated;
112
+ focusTokens = estimateTokens(`${FOCUS_TITLE}\n${trimmed.text}`);
113
+ focusMessage = { role: "system", content: `${FOCUS_TITLE}\n${trimmed.text}` };
114
+ }
115
+ // ---- 3. 历史层(超预算成对裁剪 + 末条约束收尾) ----
116
+ // 预算判定优先真实 usage 基线(决策 6);一旦判定超限触发裁剪,基线即重置
117
+ const withBaseline = estimateMessagesTokens(input.history, {
118
+ lastUsage: input.lastUsage ?? null,
119
+ });
120
+ const lastUsageReset = withBaseline > budgets.history;
121
+ const trimmedHistory = lastUsageReset
122
+ ? trimHistoryToBudget(input.history, budgets.history)
123
+ : { messages: input.history, trimmed: false };
124
+ const historyPayload = buildPayload(trimmedHistory.messages);
125
+ const historyTokens = estimateMessagesTokens(historyPayload);
126
+ // ---- 4. 汇总 ----
127
+ const messages = [{ role: "system", content: systemContent }];
128
+ if (focusMessage !== null)
129
+ messages.push(focusMessage);
130
+ messages.push(...historyPayload);
131
+ return {
132
+ messages,
133
+ tokens: {
134
+ system: systemTokens,
135
+ toolList: toolListTokens,
136
+ focus: focusTokens,
137
+ history: historyTokens,
138
+ total: systemTokens + toolListTokens + focusTokens + historyTokens,
139
+ },
140
+ meta: {
141
+ systemOverBudget,
142
+ focusTruncated,
143
+ historyTrimmed: trimmedHistory.trimmed,
144
+ historyMessageCount: historyPayload.length,
145
+ lastUsageReset,
146
+ effectiveLastUsage: lastUsageReset ? null : (input.lastUsage ?? null),
147
+ },
148
+ };
149
+ }
150
+ //# sourceMappingURL=context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context.js","sourceRoot":"","sources":["../src/context.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,86 @@
1
+ import { type Proposal, type ToolContext } from "@whispering233/ai-editor-tools";
2
+ import type { ToolDispatcher } from "./run.js";
3
+ /** 提案 TTL(决策 14:10 分钟;S7.5 confirm/reject 引用——超期按不存在处理) */
4
+ export declare const PROPOSAL_TTL_MS: number;
5
+ /**
6
+ * 提案条数上限(决策 14:超限淘汰 createdAt 最旧)。
7
+ * 决策原文未定数——取 200 为数量级防御:正常会话一轮最多 14 个提案、挂卡不确认的
8
+ * 残留按 TTL 自动过期,200 足以覆盖极端多轮场景且内存占用可忽略(防无限增长)。
9
+ */
10
+ export declare const PROPOSAL_MAX_COUNT = 200;
11
+ /** 提案仓接口(S7.5 路由消费:confirm/reject 经 get/peek;S7.6 切换项目调 clear) */
12
+ export interface ProposalStore {
13
+ /** 存入提案(TTL 从 createdAt 起算;先清过期、超限淘汰最旧) */
14
+ set(proposal: Proposal): void;
15
+ /**
16
+ * 按 id + 项目取:不存在 / 过期 / **跨项目** → null。
17
+ * 跨项目返回 null 即防御 PROPOSAL_PROJECT_MISMATCH 语义(决策 14 修订——
18
+ * 提案绑定 project_id,确认时校验与当前项目一致)。
19
+ */
20
+ get(proposalId: string, projectId: string): Proposal | null;
21
+ /**
22
+ * 仅按 id 取(不校验项目):S7.5 区分「404 PROPOSAL_NOT_FOUND」与
23
+ * 「409 PROPOSAL_PROJECT_MISMATCH」用——get 返回 null 后 peek 仍可见 ⇒ 跨项目误操作。
24
+ * 过期条目同样视为不存在(惰性清理)。
25
+ */
26
+ peek(proposalId: string): Proposal | null;
27
+ /**
28
+ * 按 id 移除单条(S7.5 一次性消费:confirm/reject 终态后移除——决策 14 瞬态交互对象,
29
+ * 确认/拒绝动作即消费,残留只会让重复 confirm 产生重复执行或反复 409)。
30
+ * 与 clear() 的区别:clear 清空全部(S7.6 切换项目用),remove 只移除指定提案。
31
+ */
32
+ remove(proposalId: string): void;
33
+ /** 清空全部项目提案(决策 14 修订:create/open/close 切换项目时由 S7.6 调用) */
34
+ clear(): void;
35
+ /** 当前仓内提案数(测试/诊断) */
36
+ size(): number;
37
+ }
38
+ /** createProposalStore 选项(测试覆盖 TTL/上限用;缺省取导出的常量) */
39
+ export interface ProposalStoreOptions {
40
+ /** TTL 覆盖 ms(缺省 PROPOSAL_TTL_MS) */
41
+ ttlMs?: number;
42
+ /** 条数上限覆盖(缺省 PROPOSAL_MAX_COUNT) */
43
+ maxCount?: number;
44
+ }
45
+ /**
46
+ * 创建提案仓实例。
47
+ * 过期策略:**惰性过期 + 按需清理**(决策 14「超期自动清除」落地)——get/peek 只清理命中的
48
+ * 单条,set 前扫全仓清理;无定时器(定时器引入时钟耦合且小仓无必要,测试用假时钟直接断言)。
49
+ * 淘汰策略:条数超限时淘汰 createdAt **最旧**的提案(sweep 过期后仍满则循环淘汰最旧)。
50
+ */
51
+ export declare function createProposalStore(options?: ProposalStoreOptions): ProposalStore;
52
+ /** 默认提案仓单例(S7.5 confirm/reject 与 S7.6 切换项目 clear 直接引用;测试用 createProposalStore 独立实例) */
53
+ export declare const defaultProposalStore: ProposalStore;
54
+ /**
55
+ * 提案 build 层入口签名(args 已过 argsSchema 校验——执行前 safeParse 的 parsed.data)。
56
+ * run 层裁剪只返回 { proposal_id, summary }(tools.md「提案类」2026-08 修订),
57
+ * 完整 Proposal 对象须经 build 层重建(与 run 内部产出同源确定:同 ctx + 同 args 结果一致)。
58
+ */
59
+ type ProposalBuilder = (ctx: ToolContext, args: unknown) => Proposal;
60
+ /**
61
+ * 14 个 propose_* 工具名 → build 层函数。
62
+ * 运行时查表缺失(非 propose 工具)走普通结果路径;完整性由测试断言覆盖
63
+ * (Object.keys(PROPOSAL_BUILDERS) === PROPOSAL_TOOLS)——S6.6 后续新增提案工具须同步登记。
64
+ * 导出仅为完整性测试断言;业务侧经 createToolDispatcher 间接使用。
65
+ */
66
+ export declare const PROPOSAL_BUILDERS: Record<string, ProposalBuilder>;
67
+ /** createToolDispatcher 选项 */
68
+ export interface CreateToolDispatcherOptions {
69
+ /** 提案仓(缺省 defaultProposalStore 单例——S7.5 confirm/reject 与调度同仓消费) */
70
+ store?: ProposalStore;
71
+ }
72
+ /**
73
+ * 创建工具调度器(run.ts ToolDispatcher 真实现):
74
+ * 1. **批量校验 fail fast**(tools.md「工具执行契约」):先全部 getTool(name).argsSchema
75
+ * safeParse——工具不存在 / 参数非法 → 该调用合成 isError 结果(**不中断其他调用**,
76
+ * 「fail fast」指校验先于执行,错误编码进 isError 喂回 LLM 自纠);全部校验完再逐个执行
77
+ * 2. **执行中检查 signal**(决策 16 ③):调度前 + 每个工具执行前 throwIfAborted,run 调用
78
+ * 透传 signal(长分析工具内部周期检查);命中取消抛 AbortedError **按取消语义传播**
79
+ * (原样抛穿,run.ts 识别后按用户取消终止——不喂回、不视为工具失败)
80
+ * 3. 工具执行抛错(非 AbortedError)→ 统一 isError 结构化(工具名+参数+错误信息,决策 15)
81
+ * 4. propose_* 执行成功 → build 层重建完整 Proposal 入仓 + 构造 preview(S7.6 推 GUI)→
82
+ * DispatchResult.proposal 透传 run.ts(proposal 事件在 tool_result 后、循环继续前——run.ts 保证)
83
+ */
84
+ export declare function createToolDispatcher(ctx: ToolContext, options?: CreateToolDispatcherOptions): ToolDispatcher;
85
+ export {};
86
+ //# sourceMappingURL=executor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.d.ts","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"AAmCA,OAAO,EAkBL,KAAK,QAAQ,EACb,KAAK,WAAW,EAEjB,MAAM,gCAAgC,CAAC;AACxC,OAAO,KAAK,EAAoC,cAAc,EAAE,MAAM,UAAU,CAAC;AAIjF,2DAA2D;AAC3D,eAAO,MAAM,eAAe,QAAc,CAAC;AAE3C;;;;GAIG;AACH,eAAO,MAAM,kBAAkB,MAAM,CAAC;AAEtC,kEAAkE;AAClE,MAAM,WAAW,aAAa;IAC5B,2CAA2C;IAC3C,GAAG,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,CAAC;IAC9B;;;;OAIG;IACH,GAAG,CAAC,UAAU,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC5D;;;;OAIG;IACH,IAAI,CAAC,UAAU,EAAE,MAAM,GAAG,QAAQ,GAAG,IAAI,CAAC;IAC1C;;;;OAIG;IACH,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,0DAA0D;IAC1D,KAAK,IAAI,IAAI,CAAC;IACd,qBAAqB;IACrB,IAAI,IAAI,MAAM,CAAC;CAChB;AAED,oDAAoD;AACpD,MAAM,WAAW,oBAAoB;IACnC,oCAAoC;IACpC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,oCAAoC;IACpC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,GAAE,oBAAyB,GAAG,aAAa,CA+DrF;AAED,uFAAuF;AACvF,eAAO,MAAM,oBAAoB,EAAE,aAAqC,CAAC;AAIzE;;;;GAIG;AACH,KAAK,eAAe,GAAG,CAAC,GAAG,EAAE,WAAW,EAAE,IAAI,EAAE,OAAO,KAAK,QAAQ,CAAC;AAErE;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,eAAe,CAe7D,CAAC;AAEF,8BAA8B;AAC9B,MAAM,WAAW,2BAA2B;IAC1C,mEAAmE;IACnE,KAAK,CAAC,EAAE,aAAa,CAAC;CACvB;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,oBAAoB,CAAC,GAAG,EAAE,WAAW,EAAE,OAAO,GAAE,2BAAgC,GAAG,cAAc,CAsGhH"}
@@ -0,0 +1,268 @@
1
+ // @whispering233/ai-editor-agent 工具调度器 + 提案内存仓(S7.4)
2
+ // 契约来源:
3
+ // - doc/design/tasks.md S7.4(调度 query/analysis/proposal;提案仓 TTL 10 分钟 + 条数上限 +
4
+ // project_id 绑定、切换项目清空;批量 tool_call 先全部校验再执行、错误统一结构化回填、
5
+ // 执行中检查取消 signal)
6
+ // - doc/api/tools.md「工具执行契约」(抛错即失败不抛穿循环、批量先校验 fail fast 再逐个执行、
7
+ // 结果按 tool_call_id 一一回填、截断显式告知——截断由 S7.2 上下文裁剪承担,本层不截断)
8
+ // 与「提案类」返回语义(tool_result 仅 { proposal_id, summary },完整预览经 SSE proposal
9
+ // 事件推送 GUI)
10
+ // - doc/design/decisions.md 决策 14(提案仅内存:TTL/上限/项目绑定/切换清空)、
11
+ // 决策 15(工具失败结构化喂回自纠:工具名+参数+错误信息)、
12
+ // 决策 16 ③(工具执行中检查取消信号——AbortedError 按取消语义传播,不喂回、不视为工具失败)
13
+ // - run.ts(S7.3)ToolDispatcher 契约:同序等长、不抛错(失败编码进 isError)、proposal 透传;
14
+ // AbortedError 经**抛错路径**传播(本文件抛、run.ts dispatcher catch 识别后按用户取消终止)
15
+ //
16
+ // 架构边界:本包**不依赖 db**——ToolContext(db/outlineDir/projectId)由 S7.6 server 层注入
17
+ // (createToolDispatcher 闭包捕获),这里只透传给工具 run/build;agent 侧不触碰 db 类型。
18
+ import { AbortedError, buildProposeAbandonHook, buildProposeAddDelta, buildProposeAddRelation, buildProposeAdvanceHook, buildProposeCreateEntity, buildProposeCreateHook, buildProposeDeleteEntity, buildProposeDeleteNode, buildProposeMoveNode, buildProposeOutlineNode, buildProposeRemoveRelation, buildProposeResolveHook, buildProposeUpdateEntity, buildProposeUpdateHook, getTool, throwIfAborted, } from "@whispering233/ai-editor-tools";
19
+ // ============ 提案内存仓(决策 14:仅内存、不落盘) ============
20
+ /** 提案 TTL(决策 14:10 分钟;S7.5 confirm/reject 引用——超期按不存在处理) */
21
+ export const PROPOSAL_TTL_MS = 10 * 60_000;
22
+ /**
23
+ * 提案条数上限(决策 14:超限淘汰 createdAt 最旧)。
24
+ * 决策原文未定数——取 200 为数量级防御:正常会话一轮最多 14 个提案、挂卡不确认的
25
+ * 残留按 TTL 自动过期,200 足以覆盖极端多轮场景且内存占用可忽略(防无限增长)。
26
+ */
27
+ export const PROPOSAL_MAX_COUNT = 200;
28
+ /**
29
+ * 创建提案仓实例。
30
+ * 过期策略:**惰性过期 + 按需清理**(决策 14「超期自动清除」落地)——get/peek 只清理命中的
31
+ * 单条,set 前扫全仓清理;无定时器(定时器引入时钟耦合且小仓无必要,测试用假时钟直接断言)。
32
+ * 淘汰策略:条数超限时淘汰 createdAt **最旧**的提案(sweep 过期后仍满则循环淘汰最旧)。
33
+ */
34
+ export function createProposalStore(options = {}) {
35
+ const ttlMs = options.ttlMs ?? PROPOSAL_TTL_MS;
36
+ const maxCount = options.maxCount ?? PROPOSAL_MAX_COUNT;
37
+ /** 仓内条目:proposal + 过期时刻(set 时按 createdAt 预计算;createdAt 不可解析按立即过期防御) */
38
+ const entries = new Map();
39
+ const isExpired = (entry) => Date.now() >= entry.expiresAt;
40
+ const sweepExpired = () => {
41
+ for (const [id, entry] of entries) {
42
+ if (isExpired(entry))
43
+ entries.delete(id);
44
+ }
45
+ };
46
+ return {
47
+ set(proposal) {
48
+ sweepExpired();
49
+ const createdAtMs = Date.parse(proposal.createdAt);
50
+ entries.set(proposal.proposal_id, {
51
+ proposal,
52
+ expiresAt: Number.isNaN(createdAtMs) ? 0 : createdAtMs + ttlMs,
53
+ });
54
+ // 条数上限:循环淘汰最旧直到回到上限内(createdAt 同值取先插入者——迭代序即插入序)
55
+ while (entries.size > maxCount) {
56
+ let oldestId = null;
57
+ let oldestTime = Number.POSITIVE_INFINITY;
58
+ for (const [id, entry] of entries) {
59
+ const t = Date.parse(entry.proposal.createdAt);
60
+ if (t < oldestTime) {
61
+ oldestTime = t;
62
+ oldestId = id;
63
+ }
64
+ }
65
+ if (oldestId === null)
66
+ break; // 防御:理论不可达(size > 0 必有最旧)
67
+ entries.delete(oldestId);
68
+ }
69
+ },
70
+ get(proposalId, projectId) {
71
+ const entry = entries.get(proposalId);
72
+ if (entry === undefined)
73
+ return null;
74
+ if (isExpired(entry)) {
75
+ entries.delete(proposalId); // 惰性过期清理
76
+ return null;
77
+ }
78
+ if (entry.proposal.project_id !== projectId)
79
+ return null; // 项目绑定(决策 14 修订)
80
+ return entry.proposal;
81
+ },
82
+ peek(proposalId) {
83
+ const entry = entries.get(proposalId);
84
+ if (entry === undefined)
85
+ return null;
86
+ if (isExpired(entry)) {
87
+ entries.delete(proposalId);
88
+ return null;
89
+ }
90
+ return entry.proposal;
91
+ },
92
+ remove(proposalId) {
93
+ entries.delete(proposalId);
94
+ },
95
+ clear() {
96
+ entries.clear();
97
+ },
98
+ size() {
99
+ return entries.size;
100
+ },
101
+ };
102
+ }
103
+ /** 默认提案仓单例(S7.5 confirm/reject 与 S7.6 切换项目 clear 直接引用;测试用 createProposalStore 独立实例) */
104
+ export const defaultProposalStore = createProposalStore();
105
+ /**
106
+ * 14 个 propose_* 工具名 → build 层函数。
107
+ * 运行时查表缺失(非 propose 工具)走普通结果路径;完整性由测试断言覆盖
108
+ * (Object.keys(PROPOSAL_BUILDERS) === PROPOSAL_TOOLS)——S6.6 后续新增提案工具须同步登记。
109
+ * 导出仅为完整性测试断言;业务侧经 createToolDispatcher 间接使用。
110
+ */
111
+ export const PROPOSAL_BUILDERS = {
112
+ propose_create_entity: (ctx, args) => buildProposeCreateEntity(ctx, args),
113
+ propose_update_entity: (ctx, args) => buildProposeUpdateEntity(ctx, args),
114
+ propose_delete_entity: (ctx, args) => buildProposeDeleteEntity(ctx, args),
115
+ propose_add_relation: (ctx, args) => buildProposeAddRelation(ctx, args),
116
+ propose_remove_relation: (ctx, args) => buildProposeRemoveRelation(ctx, args),
117
+ propose_add_delta: (ctx, args) => buildProposeAddDelta(ctx, args),
118
+ propose_outline_node: (ctx, args) => buildProposeOutlineNode(ctx, args),
119
+ propose_move_node: (ctx, args) => buildProposeMoveNode(ctx, args),
120
+ propose_delete_node: (ctx, args) => buildProposeDeleteNode(ctx, args),
121
+ propose_create_hook: (ctx, args) => buildProposeCreateHook(ctx, args),
122
+ propose_update_hook: (ctx, args) => buildProposeUpdateHook(ctx, args),
123
+ propose_advance_hook: (ctx, args) => buildProposeAdvanceHook(ctx, args),
124
+ propose_resolve_hook: (ctx, args) => buildProposeResolveHook(ctx, args),
125
+ propose_abandon_hook: (ctx, args) => buildProposeAbandonHook(ctx, args),
126
+ };
127
+ /**
128
+ * 创建工具调度器(run.ts ToolDispatcher 真实现):
129
+ * 1. **批量校验 fail fast**(tools.md「工具执行契约」):先全部 getTool(name).argsSchema
130
+ * safeParse——工具不存在 / 参数非法 → 该调用合成 isError 结果(**不中断其他调用**,
131
+ * 「fail fast」指校验先于执行,错误编码进 isError 喂回 LLM 自纠);全部校验完再逐个执行
132
+ * 2. **执行中检查 signal**(决策 16 ③):调度前 + 每个工具执行前 throwIfAborted,run 调用
133
+ * 透传 signal(长分析工具内部周期检查);命中取消抛 AbortedError **按取消语义传播**
134
+ * (原样抛穿,run.ts 识别后按用户取消终止——不喂回、不视为工具失败)
135
+ * 3. 工具执行抛错(非 AbortedError)→ 统一 isError 结构化(工具名+参数+错误信息,决策 15)
136
+ * 4. propose_* 执行成功 → build 层重建完整 Proposal 入仓 + 构造 preview(S7.6 推 GUI)→
137
+ * DispatchResult.proposal 透传 run.ts(proposal 事件在 tool_result 后、循环继续前——run.ts 保证)
138
+ */
139
+ export function createToolDispatcher(ctx, options = {}) {
140
+ const store = options.store ?? defaultProposalStore;
141
+ return async (calls, signal) => {
142
+ // 调度前取消检查(决策 16 ③):已取消则一个工具都不执行,直接按取消传播
143
+ throwIfAborted(toAbortSignal(signal));
144
+ // ---- 1. 批量校验 fail fast:全部先校验,再逐个执行 ----
145
+ const plans = calls.map((call) => {
146
+ const def = getTool(call.tool);
147
+ if (def === undefined) {
148
+ // 工具不存在:执行类工具不注册不暴露(tools.md「核心设计原则」——AI 只能提案不能直接写)
149
+ return {
150
+ call,
151
+ error: `工具不存在或不可调用:${call.tool}(AI 只能调用已注册的查询/分析/提案工具,执行类工具不暴露)`,
152
+ };
153
+ }
154
+ const parsed = def.argsSchema.safeParse(call.args);
155
+ if (!parsed.success) {
156
+ return { call, error: formatValidationError(call, parsed.error) };
157
+ }
158
+ return { call, def, parsed: parsed.data };
159
+ });
160
+ // ---- 2. 逐个执行(结果按输入顺序一一回填,同序等长——run.ts 契约) ----
161
+ const results = [];
162
+ for (const plan of plans) {
163
+ // 批量执行间隙被取消(决策 16 ③):立即中止后续工具并传播取消
164
+ throwIfAborted(toAbortSignal(signal));
165
+ const def = plan.def;
166
+ if (def === undefined) {
167
+ // 校验失败槽位:isError 直接回填(不执行——校验即失败,喂回 LLM 自纠)
168
+ results.push({
169
+ id: plan.call.id,
170
+ tool: plan.call.tool,
171
+ ok: false,
172
+ isError: true,
173
+ content: plan.error ?? "工具参数校验失败",
174
+ });
175
+ continue;
176
+ }
177
+ try {
178
+ const raw = await def.run(ctx, plan.parsed, toAbortSignal(signal));
179
+ const builder = PROPOSAL_BUILDERS[plan.call.tool];
180
+ if (builder === undefined) {
181
+ // 普通工具(query/analysis):结果序列化为 content 回填
182
+ results.push({
183
+ id: plan.call.id,
184
+ tool: plan.call.tool,
185
+ ok: true,
186
+ isError: false,
187
+ content: serializeToolResult(raw),
188
+ });
189
+ continue;
190
+ }
191
+ // ---- 提案路径(propose_*,决策 14 + tools.md「提案类」) ----
192
+ // run 层裁剪只返回 { proposal_id, summary };完整 Proposal 经 build 层重建入仓——
193
+ // proposal_id 以 run 返回为准覆盖(build 每次生成新 id),保证
194
+ // tool_result / proposal 事件 / 仓 key 三处同一 id(S7.5 confirm 按事件 id 取仓)
195
+ const runResult = raw;
196
+ if (runResult === null ||
197
+ typeof runResult !== "object" ||
198
+ typeof runResult.proposal_id !== "string") {
199
+ throw new Error(`提案工具 ${plan.call.tool} 返回结果不符合契约(缺 proposal_id)`);
200
+ }
201
+ const summary = typeof runResult.summary === "string" ? runResult.summary : "";
202
+ const proposal = { ...builder(ctx, plan.parsed), proposal_id: runResult.proposal_id };
203
+ store.set(proposal); // 供 S7.5 confirm/reject 取用
204
+ results.push({
205
+ id: plan.call.id,
206
+ tool: plan.call.tool,
207
+ ok: true,
208
+ isError: false,
209
+ // tool_result 严格 { proposal_id, summary }——不含预览细节(避免 LLM 误以为提案已生效而重复提案)
210
+ content: JSON.stringify({ proposal_id: proposal.proposal_id, summary }),
211
+ // preview 由 S7.6 经 SSE proposal 事件推 GUI(完整预览不走 tool_result)
212
+ proposal: {
213
+ proposal_id: proposal.proposal_id,
214
+ type: proposal.type,
215
+ preview: { type: proposal.type, summary: proposal.summary, args: proposal.args },
216
+ },
217
+ });
218
+ }
219
+ catch (err) {
220
+ // 取消语义传播(决策 16):AbortedError 不喂回、不计失败轮——原样抛出,
221
+ // run.ts dispatcher catch 识别后按用户取消终止(aborted=true,不重试)。
222
+ // signal 已置位时一律按取消传播(双保险,与 run.ts catch 对称):覆盖「工具未检查
223
+ // signal 即抛普通 Error」的取消竞态,以及跨包副本 AbortedError 的 instanceof 失效场景
224
+ // (工具包内抛出的 AbortedError 若经打包/深拷贝丢失原型链,仍可按 signal 状态识别)
225
+ if (err instanceof AbortedError || signal?.aborted === true)
226
+ throw err;
227
+ // 其余抛错:结构化 isError 回填(决策 15:工具名 + 参数 + 错误信息,喂回 LLM 自纠)
228
+ results.push({
229
+ id: plan.call.id,
230
+ tool: plan.call.tool,
231
+ ok: false,
232
+ isError: true,
233
+ content: formatRunError(plan.call, err),
234
+ });
235
+ }
236
+ }
237
+ return results;
238
+ };
239
+ }
240
+ /**
241
+ * AbortSignalLike → 工具层 AbortSignal(同一运行时对象;llm 包为免 DOM lib 依赖只声明了
242
+ * 最小结构,工具 run 签名用 DOM 类型——结构兼容,仅类型适配)
243
+ */
244
+ function toAbortSignal(signal) {
245
+ return signal;
246
+ }
247
+ /** 参数校验失败结构化回填(工具名 + 校验问题明细 + 参数,喂回 LLM 修正后重试) */
248
+ function formatValidationError(call, error) {
249
+ const detail = (error.issues ?? [])
250
+ .map((issue) => `${(issue.path ?? []).map(String).join(".") || "(根)"}:${issue.message ?? "未知错误"}`)
251
+ .join(";");
252
+ return `工具 ${call.tool} 参数校验失败(未执行,请修正参数后重试):${detail};参数:${JSON.stringify(call.args)}`;
253
+ }
254
+ /** 工具执行失败结构化回填(决策 15:工具名 + 参数 + 错误信息,喂回 LLM 自纠) */
255
+ function formatRunError(call, err) {
256
+ const message = err instanceof Error ? err.message : String(err);
257
+ return `工具 ${call.tool} 执行失败:${message};参数:${JSON.stringify(call.args)}`;
258
+ }
259
+ /** 工具结果序列化(content 恒为字符串):字符串原样;对象 JSON;undefined/不可序列化兜底 */
260
+ function serializeToolResult(raw) {
261
+ if (typeof raw === "string")
262
+ return raw;
263
+ if (raw === undefined)
264
+ return "undefined";
265
+ const json = JSON.stringify(raw);
266
+ return json === undefined ? String(raw) : json;
267
+ }
268
+ //# sourceMappingURL=executor.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"executor.js","sourceRoot":"","sources":["../src/executor.ts"],"names":[],"mappings":"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"}