@downcity/agent 1.1.101 → 1.1.105

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (125) hide show
  1. package/bin/agent/local/Agent.d.ts +2 -2
  2. package/bin/agent/local/Agent.d.ts.map +1 -1
  3. package/bin/agent/local/Agent.js.map +1 -1
  4. package/bin/agent/local/AgentPluginFactory.d.ts +1 -6
  5. package/bin/agent/local/AgentPluginFactory.d.ts.map +1 -1
  6. package/bin/agent/local/AgentPluginFactory.js +1 -19
  7. package/bin/agent/local/AgentPluginFactory.js.map +1 -1
  8. package/bin/agent/local/AgentRuntimeFactory.d.ts +2 -2
  9. package/bin/agent/local/AgentRuntimeFactory.d.ts.map +1 -1
  10. package/bin/agent/local/services/AgentAssemblyService.d.ts +4 -4
  11. package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -1
  12. package/bin/agent/local/services/AgentAssemblyService.js +5 -6
  13. package/bin/agent/local/services/AgentAssemblyService.js.map +1 -1
  14. package/bin/config/AgentInitializer.d.ts.map +1 -1
  15. package/bin/config/AgentInitializer.js +6 -12
  16. package/bin/config/AgentInitializer.js.map +1 -1
  17. package/bin/config/DowncitySchema.d.ts.map +1 -1
  18. package/bin/config/DowncitySchema.js +0 -77
  19. package/bin/config/DowncitySchema.js.map +1 -1
  20. package/bin/config/Paths.d.ts +1 -1
  21. package/bin/config/Paths.js +1 -1
  22. package/bin/executor/composer/system/default/assets/core.prompt.d.ts +1 -1
  23. package/bin/executor/composer/system/default/assets/core.prompt.d.ts.map +1 -1
  24. package/bin/executor/composer/system/default/assets/core.prompt.js +1 -1
  25. package/bin/executor/composer/system/default/assets/core.prompt.js.map +1 -1
  26. package/bin/executor/composer/system/default/assets/plugin.prompt.d.ts +1 -1
  27. package/bin/executor/composer/system/default/assets/plugin.prompt.d.ts.map +1 -1
  28. package/bin/executor/composer/system/default/assets/plugin.prompt.js +1 -1
  29. package/bin/executor/composer/system/default/assets/plugin.prompt.js.map +1 -1
  30. package/bin/executor/composer/system/default/assets/task.prompt.d.ts +1 -1
  31. package/bin/executor/composer/system/default/assets/task.prompt.d.ts.map +1 -1
  32. package/bin/executor/composer/system/default/assets/task.prompt.js +1 -1
  33. package/bin/executor/composer/system/default/assets/task.prompt.js.map +1 -1
  34. package/bin/executor/core-engine/CoreEngineMessageState.d.ts +8 -0
  35. package/bin/executor/core-engine/CoreEngineMessageState.d.ts.map +1 -1
  36. package/bin/executor/core-engine/CoreEngineMessageState.js +9 -3
  37. package/bin/executor/core-engine/CoreEngineMessageState.js.map +1 -1
  38. package/bin/executor/core-engine/CoreEngineRunner.d.ts.map +1 -1
  39. package/bin/executor/core-engine/CoreEngineRunner.js +1 -0
  40. package/bin/executor/core-engine/CoreEngineRunner.js.map +1 -1
  41. package/bin/executor/messages/AssistantFileResource.d.ts +3 -2
  42. package/bin/executor/messages/AssistantFileResource.d.ts.map +1 -1
  43. package/bin/executor/messages/AssistantFileResource.js +88 -20
  44. package/bin/executor/messages/AssistantFileResource.js.map +1 -1
  45. package/bin/executor/messages/SessionAttachmentMapper.d.ts +4 -3
  46. package/bin/executor/messages/SessionAttachmentMapper.d.ts.map +1 -1
  47. package/bin/executor/messages/SessionAttachmentMapper.js +24 -7
  48. package/bin/executor/messages/SessionAttachmentMapper.js.map +1 -1
  49. package/bin/executor/messages/SessionMessageCodec.d.ts +1 -1
  50. package/bin/executor/messages/SessionMessageCodec.d.ts.map +1 -1
  51. package/bin/executor/messages/SessionMessageCodec.js +3 -3
  52. package/bin/executor/messages/SessionMessageCodec.js.map +1 -1
  53. package/bin/executor/tools/plugin/PluginToolBridge.d.ts +3 -3
  54. package/bin/executor/tools/plugin/PluginToolBridge.d.ts.map +1 -1
  55. package/bin/executor/tools/plugin/PluginToolBridge.js +1 -1
  56. package/bin/executor/tools/plugin/PluginToolBridge.js.map +1 -1
  57. package/bin/index.d.ts +1 -3
  58. package/bin/index.d.ts.map +1 -1
  59. package/bin/index.js +0 -1
  60. package/bin/index.js.map +1 -1
  61. package/bin/plugin/core/BasePlugin.d.ts +0 -14
  62. package/bin/plugin/core/BasePlugin.d.ts.map +1 -1
  63. package/bin/plugin/core/BasePlugin.js +0 -22
  64. package/bin/plugin/core/BasePlugin.js.map +1 -1
  65. package/bin/plugin/core/Manager.d.ts +1 -1
  66. package/bin/plugin/core/Manager.d.ts.map +1 -1
  67. package/bin/plugin/core/Manager.js +1 -1
  68. package/bin/plugin/core/Manager.js.map +1 -1
  69. package/bin/plugin/core/PluginRegistry.d.ts +2 -2
  70. package/bin/plugin/core/PluginRegistry.d.ts.map +1 -1
  71. package/bin/plugin/core/PluginRegistry.js.map +1 -1
  72. package/bin/plugin/core/PluginStateController.d.ts +1 -9
  73. package/bin/plugin/core/PluginStateController.d.ts.map +1 -1
  74. package/bin/plugin/core/PluginStateController.js +0 -12
  75. package/bin/plugin/core/PluginStateController.js.map +1 -1
  76. package/bin/plugin/types/Plugin.d.ts +1 -1
  77. package/bin/plugin/types/Plugin.d.ts.map +1 -1
  78. package/bin/types/config/DowncityConfig.d.ts +2 -23
  79. package/bin/types/config/DowncityConfig.d.ts.map +1 -1
  80. package/bin/types/plugin/PluginRuntime.d.ts +2 -2
  81. package/bin/types/plugin/PluginRuntime.d.ts.map +1 -1
  82. package/bin/types/runtime/agent/AgentContext.d.ts +2 -2
  83. package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
  84. package/package.json +2 -2
  85. package/scripts/assistant-file-resource.test.mjs +107 -18
  86. package/scripts/image-plugin-job.test.mjs +21 -9
  87. package/src/agent/local/Agent.ts +2 -2
  88. package/src/agent/local/AgentPluginFactory.ts +1 -33
  89. package/src/agent/local/AgentRuntimeFactory.ts +2 -2
  90. package/src/agent/local/services/AgentAssemblyService.ts +6 -9
  91. package/src/config/AgentInitializer.ts +6 -12
  92. package/src/config/DowncitySchema.ts +0 -77
  93. package/src/config/Paths.ts +1 -1
  94. package/src/executor/composer/system/default/assets/core.prompt.ts +1 -1
  95. package/src/executor/composer/system/default/assets/core.prompt.ts.txt +3 -3
  96. package/src/executor/composer/system/default/assets/plugin.prompt.ts +1 -1
  97. package/src/executor/composer/system/default/assets/plugin.prompt.ts.txt +13 -23
  98. package/src/executor/composer/system/default/assets/task.prompt.ts +1 -1
  99. package/src/executor/composer/system/default/assets/task.prompt.ts.txt +1 -1
  100. package/src/executor/core-engine/CoreEngineMessageState.ts +26 -2
  101. package/src/executor/core-engine/CoreEngineRunner.ts +1 -0
  102. package/src/executor/messages/AssistantFileResource.ts +111 -22
  103. package/src/executor/messages/SessionAttachmentMapper.ts +29 -6
  104. package/src/executor/messages/SessionMessageCodec.ts +6 -2
  105. package/src/executor/tools/plugin/PluginToolBridge.ts +5 -5
  106. package/src/index.ts +1 -11
  107. package/src/plugin/core/BasePlugin.ts +0 -27
  108. package/src/plugin/core/Manager.ts +0 -2
  109. package/src/plugin/core/PluginRegistry.ts +2 -1
  110. package/src/plugin/core/PluginStateController.ts +0 -15
  111. package/src/plugin/types/Plugin.ts +1 -1
  112. package/src/types/config/DowncityConfig.ts +1 -24
  113. package/src/types/plugin/PluginRuntime.ts +2 -2
  114. package/src/types/runtime/agent/AgentContext.ts +2 -2
  115. package/tsconfig.tsbuildinfo +1 -1
  116. package/bin/plugin/core/ImagePlugin.d.ts +0 -57
  117. package/bin/plugin/core/ImagePlugin.d.ts.map +0 -1
  118. package/bin/plugin/core/ImagePlugin.js +0 -112
  119. package/bin/plugin/core/ImagePlugin.js.map +0 -1
  120. package/bin/types/plugin/ImagePlugin.d.ts +0 -94
  121. package/bin/types/plugin/ImagePlugin.d.ts.map +0 -1
  122. package/bin/types/plugin/ImagePlugin.js +0 -10
  123. package/bin/types/plugin/ImagePlugin.js.map +0 -1
  124. package/src/plugin/core/ImagePlugin.ts +0 -132
  125. package/src/types/plugin/ImagePlugin.ts +0 -103
