@downcity/agent 1.1.195 → 1.1.196
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/bin/agent/local/Agent.d.ts.map +1 -1
- package/bin/agent/local/Agent.js +5 -2
- package/bin/agent/local/Agent.js.map +1 -1
- package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -1
- package/bin/agent/local/services/AgentAssemblyService.js +2 -2
- package/bin/agent/local/services/AgentAssemblyService.js.map +1 -1
- package/bin/agent/local/services/AgentSessionManager.d.ts +5 -0
- package/bin/agent/local/services/AgentSessionManager.d.ts.map +1 -1
- package/bin/agent/local/services/AgentSessionManager.js +3 -0
- package/bin/agent/local/services/AgentSessionManager.js.map +1 -1
- package/bin/executor/tools/plugin/PluginToolBridge.d.ts +4 -8
- package/bin/executor/tools/plugin/PluginToolBridge.d.ts.map +1 -1
- package/bin/executor/tools/plugin/PluginToolBridge.js +6 -21
- package/bin/executor/tools/plugin/PluginToolBridge.js.map +1 -1
- package/bin/executor/tools/plugin/PluginToolDefinition.d.ts +7 -19
- package/bin/executor/tools/plugin/PluginToolDefinition.d.ts.map +1 -1
- package/bin/executor/tools/plugin/PluginToolDefinition.js +29 -18
- package/bin/executor/tools/plugin/PluginToolDefinition.js.map +1 -1
- package/bin/plugin/core/PluginRegistry.d.ts.map +1 -1
- package/bin/plugin/core/PluginRegistry.js +0 -4
- package/bin/plugin/core/PluginRegistry.js.map +1 -1
- package/bin/types/plugin/PluginToolRuntime.d.ts +75 -0
- package/bin/types/plugin/PluginToolRuntime.d.ts.map +1 -0
- package/bin/types/plugin/PluginToolRuntime.js +9 -0
- package/bin/types/plugin/PluginToolRuntime.js.map +1 -0
- package/package.json +1 -1
- package/scripts/agent-ready.test.mjs +168 -0
- package/scripts/plugin-tool-bridge.test.mjs +114 -10
- package/src/agent/local/Agent.ts +5 -2
- package/src/agent/local/services/AgentAssemblyService.ts +2 -3
- package/src/agent/local/services/AgentSessionManager.ts +8 -0
- package/src/executor/tools/plugin/PluginToolBridge.ts +10 -26
- package/src/executor/tools/plugin/PluginToolDefinition.ts +39 -22
- package/src/plugin/core/PluginRegistry.ts +0 -4
- package/src/types/plugin/PluginToolRuntime.ts +86 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Plugin tool runtime 绑定类型。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - plugin_call / plugin_read 必须绑定当前 Agent 自己的 plugin registry。
|
|
6
|
+
* - 这些类型描述 tool 工厂与 bridge 调用时需要的显式 runtime 依赖。
|
|
7
|
+
*/
|
|
8
|
+
import type { Tool } from "ai";
|
|
9
|
+
import type { AgentPlugins } from "../../types/plugin/PluginRuntime.js";
|
|
10
|
+
import type { PluginCallInput, PluginReadInput } from "../../executor/tools/plugin/types/PluginTool.js";
|
|
11
|
+
/**
|
|
12
|
+
* 创建 plugin tools 的参数。
|
|
13
|
+
*/
|
|
14
|
+
export interface CreatePluginToolsOptions {
|
|
15
|
+
/**
|
|
16
|
+
* 当前 Agent 自己的 plugin 调用面。
|
|
17
|
+
*
|
|
18
|
+
* 关键点(中文)
|
|
19
|
+
* - 该引用必须来自当前 Agent 装配出的 plugin registry。
|
|
20
|
+
* - tool 执行时只能通过它访问 plugin,避免跨 Agent 读到其他 registry。
|
|
21
|
+
*/
|
|
22
|
+
plugins: AgentPlugins;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* 当前 Agent 专属 plugin tools。
|
|
26
|
+
*/
|
|
27
|
+
export interface AgentPluginTools {
|
|
28
|
+
/**
|
|
29
|
+
* 读取 plugin metadata 的模型工具。
|
|
30
|
+
*
|
|
31
|
+
* 关键点(中文)
|
|
32
|
+
* - 该 tool 通过闭包绑定当前 Agent 的 `plugins`。
|
|
33
|
+
* - 不读取任何模块级全局 runtime。
|
|
34
|
+
*/
|
|
35
|
+
plugin_read: Tool;
|
|
36
|
+
/**
|
|
37
|
+
* 执行 plugin action 的模型工具。
|
|
38
|
+
*
|
|
39
|
+
* 关键点(中文)
|
|
40
|
+
* - 该 tool 通过闭包绑定当前 Agent 的 `plugins`。
|
|
41
|
+
* - 多 Agent 并发时,每个 Agent 都有自己的 tool 实例。
|
|
42
|
+
*/
|
|
43
|
+
plugin_call: Tool;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* 调用 plugin_call bridge 的参数。
|
|
47
|
+
*/
|
|
48
|
+
export interface InvokePluginCallToolOptions {
|
|
49
|
+
/**
|
|
50
|
+
* 当前 Agent 自己的 plugin 调用面。
|
|
51
|
+
*
|
|
52
|
+
* 关键点(中文)
|
|
53
|
+
* - 由 Agent 级 tool 工厂注入。
|
|
54
|
+
* - 不能在 bridge 内通过全局变量读取。
|
|
55
|
+
*/
|
|
56
|
+
plugins: AgentPlugins;
|
|
57
|
+
/**
|
|
58
|
+
* 模型提交给 plugin_call 的结构化输入。
|
|
59
|
+
*/
|
|
60
|
+
input: PluginCallInput;
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* 调用 plugin_read bridge 的参数。
|
|
64
|
+
*/
|
|
65
|
+
export interface InvokePluginReadToolOptions {
|
|
66
|
+
/**
|
|
67
|
+
* 当前 Agent 自己的 plugin 调用面。
|
|
68
|
+
*/
|
|
69
|
+
plugins: AgentPlugins;
|
|
70
|
+
/**
|
|
71
|
+
* 模型提交给 plugin_read 的结构化输入。
|
|
72
|
+
*/
|
|
73
|
+
input: PluginReadInput;
|
|
74
|
+
}
|
|
75
|
+
//# sourceMappingURL=PluginToolRuntime.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PluginToolRuntime.d.ts","sourceRoot":"","sources":["../../../src/types/plugin/PluginToolRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,iCAAiC,CAAC;AACpE,OAAO,KAAK,EACV,eAAe,EACf,eAAe,EAChB,MAAM,6CAA6C,CAAC;AAErD;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;OAMG;IACH,OAAO,EAAE,YAAY,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;;;;OAMG;IACH,WAAW,EAAE,IAAI,CAAC;IAElB;;;;;;OAMG;IACH,WAAW,EAAE,IAAI,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;;;;;OAMG;IACH,OAAO,EAAE,YAAY,CAAC;IAEtB;;OAEG;IACH,KAAK,EAAE,eAAe,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,2BAA2B;IAC1C;;OAEG;IACH,OAAO,EAAE,YAAY,CAAC;IAEtB;;OAEG;IACH,KAAK,EAAE,eAAe,CAAC;CACxB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"PluginToolRuntime.js","sourceRoot":"","sources":["../../../src/types/plugin/PluginToolRuntime.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 验证 session.prompt 会等待当前 Agent 后台能力 ready。
|
|
3
|
+
*
|
|
4
|
+
* 关键点(中文)
|
|
5
|
+
* - 这里走编译后的公开 SDK,覆盖宿主真实入口。
|
|
6
|
+
* - plugin lifecycle 未完成前,session.prompt 不应进入模型执行。
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import assert from "node:assert/strict";
|
|
11
|
+
import os from "node:os";
|
|
12
|
+
import path from "node:path";
|
|
13
|
+
import fs from "node:fs/promises";
|
|
14
|
+
|
|
15
|
+
import { MockLanguageModelV3 } from "ai/test";
|
|
16
|
+
import { Agent } from "../bin/index.js";
|
|
17
|
+
import { createPlugin } from "../bin/plugin/core/PluginActionFactory.js";
|
|
18
|
+
|
|
19
|
+
function create_deferred() {
|
|
20
|
+
let resolve;
|
|
21
|
+
const promise = new Promise((inner_resolve) => {
|
|
22
|
+
resolve = inner_resolve;
|
|
23
|
+
});
|
|
24
|
+
return {
|
|
25
|
+
promise,
|
|
26
|
+
resolve,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async function is_settled(promise) {
|
|
31
|
+
const marker = {};
|
|
32
|
+
const result = await Promise.race([
|
|
33
|
+
promise.then(
|
|
34
|
+
() => true,
|
|
35
|
+
() => true,
|
|
36
|
+
),
|
|
37
|
+
new Promise((resolve) => setTimeout(() => resolve(marker), 0)),
|
|
38
|
+
]);
|
|
39
|
+
return result !== marker;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function create_stream_text_result(text) {
|
|
43
|
+
return {
|
|
44
|
+
stream: new ReadableStream({
|
|
45
|
+
start(controller) {
|
|
46
|
+
controller.enqueue({
|
|
47
|
+
type: "stream-start",
|
|
48
|
+
warnings: [],
|
|
49
|
+
});
|
|
50
|
+
controller.enqueue({
|
|
51
|
+
type: "text-start",
|
|
52
|
+
id: "text_1",
|
|
53
|
+
});
|
|
54
|
+
controller.enqueue({
|
|
55
|
+
type: "text-delta",
|
|
56
|
+
id: "text_1",
|
|
57
|
+
delta: text,
|
|
58
|
+
});
|
|
59
|
+
controller.enqueue({
|
|
60
|
+
type: "text-end",
|
|
61
|
+
id: "text_1",
|
|
62
|
+
});
|
|
63
|
+
controller.enqueue({
|
|
64
|
+
type: "finish",
|
|
65
|
+
finishReason: {
|
|
66
|
+
unified: "stop",
|
|
67
|
+
raw: "stop",
|
|
68
|
+
},
|
|
69
|
+
usage: {
|
|
70
|
+
inputTokens: {
|
|
71
|
+
total: 0,
|
|
72
|
+
noCache: 0,
|
|
73
|
+
cacheRead: 0,
|
|
74
|
+
cacheWrite: 0,
|
|
75
|
+
},
|
|
76
|
+
outputTokens: {
|
|
77
|
+
total: 0,
|
|
78
|
+
text: 0,
|
|
79
|
+
reasoning: 0,
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
});
|
|
83
|
+
controller.close();
|
|
84
|
+
},
|
|
85
|
+
}),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
test("session.prompt waits for agent background ready before model execution", async () => {
|
|
90
|
+
const agent_path = await fs.mkdtemp(
|
|
91
|
+
path.join(os.tmpdir(), "downcity-agent-ready-"),
|
|
92
|
+
);
|
|
93
|
+
const lifecycle_ready = create_deferred();
|
|
94
|
+
let model_stream_calls = 0;
|
|
95
|
+
|
|
96
|
+
const blocking_plugin = createPlugin({
|
|
97
|
+
name: "blocking",
|
|
98
|
+
title: "Blocking",
|
|
99
|
+
description: "Blocks lifecycle start until the test releases it",
|
|
100
|
+
lifecycle: {
|
|
101
|
+
start: async () => {
|
|
102
|
+
await lifecycle_ready.promise;
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
const model = new MockLanguageModelV3({
|
|
107
|
+
modelId: "agent-ready-model",
|
|
108
|
+
doStream: async () => {
|
|
109
|
+
model_stream_calls += 1;
|
|
110
|
+
return create_stream_text_result("ready");
|
|
111
|
+
},
|
|
112
|
+
doGenerate: async () => ({
|
|
113
|
+
content: [
|
|
114
|
+
{
|
|
115
|
+
type: "text",
|
|
116
|
+
text: "Ready title",
|
|
117
|
+
},
|
|
118
|
+
],
|
|
119
|
+
finishReason: {
|
|
120
|
+
unified: "stop",
|
|
121
|
+
raw: "stop",
|
|
122
|
+
},
|
|
123
|
+
usage: {
|
|
124
|
+
inputTokens: {
|
|
125
|
+
total: 0,
|
|
126
|
+
noCache: 0,
|
|
127
|
+
cacheRead: 0,
|
|
128
|
+
cacheWrite: 0,
|
|
129
|
+
},
|
|
130
|
+
outputTokens: {
|
|
131
|
+
total: 0,
|
|
132
|
+
text: 0,
|
|
133
|
+
reasoning: 0,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
warnings: [],
|
|
137
|
+
}),
|
|
138
|
+
});
|
|
139
|
+
const agent = new Agent({
|
|
140
|
+
id: "ready_agent",
|
|
141
|
+
path: agent_path,
|
|
142
|
+
plugins: [blocking_plugin],
|
|
143
|
+
model,
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
try {
|
|
147
|
+
const session = await agent.session_collection().create_session({
|
|
148
|
+
sessionId: "ready_session",
|
|
149
|
+
});
|
|
150
|
+
const prompt_promise = session.prompt({
|
|
151
|
+
query: "hello",
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
assert.equal(await is_settled(prompt_promise), false);
|
|
155
|
+
assert.equal(model_stream_calls, 0);
|
|
156
|
+
|
|
157
|
+
lifecycle_ready.resolve();
|
|
158
|
+
const turn = await prompt_promise;
|
|
159
|
+
const result = await turn.finished;
|
|
160
|
+
|
|
161
|
+
assert.equal(result.success, true);
|
|
162
|
+
assert.equal(model_stream_calls, 1);
|
|
163
|
+
} finally {
|
|
164
|
+
lifecycle_ready.resolve();
|
|
165
|
+
await agent.dispose();
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
@@ -15,8 +15,8 @@ import fs from "node:fs/promises";
|
|
|
15
15
|
import {
|
|
16
16
|
invokePluginCallTool,
|
|
17
17
|
invokePluginReadTool,
|
|
18
|
-
setPluginToolRuntime,
|
|
19
18
|
} from "../bin/executor/tools/plugin/PluginToolBridge.js";
|
|
19
|
+
import { createPluginTools } from "../bin/executor/tools/plugin/PluginToolDefinition.js";
|
|
20
20
|
import { withSessionRunScope } from "../bin/executor/SessionRunScope.js";
|
|
21
21
|
import { createAction, createPlugin } from "../bin/plugin/core/PluginActionFactory.js";
|
|
22
22
|
import { createAgentPluginRegistry } from "../bin/agent/local/AgentPluginFactory.js";
|
|
@@ -38,7 +38,7 @@ test("invokePluginCallTool returns absolute paths for materialized file parts",
|
|
|
38
38
|
);
|
|
39
39
|
const bytes = Buffer.from("png-bytes-for-plugin-tool", "utf8");
|
|
40
40
|
|
|
41
|
-
|
|
41
|
+
const plugins = {
|
|
42
42
|
list: () => [],
|
|
43
43
|
read: () => ({ plugins: [] }),
|
|
44
44
|
availability: async () => ({ enabled: true, available: true, reasons: [] }),
|
|
@@ -64,14 +64,17 @@ test("invokePluginCallTool returns absolute paths for materialized file parts",
|
|
|
64
64
|
resolve: async () => {
|
|
65
65
|
throw new Error("not implemented");
|
|
66
66
|
},
|
|
67
|
-
}
|
|
67
|
+
};
|
|
68
68
|
|
|
69
69
|
const run_context = create_run_context(project_root);
|
|
70
70
|
const result = await withSessionRunScope({ runContext: run_context }, () =>
|
|
71
71
|
invokePluginCallTool({
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
72
|
+
plugins,
|
|
73
|
+
input: {
|
|
74
|
+
plugin: "image",
|
|
75
|
+
action: "image_result",
|
|
76
|
+
payload: { job_id: "img_1" },
|
|
77
|
+
},
|
|
75
78
|
}),
|
|
76
79
|
);
|
|
77
80
|
|
|
@@ -93,7 +96,7 @@ test("invokePluginCallTool returns absolute paths for materialized file parts",
|
|
|
93
96
|
});
|
|
94
97
|
|
|
95
98
|
test("invokePluginReadTool returns plugin action metadata", async () => {
|
|
96
|
-
|
|
99
|
+
const plugins = {
|
|
97
100
|
list: () => [],
|
|
98
101
|
read: () => ({
|
|
99
102
|
name: "image",
|
|
@@ -130,11 +133,14 @@ test("invokePluginReadTool returns plugin action metadata", async () => {
|
|
|
130
133
|
resolve: async () => {
|
|
131
134
|
throw new Error("not implemented");
|
|
132
135
|
},
|
|
133
|
-
}
|
|
136
|
+
};
|
|
134
137
|
|
|
135
138
|
const result = await invokePluginReadTool({
|
|
136
|
-
|
|
137
|
-
|
|
139
|
+
plugins,
|
|
140
|
+
input: {
|
|
141
|
+
plugin: "image",
|
|
142
|
+
action: "image_create",
|
|
143
|
+
},
|
|
138
144
|
});
|
|
139
145
|
|
|
140
146
|
assert.equal(result.success, true);
|
|
@@ -174,6 +180,7 @@ test("PluginRegistry validates action payload with metadata schema", async () =>
|
|
|
174
180
|
});
|
|
175
181
|
const registry = createAgentPluginRegistry({
|
|
176
182
|
plugins: [plugin],
|
|
183
|
+
plugin_instances: new Map(),
|
|
177
184
|
get_context: () => ({ rootPath: process.cwd() }),
|
|
178
185
|
});
|
|
179
186
|
|
|
@@ -197,3 +204,100 @@ test("PluginRegistry validates action payload with metadata schema", async () =>
|
|
|
197
204
|
assert.equal(valid.success, true);
|
|
198
205
|
assert.equal(valid.data.text, "hello");
|
|
199
206
|
});
|
|
207
|
+
|
|
208
|
+
test("createPluginTools binds plugin_call to the current registry", async () => {
|
|
209
|
+
function create_owner_registry(owner) {
|
|
210
|
+
const plugin = createPlugin({
|
|
211
|
+
name: "skill",
|
|
212
|
+
title: `Skill ${owner}`,
|
|
213
|
+
description: "Owner scoped skill plugin",
|
|
214
|
+
actions: {
|
|
215
|
+
lookup: createAction({
|
|
216
|
+
description: "Return registry owner",
|
|
217
|
+
execute: async () => ({
|
|
218
|
+
success: true,
|
|
219
|
+
data: { owner },
|
|
220
|
+
message: owner,
|
|
221
|
+
}),
|
|
222
|
+
}),
|
|
223
|
+
},
|
|
224
|
+
});
|
|
225
|
+
return createAgentPluginRegistry({
|
|
226
|
+
plugins: [plugin],
|
|
227
|
+
plugin_instances: new Map(),
|
|
228
|
+
get_context: () => ({ rootPath: process.cwd() }),
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const registry_a = create_owner_registry("agent_a");
|
|
233
|
+
const registry_b = create_owner_registry("agent_b");
|
|
234
|
+
const tools_a = createPluginTools({ plugins: registry_a });
|
|
235
|
+
const tools_b = createPluginTools({ plugins: registry_b });
|
|
236
|
+
|
|
237
|
+
const result_a = await tools_a.plugin_call.execute({
|
|
238
|
+
plugin: "skill",
|
|
239
|
+
action: "lookup",
|
|
240
|
+
payload: { name: "anything" },
|
|
241
|
+
});
|
|
242
|
+
const result_b = await tools_b.plugin_call.execute({
|
|
243
|
+
plugin: "skill",
|
|
244
|
+
action: "lookup",
|
|
245
|
+
payload: { name: "anything" },
|
|
246
|
+
});
|
|
247
|
+
|
|
248
|
+
assert.equal(result_a.success, true);
|
|
249
|
+
assert.equal(result_a.data.value.owner, "agent_a");
|
|
250
|
+
assert.equal(result_b.success, true);
|
|
251
|
+
assert.equal(result_b.data.value.owner, "agent_b");
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
test("PluginRegistry keeps plugin ready after action business failure", async () => {
|
|
255
|
+
let call_count = 0;
|
|
256
|
+
const plugin = createPlugin({
|
|
257
|
+
name: "skill",
|
|
258
|
+
title: "Skill",
|
|
259
|
+
description: "Retryable skill plugin",
|
|
260
|
+
actions: {
|
|
261
|
+
lookup: createAction({
|
|
262
|
+
description: "Fail once then succeed",
|
|
263
|
+
execute: async () => {
|
|
264
|
+
call_count += 1;
|
|
265
|
+
if (call_count === 1) {
|
|
266
|
+
return {
|
|
267
|
+
success: false,
|
|
268
|
+
error: "Skill not found: missing",
|
|
269
|
+
message: "Skill not found: missing",
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
return {
|
|
273
|
+
success: true,
|
|
274
|
+
data: { loaded: true },
|
|
275
|
+
message: "loaded",
|
|
276
|
+
};
|
|
277
|
+
},
|
|
278
|
+
}),
|
|
279
|
+
},
|
|
280
|
+
});
|
|
281
|
+
const registry = createAgentPluginRegistry({
|
|
282
|
+
plugins: [plugin],
|
|
283
|
+
plugin_instances: new Map(),
|
|
284
|
+
get_context: () => ({ rootPath: process.cwd() }),
|
|
285
|
+
});
|
|
286
|
+
|
|
287
|
+
const failed = await registry.runAction({
|
|
288
|
+
plugin: "skill",
|
|
289
|
+
action: "lookup",
|
|
290
|
+
payload: { name: "missing" },
|
|
291
|
+
});
|
|
292
|
+
assert.equal(failed.success, false);
|
|
293
|
+
assert.equal(registry.status("skill").state, "ready");
|
|
294
|
+
|
|
295
|
+
const retry = await registry.runAction({
|
|
296
|
+
plugin: "skill",
|
|
297
|
+
action: "lookup",
|
|
298
|
+
payload: { name: "exists" },
|
|
299
|
+
});
|
|
300
|
+
assert.equal(retry.success, true);
|
|
301
|
+
assert.equal(retry.data.loaded, true);
|
|
302
|
+
assert.equal(registry.status("skill").state, "ready");
|
|
303
|
+
});
|
package/src/agent/local/Agent.ts
CHANGED
|
@@ -90,14 +90,14 @@ export class Agent {
|
|
|
90
90
|
this.instruction = assembly.instruction;
|
|
91
91
|
this.shell = assembly.shell;
|
|
92
92
|
|
|
93
|
-
this.sessionManager = this.create_session_manager(assembly);
|
|
94
|
-
session_manager_ref = this.sessionManager;
|
|
95
93
|
// 关键点(中文):构造完成即触发后台能力启动;调用方可 `await agent.ready()` 等待。
|
|
96
94
|
this.backgroundService = new AgentBackgroundService({
|
|
97
95
|
logger: this.logger,
|
|
98
96
|
agent_context: this.agentContext,
|
|
99
97
|
get_shell: () => this.shell,
|
|
100
98
|
});
|
|
99
|
+
this.sessionManager = this.create_session_manager(assembly);
|
|
100
|
+
session_manager_ref = this.sessionManager;
|
|
101
101
|
}
|
|
102
102
|
|
|
103
103
|
/**
|
|
@@ -269,6 +269,9 @@ export class Agent {
|
|
|
269
269
|
get_agent_context: () => this.agentContext ?? assembly.agent_context,
|
|
270
270
|
get_instruction: () => this.instruction,
|
|
271
271
|
plugin_instances: assembly.plugin_instances,
|
|
272
|
+
ensure_agent_ready: async () => {
|
|
273
|
+
await this.backgroundService.ready();
|
|
274
|
+
},
|
|
272
275
|
default_model: this.defaultModel,
|
|
273
276
|
SessionClass: this.SessionClass,
|
|
274
277
|
});
|
|
@@ -28,8 +28,7 @@ import {
|
|
|
28
28
|
createAgentPluginConfigRuntime,
|
|
29
29
|
} from "@/agent/local/AgentRuntimeAssembly.js";
|
|
30
30
|
import {
|
|
31
|
-
|
|
32
|
-
setPluginToolRuntime,
|
|
31
|
+
createPluginTools,
|
|
33
32
|
} from "@executor/tools/plugin/PluginToolDefinition.js";
|
|
34
33
|
import type { AgentManagedSession } from "@/types/agent/AgentTypes.js";
|
|
35
34
|
import type { SessionPort } from "@/types/runtime/agent/AgentContext.js";
|
|
@@ -183,6 +182,7 @@ export class AgentAssemblyService {
|
|
|
183
182
|
});
|
|
184
183
|
const plugins = plugin_registry;
|
|
185
184
|
if (this.should_register_plugin_call_tool(plugin_instances)) {
|
|
185
|
+
const plugin_tools = createPluginTools({ plugins });
|
|
186
186
|
tools.plugin_read = tools.plugin_read || plugin_tools.plugin_read;
|
|
187
187
|
tools.plugin_call = tools.plugin_call || plugin_tools.plugin_call;
|
|
188
188
|
}
|
|
@@ -228,7 +228,6 @@ export class AgentAssemblyService {
|
|
|
228
228
|
});
|
|
229
229
|
Object.assign(tools, shell.tools);
|
|
230
230
|
}
|
|
231
|
-
setPluginToolRuntime(plugins);
|
|
232
231
|
|
|
233
232
|
return {
|
|
234
233
|
id,
|
|
@@ -87,6 +87,11 @@ type AgentSessionManagerOptions = {
|
|
|
87
87
|
*/
|
|
88
88
|
plugin_instances: Map<string, Plugin>;
|
|
89
89
|
|
|
90
|
+
/**
|
|
91
|
+
* 等待当前 Agent 后台能力启动完成。
|
|
92
|
+
*/
|
|
93
|
+
ensure_agent_ready: () => Promise<void>;
|
|
94
|
+
|
|
90
95
|
/**
|
|
91
96
|
* 当前默认模型实例。
|
|
92
97
|
*/
|
|
@@ -109,6 +114,7 @@ export class AgentSessionManager {
|
|
|
109
114
|
private readonly get_agent_context: AgentSessionManagerOptions["get_agent_context"];
|
|
110
115
|
private readonly get_instruction: AgentSessionManagerOptions["get_instruction"];
|
|
111
116
|
private readonly plugin_instances: Map<string, Plugin>;
|
|
117
|
+
private readonly ensure_agent_ready: AgentSessionManagerOptions["ensure_agent_ready"];
|
|
112
118
|
private readonly default_model?: AgentModel;
|
|
113
119
|
private readonly SessionClass: AgentSessionConstructor;
|
|
114
120
|
private readonly sessions_by_id = new Map<string, AgentManagedSession>();
|
|
@@ -123,6 +129,7 @@ export class AgentSessionManager {
|
|
|
123
129
|
this.get_agent_context = options.get_agent_context;
|
|
124
130
|
this.get_instruction = options.get_instruction;
|
|
125
131
|
this.plugin_instances = options.plugin_instances;
|
|
132
|
+
this.ensure_agent_ready = options.ensure_agent_ready;
|
|
126
133
|
this.default_model = options.default_model;
|
|
127
134
|
this.SessionClass = options.SessionClass || Session;
|
|
128
135
|
this.session_collection = {
|
|
@@ -350,6 +357,7 @@ export class AgentSessionManager {
|
|
|
350
357
|
getManagedPluginSystemBlocks: async () => [],
|
|
351
358
|
getPluginSystemBlocks: async () => await this.load_plugin_system_blocks(),
|
|
352
359
|
ensureConfigured: async (session) => {
|
|
360
|
+
await this.ensure_agent_ready();
|
|
353
361
|
await this.apply_session_defaults(session);
|
|
354
362
|
},
|
|
355
363
|
});
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
import path from "node:path";
|
|
11
11
|
import type { FileUIPart } from "ai";
|
|
12
12
|
import type { JsonObject, JsonValue } from "@/types/common/Json.js";
|
|
13
|
-
import type { AgentPlugins } from "@/plugin/types/Plugin.js";
|
|
14
13
|
import type {
|
|
15
14
|
PluginCallInput,
|
|
16
15
|
PluginCallToolFileResult,
|
|
@@ -18,6 +17,10 @@ import type {
|
|
|
18
17
|
PluginReadInput,
|
|
19
18
|
PluginReadToolResult,
|
|
20
19
|
} from "@/executor/tools/plugin/types/PluginTool.js";
|
|
20
|
+
import type {
|
|
21
|
+
InvokePluginCallToolOptions,
|
|
22
|
+
InvokePluginReadToolOptions,
|
|
23
|
+
} from "@/types/plugin/PluginToolRuntime.js";
|
|
21
24
|
import {
|
|
22
25
|
materializeAssistantFileParts,
|
|
23
26
|
resolveAgentFilePath,
|
|
@@ -27,25 +30,6 @@ import {
|
|
|
27
30
|
getSessionRunContext,
|
|
28
31
|
} from "@executor/SessionRunScope.js";
|
|
29
32
|
|
|
30
|
-
let plugin_tool_runtime: AgentPlugins | null = null;
|
|
31
|
-
|
|
32
|
-
/**
|
|
33
|
-
* 注入 plugin tool 运行时。
|
|
34
|
-
*/
|
|
35
|
-
export function setPluginToolRuntime(next: AgentPlugins): void {
|
|
36
|
-
plugin_tool_runtime = next;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* 读取已注入的 plugin tool 运行时。
|
|
41
|
-
*/
|
|
42
|
-
function require_plugin_tool_runtime(): AgentPlugins {
|
|
43
|
-
if (plugin_tool_runtime) return plugin_tool_runtime;
|
|
44
|
-
throw new Error(
|
|
45
|
-
"Plugin tool runtime is not initialized. Ensure agent assembly completed before using plugin_call.",
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
33
|
/**
|
|
50
34
|
* 判断值是否为普通对象。
|
|
51
35
|
*/
|
|
@@ -126,8 +110,9 @@ function summarize_action_data(data: JsonValue | undefined): JsonObject | undefi
|
|
|
126
110
|
* 调用 plugin action 并桥接最终 assistant file parts。
|
|
127
111
|
*/
|
|
128
112
|
export async function invokePluginCallTool(
|
|
129
|
-
|
|
113
|
+
params: InvokePluginCallToolOptions,
|
|
130
114
|
): Promise<PluginCallToolResult> {
|
|
115
|
+
const input: PluginCallInput = params.input;
|
|
131
116
|
const plugin = String(input.plugin || "").trim();
|
|
132
117
|
const action = String(input.action || "").trim();
|
|
133
118
|
const payload = to_json_object(input.payload ?? {}) ?? {};
|
|
@@ -153,8 +138,7 @@ export async function invokePluginCallTool(
|
|
|
153
138
|
}
|
|
154
139
|
|
|
155
140
|
try {
|
|
156
|
-
const
|
|
157
|
-
const result = await runtime.runAction({
|
|
141
|
+
const result = await params.plugins.runAction({
|
|
158
142
|
plugin,
|
|
159
143
|
action,
|
|
160
144
|
payload,
|
|
@@ -203,11 +187,11 @@ export async function invokePluginCallTool(
|
|
|
203
187
|
* 读取 plugin / action metadata。
|
|
204
188
|
*/
|
|
205
189
|
export async function invokePluginReadTool(
|
|
206
|
-
|
|
190
|
+
params: InvokePluginReadToolOptions,
|
|
207
191
|
): Promise<PluginReadToolResult> {
|
|
192
|
+
const input: PluginReadInput = params.input;
|
|
208
193
|
try {
|
|
209
|
-
const
|
|
210
|
-
const data = runtime.read({
|
|
194
|
+
const data = params.plugins.read({
|
|
211
195
|
plugin: typeof input.plugin === "string" ? input.plugin : undefined,
|
|
212
196
|
action: typeof input.action === "string" ? input.action : undefined,
|
|
213
197
|
});
|