@sepveneto/dnde-agent 0.2.0-beta.9 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,17 +1,20 @@
1
1
  # `@sepveneto/dnde-agent`
2
2
 
3
- 装修助手以一个独立自定义元素接入编辑器。Agent 实现来自
4
- `llm-agent`:Agents SDK 负责 Agent Loop、工具调用、流式输出和会话历史;
5
- 本包只保留 Web Component 外壳、宿主 API 协议和业务工具适配。
3
+ `@sepveneto/dnde-agent` 提供 Web Component 外壳和最小 Agent Runtime。运行时本身只有
4
+ 一个主 Agent;主 Agent 不持有任何 function tool,只通过 handoff 调度宿主提供的子
5
+ Agent。
6
+
7
+ 本包同时公开版本化的编辑器 DSL,以及给宿主子 Agent 使用的 DSL 工具。具体业务角色、
8
+ 业务工具和编辑策略均由宿主定义。
6
9
 
7
10
  ## 接入
8
11
 
9
- 宿主提供编辑器 API、业务工具和运行时上下文:
12
+ 宿主提供模型配置、编辑器 DSL API、子 Agent 和运行上下文:
10
13
 
11
14
  ```vue
12
15
  <dnd-agent
13
16
  :api="agentApi"
14
- :tools="businessTools"
17
+ :sub-agents="subAgents"
15
18
  :context="getContext"
16
19
  ></dnd-agent>
17
20
  ```
@@ -31,112 +34,186 @@ register({
31
34
  })
32
35
  ```
33
36
 
37
+ 编辑器侧通过 `<mpd-editor :agent-sub-agents="subAgents">` 注入子 Agent。
38
+
39
+ ## Runtime 边界
40
+
41
+ - 运行时只构造固定主 Agent `Diy Copilot`。
42
+ - 主 Agent 的 `tools` 始终为空,只负责分诊和 handoff。
43
+ - 子 Agent 由宿主定义,或直接注入已经构造好的 Agents SDK `Agent`。
44
+ - 子 Agent 负责实际分析、业务查询和页面编辑。
45
+ - 未提供匹配子 Agent 时,主 Agent 会说明当前能力不足,不会自行读取或修改页面。
46
+
47
+ 这种边界可确保业务权限、工具实现和具体编辑策略不进入 Agent Runtime。
48
+
34
49
  ## 宿主 API
35
50
 
51
+ 编辑器向 Runtime 提供查询、校验和应用 DSL 所需的能力;宿主需要将导出的 DSL 工具
52
+ 显式加入具体子 Agent 的 `tools`:
53
+
36
54
  ```ts
