@agentdevjs/core 0.1.0 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/BasicAgent-UBGLABDV.js +12 -0
- package/dist/ExplorerAgent-C62LOUTX.js +12 -0
- package/dist/{chunk-BVF7RUXV.js → chunk-KCCLSQC4.js} +1 -1
- package/dist/chunk-KCCLSQC4.js.map +1 -0
- package/dist/{chunk-KO4DJKM6.js → chunk-OFBE43IS.js} +1 -1
- package/dist/{chunk-KO4DJKM6.js.map → chunk-OFBE43IS.js.map} +1 -1
- package/dist/{chunk-YFSM6IYU.js → chunk-TKVOWEJ2.js} +154 -49
- package/dist/chunk-TKVOWEJ2.js.map +1 -0
- package/dist/{chunk-574NG4UB.js → chunk-U3LWOJ3Q.js} +4 -4
- package/dist/chunk-U3LWOJ3Q.js.map +1 -0
- package/dist/{chunk-UCEIJEM5.js → chunk-V3WM6EII.js} +2 -2
- package/dist/chunk-V3WM6EII.js.map +1 -0
- package/dist/{chunk-TIJV6GJW.js → chunk-YYNP7MZC.js} +4 -3
- package/dist/chunk-YYNP7MZC.js.map +1 -0
- package/dist/features/example-feature/templates/example-tool.render.js.map +1 -1
- package/dist/features/opencode-basic/templates/edit.render.js +1 -1
- package/dist/features/opencode-basic/templates/edit.render.js.map +1 -1
- package/dist/features/opencode-basic/templates/glob.render.js.map +1 -1
- package/dist/features/opencode-basic/templates/grep.render.js.map +1 -1
- package/dist/features/opencode-basic/templates/ls.render.js.map +1 -1
- package/dist/features/opencode-basic/templates/read.render.js +1 -1
- package/dist/features/opencode-basic/templates/read.render.js.map +1 -1
- package/dist/features/opencode-basic/templates/write.render.js +1 -1
- package/dist/features/opencode-basic/templates/write.render.js.map +1 -1
- package/dist/features/skill/templates/skill.render.js.map +1 -1
- package/dist/features/subagent/templates/agent-close.render.js.map +1 -1
- package/dist/features/subagent/templates/agent-list.render.js.map +1 -1
- package/dist/features/subagent/templates/agent-send.render.js.map +1 -1
- package/dist/features/subagent/templates/agent-spawn.render.js.map +1 -1
- package/dist/features/subagent/templates/wait.render.js.map +1 -1
- package/dist/features/todo/templates/task-clear.render.js.map +1 -1
- package/dist/features/todo/templates/task-create.render.js.map +1 -1
- package/dist/features/todo/templates/task-list.render.js.map +1 -1
- package/dist/features/todo/templates/task-update.render.js.map +1 -1
- package/dist/index.d.ts +2276 -2024
- package/dist/index.js +519 -205
- package/dist/index.js.map +1 -1
- package/dist/{notification-5DFBYKKN.js → notification-CASV7QYT.js} +2 -2
- package/dist/{resolver-F5SM2RHQ.js → resolver-4JA7VPYP.js} +2 -2
- package/dist/{session-events-TETMQI45.js → session-events-S7LMJ6MM.js} +2 -2
- package/package.json +55 -55
- package/dist/BasicAgent-RPYJHYCM.js +0 -12
- package/dist/ExplorerAgent-ITTSJRJI.js +0 -12
- package/dist/chunk-574NG4UB.js.map +0 -1
- package/dist/chunk-BVF7RUXV.js.map +0 -1
- package/dist/chunk-TIJV6GJW.js.map +0 -1
- package/dist/chunk-UCEIJEM5.js.map +0 -1
- package/dist/chunk-YFSM6IYU.js.map +0 -1
- /package/dist/{BasicAgent-RPYJHYCM.js.map → BasicAgent-UBGLABDV.js.map} +0 -0
- /package/dist/{ExplorerAgent-ITTSJRJI.js.map → ExplorerAgent-C62LOUTX.js.map} +0 -0
- /package/dist/{notification-5DFBYKKN.js.map → notification-CASV7QYT.js.map} +0 -0
- /package/dist/{resolver-F5SM2RHQ.js.map → resolver-4JA7VPYP.js.map} +0 -0
- /package/dist/{session-events-TETMQI45.js.map → session-events-S7LMJ6MM.js.map} +0 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
BasicAgent
|
|
3
|
+
} from "./chunk-YYNP7MZC.js";
|
|
4
|
+
import "./chunk-TKVOWEJ2.js";
|
|
5
|
+
import "./chunk-OFBE43IS.js";
|
|
6
|
+
import "./chunk-U3LWOJ3Q.js";
|
|
7
|
+
import "./chunk-KCCLSQC4.js";
|
|
8
|
+
import "./chunk-DGUM43GV.js";
|
|
9
|
+
export {
|
|
10
|
+
BasicAgent
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=BasicAgent-UBGLABDV.js.map
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ExplorerAgent
|
|
3
|
+
} from "./chunk-V3WM6EII.js";
|
|
4
|
+
import "./chunk-TKVOWEJ2.js";
|
|
5
|
+
import "./chunk-OFBE43IS.js";
|
|
6
|
+
import "./chunk-U3LWOJ3Q.js";
|
|
7
|
+
import "./chunk-KCCLSQC4.js";
|
|
8
|
+
import "./chunk-DGUM43GV.js";
|
|
9
|
+
export {
|
|
10
|
+
ExplorerAgent
|
|
11
|
+
};
|
|
12
|
+
//# sourceMappingURL=ExplorerAgent-C62LOUTX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/template/resolver.ts"],"sourcesContent":["/**\r\n * 占位符解析器\r\n * 使用正则表达式实现占位符替换\r\n */\r\n\r\nimport type { PlaceholderContext } from './types.js';\r\n\r\n/**\r\n * 占位符解析器\r\n */\r\nexport class PlaceholderResolver {\r\n // 正则表达式模式\r\n private static PATTERNS = {\r\n // {{#each}}...{{/each}} 循环渲染\r\n each: /\\{\\{#each\\}\\}(.*?)\\{\\{\\/each\\}\\}/gs,\r\n // {{#if}}...{{/if}} 条件渲染\r\n conditional: /\\{\\{#if\\}\\}(.*?)\\{\\{\\/if\\}\\}/gs,\r\n // {{variable}} 或 {{obj.key}} 或 {{var|default}}\r\n variable: /\\{\\{([^}]+)\\}\\}/g,\r\n };\r\n\r\n /**\r\n * 解析模板中的占位符\r\n * @param template 模板内容\r\n * @param context 变量上下文\r\n * @returns 解析后的内容\r\n */\r\n static resolve(template: string, context: PlaceholderContext = {}): string {\r\n // 1. 处理循环渲染 {{#each}}...{{/each}}\r\n let result = PlaceholderResolver.processEach(template, context);\r\n\r\n // 2. 处理条件渲染 {{#if}}...{{/if}}\r\n result = PlaceholderResolver.processConditionals(result, context);\r\n\r\n // 2. 处理变量替换(支持嵌套和默认值)\r\n result = result.replace(PlaceholderResolver.PATTERNS.variable, (_, expr) => {\r\n const trimmed = expr.trim();\r\n\r\n // 分离默认值: var|default\r\n const pipeIndex = trimmed.indexOf('|');\r\n let path: string;\r\n let defaultValue: string | undefined;\r\n\r\n if (pipeIndex !== -1) {\r\n path = trimmed.slice(0, pipeIndex).trim();\r\n defaultValue = trimmed.slice(pipeIndex + 1).trim();\r\n } else {\r\n path = trimmed;\r\n }\r\n\r\n // 获取值(支持嵌套访问)\r\n const value = PlaceholderResolver.getNestedValue(context, path);\r\n\r\n // 返回值或默认值\r\n if (value !== undefined && value !== null) {\r\n return String(value);\r\n }\r\n return defaultValue ?? '';\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * 处理循环渲染 {{#each}}...{{/each}}\r\n * 格式:{{#each}}arrayName\r\n * ...template...\r\n * {{/each}}\r\n * 在循环内可使用数组元素的字段(如 {{name}}, {{description}})或 {{this}} 引用整个对象\r\n */\r\n private static processEach(\r\n template: string,\r\n context: PlaceholderContext\r\n ): string {\r\n return template.replace(\r\n PlaceholderResolver.PATTERNS.each,\r\n (match, body: string) => {\r\n // 解析内容:第一行是数组变量名,其余是循环模板\r\n const lines = body.split('\\n');\r\n const arrayName = lines[0].trim();\r\n const loopTemplate = lines.slice(1).join('\\n');\r\n\r\n // 获取数组数据\r\n const array = PlaceholderResolver.getNestedValue(context, arrayName);\r\n\r\n // 不是数组则返回空\r\n if (!Array.isArray(array)) {\r\n return '';\r\n }\r\n\r\n // 渲染每个元素\r\n return array.map((item) => {\r\n // 将 item 的字段作为上下文,也提供 this 引用整个对象\r\n const itemContext: PlaceholderContext = {\r\n ...context,\r\n ...item,\r\n this: item,\r\n };\r\n return PlaceholderResolver.resolve(loopTemplate, itemContext);\r\n }).join('');\r\n });\r\n }\r\n\r\n /**\r\n * 处理条件渲染 {{#if}}...{{/if}}\r\n * 条件格式:{{#if}}varName{{/if}} - 只有当 varName 为真时才显示内容\r\n */\r\n private static processConditionals(\r\n template: string,\r\n context: PlaceholderContext\r\n ): string {\r\n return template.replace(\r\n PlaceholderResolver.PATTERNS.conditional,\r\n (_, content: string) => {\r\n // 检查条件变量是否存在且为真\r\n const trimmed = content.trim();\r\n const value = PlaceholderResolver.getNestedValue(context, trimmed);\r\n\r\n // 布尔值判断\r\n if (value === true || value === 1 || value === 'true') {\r\n return ''; // 条件为真,保留内容(但这里逻辑反了,应该返回内容)\r\n }\r\n if (value) {\r\n return content;\r\n }\r\n return ''; // 条件为假,移除内容\r\n });\r\n }\r\n\r\n /**\r\n * 获取嵌套对象的值\r\n * 支持点号分隔的路径,如 \"user.name\"\r\n */\r\n private static getNestedValue(obj: any, path: string): any {\r\n const keys = path.split('.');\r\n let current = obj;\r\n\r\n for (const key of keys) {\r\n if (current === null || current === undefined) {\r\n return undefined;\r\n }\r\n current = current[key];\r\n }\r\n\r\n return current;\r\n }\r\n\r\n /**\r\n * 获取模板中使用的变量列表\r\n */\r\n static extractVariables(template: string): string[] {\r\n const variables = new Set<string>();\r\n let match: RegExpExecArray | null;\r\n\r\n // 提取所有变量(不包括条件渲染)\r\n const regex = /\\{\\{([^}|]+?)(?:\\|[^}]*)?\\}\\}/g;\r\n while ((match = regex.exec(template)) !== null) {\r\n variables.add(match[1].trim());\r\n }\r\n\r\n return Array.from(variables);\r\n }\r\n\r\n /**\r\n * 验证上下文是否包含所需变量\r\n * @returns 缺失的变量数组\r\n */\r\n static validate(\r\n template: string,\r\n context: PlaceholderContext\r\n ): string[] {\r\n const required = PlaceholderResolver.extractVariables(template);\r\n const missing: string[] = [];\r\n\r\n for (const path of required) {\r\n const value = PlaceholderResolver.getNestedValue(context, path);\r\n if (value === undefined || value === null || value === '') {\r\n missing.push(path);\r\n }\r\n }\r\n\r\n return missing;\r\n }\r\n}\r\n"],"mappings":"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","names":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/core/session-events.ts"],"sourcesContent":["/**\r\n * Session Events - 会话事件流\r\n *\r\n * 职责:\r\n * - 定义无头模式(headless)消费的会话事件模型(codex exec 风格)\r\n * - 提供进程内订阅 API,消息变更单点发射\r\n *\r\n * 与通知系统(notification.ts)的分工:\r\n * - notification 是 UI 状态信号:节流、轻量、面向 Web UI 进度指示\r\n * - session event 是审计数据:不节流、完整、面向无头模式 stdout JSONL\r\n *\r\n * 事件模型(对齐 codex exec JSONL):\r\n * - thread.started 线程(会话)开始\r\n * - turn.started 一次用户输入触发的回合开始\r\n * - item.started / item.completed 会话条目生命周期\r\n * - turn.completed 回合完成(含 token 用量)\r\n * - turn.failed 回合失败\r\n *\r\n * 术语(ADR-0002/Q6,一词不混用):本事件流里的 `thread.started` 的 thread\r\n * 指**会话本身**(codex exec 对外 jsonl 审计契约,永不改义);框架的接续链概念\r\n * 定名 **WorkThread**(见 `core/workthread/`),一处不混用。\r\n *\r\n * item 类型:\r\n * - agent_message agent 的自然语言回复\r\n * - reasoning agent 的思考摘要\r\n * - tool_call 工具调用(参数、结果、错误、状态)\r\n */\r\n\r\nimport type { LLMResponse, ToolCall } from './types.js';\r\nimport type { ToolExecResult } from './context.js';\r\n\r\n// ========== Item 模型 ==========\r\n\r\nexport interface SessionItemBase {\r\n /** 条目唯一 ID:tool_call 用 LLM 生成的 call.id,其余用递增 item_N */\r\n id: string;\r\n /** 所属回合(callIndex) */\r\n turn: number;\r\n}\r\n\r\nexport interface AgentMessageItem extends SessionItemBase {\r\n type: 'agent_message';\r\n text: string;\r\n}\r\n\r\nexport interface ReasoningItem extends SessionItemBase {\r\n type: 'reasoning';\r\n text: string;\r\n}\r\n\r\nexport type ToolCallStatus = 'in_progress' | 'completed' | 'failed';\r\n\r\nexport interface ToolCallItem extends SessionItemBase {\r\n type: 'tool_call';\r\n /** 工具名称(如 shell / read / lsp_hover) */\r\n tool: string;\r\n /** 工具调用参数 */\r\n arguments?: unknown;\r\n status: ToolCallStatus;\r\n /** 成功时的结果内容 */\r\n result?: unknown;\r\n /** 失败时的错误信息 */\r\n error?: string;\r\n /**\r\n * 终止标注(ticket 023 / ADR-0005):工具被超时/用户打断终止但仍在 settle\r\n * 窗口内收尾时出现。success 结果可携带此字段,无头 jsonl 审计据此区分\r\n * 正常完成与被终止收尾。\r\n */\r\n interrupted?: { reason: 'timeout' | 'user' };\r\n}\r\n\r\nexport type SessionItem = AgentMessageItem | ReasoningItem | ToolCallItem;\r\n\r\n// ========== 事件模型 ==========\r\n\r\nexport interface TurnUsage {\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n}\r\n\r\n/**\r\n * 回合失败的结构化事实。\r\n *\r\n * message 只供人读;reason/category/retryable 供机器分流,\r\n * 与 CallOutcome 对齐(无头 CLI/JSONL 消费同一套语义)。\r\n */\r\nexport interface TurnFailure {\r\n message: string;\r\n /** 框架级终止原因(ExecutionReason) */\r\n reason?: string;\r\n /** 分类错误类型(APIErrorType / runtime_error / exception) */\r\n category?: string;\r\n statusCode?: number;\r\n retryable?: boolean;\r\n}\r\n\r\nexport type SessionEvent =\r\n | { type: 'thread.started'; threadId: string }\r\n | { type: 'turn.started'; turn: number }\r\n | { type: 'item.started'; item: SessionItem }\r\n | { type: 'item.completed'; item: SessionItem }\r\n | { type: 'turn.completed'; turn: number; usage?: TurnUsage }\r\n | { type: 'turn.failed'; turn: number; error: TurnFailure };\r\n\r\nexport type SessionEventListener = (event: SessionEvent) => void;\r\n\r\n// ========== 订阅管理 ==========\r\n\r\nconst listeners = new Set<SessionEventListener>();\r\n\r\nlet nextItemSeq = 0;\r\n\r\nfunction nextItemId(): string {\r\n return `item_${nextItemSeq++}`;\r\n}\r\n\r\n/**\r\n * 订阅会话事件流。\r\n * @returns 退订函数\r\n */\r\nexport function subscribeSessionEvents(listener: SessionEventListener): () => void {\r\n listeners.add(listener);\r\n return () => {\r\n listeners.delete(listener);\r\n };\r\n}\r\n\r\n/**\r\n * 发射会话事件。无订阅者时零开销。\r\n */\r\nexport function emitSessionEvent(event: SessionEvent): void {\r\n if (listeners.size === 0) return;\r\n for (const listener of listeners) {\r\n try {\r\n listener(event);\r\n } catch {\r\n // 订阅者异常不影响主流程\r\n }\r\n }\r\n}\r\n\r\n// ========== 发射辅助(context / agent 调用) ==========\r\n\r\n/**\r\n * 发射 assistant 响应产生的条目事件:\r\n * reasoning / agent_message 以 completed 出现(一次性内容),\r\n * 每个 toolCall 以 started 出现(等待执行结果)。\r\n */\r\nexport function emitAssistantResponseEvents(response: LLMResponse, turn: number): void {\r\n if (listeners.size === 0) return;\r\n\r\n if (response.reasoning && response.reasoning.trim()) {\r\n emitSessionEvent({\r\n type: 'item.completed',\r\n item: { id: nextItemId(), turn, type: 'reasoning', text: response.reasoning },\r\n });\r\n }\r\n\r\n if (response.content && response.content.trim()) {\r\n emitSessionEvent({\r\n type: 'item.completed',\r\n item: { id: nextItemId(), turn, type: 'agent_message', text: response.content },\r\n });\r\n }\r\n\r\n if (response.toolCalls) {\r\n for (const call of response.toolCalls) {\r\n emitSessionEvent({\r\n type: 'item.started',\r\n item: {\r\n id: call.id ?? nextItemId(),\r\n turn,\r\n type: 'tool_call',\r\n tool: call.name,\r\n arguments: call.arguments,\r\n status: 'in_progress',\r\n },\r\n });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 发射工具结果条目事件(与 emitAssistantResponseEvents 的 started 配对,靠 call.id 关联)。\r\n */\r\nexport function emitToolResultEvents(call: ToolCall, result: ToolExecResult, turn: number): void {\r\n if (listeners.size === 0) return;\r\n\r\n emitSessionEvent({\r\n type: 'item.completed',\r\n item: {\r\n id: call.id ?? nextItemId(),\r\n turn,\r\n type: 'tool_call',\r\n tool: call.name,\r\n arguments: call.arguments,\r\n status: result.success ? 'completed' : 'failed',\r\n ...(result.success\r\n ? { result: result.result }\r\n : { error: result.error ?? 'tool execution failed' }),\r\n ...(result.interrupted ? { interrupted: result.interrupted } : {}),\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * 发射回合完成事件(附 token 用量)。\r\n */\r\nexport function emitTurnCompleted(turn: number, usage?: TurnUsage): void {\r\n emitSessionEvent({ type: 'turn.completed', turn, ...(usage ? { usage } : {}) });\r\n}\r\n\r\n/**\r\n * 发射回合失败事件。\r\n *\r\n * 支持两种入参:\r\n * - CallOutcome(agent.ts 主链路):展开为结构化 TurnFailure\r\n * - 简单 message(宿主致命错误等):仅带 message\r\n */\r\nexport function emitTurnFailed(turn: number, source: string |
|
|
1
|
+
{"version":3,"sources":["../src/core/session-events.ts"],"sourcesContent":["/**\r\n * Session Events - 会话事件流\r\n *\r\n * 职责:\r\n * - 定义无头模式(headless)消费的会话事件模型(codex exec 风格)\r\n * - 提供进程内订阅 API,消息变更单点发射\r\n *\r\n * 与通知系统(notification.ts)的分工:\r\n * - notification 是 UI 状态信号:节流、轻量、面向 Web UI 进度指示\r\n * - session event 是审计数据:不节流、完整、面向无头模式 stdout JSONL\r\n *\r\n * 事件模型(对齐 codex exec JSONL):\r\n * - thread.started 线程(会话)开始\r\n * - turn.started 一次用户输入触发的回合开始\r\n * - item.started / item.completed 会话条目生命周期\r\n * - turn.completed 回合完成(含 token 用量)\r\n * - turn.failed 回合失败\r\n *\r\n * 术语(ADR-0002/Q6,一词不混用):本事件流里的 `thread.started` 的 thread\r\n * 指**会话本身**(codex exec 对外 jsonl 审计契约,永不改义);框架的接续链概念\r\n * 定名 **WorkThread**(见 `core/workthread/`),一处不混用。\r\n *\r\n * item 类型:\r\n * - agent_message agent 的自然语言回复\r\n * - reasoning agent 的思考摘要\r\n * - tool_call 工具调用(参数、结果、错误、状态)\r\n */\r\n\r\nimport type { LLMResponse, ToolCall } from './types.js';\r\nimport type { ToolExecResult } from './context.js';\r\n\r\n// ========== Item 模型 ==========\r\n\r\nexport interface SessionItemBase {\r\n /** 条目唯一 ID:tool_call 用 LLM 生成的 call.id,其余用递增 item_N */\r\n id: string;\r\n /** 所属回合(callIndex) */\r\n turn: number;\r\n}\r\n\r\nexport interface AgentMessageItem extends SessionItemBase {\r\n type: 'agent_message';\r\n text: string;\r\n}\r\n\r\nexport interface ReasoningItem extends SessionItemBase {\r\n type: 'reasoning';\r\n text: string;\r\n}\r\n\r\nexport type ToolCallStatus = 'in_progress' | 'completed' | 'failed';\r\n\r\nexport interface ToolCallItem extends SessionItemBase {\r\n type: 'tool_call';\r\n /** 工具名称(如 shell / read / lsp_hover) */\r\n tool: string;\r\n /** 工具调用参数 */\r\n arguments?: unknown;\r\n status: ToolCallStatus;\r\n /** 成功时的结果内容 */\r\n result?: unknown;\r\n /** 失败时的错误信息 */\r\n error?: string;\r\n /**\r\n * 终止标注(ticket 023 / ADR-0005):工具被超时/用户打断终止但仍在 settle\r\n * 窗口内收尾时出现。success 结果可携带此字段,无头 jsonl 审计据此区分\r\n * 正常完成与被终止收尾。\r\n */\r\n interrupted?: { reason: 'timeout' | 'user' };\r\n}\r\n\r\nexport type SessionItem = AgentMessageItem | ReasoningItem | ToolCallItem;\r\n\r\n// ========== 事件模型 ==========\r\n\r\nexport interface TurnUsage {\r\n inputTokens: number;\r\n outputTokens: number;\r\n totalTokens: number;\r\n}\r\n\r\n/**\r\n * 回合失败的结构化事实。\r\n *\r\n * message 只供人读;reason/category/retryable 供机器分流,\r\n * 与 CallOutcome 对齐(无头 CLI/JSONL 消费同一套语义)。\r\n */\r\nexport interface TurnFailure {\r\n message: string;\r\n /** 框架级终止原因(ExecutionReason) */\r\n reason?: string;\r\n /** 分类错误类型(APIErrorType / runtime_error / exception) */\r\n category?: string;\r\n statusCode?: number;\r\n retryable?: boolean;\r\n}\r\n\r\nexport type SessionEvent =\r\n | { type: 'thread.started'; threadId: string }\r\n | { type: 'turn.started'; turn: number }\r\n | { type: 'item.started'; item: SessionItem }\r\n | { type: 'item.completed'; item: SessionItem }\r\n | { type: 'turn.completed'; turn: number; usage?: TurnUsage }\r\n | { type: 'turn.failed'; turn: number; error: TurnFailure };\r\n\r\nexport type SessionEventListener = (event: SessionEvent) => void;\r\n\r\n// ========== 订阅管理 ==========\r\n\r\nconst listeners = new Set<SessionEventListener>();\r\n\r\nlet nextItemSeq = 0;\r\n\r\nfunction nextItemId(): string {\r\n return `item_${nextItemSeq++}`;\r\n}\r\n\r\n/**\r\n * 订阅会话事件流。\r\n * @returns 退订函数\r\n */\r\nexport function subscribeSessionEvents(listener: SessionEventListener): () => void {\r\n listeners.add(listener);\r\n return () => {\r\n listeners.delete(listener);\r\n };\r\n}\r\n\r\n/**\r\n * 发射会话事件。无订阅者时零开销。\r\n */\r\nexport function emitSessionEvent(event: SessionEvent): void {\r\n if (listeners.size === 0) return;\r\n for (const listener of listeners) {\r\n try {\r\n listener(event);\r\n } catch {\r\n // 订阅者异常不影响主流程\r\n }\r\n }\r\n}\r\n\r\n// ========== 发射辅助(context / agent 调用) ==========\r\n\r\n/**\r\n * 发射 assistant 响应产生的条目事件:\r\n * reasoning / agent_message 以 completed 出现(一次性内容),\r\n * 每个 toolCall 以 started 出现(等待执行结果)。\r\n */\r\nexport function emitAssistantResponseEvents(response: LLMResponse, turn: number): void {\r\n if (listeners.size === 0) return;\r\n\r\n if (response.reasoning && response.reasoning.trim()) {\r\n emitSessionEvent({\r\n type: 'item.completed',\r\n item: { id: nextItemId(), turn, type: 'reasoning', text: response.reasoning },\r\n });\r\n }\r\n\r\n if (response.content && response.content.trim()) {\r\n emitSessionEvent({\r\n type: 'item.completed',\r\n item: { id: nextItemId(), turn, type: 'agent_message', text: response.content },\r\n });\r\n }\r\n\r\n if (response.toolCalls) {\r\n for (const call of response.toolCalls) {\r\n emitSessionEvent({\r\n type: 'item.started',\r\n item: {\r\n id: call.id ?? nextItemId(),\r\n turn,\r\n type: 'tool_call',\r\n tool: call.name,\r\n arguments: call.arguments,\r\n status: 'in_progress',\r\n },\r\n });\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * 发射工具结果条目事件(与 emitAssistantResponseEvents 的 started 配对,靠 call.id 关联)。\r\n */\r\nexport function emitToolResultEvents(call: ToolCall, result: ToolExecResult, turn: number): void {\r\n if (listeners.size === 0) return;\r\n\r\n emitSessionEvent({\r\n type: 'item.completed',\r\n item: {\r\n id: call.id ?? nextItemId(),\r\n turn,\r\n type: 'tool_call',\r\n tool: call.name,\r\n arguments: call.arguments,\r\n status: result.success ? 'completed' : 'failed',\r\n ...(result.success\r\n ? { result: result.result }\r\n : { error: result.error ?? 'tool execution failed' }),\r\n ...(result.interrupted ? { interrupted: result.interrupted } : {}),\r\n },\r\n });\r\n}\r\n\r\n/**\r\n * 发射回合完成事件(附 token 用量)。\r\n */\r\nexport function emitTurnCompleted(turn: number, usage?: TurnUsage): void {\r\n emitSessionEvent({ type: 'turn.completed', turn, ...(usage ? { usage } : {}) });\r\n}\r\n\r\n/**\r\n * 发射回合失败事件。\r\n *\r\n * 支持两种入参:\r\n * - CallOutcome(agent.ts 主链路):展开为结构化 TurnFailure\r\n * - 简单 message(宿主致命错误等):仅带 message\r\n */\r\nimport type { CallOutcome } from './lifecycle.js';\r\n\r\nexport function emitTurnFailed(turn: number, source: string | CallOutcome): void {\r\n if (typeof source === 'string') {\r\n emitSessionEvent({ type: 'turn.failed', turn, error: { message: source } });\r\n return;\r\n }\r\n emitSessionEvent({\r\n type: 'turn.failed',\r\n turn,\r\n error: {\r\n message: source.error?.message ?? source.response ?? source.reason,\r\n reason: source.reason,\r\n ...(source.error ? {\r\n category: source.error.category,\r\n ...(typeof source.error.statusCode === 'number' ? { statusCode: source.error.statusCode } : {}),\r\n ...(typeof source.error.retryable === 'boolean' ? { retryable: source.error.retryable } : {}),\r\n } : {}),\r\n },\r\n });\r\n}\r\n"],"mappings":";AA6GA,IAAM,YAAY,oBAAI,IAA0B;AAEhD,IAAI,cAAc;AAElB,SAAS,aAAqB;AAC5B,SAAO,QAAQ,aAAa;AAC9B;AAMO,SAAS,uBAAuB,UAA4C;AACjF,YAAU,IAAI,QAAQ;AACtB,SAAO,MAAM;AACX,cAAU,OAAO,QAAQ;AAAA,EAC3B;AACF;AAKO,SAAS,iBAAiB,OAA2B;AAC1D,MAAI,UAAU,SAAS,EAAG;AAC1B,aAAW,YAAY,WAAW;AAChC,QAAI;AACF,eAAS,KAAK;AAAA,IAChB,QAAQ;AAAA,IAER;AAAA,EACF;AACF;AASO,SAAS,4BAA4B,UAAuB,MAAoB;AACrF,MAAI,UAAU,SAAS,EAAG;AAE1B,MAAI,SAAS,aAAa,SAAS,UAAU,KAAK,GAAG;AACnD,qBAAiB;AAAA,MACf,MAAM;AAAA,MACN,MAAM,EAAE,IAAI,WAAW,GAAG,MAAM,MAAM,aAAa,MAAM,SAAS,UAAU;AAAA,IAC9E,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,WAAW,SAAS,QAAQ,KAAK,GAAG;AAC/C,qBAAiB;AAAA,MACf,MAAM;AAAA,MACN,MAAM,EAAE,IAAI,WAAW,GAAG,MAAM,MAAM,iBAAiB,MAAM,SAAS,QAAQ;AAAA,IAChF,CAAC;AAAA,EACH;AAEA,MAAI,SAAS,WAAW;AACtB,eAAW,QAAQ,SAAS,WAAW;AACrC,uBAAiB;AAAA,QACf,MAAM;AAAA,QACN,MAAM;AAAA,UACJ,IAAI,KAAK,MAAM,WAAW;AAAA,UAC1B;AAAA,UACA,MAAM;AAAA,UACN,MAAM,KAAK;AAAA,UACX,WAAW,KAAK;AAAA,UAChB,QAAQ;AAAA,QACV;AAAA,MACF,CAAC;AAAA,IACH;AAAA,EACF;AACF;AAKO,SAAS,qBAAqB,MAAgB,QAAwB,MAAoB;AAC/F,MAAI,UAAU,SAAS,EAAG;AAE1B,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN,MAAM;AAAA,MACJ,IAAI,KAAK,MAAM,WAAW;AAAA,MAC1B;AAAA,MACA,MAAM;AAAA,MACN,MAAM,KAAK;AAAA,MACX,WAAW,KAAK;AAAA,MAChB,QAAQ,OAAO,UAAU,cAAc;AAAA,MACvC,GAAI,OAAO,UACP,EAAE,QAAQ,OAAO,OAAO,IACxB,EAAE,OAAO,OAAO,SAAS,wBAAwB;AAAA,MACrD,GAAI,OAAO,cAAc,EAAE,aAAa,OAAO,YAAY,IAAI,CAAC;AAAA,IAClE;AAAA,EACF,CAAC;AACH;AAKO,SAAS,kBAAkB,MAAc,OAAyB;AACvE,mBAAiB,EAAE,MAAM,kBAAkB,MAAM,GAAI,QAAQ,EAAE,MAAM,IAAI,CAAC,EAAG,CAAC;AAChF;AAWO,SAAS,eAAe,MAAc,QAAoC;AAC/E,MAAI,OAAO,WAAW,UAAU;AAC9B,qBAAiB,EAAE,MAAM,eAAe,MAAM,OAAO,EAAE,SAAS,OAAO,EAAE,CAAC;AAC1E;AAAA,EACF;AACA,mBAAiB;AAAA,IACf,MAAM;AAAA,IACN;AAAA,IACA,OAAO;AAAA,MACL,SAAS,OAAO,OAAO,WAAW,OAAO,YAAY,OAAO;AAAA,MAC5D,QAAQ,OAAO;AAAA,MACf,GAAI,OAAO,QAAQ;AAAA,QACjB,UAAU,OAAO,MAAM;AAAA,QACvB,GAAI,OAAO,OAAO,MAAM,eAAe,WAAW,EAAE,YAAY,OAAO,MAAM,WAAW,IAAI,CAAC;AAAA,QAC7F,GAAI,OAAO,OAAO,MAAM,cAAc,YAAY,EAAE,WAAW,OAAO,MAAM,UAAU,IAAI,CAAC;AAAA,MAC7F,IAAI,CAAC;AAAA,IACP;AAAA,EACF,CAAC;AACH;","names":[]}
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import {
|
|
2
2
|
emitAssistantResponseEvents,
|
|
3
3
|
emitToolResultEvents
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-OFBE43IS.js";
|
|
5
5
|
import {
|
|
6
6
|
DebugHub,
|
|
7
7
|
runWithNotificationScope
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-U3LWOJ3Q.js";
|
|
9
9
|
import {
|
|
10
10
|
PlaceholderResolver
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-KCCLSQC4.js";
|
|
12
12
|
|
|
13
13
|
// src/core/tool.ts
|
|
14
14
|
function createTool(config, sourceFile) {
|
|
@@ -1126,11 +1126,11 @@ var CoreLifecycle = /* @__PURE__ */ ((CoreLifecycle2) => {
|
|
|
1126
1126
|
CoreLifecycle2["ToolResultTransform"] = "ToolResultTransform";
|
|
1127
1127
|
return CoreLifecycle2;
|
|
1128
1128
|
})(CoreLifecycle || {});
|
|
1129
|
-
var Decision = /* @__PURE__ */ ((
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
return
|
|
1129
|
+
var Decision = /* @__PURE__ */ ((Decision2) => {
|
|
1130
|
+
Decision2["Approve"] = "approve";
|
|
1131
|
+
Decision2["Deny"] = "deny";
|
|
1132
|
+
Decision2["Continue"] = "continue";
|
|
1133
|
+
return Decision2;
|
|
1134
1134
|
})(Decision || {});
|
|
1135
1135
|
function normalizeDecision(result) {
|
|
1136
1136
|
if (typeof result === "string") {
|
|
@@ -1717,11 +1717,8 @@ function getDefaultSessionStore() {
|
|
|
1717
1717
|
|
|
1718
1718
|
// src/template/loader.ts
|
|
1719
1719
|
import { readFile as readFile2 } from "fs/promises";
|
|
1720
|
-
import { resolve as resolve2
|
|
1721
|
-
import { fileURLToPath } from "url";
|
|
1720
|
+
import { resolve as resolve2 } from "path";
|
|
1722
1721
|
import { existsSync } from "fs";
|
|
1723
|
-
var __filename = fileURLToPath(import.meta.url);
|
|
1724
|
-
var projectRoot = resolve2(dirname(__filename), "..");
|
|
1725
1722
|
var TemplateLoader = class {
|
|
1726
1723
|
cache;
|
|
1727
1724
|
searchDirs;
|
|
@@ -1779,7 +1776,7 @@ Please ensure the file exists.`
|
|
|
1779
1776
|
/**
|
|
1780
1777
|
* 加载模板(同步)
|
|
1781
1778
|
*/
|
|
1782
|
-
loadSync(
|
|
1779
|
+
loadSync(_templatePath) {
|
|
1783
1780
|
throw new Error("Synchronous load not implemented. Use async load() instead.");
|
|
1784
1781
|
}
|
|
1785
1782
|
/**
|
|
@@ -2172,7 +2169,7 @@ function createListRenderer(config) {
|
|
|
2172
2169
|
|
|
2173
2170
|
// src/skills/loader.ts
|
|
2174
2171
|
import { readdir as readdir2, readFile as readFile3 } from "fs/promises";
|
|
2175
|
-
import { resolve as resolve3, isAbsolute, join
|
|
2172
|
+
import { resolve as resolve3, isAbsolute, join, normalize } from "path";
|
|
2176
2173
|
import { existsSync as existsSync2 } from "fs";
|
|
2177
2174
|
import { cwd as processCwd } from "process";
|
|
2178
2175
|
import yaml from "js-yaml";
|
|
@@ -2249,7 +2246,7 @@ async function collectSkillFiles(dir) {
|
|
|
2249
2246
|
try {
|
|
2250
2247
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
2251
2248
|
for (const entry of entries) {
|
|
2252
|
-
const fullPath =
|
|
2249
|
+
const fullPath = join(dir, entry.name);
|
|
2253
2250
|
if (entry.isFile() && entry.name === "SKILL.md") {
|
|
2254
2251
|
skillFiles.push(normalize(fullPath));
|
|
2255
2252
|
continue;
|
|
@@ -2268,7 +2265,7 @@ async function collectSkillFiles(dir) {
|
|
|
2268
2265
|
const subEntries = await readdir2(fullPath, { withFileTypes: true });
|
|
2269
2266
|
for (const subEntry of subEntries) {
|
|
2270
2267
|
if (subEntry.isFile() && subEntry.name === "SKILL.md") {
|
|
2271
|
-
skillFiles.push(normalize(
|
|
2268
|
+
skillFiles.push(normalize(join(fullPath, "SKILL.md")));
|
|
2272
2269
|
break;
|
|
2273
2270
|
}
|
|
2274
2271
|
}
|
|
@@ -2717,7 +2714,9 @@ function withDeadline(signal, timeoutMs) {
|
|
|
2717
2714
|
merged.abort(signal.reason);
|
|
2718
2715
|
return merged.signal;
|
|
2719
2716
|
}
|
|
2720
|
-
|
|
2717
|
+
const onExternalAbort = () => merged.abort(signal.reason);
|
|
2718
|
+
signal.addEventListener("abort", onExternalAbort, { once: true });
|
|
2719
|
+
merged.signal.addEventListener("abort", () => signal.removeEventListener("abort", onExternalAbort), { once: true });
|
|
2721
2720
|
}
|
|
2722
2721
|
const timer = setTimeout(
|
|
2723
2722
|
() => merged.abort(new DOMException(`Model call deadline exceeded after ${timeoutMs}ms`, "TimeoutError")),
|
|
@@ -3234,8 +3233,9 @@ var UsageStats = class {
|
|
|
3234
3233
|
* @param callIndex Call 序号
|
|
3235
3234
|
* @param step Step 序号
|
|
3236
3235
|
* @param usage 用量数据
|
|
3236
|
+
* @param model 该次请求的模型归因;缺省时不产生分段
|
|
3237
3237
|
*/
|
|
3238
|
-
record(callIndex, step, usage) {
|
|
3238
|
+
record(callIndex, step, usage, model) {
|
|
3239
3239
|
this.totalUsage.inputTokens += usage.inputTokens;
|
|
3240
3240
|
this.totalUsage.outputTokens += usage.outputTokens;
|
|
3241
3241
|
this.totalUsage.totalTokens += usage.totalTokens;
|
|
@@ -3287,6 +3287,43 @@ var UsageStats = class {
|
|
|
3287
3287
|
if (usage.audioTokens) {
|
|
3288
3288
|
callSummary.totalUsage.audioTokens = (callSummary.totalUsage.audioTokens || 0) + usage.audioTokens;
|
|
3289
3289
|
}
|
|
3290
|
+
if (model) {
|
|
3291
|
+
if (!callSummary.modelSegments) callSummary.modelSegments = [];
|
|
3292
|
+
const segModelName = model.modelName || "";
|
|
3293
|
+
const segPresetName = model.presetName || "";
|
|
3294
|
+
let segment = callSummary.modelSegments.find(
|
|
3295
|
+
(s) => s.modelName === segModelName && s.presetName === segPresetName
|
|
3296
|
+
);
|
|
3297
|
+
if (!segment) {
|
|
3298
|
+
segment = {
|
|
3299
|
+
modelName: segModelName,
|
|
3300
|
+
presetName: segPresetName,
|
|
3301
|
+
usage: { inputTokens: 0, outputTokens: 0, totalTokens: 0 },
|
|
3302
|
+
requests: 0,
|
|
3303
|
+
cacheHitRequests: 0
|
|
3304
|
+
};
|
|
3305
|
+
callSummary.modelSegments.push(segment);
|
|
3306
|
+
}
|
|
3307
|
+
segment.usage.inputTokens += usage.inputTokens;
|
|
3308
|
+
segment.usage.outputTokens += usage.outputTokens;
|
|
3309
|
+
segment.usage.totalTokens += usage.totalTokens;
|
|
3310
|
+
if (usage.cacheCreationTokens) {
|
|
3311
|
+
segment.usage.cacheCreationTokens = (segment.usage.cacheCreationTokens || 0) + usage.cacheCreationTokens;
|
|
3312
|
+
}
|
|
3313
|
+
if (usage.cacheReadTokens) {
|
|
3314
|
+
segment.usage.cacheReadTokens = (segment.usage.cacheReadTokens || 0) + usage.cacheReadTokens;
|
|
3315
|
+
}
|
|
3316
|
+
if (usage.reasoningTokens) {
|
|
3317
|
+
segment.usage.reasoningTokens = (segment.usage.reasoningTokens || 0) + usage.reasoningTokens;
|
|
3318
|
+
}
|
|
3319
|
+
if (usage.audioTokens) {
|
|
3320
|
+
segment.usage.audioTokens = (segment.usage.audioTokens || 0) + usage.audioTokens;
|
|
3321
|
+
}
|
|
3322
|
+
segment.requests++;
|
|
3323
|
+
if (usage.cacheReadTokens) {
|
|
3324
|
+
segment.cacheHitRequests++;
|
|
3325
|
+
}
|
|
3326
|
+
}
|
|
3290
3327
|
this.lastRequestUsage = { ...usage };
|
|
3291
3328
|
}
|
|
3292
3329
|
/**
|
|
@@ -3305,19 +3342,29 @@ var UsageStats = class {
|
|
|
3305
3342
|
getTotalUsage() {
|
|
3306
3343
|
return { ...this.totalUsage };
|
|
3307
3344
|
}
|
|
3345
|
+
/** Call 汇总的深拷贝(totalUsage 与 modelSegments 均独立副本) */
|
|
3346
|
+
cloneCallSummary(summary) {
|
|
3347
|
+
return {
|
|
3348
|
+
...summary,
|
|
3349
|
+
totalUsage: { ...summary.totalUsage },
|
|
3350
|
+
...summary.modelSegments ? {
|
|
3351
|
+
modelSegments: summary.modelSegments.map((s) => ({ ...s, usage: { ...s.usage } }))
|
|
3352
|
+
} : {}
|
|
3353
|
+
};
|
|
3354
|
+
}
|
|
3308
3355
|
/**
|
|
3309
3356
|
* 获取指定 Call 的用量汇总
|
|
3310
3357
|
* @param callIndex Call 序号
|
|
3311
3358
|
*/
|
|
3312
3359
|
getCallUsage(callIndex) {
|
|
3313
3360
|
const summary = this.currentCallUsage.get(callIndex);
|
|
3314
|
-
return summary ?
|
|
3361
|
+
return summary ? this.cloneCallSummary(summary) : void 0;
|
|
3315
3362
|
}
|
|
3316
3363
|
/**
|
|
3317
3364
|
* 获取所有 Call 的用量汇总
|
|
3318
3365
|
*/
|
|
3319
3366
|
getAllCallUsage() {
|
|
3320
|
-
return Array.from(this.currentCallUsage.values()).map((s) => (
|
|
3367
|
+
return Array.from(this.currentCallUsage.values()).map((s) => this.cloneCallSummary(s));
|
|
3321
3368
|
}
|
|
3322
3369
|
/**
|
|
3323
3370
|
* 获取总请求次数
|
|
@@ -3377,7 +3424,7 @@ var UsageStats = class {
|
|
|
3377
3424
|
this.totalCacheHitRequests = snapshot.totalCacheHitRequests ?? 0;
|
|
3378
3425
|
this.currentCallUsage.clear();
|
|
3379
3426
|
for (const call of snapshot.calls) {
|
|
3380
|
-
this.currentCallUsage.set(call.callIndex,
|
|
3427
|
+
this.currentCallUsage.set(call.callIndex, this.cloneCallSummary(call));
|
|
3381
3428
|
}
|
|
3382
3429
|
this.lastRequestUsage = snapshot.lastRequestUsage ? { ...snapshot.lastRequestUsage } : null;
|
|
3383
3430
|
this.currentStepRecords = [];
|
|
@@ -3401,8 +3448,8 @@ var UsageStats = class {
|
|
|
3401
3448
|
|
|
3402
3449
|
// src/core/agent.ts
|
|
3403
3450
|
import { existsSync as existsSync3, realpathSync } from "fs";
|
|
3404
|
-
import { dirname
|
|
3405
|
-
import { fileURLToPath
|
|
3451
|
+
import { dirname, join as join2, relative, sep } from "path";
|
|
3452
|
+
import { fileURLToPath } from "url";
|
|
3406
3453
|
|
|
3407
3454
|
// src/core/agent/hooks-executor.ts
|
|
3408
3455
|
var hookLogger = createLogger("agent.forward-hook");
|
|
@@ -3486,7 +3533,7 @@ var TemplateResolver = class {
|
|
|
3486
3533
|
...this.systemContext
|
|
3487
3534
|
};
|
|
3488
3535
|
if (typeof this.systemMessage === "string") {
|
|
3489
|
-
const { PlaceholderResolver: PlaceholderResolver2 } = await import("./resolver-
|
|
3536
|
+
const { PlaceholderResolver: PlaceholderResolver2 } = await import("./resolver-4JA7VPYP.js");
|
|
3490
3537
|
return PlaceholderResolver2.resolve(this.systemMessage, context);
|
|
3491
3538
|
}
|
|
3492
3539
|
if (this.templateComposer) {
|
|
@@ -3496,7 +3543,7 @@ var TemplateResolver = class {
|
|
|
3496
3543
|
}
|
|
3497
3544
|
if (this.systemMessage && typeof this.systemMessage === "object" && "file" in this.systemMessage) {
|
|
3498
3545
|
const content = await this.templateLoader.load(this.systemMessage.file);
|
|
3499
|
-
const { PlaceholderResolver: PlaceholderResolver2 } = await import("./resolver-
|
|
3546
|
+
const { PlaceholderResolver: PlaceholderResolver2 } = await import("./resolver-4JA7VPYP.js");
|
|
3500
3547
|
return PlaceholderResolver2.resolve(content, context);
|
|
3501
3548
|
}
|
|
3502
3549
|
return "";
|
|
@@ -3578,7 +3625,7 @@ var ToolExecutor = class {
|
|
|
3578
3625
|
/**
|
|
3579
3626
|
* 执行单个工具
|
|
3580
3627
|
*/
|
|
3581
|
-
async execute(call, input, context, step,
|
|
3628
|
+
async execute(call, input, context, step, _callIndex) {
|
|
3582
3629
|
return await runWithLogScope({
|
|
3583
3630
|
step,
|
|
3584
3631
|
toolName: call.name,
|
|
@@ -3733,7 +3780,7 @@ var ToolExecutor = class {
|
|
|
3733
3780
|
termination: () => null
|
|
3734
3781
|
};
|
|
3735
3782
|
try {
|
|
3736
|
-
const { emitNotification, createToolStart } = await import("./notification-
|
|
3783
|
+
const { emitNotification, createToolStart } = await import("./notification-CASV7QYT.js");
|
|
3737
3784
|
emitNotification(createToolStart(call.name));
|
|
3738
3785
|
} catch {
|
|
3739
3786
|
}
|
|
@@ -3900,7 +3947,7 @@ var ToolExecutor = class {
|
|
|
3900
3947
|
success: result.success
|
|
3901
3948
|
});
|
|
3902
3949
|
try {
|
|
3903
|
-
const { emitNotification, createToolComplete } = await import("./notification-
|
|
3950
|
+
const { emitNotification, createToolComplete } = await import("./notification-CASV7QYT.js");
|
|
3904
3951
|
emitNotification(createToolComplete(call.name, result.success, result.duration));
|
|
3905
3952
|
} catch {
|
|
3906
3953
|
}
|
|
@@ -3968,7 +4015,7 @@ var ReActLoopRunner = class {
|
|
|
3968
4015
|
* @returns 执行结果
|
|
3969
4016
|
*/
|
|
3970
4017
|
async run(input, context, options) {
|
|
3971
|
-
const {
|
|
4018
|
+
const { callIndex, signal } = options;
|
|
3972
4019
|
logger3.debug("ReAct loop started", { callIndex, hasSignal: !!signal, signalAborted: signal?.aborted });
|
|
3973
4020
|
let completed = false;
|
|
3974
4021
|
let finalResponse = "";
|
|
@@ -4002,6 +4049,7 @@ var ReActLoopRunner = class {
|
|
|
4002
4049
|
let hasToolCalls = false;
|
|
4003
4050
|
for (let emptyAttempt = 0; emptyAttempt <= MAX_EMPTY_RETRIES; emptyAttempt++) {
|
|
4004
4051
|
const llmStartTime = Date.now();
|
|
4052
|
+
const modelAtRequest = this.agent.getLLMMeta();
|
|
4005
4053
|
response = await runWithLogScope({
|
|
4006
4054
|
lifecycle: "LLM",
|
|
4007
4055
|
namespace: "agent.llm",
|
|
@@ -4021,7 +4069,10 @@ var ReActLoopRunner = class {
|
|
|
4021
4069
|
});
|
|
4022
4070
|
providerStopReason = response.stopReason;
|
|
4023
4071
|
if (response.usage) {
|
|
4024
|
-
this.agent.recordUsage(callIndex, step, response.usage
|
|
4072
|
+
this.agent.recordUsage(callIndex, step, response.usage, {
|
|
4073
|
+
...modelAtRequest.modelName ? { modelName: modelAtRequest.modelName } : {},
|
|
4074
|
+
...modelAtRequest.presetName ? { presetName: modelAtRequest.presetName } : {}
|
|
4075
|
+
});
|
|
4025
4076
|
}
|
|
4026
4077
|
hasToolCalls = !!(response.toolCalls && response.toolCalls.length > 0);
|
|
4027
4078
|
if (!response.content && !hasToolCalls) {
|
|
@@ -4431,7 +4482,7 @@ var ReActLoopRunner = class {
|
|
|
4431
4482
|
};
|
|
4432
4483
|
}
|
|
4433
4484
|
return null;
|
|
4434
|
-
} catch
|
|
4485
|
+
} catch {
|
|
4435
4486
|
return null;
|
|
4436
4487
|
}
|
|
4437
4488
|
}
|
|
@@ -4570,11 +4621,14 @@ var AgentBase = class {
|
|
|
4570
4621
|
_llmMeta = {};
|
|
4571
4622
|
/** 外部消费者通过 onLLMSwap() 注册的回调 */
|
|
4572
4623
|
_llmSwapCallbacks = [];
|
|
4624
|
+
/** 应用层注入的 preset 解析服务(构造时注入;资产留在注入方) */
|
|
4625
|
+
_modelResolver = null;
|
|
4573
4626
|
constructor(config) {
|
|
4574
4627
|
installConsoleBridge();
|
|
4575
4628
|
this.agentId = DebugHub.getInstance().allocateAgentId();
|
|
4576
4629
|
this.config = config;
|
|
4577
4630
|
this.llm = config.llm;
|
|
4631
|
+
this._modelResolver = config.modelResolver ?? null;
|
|
4578
4632
|
this.maxTurns = config.maxTurns ?? 10;
|
|
4579
4633
|
this.systemMessage = config.systemMessage;
|
|
4580
4634
|
this.templateLoader = new TemplateLoader();
|
|
@@ -4778,12 +4832,12 @@ var AgentBase = class {
|
|
|
4778
4832
|
preCallBoundary = context.captureBoundary();
|
|
4779
4833
|
preCallRuntimeState = await this.captureRuntimeStateWithoutContext(this._callIndex - 1);
|
|
4780
4834
|
try {
|
|
4781
|
-
const { emitNotification, createCallStart } = await import("./notification-
|
|
4835
|
+
const { emitNotification, createCallStart } = await import("./notification-CASV7QYT.js");
|
|
4782
4836
|
emitNotification(createCallStart());
|
|
4783
4837
|
} catch {
|
|
4784
4838
|
}
|
|
4785
4839
|
try {
|
|
4786
|
-
const { emitSessionEvent } = await import("./session-events-
|
|
4840
|
+
const { emitSessionEvent } = await import("./session-events-S7LMJ6MM.js");
|
|
4787
4841
|
emitSessionEvent({ type: "turn.started", turn: this._callIndex });
|
|
4788
4842
|
} catch {
|
|
4789
4843
|
}
|
|
@@ -4825,12 +4879,12 @@ var AgentBase = class {
|
|
|
4825
4879
|
outcome
|
|
4826
4880
|
});
|
|
4827
4881
|
try {
|
|
4828
|
-
const { emitNotification, createCallFinish } = await import("./notification-
|
|
4882
|
+
const { emitNotification, createCallFinish } = await import("./notification-CASV7QYT.js");
|
|
4829
4883
|
emitNotification(createCallFinish(outcome));
|
|
4830
4884
|
} catch {
|
|
4831
4885
|
}
|
|
4832
4886
|
try {
|
|
4833
|
-
const {
|
|
4887
|
+
const { emitTurnCompleted, emitTurnFailed } = await import("./session-events-S7LMJ6MM.js");
|
|
4834
4888
|
if (result.completed) {
|
|
4835
4889
|
const callUsage = this.usageStats.getCallUsage(this._callIndex)?.totalUsage;
|
|
4836
4890
|
emitTurnCompleted(
|
|
@@ -4905,12 +4959,12 @@ var AgentBase = class {
|
|
|
4905
4959
|
outcome
|
|
4906
4960
|
});
|
|
4907
4961
|
try {
|
|
4908
|
-
const { emitNotification, createCallFinish } = await import("./notification-
|
|
4962
|
+
const { emitNotification, createCallFinish } = await import("./notification-CASV7QYT.js");
|
|
4909
4963
|
emitNotification(createCallFinish(outcome));
|
|
4910
4964
|
} catch {
|
|
4911
4965
|
}
|
|
4912
4966
|
try {
|
|
4913
|
-
const { emitTurnFailed } = await import("./session-events-
|
|
4967
|
+
const { emitTurnFailed } = await import("./session-events-S7LMJ6MM.js");
|
|
4914
4968
|
emitTurnFailed(this._callIndex, outcome);
|
|
4915
4969
|
} catch {
|
|
4916
4970
|
}
|
|
@@ -4955,8 +5009,8 @@ var AgentBase = class {
|
|
|
4955
5009
|
}
|
|
4956
5010
|
for (const templateName of templateNames) {
|
|
4957
5011
|
const candidates = [
|
|
4958
|
-
|
|
4959
|
-
|
|
5012
|
+
join2(pkgInfo.root, "dist", "templates", `${templateName}.render.js`),
|
|
5013
|
+
join2(pkgInfo.root, "dist", "features", feature.name, "templates", `${templateName}.render.js`)
|
|
4960
5014
|
];
|
|
4961
5015
|
const templatePath = candidates.find((p) => existsSync3(p));
|
|
4962
5016
|
if (!templatePath) continue;
|
|
@@ -5142,6 +5196,49 @@ var AgentBase = class {
|
|
|
5142
5196
|
getLLMMeta() {
|
|
5143
5197
|
return { ...this._llmMeta };
|
|
5144
5198
|
}
|
|
5199
|
+
/**
|
|
5200
|
+
* 按 preset 名一键切换模型
|
|
5201
|
+
*
|
|
5202
|
+
* 编排:resolver.resolve → setLLM(换实例 / 贴 meta 标 / 通知 Feature / 推 Overview)。
|
|
5203
|
+
* preset 表与凭证由应用层注入的 resolver 提供,本方法只编排流程,因此宿主与
|
|
5204
|
+
* Feature 消费同一入口,切换后的模型状态以 getLLMMeta() 为唯一权威。
|
|
5205
|
+
*
|
|
5206
|
+
* 允许在 onCall 运行期间调用(mid-turn swap),语义同 setLLM。
|
|
5207
|
+
*
|
|
5208
|
+
* @param presetName preset 名
|
|
5209
|
+
* @param opts.thinkingEffort 运行时档位覆盖;null 表示清除为厂商默认
|
|
5210
|
+
* @param opts.source 切换发起方标记('user' / 'feature:<name>' 等),写入 meta 供让位策略区分
|
|
5211
|
+
* @returns false 表示 preset 解析失败(名字不存在 / 凭证缺失),模型未变
|
|
5212
|
+
* @throws 未注入 modelResolver 时抛出 —— 装配缺失应显式失败而非静默
|
|
5213
|
+
*/
|
|
5214
|
+
setModel(presetName, opts) {
|
|
5215
|
+
if (!this._modelResolver) {
|
|
5216
|
+
throw new Error("setModel: no modelResolver injected (pass config.modelResolver at assembly time)");
|
|
5217
|
+
}
|
|
5218
|
+
const overrides = opts && "thinkingEffort" in opts ? { thinkingEffort: opts.thinkingEffort ?? null } : void 0;
|
|
5219
|
+
const resolved = this._modelResolver.resolve(presetName, overrides);
|
|
5220
|
+
if (!resolved?.llm) return false;
|
|
5221
|
+
this.setLLM(resolved.llm, {
|
|
5222
|
+
...resolved.meta,
|
|
5223
|
+
...opts?.source ? { source: opts.source } : {}
|
|
5224
|
+
});
|
|
5225
|
+
return true;
|
|
5226
|
+
}
|
|
5227
|
+
/**
|
|
5228
|
+
* 仅调整思考档位(不动模型):按当前 presetName 重 resolve。
|
|
5229
|
+
* 档位固化在 LLM 实例构造里,因此调整档位 = 以覆盖档位重造客户端。
|
|
5230
|
+
*
|
|
5231
|
+
* @param effort 档位;null 表示清除为厂商默认
|
|
5232
|
+
* @returns false 表示当前模型无 presetName 可锚定或解析失败
|
|
5233
|
+
*/
|
|
5234
|
+
setThinkingEffort(effort, opts) {
|
|
5235
|
+
const presetName = this._llmMeta.presetName;
|
|
5236
|
+
if (!presetName) return false;
|
|
5237
|
+
return this.setModel(presetName, {
|
|
5238
|
+
thinkingEffort: effort,
|
|
5239
|
+
...opts?.source ? { source: opts.source } : {}
|
|
5240
|
+
});
|
|
5241
|
+
}
|
|
5145
5242
|
async rollbackToCall(callIndex) {
|
|
5146
5243
|
await this.ensureFeatureTools();
|
|
5147
5244
|
const checkpoint = this._callCheckpoints.find((entry) => entry.callIndex === callIndex);
|
|
@@ -5350,9 +5447,10 @@ var AgentBase = class {
|
|
|
5350
5447
|
* @param callIndex Call 序号
|
|
5351
5448
|
* @param step Step 序号
|
|
5352
5449
|
* @param usage 用量数据
|
|
5450
|
+
* @param model 该次请求的模型归因(缺省不计入分段)
|
|
5353
5451
|
*/
|
|
5354
|
-
recordUsage(callIndex, step, usage) {
|
|
5355
|
-
this.usageStats.record(callIndex, step, usage);
|
|
5452
|
+
recordUsage(callIndex, step, usage, model) {
|
|
5453
|
+
this.usageStats.record(callIndex, step, usage, model);
|
|
5356
5454
|
this.pushOverviewSnapshot();
|
|
5357
5455
|
}
|
|
5358
5456
|
/**
|
|
@@ -5400,14 +5498,14 @@ var AgentBase = class {
|
|
|
5400
5498
|
if (factory) return factory();
|
|
5401
5499
|
switch (type) {
|
|
5402
5500
|
case "ExplorerAgent": {
|
|
5403
|
-
const { ExplorerAgent } = await import("./ExplorerAgent-
|
|
5501
|
+
const { ExplorerAgent } = await import("./ExplorerAgent-C62LOUTX.js");
|
|
5404
5502
|
return new ExplorerAgent({
|
|
5405
5503
|
llm: this.llm
|
|
5406
5504
|
});
|
|
5407
5505
|
}
|
|
5408
5506
|
case "BasicAgent":
|
|
5409
5507
|
default: {
|
|
5410
|
-
const { BasicAgent } = await import("./BasicAgent-
|
|
5508
|
+
const { BasicAgent } = await import("./BasicAgent-UBGLABDV.js");
|
|
5411
5509
|
return new BasicAgent({
|
|
5412
5510
|
llm: this.llm,
|
|
5413
5511
|
tools: this.tools.getAll().slice(0, 3)
|
|
@@ -6001,7 +6099,7 @@ var AgentBase = class {
|
|
|
6001
6099
|
} catch (rollbackError) {
|
|
6002
6100
|
throw new Error(
|
|
6003
6101
|
`Feature '${featureName}' reload failed at stage '${stage}', and rollback also failed. Original error: ${error instanceof Error ? error.message : String(error)}. Rollback error: ${rollbackError instanceof Error ? rollbackError.message : String(rollbackError)}`,
|
|
6004
|
-
{ cause:
|
|
6102
|
+
{ cause: rollbackError }
|
|
6005
6103
|
);
|
|
6006
6104
|
}
|
|
6007
6105
|
const message = error instanceof Error ? error.message : String(error);
|
|
@@ -6122,8 +6220,8 @@ var AgentBase = class {
|
|
|
6122
6220
|
for (const [name, feature] of this.features) {
|
|
6123
6221
|
if (name === "skill") continue;
|
|
6124
6222
|
if (!feature.source) continue;
|
|
6125
|
-
const filePath = feature.source.startsWith("file://") ?
|
|
6126
|
-
const skillsDir =
|
|
6223
|
+
const filePath = feature.source.startsWith("file://") ? fileURLToPath(feature.source) : feature.source;
|
|
6224
|
+
const skillsDir = join2(dirname(filePath), "skills");
|
|
6127
6225
|
if (!existsSync3(skillsDir)) continue;
|
|
6128
6226
|
const found = await discover({ dir: skillsDir });
|
|
6129
6227
|
if (found.length > 0) {
|
|
@@ -6192,11 +6290,17 @@ var AgentBase = class {
|
|
|
6192
6290
|
debugPusher,
|
|
6193
6291
|
features: this.features,
|
|
6194
6292
|
hooksRegistry: this.hooksRegistry,
|
|
6195
|
-
recordUsage: (callIndex, step, usage) => this.recordUsage(callIndex, step, usage),
|
|
6293
|
+
recordUsage: (callIndex, step, usage, model) => this.recordUsage(callIndex, step, usage, model),
|
|
6196
6294
|
endCallUsage: (callIndex) => this.endCallUsage(callIndex),
|
|
6197
6295
|
dispatchTurnActivations: (refs, context) => this.dispatchTurnActivations(refs, context),
|
|
6198
6296
|
stepSaveFn: this._createStepSaveFn(),
|
|
6199
|
-
peekContinuationRequest: () => this._continuationRequest
|
|
6297
|
+
peekContinuationRequest: () => this._continuationRequest,
|
|
6298
|
+
// 模型热切换族(ADR-0009):hook ctx 的 agent 是 facade,转发到本体,
|
|
6299
|
+
// 保证 ctx.agent.setModel / setLLM / getLLMMeta 与直调行为一致。
|
|
6300
|
+
setModel: (presetName, opts) => this.setModel(presetName, opts),
|
|
6301
|
+
setThinkingEffort: (effort, opts) => this.setThinkingEffort(effort, opts),
|
|
6302
|
+
setLLM: (llm, meta) => this.setLLM(llm, meta),
|
|
6303
|
+
getLLMMeta: () => this.getLLMMeta()
|
|
6200
6304
|
},
|
|
6201
6305
|
(hookName, hookFn, options) => executeHook(this, hookFn, { hookName, ...options }),
|
|
6202
6306
|
(call, input, context, step, callIndex) => this.toolExecutor.execute(call, input, context, step, callIndex),
|
|
@@ -6399,6 +6503,7 @@ var AgentBase = class {
|
|
|
6399
6503
|
...typeof this.llm?.modelName === "string" ? { modelName: this.llm.modelName } : {},
|
|
6400
6504
|
...typeof this._llmMeta.presetName === "string" && this._llmMeta.presetName ? { presetName: this._llmMeta.presetName } : {},
|
|
6401
6505
|
...this._llmMeta.thinkingEffort != null ? { thinkingEffort: this._llmMeta.thinkingEffort } : {},
|
|
6506
|
+
...typeof this._llmMeta.provider === "string" && this._llmMeta.provider ? { provider: this._llmMeta.provider } : {},
|
|
6402
6507
|
...typeof this._llmMeta.contextLength === "number" && this._llmMeta.contextLength > 0 ? { contextLength: this._llmMeta.contextLength } : {},
|
|
6403
6508
|
...typeof this._llmMeta.compressRatio === "number" && this._llmMeta.compressRatio > 0 ? { compressRatio: this._llmMeta.compressRatio } : {}
|
|
6404
6509
|
};
|
|
@@ -6569,4 +6674,4 @@ export {
|
|
|
6569
6674
|
HooksRegistry,
|
|
6570
6675
|
AgentBase
|
|
6571
6676
|
};
|
|
6572
|
-
//# sourceMappingURL=chunk-
|
|
6677
|
+
//# sourceMappingURL=chunk-TKVOWEJ2.js.map
|