@@ -33,6 +33,11 @@ export class CoreEngineMessageState {
33
33
  */
34
34
  private readonly tools: Record<string, Tool>;
35
35
 
36
+ /**
37
+ * 当前项目根目录,用于解析历史中的 `resources://` file part。
38
+ */
39
+ private readonly projectRoot?: string;
40
+
36
41
  private constructor(params: {
37
42
  /**
38
43
  * 当前运行时 session 语义消息。
@@ -46,10 +51,15 @@ export class CoreEngineMessageState {
46
51
  * 当前轮可用工具集合。
47
52
  */
48
53
  tools: Record<string, Tool>;
54
+ /**
55
+ * 当前项目根目录。
56
+ */
57
+ projectRoot?: string;
49
58
  }) {
50
59
  this.sessionMessages = params.sessionMessages;
51
60
  this.currentModelMessages = params.modelMessages;
52
61
  this.tools = params.tools;
62
+ this.projectRoot = params.projectRoot;
53
63
  }
54
64
 
55
65
  /**
@@ -64,14 +74,23 @@ export class CoreEngineMessageState {
64
74
  * 当前轮可用工具集合。
65
75
  */
66
76
  tools: Record<string, Tool>;
77
+ /**
78
+ * 当前项目根目录。
79
+ */
80
+ projectRoot?: string;
67
81
  }): Promise<CoreEngineMessageState> {
68
82
  const sessionMessages = Array.isArray(params.messages)
69
83
  ? [...params.messages]
70
84
  : [];
71
85
  return new CoreEngineMessageState({
72
86
  sessionMessages,
73
- modelMessages: await toModelMessages(sessionMessages, params.tools),
87
+ modelMessages: await toModelMessages(
88
+ sessionMessages,
89
+ params.tools,
90
+ params.projectRoot,
91
+ ),
74
92
  tools: params.tools,
93
+ projectRoot: params.projectRoot,
75
94
  });
76
95
  }