37
- interface AgentHostApi {
38
- model?: {
39
- baseURL?: string
40
- model?: string
41
- apiMode?: 'responses' | 'chat_completions'
42
- }
43
- editor?: {
44
- getCurrentEditor: () => unknown | Promise<unknown>
45
- getWidgets: () => unknown | Promise<unknown>
46
- operateEditor: (operation: AgentEditorOperation) => unknown | Promise<unknown>
47
- }
55
+ import type { AgentHostApi } from '@sepveneto/dnde-agent'
56
+
57
+ const agentApi: AgentHostApi = {
58
+ model: {
59
+ baseURL: 'http://localhost:4000/v1',
60
+ model: 'qwen3.6-plus',
61
+ apiMode: 'responses',
62
+ },
63
+ editor: {
64
+ getCurrentEditor: () => editor.getAgentEditor(),
65
+ getWidgets: () => editor.getCapabilities(),
66
+ validateDsl: dsl => editor.validateDsl(dsl),
67
+ applyDsl: dsl => editor.applyDsl(dsl),
68
+ undo: revision => editor.undo(revision),
69
+ },
48
70
  }
49
71
  ```
50
72
 
51
- 默认带三个基于编辑器 API 的工具:`get_current_editor`、`get_widgets`、
52
- `editor_operator`。写操作和历史不可逆操作应先取得用户确认。
73
+ `<mpd-editor>` 会自动注入这组编辑器 API,业务宿主通常只需配置 `model`。
74
+
75
+ ## 宿主子 Agent
76
+
77
+ 宿主直接使用 OpenAI Agents SDK 创建子 Agent,并将 `Agent[]` 传给编辑器:
78
+
79
+ ```ts
80
+ import {
81
+ Agent,
82
+ createEditorDslTools,
83
+ type AgentSubAgent,
84
+ } from '@sepveneto/dnde-agent'
85
+
86
+ const editorTools = createEditorDslTools(agentApi)
87
+
88
+ const pageAgent = new Agent({
89
+ name: 'Page Agent',
90
+ handoffDescription: '处理页面布局、组件编辑和业务数据查询。',
91
+ model: 'qwen3.6-plus',
92
+ instructions: [
93
+ '先调用 get_current_editor 和 get_widgets。',
94
+ '页面修改必须通过 apply_editor_dsl 提交完整、版本化的 DSL。',
95
+ '完成后直接结束并返回处理结果。',
96
+ ].join('\n'),
97
+ tools: [
98
+ ...editorTools,
99
+ queryProduct,
100
+ queryCoupon,
101
+ ],
102
+ })
103
+
104
+ const subAgents: AgentSubAgent[] = [pageAgent]
105
+ ```
106
+
107
+ `Agent` 会原样成为主 Agent 的 handoff 值,模型、工具、结构化输出等配置都由宿主
108
+ 决定。运行时不会重复创建 Agent、包装 handoff 或适配普通工具定义,也不会修改子
109
+ Agent 的 `handoffs`。
53
110
 
54
- ### 初始化与增量修改
111
+ 只读子 Agent 不添加 `editorTools` 即可,不会自动获得文档修改能力。结构化输出等其它
112
+ SDK 能力直接在创建 Agent 时配置 `outputType`。
55
113
 
56
- Agent 会先读取 `get_current_editor` 的快照,再结合用户当前消息、聊天记录和
57
- 附件内容判断工作模式:
114
+ ## 编辑器 DSL
58
115
 
59
- - **初始化**:用户要求按清单、文档或整页需求从零创建页面,或明确要求重建;
60
- - **增量修改**:用户要求在现有页面上新增、编辑、删除或移动局部内容。
116
+ Agent 不提供节点级增删改工具,而是将完整 DSL 作为一个受校验、可批量执行的操作批次:
61
117
 
62
- 页面已有内容、但请求又像是一份完整清单时,Agent 会先向用户确认是否覆盖,
63
- 不会自行清空页面。初始化和增量修改都可以通过同一个 `editor_operator` 写入,
64
- 统一按普通编辑流程依次调用组件查询、业务工具和 `editor_operator`。初始化不会
65
- 使用单独的子 Agent 或专用初始化工具。
118
+ ```ts
119
+ interface EditorDsl {
120
+ dslVersion: '1.1'
121
+ baseRevision: number
122
+ scope: {
123
+ mode: 'style_only' | 'content_only' | 'layout_only' | 'any'
124
+ targets: string[]
125
+ }
126
+ operations: Array<
127
+ | {
128
+ op: 'add'
129
+ key: string
130
+ widget: string
131
+ placement?: {
132
+ afterUuid?: string
133
+ beforeUuid?: string
134
+ afterKey?: string
135
+ beforeKey?: string
136
+ group?: string
137
+ }
138
+ props?: Record<string, unknown>
139
+ style?: Record<string, unknown>
140
+ custom?: DslCustomOperation[]
141
+ }
142
+ | {
143
+ op: 'update'
144
+ uuid: string
145
+ props?: Record<string, unknown>
146
+ style?: Record<string, unknown>
147
+ custom?: DslCustomOperation[]
148
+ }
149
+ | {
150
+ op: 'delete'
151
+ uuid: string
152
+ confirmToken?: string
153
+ }
154
+ >
155
+ }
156
+ ```
66
157
 
67
- ### 图片参考
158
+ `baseRevision` 必须来自 `get_current_editor`。宿主在应用前校验 revision、组件能力、
159
+ 字段作用域和删除确认令牌;整批操作由编辑器原子执行。
68
160
 
69
- 输入框支持多选和分批追加附件,包括 `.docx` 文档以及 JPG、PNG、WebP、BMP、
70
- TIFF、HEIC 图片;一次最多 6 个附件、总计不超过 20 MB。例如可以同时上传一份
71
- 装修文档和一张参考图。文档会逐个转换为文本上下文,所有图片会作为多模态内容
72
- 一起发送。图片上传后显示缩略图,Agent 会按以下顺序执行:
161
+ ### 公开 DSL 工具
73
162
 
74
- 1. 解析图片的布局、层级、间距、比例、配色和组件关系,确定需要的组件角色及数量;
75
- 2. 查询可用组件,结合组件功能描述和清单配置需求,先规划每个组件承载的内容;
76
- 3. 整页组件和内容计划完成后,再查询业务数据、准备填充内容并调用 `editor_operator`。
163
+ `createEditorDslTools(api)` 是可选导出,不会由 Runtime 自动注册。将返回值展开到某个
164
+ 子 Agent 自己的 `tools` 中后,该子 Agent 才会获得以下工具:
77
165
 
78
- 图片中的业务数据不会被当作真实接口数据,商品等内容仍须通过宿主业务工具准备。
166
+ | 工具 | 作用 |
167
+ | --- | --- |
168
+ | `get_current_editor` | 读取文档、选中节点和 revision |
169
+ | `get_widgets` | 读取组件能力目录 |
170
+ | `validate_editor_dsl` | 校验 DSL,不修改文档 |
171
+ | `apply_editor_dsl` | 校验后一次性应用 DSL |
172
+ | `undo_editor_dsl` | 按 revision 撤销最近一次 DSL 操作 |
79
173
 
80
- 本地图片遵循千问 OpenAI 兼容接口的多模态格式:BFF 将图片转换为
81
- `data:image/<type>;base64,...`,与文本一起放入同一条用户消息。会话历史保存
82
- 图片引用而不是 Base64,避免撑满浏览器 `localStorage`。例如把 `qwen3.6-plus`
83
- 作为模型时,可以直接使用“参考这张图的布局设计首页”这类指令。包含图片的
84
- Qwen 对话会使用官方文档中的 Chat Completions 兼容格式;纯文本对话继续遵循
85
- 宿主配置的 API 模式。
174
+ 也可以通过 `CreateEditorDslToolsOptions` 只注册需要的工具。不需要编辑器能力时,
175
+ 直接省略 `createEditorDslTools(api)`。
86
176
 
87
- 图片输入格式参考千问官方文档:
88
- <https://help.aliyun.com/zh/model-studio/vision>。
177
+ 所有 DSL 类型,包括 `EditorDsl`、`DslOperation`、`DslValidationResult`,均从
178
+ `@sepveneto/dnde-agent` 导出。
89
179
 
90
- ### 添加组件时的业务数据
180
+ ## 业务工具
91
181
 
92
- `items` 通常是由业务 ID 查询并组装的复杂数据,不能交给模型生成。业务工具
93
- 应把组装结果写入当前 Agent Run 的 Context,`editor_operator` 会在 `add`
94
- 时自动注入,并在操作成功后一次性消费:
182
+ 业务工具属于宿主,由宿主挂到具体子 Agent 上。工具可以把需要跨工具共享的数据写入
183
+ 当前 Run 的 Context:
95
184
 
96
185
  ```ts
97
- const queryProduct = defineAgentTool({
186
+ import { tool } from '@sepveneto/dnde-agent'
187
+
188
+ const queryProduct = tool({
98
189
  name: 'query_product',
99
190
  description: '查询商品并准备添加到编辑器。',
100
- parameters: z.object({ keyword: z.string() }),
101
- async execute({ keyword }, toolContext) {
191
+ parameters: {
192
+ type: 'object',
193
+ properties: {
194
+ keyword: { type: 'string' },
195
+ },
196
+ required: ['keyword'],
197
+ additionalProperties: false,
198
+ },
199
+ strict: false,
200
+ async execute(input, runContext) {
201
+ const { keyword } = input as { keyword: string }
102
202
  const product = await api.product.search(keyword)
103
- const items = await api.product.buildEditorItems([product.id])
104
-
105
- // 只返回模型需要做决策的轻量信息。
106
- toolContext.context.pendingItems = items
203
+ runContext.context.product = product
107
204
  return { id: product.id, name: product.name }
108
205
  },
109
206
  })
110
207
  ```
111
208
 
112
- 模型随后只调用
113
- `editor_operator({ action: 'add', widget: 'product', target: '页面节点 UUID' })`。
114
- 工具参数不包含 `items`;即使模型额外传入也会被移除。每次 Agent Run 都会
115
- 创建独立的 Context,失败时保留待添加数据以支持同一轮重试,成功后清空。
116
- 同一轮模型回复中的 function tools 会按调用顺序串行执行,保证前序工具写入的
117
- Context 在后续工具(例如 `editor_operator`)执行时已经可见。
118
-
119
- ## 业务工具
120
-
121
- ```ts
122
- import { defineAgentTool } from '@sepveneto/dnde-agent'
123
- import { z } from 'zod'
124
-
125
- const queryOrder = defineAgentTool({
126
- name: 'query_order',
127
- description: '查询订单状态',
128
- parameters: z.object({ id: z.string() }),
129
- async execute({ id }, context) {
130
- return fetch(`/api/orders/${id}`).then(response => response.json())
131
- },
132
- })
133
- ```
209
+ 宿主初始 Context 会复制到当前 Run。模型请求走宿主代理或 BFF,浏览器侧不保存真实
210
+ 密钥。
134
211
 
135
- 工具的 `execute` 在浏览器内运行,并可读取宿主通过 `context` 注入的只读上下文。
136
- 模型请求走兼容 OpenAI 的宿主代理/BFF,浏览器侧不保存真实密钥。
212
+ ## 附件与会话
137
213
 
138
- ## UI
214
+ 输入框支持多选附件,包括 `.docx`、JPG、PNG、WebP、BMP、TIFF 和 HEIC;一次最多
215
+ 6 个附件、总计不超过 20 MB。文档会转换为文本上下文,图片以千问 OpenAI 兼容的多模态
216
+ 格式发送。会话历史保存图片引用而不是 Base64。
139
217
 
140
- UI 保留原装修助手的悬浮按钮交互。展开后使用 `llm-agent` 的聊天视图:
141
- 空状态示例、图片/文档附件、Markdown、reasoning、工具调用、数据事件、流式文本、停止生成和历史记录,
142
- 全部位于 `dnd-agent` 的 Shadow DOM 内。
218
+ 聊天 UI、流式输出、reasoning、工具调用、停止生成和历史记录全部位于
219
+ `<dnd-agent>` 的 Shadow DOM 内。
@@ -1,7 +1,7 @@
1
1
  var V = Object.defineProperty;
2
2
  var Q = (t, s, e) => s in t ? V(t, s, { enumerable: !0, configurable: !0, writable: !0, value: e }) : t[s] = e;
3
3
  var a = (t, s, e) => Q(t, typeof s != "symbol" ? s + "" : s, e);
4
- import { C as E, U as x, r as X, t as I, o as v, s as y, a as Z, w as A, i as q, e as ee, A as te, n as k, b as R, p as C, c as se, S as ie, d as ne, M as re, f as ae } from "./index-DiCtdOy1.js";
4
+ import { C as E, U as x, r as X, t as I, o as v, s as y, a as Z, w as A, i as q, e as ee, A as te, n as k, b as R, p as C, c as se, S as ie, d as ne, M as re, f as ae } from "./index-DGG6v34-.js";
5
5
  function oe(t) {
6
6
  return t ? !(typeof t == "object" && t && "constructor" in t && typeof t.constructor == "function" ? t.constructor.name : "").includes("ChatCompletions") : !0;
7
7
  }
@@ -1,2 +1,13 @@
1
- export declare function buildAgentInstructions(): string;
1
+ export declare const MAIN_AGENT_NAME = "Diy Copilot";
2
+ export interface MainAgentInfo {
3
+ name: string;
4
+ description: string;
5
+ }
6
+ /**
7
+ * The main agent is a dispatcher only. It owns no function tools and cannot
8
+ * inspect or mutate the editor directly.
9
+ */
10
+ export declare function buildMainAgentInstructions(options?: {
11
+ subAgents?: MainAgentInfo[];
12
+ }): string;
2
13
  //# sourceMappingURL=agentInstructions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agentInstructions.d.ts","sourceRoot":"","sources":["../src/agentInstructions.ts"],"names":[],"mappings":"AAAA,wBAAgB,sBAAsB,WAmBrC"}
1
+ {"version":3,"file":"agentInstructions.d.ts","sourceRoot":"","sources":["../src/agentInstructions.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,eAAe,gBAAgB,CAAA;AAE5C,MAAM,WAAW,aAAa;IAC5B,IAAI,EAAE,MAAM,CAAA;IACZ,WAAW,EAAE,MAAM,CAAA;CACpB;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,GAAE;IACP,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;CACvB,UAuBP"}
package/dist/agents.d.ts CHANGED
@@ -1,20 +1,19 @@
1
- import type { AgentToolDefinition } from './tool';
2
- import type { AgentHostApi } from './types';
3
1
  import { Agent } from '@openai/agents';
4
2
  export declare const DEFAULT_AGENT_MODEL = "qwen3.6-flash";
5
3
  export declare const DEFAULT_AGENT_BFF_URL = "http://localhost:4000/v1";
4
+ /** A host-created OpenAI Agents SDK Agent. */
5
+ export type AgentSubAgent = Agent<any, any>;
6
6
  export interface CreateAgentsOptions {
7
7
  model?: string;
8
- api: AgentHostApi;
9
- tools?: AgentToolDefinition[];
10
- getContext?: () => Record<string, unknown>;
8
+ subAgents?: AgentSubAgent[];
11
9
  }
12
10
  /**
13
- * llm-agent 的 Agent 定义已迁移到这里。业务工具通过 options.tools 注入,
14
- * 编辑器能力通过 options.api 注入,Agent 本身不持有宿主状态。
11
+ * Build the runtime topology. The main agent is always a dispatcher with no
12
+ * function tools; only host-provided sub-agents can perform work.
15
13
  */
16
14
  export declare function createAgents(options: CreateAgentsOptions): {
17
- agent: Agent<any, "text">;
18
- agents: Agent<any, "text">[];
15
+ agent: Agent<unknown, "text">;
16
+ subAgents: AgentSubAgent[];
17
+ agents: (AgentSubAgent | Agent<unknown, "text">)[];
19
18
  };
20
19
  //# sourceMappingURL=agents.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAA;AACjD,OAAO,KAAK,EAAwB,YAAY,EAAsB,MAAM,SAAS,CAAA;AACrF,OAAO,EAAE,KAAK,EAAQ,MAAM,gBAAgB,CAAA;AAQ5C,eAAO,MAAM,mBAAmB,kBAAkB,CAAA;AAClD,eAAO,MAAM,qBAAqB,6BAA6B,CAAA;AAE/D,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,GAAG,EAAE,YAAY,CAAA;IACjB,KAAK,CAAC,EAAE,mBAAmB,EAAE,CAAA;IAC7B,UAAU,CAAC,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAC3C;AAuLD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB;;;EAyBxD"}
1
+ {"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../src/agents.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,gBAAgB,CAAA;AAGtC,eAAO,MAAM,mBAAmB,kBAAkB,CAAA;AAClD,eAAO,MAAM,qBAAqB,6BAA6B,CAAA;AAE/D,8CAA8C;AAC9C,MAAM,MAAM,aAAa,GAAG,KAAK,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAE3C,MAAM,WAAW,mBAAmB;IAClC,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,SAAS,CAAC,EAAE,aAAa,EAAE,CAAA;CAC5B;AAmBD;;;GAGG;AACH,wBAAgB,YAAY,CAAC,OAAO,EAAE,mBAAmB;;;;EA+BxD"}
@@ -0,0 +1,25 @@
1
+ import type { FunctionTool } from '@openai/agents';
2
+ import type { AgentHostApi } from './types';
3
+ export declare const GET_CURRENT_EDITOR_TOOL_NAME = "get_current_editor";
4
+ export declare const GET_WIDGETS_TOOL_NAME = "get_widgets";
5
+ export declare const VALIDATE_EDITOR_DSL_TOOL_NAME = "validate_editor_dsl";
6
+ export declare const APPLY_EDITOR_DSL_TOOL_NAME = "apply_editor_dsl";
7
+ export declare const UNDO_EDITOR_DSL_TOOL_NAME = "undo_editor_dsl";
8
+ export interface CreateEditorDslToolsOptions {
9
+ /** Include the current editor snapshot query. Defaults to true. */
10
+ currentEditor?: boolean;
11
+ /** Include the widget capability catalog query. Defaults to true. */
12
+ widgets?: boolean;
13
+ /** Include deterministic DSL validation. Defaults to true. */
14
+ validate?: boolean;
15
+ /** Include revision-bound undo. Defaults to true. */
16
+ undo?: boolean;
17
+ }
18
+ /**
19
+ * Build the complete model-facing editor contract. These tools are intended
20
+ * for host-owned sub-agents; the main dispatcher never receives them.
21
+ */
22
+ export declare function createEditorDslTools(api: AgentHostApi, options?: CreateEditorDslToolsOptions): FunctionTool<any, any, any>[];
23
+ /** Convenience alias for hosts that refer to this contract as editor tools. */
24
+ export declare const createEditorTools: typeof createEditorDslTools;
25
+ //# sourceMappingURL=editorDsl.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editorDsl.d.ts","sourceRoot":"","sources":["../src/editorDsl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAc,MAAM,gBAAgB,CAAA;AAC9D,OAAO,KAAK,EAEV,YAAY,EAEb,MAAM,SAAS,CAAA;AAGhB,eAAO,MAAM,4BAA4B,uBAAuB,CAAA;AAChE,eAAO,MAAM,qBAAqB,gBAAgB,CAAA;AAClD,eAAO,MAAM,6BAA6B,wBAAwB,CAAA;AAClE,eAAO,MAAM,0BAA0B,qBAAqB,CAAA;AAC5D,eAAO,MAAM,yBAAyB,oBAAoB,CAAA;AAsH1D,MAAM,WAAW,2BAA2B;IAC1C,mEAAmE;IACnE,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,qEAAqE;IACrE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,8DAA8D;IAC9D,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,qDAAqD;IACrD,IAAI,CAAC,EAAE,OAAO,CAAA;CACf;AAwCD;;;GAGG;AACH,wBAAgB,oBAAoB,CAClC,GAAG,EAAE,YAAY,EACjB,OAAO,GAAE,2BAAgC,GACxC,YAAY,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,EAAE,CAgG/B;AAED,+EAA+E;AAC/E,eAAO,MAAM,iBAAiB,6BAAuB,CAAA"}