77
96
 
@@ -119,7 +138,11 @@ export class CoreEngineMessageState {
119
138
  messages: SessionMessageV1[],
120
139
  ): Promise<ModelMessage[]> {
121
140
  this.sessionMessages = [...this.sessionMessages, ...messages];
122
- const modelMessages = await toModelMessages(messages, this.tools);
141
+ const modelMessages = await toModelMessages(
142
+ messages,
143
+ this.tools,
144
+ this.projectRoot,
145
+ );
123
146
  if (modelMessages.length > 0) {
124
147
  this.currentModelMessages = [...this.currentModelMessages, ...modelMessages];
125
148
  return modelMessages;
@@ -127,6 +150,7 @@ export class CoreEngineMessageState {
127
150
  this.currentModelMessages = await toModelMessages(
128
151
  this.sessionMessages,
129
152
  this.tools,
153
+ this.projectRoot,
130
154
  );
131
155
  return [];
132
156
  }
@@ -154,6 +154,7 @@ export class CoreEngineRunner {
154
154
  const message_state = await CoreEngineMessageState.create({
155
155
  messages: input.execute_input.messages,
156
156
  tools,
157
+ projectRoot: input.run_context.projectRoot,
157
158
  });
158
159
 
159
160
  const append_merged_user_messages = (messages: SessionMessageV1[]) =>
@@ -3,13 +3,14 @@
3
3
  *
4
4
  * 关键点(中文)
5
5
  * - 只处理运行期产生的 assistant file part,不参与 user 附件注入。
6
- * - 将 `data:*;base64,...` 写入 `.downcity/resources`,历史中只保留 `file://` 绝对 URL。
6
+ * - 将 data URL、远程 URL 与本地文件统一写入 `.downcity/resources`。
7
+ * - 历史中只保留 `resources://<project-relative-path>`,避免暴露本机绝对路径。
7
8
  * - 资源文件按内容 hash 命名,天然去重并避免重复写入大文件。
8
9
  */
9
10
 
10
11
  import crypto from "node:crypto";
11
12
  import path from "node:path";
12
- import { pathToFileURL } from "node:url";
13
+ import { fileURLToPath } from "node:url";
13
14
  import fs from "fs-extra";
14
15
  import type { FileUIPart } from "ai";
15
16
  import { getDowncityResourcesDirPath } from "@/config/Paths.js";
@@ -92,6 +93,23 @@ function extension_from_filename(filename: string | undefined): string {
92
93
  return /^[.][a-z0-9]+$/u.test(ext) ? ext : "";
93
94
  }
94
95
 
96
+ function filename_from_url(raw_url: string): string | undefined {
97
+ try {
98
+ const parsed = new URL(raw_url);
99
+ return path.basename(decodeURIComponent(parsed.pathname)) || undefined;
100
+ } catch {
101
+ return undefined;
102
+ }
103
+ }
104
+
105
+ function to_resources_url(projectRoot: string, filePath: string): string {
106
+ const relative = path
107
+ .relative(projectRoot, filePath)
108
+ .split(path.sep)
109
+ .join("/");
110
+ return `resources://${relative}`;
111
+ }
112
+
95
113
  async function write_resource_file(params: {
96
114
  projectRoot: string;
97
115
  mediaType: string;
@@ -117,8 +135,97 @@ async function write_resource_file(params: {
117
135
  return file_path;
118
136
  }
119
137
 
138
+ async function read_remote_resource(raw_url: string): Promise<{
139
+ mediaType?: string;
140
+ filename?: string;
141
+ bytes: Buffer;
142
+ }> {
143
+ const response = await fetch(raw_url);
144
+ if (!response.ok) {
145
+ throw new Error(`Failed to download assistant file resource: ${response.status}`);
146
+ }
147
+ const array_buffer = await response.arrayBuffer();
148
+ const bytes = Buffer.from(array_buffer);
149
+ if (bytes.length === 0) {
150
+ throw new Error("Downloaded assistant file resource is empty");
151
+ }
152
+ return {
153
+ mediaType: response.headers.get("content-type")?.split(";")[0]?.trim(),
154
+ filename: filename_from_url(raw_url),
155
+ bytes,
156
+ };
157
+ }
158
+
159
+ async function read_local_resource(
160
+ projectRoot: string,
161
+ raw_url: string,
162
+ ): Promise<{
163
+ mediaType?: string;
164
+ filename?: string;
165
+ bytes: Buffer;
166
+ }> {
167
+ const raw = String(raw_url || "").trim();
168
+ const file_path = raw.startsWith("file://")
169
+ ? fileURLToPath(raw)
170
+ : path.isAbsolute(raw)
171
+ ? raw
172
+ : path.resolve(projectRoot, raw);
173
+ const bytes = await fs.readFile(file_path);
174
+ if (bytes.length === 0) {
175
+ throw new Error(`Assistant file resource is empty: ${file_path}`);
176
+ }
177
+ return {
178
+ filename: path.basename(file_path),
179
+ bytes,
180
+ };
181
+ }
182
+
183
+ async function materialize_file_part(params: {
184
+ projectRoot: string;
185
+ part: FileUIPart;
186
+ }): Promise<FileUIPart> {
187
+ const raw_url = String(params.part.url || "").trim();
188
+ if (!raw_url) {
189
+ throw new Error("Assistant file part url is required");
190
+ }
191
+ if (raw_url.startsWith("resources://")) return params.part;
192
+
193
+ const parsed_data_url = parse_data_url(raw_url);
194
+ const source = parsed_data_url
195
+ ? {
196
+ mediaType: parsed_data_url.media_type,
197
+ filename:
198
+ typeof params.part.filename === "string"
199
+ ? params.part.filename
200
+ : undefined,
201
+ bytes: parsed_data_url.bytes,
202
+ }
203
+ : raw_url.startsWith("http://") || raw_url.startsWith("https://")
204
+ ? await read_remote_resource(raw_url)
205
+ : await read_local_resource(params.projectRoot, raw_url);
206
+
207
+ const media_type =
208
+ String(params.part.mediaType || source.mediaType || "").trim() ||
209
+ "application/octet-stream";
210
+ const file_path = await write_resource_file({
211
+ projectRoot: params.projectRoot,
212
+ mediaType: media_type,
213
+ filename:
214
+ typeof params.part.filename === "string"
215
+ ? params.part.filename
216
+ : source.filename,
217
+ bytes: source.bytes,
218
+ });
219
+
220
+ return {
221
+ ...params.part,
222
+ mediaType: media_type,
223
+ url: to_resources_url(params.projectRoot, file_path),
224
+ };
225
+ }
226
+
120
227
  /**
121
- * 将 assistant file part 中的 data URL 资源落盘为 `file://` 绝对 URL。
228
+ * 将 assistant file part 中的资源统一落盘为 `resources://` 相对 URL。
122
229
  */
123
230
  export async function materializeAssistantFileParts(
124
231
  params: MaterializeAssistantFilePartsParams,
@@ -130,25 +237,7 @@ export async function materializeAssistantFileParts(
130
237
  const out: FileUIPart[] = [];
131
238
 
132
239
  for (const part of parts) {
133
- const parsed = parse_data_url(String(part.url || ""));
134
- if (!parsed) {
135
- out.push(part);
136
- continue;
137
- }
138
-
139
- const media_type = String(part.mediaType || parsed.media_type || "").trim();
140
- const file_path = await write_resource_file({
141
- projectRoot: project_root,
142
- mediaType: media_type || parsed.media_type,
143
- filename: typeof part.filename === "string" ? part.filename : undefined,
144
- bytes: parsed.bytes,
145
- });
146
-
147
- out.push({
148
- ...part,
149
- mediaType: media_type || parsed.media_type,
150
- url: pathToFileURL(file_path).href,
151
- });
240
+ out.push(await materialize_file_part({ projectRoot: project_root, part }));
152
241
  }
153
242
 
154
243
  return out;
@@ -5,6 +5,7 @@
5
5
  * - 兼容 Telegram / Feishu / TUI 等统一的 `<file>` 协议入口。
6
6
  * - 仅在本轮执行的内存消息上追加 file parts,不修改持久化历史。
7
7
  * - 当前只为图片与 PDF 注入 file part,保持多模态模型可直接消费。
8
+ * - 历史中的 `resources://` 与旧版 `file://` 会在喂给模型前临时 hydrate。
8
9
  */
9
10
 
10
11
  import fs from "fs-extra";
@@ -62,11 +63,32 @@ function buildDataUrl(mediaType: string, buffer: Buffer): string {
62
63
  return `data:${safeType};base64,${base64}`;
63
64
  }
64
65
 
65
- async function hydrateFileUrlPart(part: FileUIPart): Promise<FileUIPart> {
66
+ function resolveResourcesUrlPath(
67
+ projectRoot: string | undefined,
68
+ rawUrl: string,
69
+ ): string | null {
70
+ const prefix = "resources://";
71
+ const raw = String(rawUrl || "").trim();
72
+ if (!raw.startsWith(prefix)) return null;
73
+ const relative = raw.slice(prefix.length).replace(/^\/+/, "");
74
+ if (!relative) return null;
75
+
76
+ const root = path.resolve(String(projectRoot || "").trim() || process.cwd());
77
+ const absPath = path.resolve(root, relative);
78
+ const rel = path.relative(root, absPath);
79
+ if (rel === "" || rel.startsWith("..") || path.isAbsolute(rel)) return null;
80
+ return absPath;
81
+ }
82
+
83
+ async function hydrateFileUrlPart(
84
+ part: FileUIPart,
85
+ projectRoot?: string,
86
+ ): Promise<FileUIPart> {
66
87
  const url = String(part.url || "").trim();
67
- if (!url.startsWith("file://")) return part;
88
+ const resourcesPath = resolveResourcesUrlPath(projectRoot, url);
89
+ if (!url.startsWith("file://") && !resourcesPath) return part;
68
90
  try {
69
- const absPath = fileURLToPath(url);
91
+ const absPath = resourcesPath || fileURLToPath(url);
70
92
  const buffer = await fs.readFile(absPath);
71
93
  const mediaType =
72
94
  String(part.mediaType || "").trim() ||
@@ -83,14 +105,15 @@ async function hydrateFileUrlPart(part: FileUIPart): Promise<FileUIPart> {
83
105
  }
84
106
 
85
107
  /**
86
- * 将历史中的 `file://` file part 临时转换为模型可消费的 data URL。
108
+ * 将历史中的资源 file part 临时转换为模型可消费的 data URL。
87
109
  *
88
110
  * 关键点(中文)
89
111
  * - 该函数只修改本轮内存消息,不回写历史。
90
- * - 持久化层继续保留轻量 `file://` 绝对 URL,避免 JSONL 存储 base64。
112
+ * - 新历史保留 `resources://` 相对 URL,旧历史的 `file://` 仍继续兼容。
91
113
  */
92
114
  export async function hydrateFileUrlPartsForModel(
93
115
  messages: SessionMessageV1[],
116
+ projectRoot?: string,
94
117
  ): Promise<SessionMessageV1[]> {
95
118
  if (!Array.isArray(messages) || messages.length === 0) return messages;
96
119
 
@@ -109,7 +132,7 @@ export async function hydrateFileUrlPartsForModel(
109
132
  nextParts.push(part);
110
133
  continue;
111
134
  }
112
- const nextPart = await hydrateFileUrlPart(part as FileUIPart);
135
+ const nextPart = await hydrateFileUrlPart(part as FileUIPart, projectRoot);
113
136
  if (nextPart !== part) changed = true;
114
137
  nextParts.push(nextPart as SessionMessageV1["parts"][number]);
115
138
  }
@@ -72,6 +72,7 @@ export function pickMergedUserMessages(
72
72
  export async function toModelMessages(
73
73
  messages: SessionMessageV1[],
74
74
  tools: Record<string, Tool>,
75
+ projectRoot?: string,
75
76
  ): Promise<ModelMessage[]> {
76
77
  // 空输入快速返回,避免调用转换器的额外开销。
77
78
  if (!Array.isArray(messages) || messages.length === 0) return [];
@@ -79,8 +80,11 @@ export async function toModelMessages(
79
80
  // 第一步(中文):在 user 消息上注入 file parts(多模态附件)。
80
81
  const enrichedMessages = await injectFilePartsFromAttachments(messages);
81
82
 
82
- // 第二步(中文):把历史里的 file:// 资源在内存中 hydrate 成模型可消费的 data URL。
83
- const hydratedMessages = await hydrateFileUrlPartsForModel(enrichedMessages);
83
+ // 第二步(中文):把历史里的资源 URL 在内存中 hydrate 成模型可消费的 data URL。
84
+ const hydratedMessages = await hydrateFileUrlPartsForModel(
85
+ enrichedMessages,
86
+ projectRoot,
87
+ );
84
88
 
85
89
  // 第三步(中文):转换前先剔除 UI 层 id 字段,仅保留模型需要的数据结构。
86
90
  const input = hydratedMessages.map((message) => {
@@ -2,14 +2,14 @@
2
2
  * Plugin tool 运行时桥接。
3
3
  *
4
4
  * 关键点(中文)
5
- * - tool 层不直接持有 agent 或 plugin registry,只通过装配期注入的 PluginPort 调用 action。
5
+ * - tool 层不直接持有 agent 或 plugin registry,只通过装配期注入的 AgentPlugins 调用 action。
6
6
  * - 如果 action 返回 AI SDK UIMessage,则抽取 file parts 并入最终 assistant 消息。
7
7
  * - 返回给模型的 tool result 只保留短摘要,避免 data URL 等大内容污染上下文。
8
8
  */
9
9
 
10
10
  import type { FileUIPart } from "ai";
11
11
  import type { JsonObject, JsonValue } from "@/types/common/Json.js";
12
- import type { PluginPort } from "@/plugin/types/Plugin.js";
12
+ import type { AgentPlugins } from "@/plugin/types/Plugin.js";
13
13
  import type {
14
14
  PluginCallInput,
15
15
  PluginCallToolResult,
@@ -20,19 +20,19 @@ import {
20
20
  getSessionRunContext,
21
21
  } from "@executor/SessionRunScope.js";
22
22
 
23
- let plugin_tool_runtime: PluginPort | null = null;
23
+ let plugin_tool_runtime: AgentPlugins | null = null;
24
24
 
25
25
  /**
26
26
  * 注入 plugin tool 运行时。
27
27
  */
28
- export function setPluginToolRuntime(next: PluginPort): void {
28
+ export function setPluginToolRuntime(next: AgentPlugins): void {
29
29
  plugin_tool_runtime = next;
30
30
  }
31
31
 
32
32
  /**
33
33
  * 读取已注入的 plugin tool 运行时。
34
34
  */
35
- function require_plugin_tool_runtime(): PluginPort {
35
+ function require_plugin_tool_runtime(): AgentPlugins {
36
36
  if (plugin_tool_runtime) return plugin_tool_runtime;
37
37
  throw new Error(
38
38
  "Plugin tool runtime is not initialized. Ensure agent assembly completed before using plugin_call.",
package/src/index.ts CHANGED
@@ -69,7 +69,6 @@ export type {
69
69
 
70
70
  // Plugin 作者 API
71
71
  export { BasePlugin } from "./plugin/core/BasePlugin.js";
72
- export { ImagePlugin } from "./plugin/core/ImagePlugin.js";
73
72
 
74
73
  // Session 与即时执行集成
75
74
  export { Executor } from "./executor/Executor.js";
@@ -225,15 +224,6 @@ export type {
225
224
  } from "./types/runtime/http/InlineInstant.js";
226
225
 
227
226
  // Plugin 作者与控制面类型
228
- export type {
229
- ImagePluginContent,
230
- ImagePluginFileContent,
231
- ImagePluginInput,
232
- ImagePluginMessage,
233
- ImagePluginOptions,
234
- ImagePluginTextContent,
235
- } from "./types/plugin/ImagePlugin.js";
236
-
237
227
  export type {
238
228
  Plugin,
239
229
  PluginAction,
@@ -249,7 +239,7 @@ export type {
249
239
  PluginHooks,
250
240
  PluginHttpDefinition,
251
241
  PluginPipelineHook,
252
- PluginPort,
242
+ AgentPlugins,
253
243
  PluginResolveHook,
254
244
  PluginHttpRegistration,
255
245
  PluginActionInvokeParams,
@@ -8,7 +8,6 @@
8
8
  */
9
9
 
10
10
  import type { AgentContext } from "@/types/runtime/agent/AgentContext.js";
11
- import type { AgentRuntime } from "@/types/runtime/agent/AgentRuntime.js";
12
11
  import type {
13
12
  Plugin,
14
13
  PluginActions,
@@ -38,15 +37,6 @@ export abstract class BasePlugin implements Plugin {
38
37
  chain: Promise.resolve(),
39
38
  };
40
39
 
41
- /**
42
- * 当前 plugin 所属的 agent 宿主。
43
- */
44
- protected agent: AgentRuntime | null;
45
-
46
- constructor(agent: AgentRuntime | null = null) {
47
- this.agent = agent;
48
- }
49
-
50
40
  /**
51
41
  * 当前 plugin 名称。
52
42
  */
@@ -114,21 +104,4 @@ export abstract class BasePlugin implements Plugin {
114
104
  */
115
105
  system?(context: AgentContext): Promise<string> | string;
116
106
 
117
- /**
118
- * 绑定当前 plugin 所属的 agent 宿主。
119
- */
120
- bindAgent(agent: AgentRuntime | null): this {
121
- this.agent = agent;
122
- return this;
123
- }
124
-
125
- /**
126
- * 读取绑定的 agent 宿主。
127
- */
128
- protected requireAgent(): AgentRuntime {
129
- if (this.agent) return this.agent;
130
- throw new Error(
131
- `Plugin "${this.name}" is not bound to an agent instance.`,
132
- );
133
- }
134
107
  }
@@ -9,8 +9,6 @@ export type {
9
9
  } from "@/plugin/types/Plugin.js";
10
10
  export {
11
11
  controlPluginState,
12
- getPluginRootCommandNames,
13
- getStaticPlugins,
14
12
  isPluginRunning,
15
13
  listPluginStates,
16
14
  startAllPlugins,
@@ -12,6 +12,7 @@ import type { HookRegistry } from "@/plugin/core/HookRegistry.js";
12
12
  import type {
13
13
  Plugin,
14
14
  PluginActionResult,
15
+ AgentPlugins,
15
16
  PluginAvailability,
16
17
  PluginView,
17
18
  } from "@/plugin/types/Plugin.js";
@@ -23,7 +24,7 @@ type ContextResolver = () => AgentContext;
23
24
  /**
24
25
  * PluginRegistry:plugin 注册与调度实现。
25
26
  */
26
- export class PluginRegistry {
27
+ export class PluginRegistry implements AgentPlugins {
27
28
  private readonly contextResolver: ContextResolver;
28
29
 
29
30
  private readonly hookRegistry: HookRegistry;
@@ -5,7 +5,6 @@
5
5
  import type { AgentContext } from "@/types/runtime/agent/AgentContext.js";
6
6
  import type { AgentRuntime } from "@/types/runtime/agent/AgentRuntime.js";
7
7
  import type {
8
- Plugin,
9
8
  PluginLifecycle,
10
9
  PluginState,
11
10
  PluginStateControlAction,
@@ -132,20 +131,6 @@ export function markPluginCommand(
132
131
  record.updatedAt = nowMs();
133
132
  }
134
133
 
135
- /**
136
- * 返回静态主动型 plugin 定义清单。
137
- */
138
- export function getStaticPlugins(): Plugin[] {
139
- return [];
140
- }
141
-
142
- /**
143
- * 返回主动型 plugin 根命令名清单。
144
- */
145
- export function getPluginRootCommandNames(): string[] {
146
- return [];
147
- }
148
-
149
134
  /**
150
135
  * 列出全部主动型 plugin 状态快照。
151
136
  */
@@ -37,7 +37,7 @@ export type {
37
37
  PluginGuardHook,
38
38
  PluginHooks,
39
39
  PluginPipelineHook,
40
- PluginPort,
40
+ AgentPlugins,
41
41
  PluginResolveHook,
42
42
  PluginResolves,
43
43
  PluginView,
@@ -107,24 +107,6 @@ export interface DowncityChatPluginConfig {
107
107
  channels?: DowncityChatPluginChannelsConfig;
108
108
  }
109
109
 
110
- /**
111
- * skill 插件配置。
112
- */
113
- export interface DowncitySkillPluginConfig {
114
- /**
115
- * 当前插件是否启用。
116
- */
117
- enabled?: boolean;
118
- /**
119
- * 技能目录列表。
120
- */
121
- paths?: string[];
122
- /**
123
- * 是否允许读取项目外部技能目录。
124
- */
125
- allowExternalPaths?: boolean;
126
- }
127
-
128
110
  /**
129
111
  * downcity.json 中的插件配置映射。
130
112
  */
@@ -133,17 +115,12 @@ export interface DowncityPluginConfigMap {
133
115
  * chat 插件配置。
134
116
  */
135
117
  chat?: DowncityChatPluginConfig;
136
- /**
137
- * skill 插件配置。
138
- */
139
- skill?: DowncitySkillPluginConfig;
140
118
  /**
141
119
  * 其他插件配置。
142
120
  */
143
121
  [pluginName: string]:
144
122
  | JsonObject
145
123
  | DowncityChatPluginConfig
146
- | DowncitySkillPluginConfig
147
124
  | undefined;
148
125
  }
149
126
 
@@ -171,7 +148,7 @@ export interface DowncityConfig {
171
148
  *
172
149
  * 关键点(中文)
173
150
  * - 所有可配置能力统一收敛到 `plugins`,不再保留独立 `services` 域。
174
- * - plugin 私有配置(例如 `plugins.skill.paths`、`plugins.chat.channels`)也放在这里。
151
+ * - 需要持久化到项目文件的 plugin 配置(例如 `plugins.chat.channels`)放在这里。
175
152
  * - key 为 plugin 名称,value 为对应插件的结构化配置对象。
176
153
  * - 当前阶段允许各 plugin 自定义字段,但必须保持 JSON 可序列化。
177
154
  */
@@ -52,9 +52,9 @@ export interface PluginAvailability {
52
52
  }
53
53
 
54
54
  /**
55
- * Plugin 调用端口。
55
+ * 当前 Agent 可用的 plugin 调用面。
56
56
  */
57
- export interface PluginPort {
57
+ export interface AgentPlugins {
58
58
  /** 列出全部已注册 plugin。 */
59
59
  list(): PluginView[];
60
60
  /** 检查指定 plugin 可用性。 */
@@ -19,7 +19,7 @@ import type {
19
19
  } from "@/types/agent/AgentRuntimeAssembly.js";
20
20
  import type { DowncityConfig } from "@/types/config/DowncityConfig.js";
21
21
  import type { JsonObject, JsonValue } from "@/types/common/Json.js";
22
- import type { PluginPort } from "@/plugin/types/Plugin.js";
22
+ import type { AgentPlugins } from "@/plugin/types/Plugin.js";
23
23
  import type {
24
24
  SessionMetadataV1,
25
25
  SessionMessageV1,
@@ -261,7 +261,7 @@ export interface AgentContext {
261
261
  /**
262
262
  * Plugin 调用入口。
263
263
  */
264
- plugins: PluginPort;
264
+ plugins: AgentPlugins;
265
265
  }
266
266
 
267
267
  /**