@downcity/agent 1.1.135 → 1.1.149
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 +2 -2
- package/bin/agent/local/Agent.d.ts +3 -21
- package/bin/agent/local/Agent.d.ts.map +1 -1
- package/bin/agent/local/Agent.js +3 -30
- package/bin/agent/local/Agent.js.map +1 -1
- package/bin/agent/local/ProjectSetup.js +2 -2
- package/bin/agent/local/ProjectSetup.js.map +1 -1
- package/bin/agent/local/services/AgentAssemblyService.d.ts +1 -6
- package/bin/agent/local/services/AgentAssemblyService.d.ts.map +1 -1
- package/bin/agent/local/services/AgentAssemblyService.js +13 -20
- package/bin/agent/local/services/AgentAssemblyService.js.map +1 -1
- package/bin/agent/local/services/AgentSessionManager.d.ts +16 -7
- package/bin/agent/local/services/AgentSessionManager.d.ts.map +1 -1
- package/bin/agent/local/services/AgentSessionManager.js +83 -8
- package/bin/agent/local/services/AgentSessionManager.js.map +1 -1
- package/bin/agent/remote/RemoteAgent.d.ts +9 -13
- package/bin/agent/remote/RemoteAgent.d.ts.map +1 -1
- package/bin/agent/remote/RemoteAgent.js +23 -24
- package/bin/agent/remote/RemoteAgent.js.map +1 -1
- package/bin/agent/remote/RemoteSession.d.ts +11 -4
- package/bin/agent/remote/RemoteSession.d.ts.map +1 -1
- package/bin/agent/remote/RemoteSession.js +15 -3
- package/bin/agent/remote/RemoteSession.js.map +1 -1
- package/bin/agent/remote/RemoteTransport.d.ts +9 -1
- package/bin/agent/remote/RemoteTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts +7 -3
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +65 -2
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts +5 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.js +12 -0
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
- package/bin/config/AgentInitializer.d.ts +2 -2
- package/bin/config/AgentInitializer.js +2 -2
- package/bin/executor/Executor.d.ts +5 -0
- package/bin/executor/Executor.d.ts.map +1 -1
- package/bin/executor/Executor.js +34 -0
- package/bin/executor/Executor.js.map +1 -1
- package/bin/executor/composer/system/default/InitPrompts.d.ts +1 -1
- package/bin/executor/composer/system/default/InitPrompts.js +1 -1
- package/bin/executor/composer/system/default/SystemDomain.js +2 -2
- package/bin/executor/composer/system/default/SystemDomain.js.map +1 -1
- package/bin/executor/core-engine/CoreEngineRunner.d.ts.map +1 -1
- package/bin/executor/core-engine/CoreEngineRunner.js +16 -0
- package/bin/executor/core-engine/CoreEngineRunner.js.map +1 -1
- package/bin/executor/messages/ChatMessageMarkupTypes.d.ts +1 -1
- package/bin/executor/messages/ChatMessageMarkupTypes.js +1 -1
- package/bin/executor/types/SessionExecutor.d.ts +8 -0
- package/bin/executor/types/SessionExecutor.d.ts.map +1 -1
- package/bin/index.d.ts +2 -2
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js.map +1 -1
- package/bin/plugin/core/Activation.d.ts +0 -2
- package/bin/plugin/core/Activation.d.ts.map +1 -1
- package/bin/plugin/core/Activation.js +1 -1
- package/bin/plugin/core/Activation.js.map +1 -1
- package/bin/plugin/core/PluginCommandRequest.d.ts +1 -1
- package/bin/plugin/core/PluginCommandRequest.js +1 -1
- package/bin/plugin/core/PluginStateController.d.ts +0 -5
- package/bin/plugin/core/PluginStateController.d.ts.map +1 -1
- package/bin/plugin/core/PluginStateController.js +1 -5
- package/bin/plugin/core/PluginStateController.js.map +1 -1
- package/bin/plugin/types/PluginApi.d.ts +1 -1
- package/bin/plugin/types/PluginApi.js +1 -1
- package/bin/rpc/Client.d.ts +17 -1
- package/bin/rpc/Client.d.ts.map +1 -1
- package/bin/rpc/Client.js +47 -0
- package/bin/rpc/Client.js.map +1 -1
- package/bin/session/Session.d.ts +5 -0
- package/bin/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +7 -0
- package/bin/session/Session.js.map +1 -1
- package/bin/session/browse/Browse.d.ts +9 -1
- package/bin/session/browse/Browse.d.ts.map +1 -1
- package/bin/session/browse/Browse.js +81 -3
- package/bin/session/browse/Browse.js.map +1 -1
- package/bin/session/index.d.ts +3 -3
- package/bin/session/index.d.ts.map +1 -1
- package/bin/session/index.js +3 -3
- package/bin/session/index.js.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.d.ts +13 -0
- package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.js +82 -5
- package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
- package/bin/session/services/SessionTurnService.d.ts +5 -0
- package/bin/session/services/SessionTurnService.d.ts.map +1 -1
- package/bin/session/services/SessionTurnService.js +10 -1
- package/bin/session/services/SessionTurnService.js.map +1 -1
- package/bin/session/storage/Metadata.d.ts +15 -0
- package/bin/session/storage/Metadata.d.ts.map +1 -1
- package/bin/session/storage/Metadata.js +15 -1
- package/bin/session/storage/Metadata.js.map +1 -1
- package/bin/session/storage/Paths.d.ts +24 -0
- package/bin/session/storage/Paths.d.ts.map +1 -1
- package/bin/session/storage/Paths.js +34 -0
- package/bin/session/storage/Paths.js.map +1 -1
- package/bin/session/storage/RuntimeSessionPort.d.ts +5 -0
- package/bin/session/storage/RuntimeSessionPort.d.ts.map +1 -1
- package/bin/session/storage/RuntimeSessionPort.js +4 -0
- package/bin/session/storage/RuntimeSessionPort.js.map +1 -1
- package/bin/types/agent/AgentTypes.d.ts +2 -1
- package/bin/types/agent/AgentTypes.d.ts.map +1 -1
- package/bin/types/agent/SessionActor.d.ts +13 -4
- package/bin/types/agent/SessionActor.d.ts.map +1 -1
- package/bin/types/agent/SessionTypes.d.ts +62 -0
- package/bin/types/agent/SessionTypes.d.ts.map +1 -1
- package/bin/types/config/DowncityConfig.d.ts +1 -1
- package/bin/types/executor/SessionRunContext.d.ts +8 -0
- package/bin/types/executor/SessionRunContext.d.ts.map +1 -1
- package/bin/types/platform/Store.d.ts +2 -2
- package/bin/types/platform/Store.js +2 -2
- package/bin/types/rpc/RpcProtocol.d.ts +37 -5
- package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
- package/bin/types/rpc/RpcProtocol.js +1 -1
- package/bin/types/runtime/agent/AgentContext.d.ts +36 -11
- package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
- package/bin/types/runtime/agent/AgentContext.js +26 -8
- package/bin/types/runtime/agent/AgentContext.js.map +1 -1
- package/bin/types/runtime/platform/Platform.d.ts +8 -8
- package/bin/types/runtime/platform/PlatformGateway.d.ts +2 -2
- package/bin/types/sdk/AgentSessionStop.d.ts +36 -0
- package/bin/types/sdk/AgentSessionStop.d.ts.map +1 -0
- package/bin/types/sdk/AgentSessionStop.js +9 -0
- package/bin/types/sdk/AgentSessionStop.js.map +1 -0
- package/package.json +3 -3
- package/scripts/city-model-tool-loop.test.mjs +2 -2
- package/scripts/session-prompt-runtime.test.mjs +59 -0
- package/src/agent/local/Agent.ts +3 -41
- package/src/agent/local/ProjectSetup.ts +2 -2
- package/src/agent/local/services/AgentAssemblyService.ts +17 -28
- package/src/agent/local/services/AgentSessionManager.ts +127 -13
- package/src/agent/remote/RemoteAgent.ts +25 -34
- package/src/agent/remote/RemoteSession.ts +21 -6
- package/src/agent/remote/RemoteTransport.ts +14 -0
- package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +100 -2
- package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +26 -0
- package/src/config/AgentInitializer.ts +2 -2
- package/src/executor/Executor.ts +33 -0
- package/src/executor/composer/system/default/InitPrompts.ts +1 -1
- package/src/executor/composer/system/default/SystemDomain.ts +2 -2
- package/src/executor/core-engine/CoreEngineRunner.ts +21 -0
- package/src/executor/messages/ChatMessageMarkupTypes.ts +1 -1
- package/src/executor/types/SessionExecutor.ts +2 -1
- package/src/index.ts +6 -1
- package/src/plugin/core/Activation.ts +1 -3
- package/src/plugin/core/PluginCommandRequest.ts +1 -1
- package/src/plugin/core/PluginStateController.ts +1 -10
- package/src/plugin/types/PluginApi.ts +1 -1
- package/src/rpc/Client.ts +61 -0
- package/src/session/Session.ts +9 -0
- package/src/session/browse/Browse.ts +103 -3
- package/src/session/index.ts +7 -0
- package/src/session/runtime/SessionPromptRuntime.ts +100 -5
- package/src/session/services/SessionTurnService.ts +13 -1
- package/src/session/storage/Metadata.ts +23 -1
- package/src/session/storage/Paths.ts +70 -0
- package/src/session/storage/RuntimeSessionPort.ts +9 -0
- package/src/types/agent/AgentTypes.ts +6 -0
- package/src/types/agent/SessionActor.ts +21 -3
- package/src/types/agent/SessionTypes.ts +68 -0
- package/src/types/config/DowncityConfig.ts +1 -1
- package/src/types/executor/SessionRunContext.ts +9 -0
- package/src/types/platform/Store.ts +2 -2
- package/src/types/rpc/RpcProtocol.ts +46 -4
- package/src/types/runtime/agent/AgentContext.ts +41 -14
- package/src/types/runtime/platform/Platform.ts +8 -8
- package/src/types/runtime/platform/PlatformGateway.ts +2 -2
- package/src/types/sdk/AgentSessionStop.ts +39 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/bin/agent/local/AgentRuntimeFactory.d.ts +0 -47
- package/bin/agent/local/AgentRuntimeFactory.d.ts.map +0 -1
- package/bin/agent/local/AgentRuntimeFactory.js +0 -34
- package/bin/agent/local/AgentRuntimeFactory.js.map +0 -1
- package/bin/types/runtime/agent/AgentRuntime.d.ts +0 -79
- package/bin/types/runtime/agent/AgentRuntime.d.ts.map +0 -1
- package/bin/types/runtime/agent/AgentRuntime.js +0 -13
- package/bin/types/runtime/agent/AgentRuntime.js.map +0 -1
- package/src/agent/local/AgentRuntimeFactory.ts +0 -78
- package/src/types/runtime/agent/AgentRuntime.ts +0 -84
|
@@ -230,7 +230,7 @@ test("CityModel uses direct LanguageModel path and sends tool result back", asyn
|
|
|
230
230
|
const city = new City({
|
|
231
231
|
role: "user",
|
|
232
232
|
city_url: `http://127.0.0.1:${String(address.port)}`,
|
|
233
|
-
|
|
233
|
+
city_id: "city_demo",
|
|
234
234
|
user_token: "ub_test",
|
|
235
235
|
});
|
|
236
236
|
const catalog = await city.ai.listModels();
|
|
@@ -266,7 +266,7 @@ test("CityModel uses direct LanguageModel path and sends tool result back", asyn
|
|
|
266
266
|
assert.equal(tool_executed, true);
|
|
267
267
|
assert.equal(stream_requests, 0);
|
|
268
268
|
assert.equal(agent_requests.length, 2);
|
|
269
|
-
assert.equal(requests.every((request) => request?.
|
|
269
|
+
assert.equal(requests.every((request) => request?.city_id === "city_demo"), true);
|
|
270
270
|
assert.equal(agent_requests[0]?.model, "mock-model");
|
|
271
271
|
|
|
272
272
|
const second_request_messages = Array.isArray(agent_requests[1]?.messages)
|
|
@@ -98,6 +98,7 @@ test("SessionPromptRuntime merges queued prompts at the next step boundary", asy
|
|
|
98
98
|
assistantMessage: createAssistantMessage("done", 1),
|
|
99
99
|
};
|
|
100
100
|
},
|
|
101
|
+
stopTurn: () => false,
|
|
101
102
|
});
|
|
102
103
|
|
|
103
104
|
const firstTurn = await runtime.prompt({ query: "first" });
|
|
@@ -159,6 +160,7 @@ test("SessionPromptRuntime moves unmerged prompts into the next turn", async ()
|
|
|
159
160
|
),
|
|
160
161
|
};
|
|
161
162
|
},
|
|
163
|
+
stopTurn: () => false,
|
|
162
164
|
});
|
|
163
165
|
|
|
164
166
|
const firstTurn = await runtime.prompt({ query: "first" });
|
|
@@ -185,3 +187,60 @@ test("SessionPromptRuntime moves unmerged prompts into the next turn", async ()
|
|
|
185
187
|
["turn-start", "turn-finish", "turn-start", "turn-finish"],
|
|
186
188
|
);
|
|
187
189
|
});
|
|
190
|
+
|
|
191
|
+
test("SessionPromptRuntime stops current turn and cancels unmerged queued prompts", async () => {
|
|
192
|
+
const events = [];
|
|
193
|
+
const executionFinished = createDeferred();
|
|
194
|
+
let stopRequested = false;
|
|
195
|
+
|
|
196
|
+
const runtime = new SessionPromptRuntime({
|
|
197
|
+
sessionId: "test",
|
|
198
|
+
publish: (event) => {
|
|
199
|
+
events.push(event);
|
|
200
|
+
},
|
|
201
|
+
createAndPersistUserMessage: async (input) => {
|
|
202
|
+
return createUserMessage(input.query, events.length + 1);
|
|
203
|
+
},
|
|
204
|
+
executeTurn: async (input) => {
|
|
205
|
+
await new Promise((resolve) => {
|
|
206
|
+
input.abortSignal.addEventListener("abort", resolve, { once: true });
|
|
207
|
+
});
|
|
208
|
+
await executionFinished.promise;
|
|
209
|
+
return {
|
|
210
|
+
text: "should-not-succeed",
|
|
211
|
+
success: true,
|
|
212
|
+
assistantMessage: createAssistantMessage("should-not-succeed", 1),
|
|
213
|
+
};
|
|
214
|
+
},
|
|
215
|
+
stopTurn: () => {
|
|
216
|
+
stopRequested = true;
|
|
217
|
+
executionFinished.resolve();
|
|
218
|
+
return true;
|
|
219
|
+
},
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
const firstTurn = await runtime.prompt({ query: "first" });
|
|
223
|
+
const secondTurnPromise = runtime.prompt({ query: "second" });
|
|
224
|
+
await waitUntil(() => runtime.isActive());
|
|
225
|
+
|
|
226
|
+
const stopResult = runtime.stop();
|
|
227
|
+
const secondTurn = await secondTurnPromise;
|
|
228
|
+
const firstResult = await firstTurn.finished;
|
|
229
|
+
const secondResult = await secondTurn.finished;
|
|
230
|
+
|
|
231
|
+
assert.equal(stopRequested, true);
|
|
232
|
+
assert.equal(stopResult.stopped, true);
|
|
233
|
+
assert.equal(stopResult.turnId, firstTurn.id);
|
|
234
|
+
assert.equal(stopResult.cancelledQueuedPrompts, 1);
|
|
235
|
+
assert.equal(firstResult.success, false);
|
|
236
|
+
assert.equal(firstResult.error, "Turn stopped");
|
|
237
|
+
assert.equal(secondResult.success, false);
|
|
238
|
+
assert.equal(
|
|
239
|
+
secondResult.error,
|
|
240
|
+
"Prompt cancelled because session was stopped",
|
|
241
|
+
);
|
|
242
|
+
assert.deepEqual(
|
|
243
|
+
events.map((event) => event.type),
|
|
244
|
+
["turn-start", "turn-start", "turn-finish", "turn-finish"],
|
|
245
|
+
);
|
|
246
|
+
});
|
package/src/agent/local/Agent.ts
CHANGED
|
@@ -9,17 +9,12 @@
|
|
|
9
9
|
|
|
10
10
|
import type { Tool } from "ai";
|
|
11
11
|
import type { AgentContext } from "@/types/runtime/agent/AgentContext.js";
|
|
12
|
-
import type { AgentRuntime } from "@/types/runtime/agent/AgentRuntime.js";
|
|
13
12
|
import type { DowncityConfig } from "@/types/config/DowncityConfig.js";
|
|
14
13
|
import type { AgentPlugins } from "@/plugin/types/Plugin.js";
|
|
15
14
|
import type {
|
|
16
|
-
AgentCreateSessionInput,
|
|
17
|
-
AgentListSessionsInput,
|
|
18
15
|
AgentModel,
|
|
19
16
|
AgentOptions,
|
|
20
|
-
AgentSession,
|
|
21
17
|
AgentSessionCollection,
|
|
22
|
-
AgentSessionSummaryPage,
|
|
23
18
|
} from "@/types/agent/AgentTypes.js";
|
|
24
19
|
import type {
|
|
25
20
|
ShellApprovalMode,
|
|
@@ -50,7 +45,6 @@ export class Agent {
|
|
|
50
45
|
readonly plugins: AgentPlugins;
|
|
51
46
|
|
|
52
47
|
private readonly logger: Logger;
|
|
53
|
-
private readonly runtime: AgentRuntime;
|
|
54
48
|
private readonly agentContext: AgentContext;
|
|
55
49
|
private readonly pluginRegistry: PluginRegistry;
|
|
56
50
|
private readonly config: DowncityConfig;
|
|
@@ -89,7 +83,6 @@ export class Agent {
|
|
|
89
83
|
this.tools = assembly.tools;
|
|
90
84
|
this.plugins = assembly.plugins;
|
|
91
85
|
this.logger = assembly.logger;
|
|
92
|
-
this.runtime = assembly.runtime;
|
|
93
86
|
this.agentContext = assembly.agent_context;
|
|
94
87
|
this.pluginRegistry = assembly.plugin_registry;
|
|
95
88
|
this.config = assembly.config;
|
|
@@ -107,29 +100,6 @@ export class Agent {
|
|
|
107
100
|
});
|
|
108
101
|
}
|
|
109
102
|
|
|
110
|
-
/**
|
|
111
|
-
* 新建一个 session。
|
|
112
|
-
*/
|
|
113
|
-
async createSession(input?: AgentCreateSessionInput): Promise<AgentSession> {
|
|
114
|
-
return await this.sessionManager.create_session(input);
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* 获取一个已存在的 session。
|
|
119
|
-
*/
|
|
120
|
-
async getSession(sessionId: string): Promise<AgentSession> {
|
|
121
|
-
return await this.sessionManager.get_session(sessionId);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
/**
|
|
125
|
-
* 列出当前 agent 的 session 摘要页。
|
|
126
|
-
*/
|
|
127
|
-
async listSessions(
|
|
128
|
-
input?: AgentListSessionsInput,
|
|
129
|
-
): Promise<AgentSessionSummaryPage> {
|
|
130
|
-
return await this.sessionManager.list_sessions(input);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
103
|
/**
|
|
134
104
|
* 等待 Agent 后台能力启动完成。
|
|
135
105
|
*
|
|
@@ -267,13 +237,6 @@ export class Agent {
|
|
|
267
237
|
return this.logger;
|
|
268
238
|
}
|
|
269
239
|
|
|
270
|
-
/**
|
|
271
|
-
* 返回当前 agent runtime。
|
|
272
|
-
*/
|
|
273
|
-
getRuntime(): AgentRuntime {
|
|
274
|
-
return this.runtime;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
240
|
/**
|
|
278
241
|
* 返回当前 agent context。
|
|
279
242
|
*/
|
|
@@ -282,9 +245,9 @@ export class Agent {
|
|
|
282
245
|
}
|
|
283
246
|
|
|
284
247
|
/**
|
|
285
|
-
* 返回当前 session collection
|
|
248
|
+
* 返回当前 session collection 入口。
|
|
286
249
|
*/
|
|
287
|
-
|
|
250
|
+
session_collection(): AgentSessionCollection {
|
|
288
251
|
return this.sessionManager.get_session_collection();
|
|
289
252
|
}
|
|
290
253
|
|
|
@@ -303,8 +266,7 @@ export class Agent {
|
|
|
303
266
|
project_root: this.path || assembly.path,
|
|
304
267
|
tools: this.tools || assembly.tools,
|
|
305
268
|
logger: this.logger || assembly.logger,
|
|
306
|
-
|
|
307
|
-
get_agent_context: () => this.agentContext || assembly.agent_context,
|
|
269
|
+
get_agent_context: () => this.agentContext ?? assembly.agent_context,
|
|
308
270
|
get_instruction: () => this.instruction,
|
|
309
271
|
plugin_instances: assembly.plugin_instances,
|
|
310
272
|
default_model: this.defaultModel,
|
|
@@ -30,14 +30,14 @@ function ensureContextFiles(projectRoot: string): void {
|
|
|
30
30
|
// Check if initialized(启动入口一次性确认工程根目录与关键文件)
|
|
31
31
|
if (!fs.existsSync(getProfileMdPath(projectRoot))) {
|
|
32
32
|
console.error(
|
|
33
|
-
'❌ Project not initialized. Please run "
|
|
33
|
+
'❌ Project not initialized. Please run "downcity agent create" first',
|
|
34
34
|
);
|
|
35
35
|
process.exit(1);
|
|
36
36
|
}
|
|
37
37
|
|
|
38
38
|
if (!fs.existsSync(getDowncityJsonPath(projectRoot))) {
|
|
39
39
|
console.error(
|
|
40
|
-
'❌ downcity.json does not exist. Please run "
|
|
40
|
+
'❌ downcity.json does not exist. Please run "downcity agent create" first',
|
|
41
41
|
);
|
|
42
42
|
process.exit(1);
|
|
43
43
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* AgentAssemblyService:本地 Agent 装配服务。
|
|
3
3
|
*
|
|
4
4
|
* 关键点(中文)
|
|
5
|
-
* - 统一装配 logger、config、env、
|
|
5
|
+
* - 统一装配 logger、config、env、context 与 plugin registry。
|
|
6
6
|
* - 该服务只负责一次性长期对象装配,不负责 session 缓存和生命周期启动。
|
|
7
7
|
* - session/lifecycle service 通过它暴露的长期对象协作,避免在 facade 中重复拼装。
|
|
8
8
|
*/
|
|
@@ -10,7 +10,6 @@
|
|
|
10
10
|
import type { LanguageModel, Tool } from "ai";
|
|
11
11
|
import type { BasePlugin } from "@/plugin/core/BasePlugin.js";
|
|
12
12
|
import { AgentContext } from "@/types/runtime/agent/AgentContext.js";
|
|
13
|
-
import type { AgentRuntime } from "@/types/runtime/agent/AgentRuntime.js";
|
|
14
13
|
import type { DowncityConfig } from "@/types/config/DowncityConfig.js";
|
|
15
14
|
import type { AgentPlugins } from "@/plugin/types/Plugin.js";
|
|
16
15
|
import type { AgentOptions } from "@/types/agent/AgentTypes.js";
|
|
@@ -25,8 +24,9 @@ import {
|
|
|
25
24
|
createAgentPluginRegistry,
|
|
26
25
|
} from "@/agent/local/AgentPluginFactory.js";
|
|
27
26
|
import {
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
createAgentPathRuntime,
|
|
28
|
+
createAgentPluginConfigRuntime,
|
|
29
|
+
} from "@/agent/local/AgentRuntimeAssembly.js";
|
|
30
30
|
import {
|
|
31
31
|
plugin_tools,
|
|
32
32
|
setPluginToolRuntime,
|
|
@@ -113,11 +113,6 @@ export interface AgentAssemblyResult {
|
|
|
113
113
|
*/
|
|
114
114
|
plugins: AgentPlugins;
|
|
115
115
|
|
|
116
|
-
/**
|
|
117
|
-
* 当前 agent runtime。
|
|
118
|
-
*/
|
|
119
|
-
runtime: AgentRuntime;
|
|
120
|
-
|
|
121
116
|
/**
|
|
122
117
|
* 当前 agent context。
|
|
123
118
|
*/
|
|
@@ -166,24 +161,12 @@ export class AgentAssemblyService {
|
|
|
166
161
|
logger.bindProjectRoot(path);
|
|
167
162
|
// 关键点(中文)
|
|
168
163
|
// - 这里产出的 env 是 agent 全生命周期共享的 mutable 对象引用。
|
|
169
|
-
// -
|
|
164
|
+
// - context / shell 都持有同一引用;后续 `agent.setEnv()` 会原地修改它。
|
|
170
165
|
const env = resolveAgentEnv(path, this.options.env);
|
|
171
166
|
const instruction = normalizeInstructionInput(this.options.instruction);
|
|
172
167
|
const config = this.load_config(id, path);
|
|
173
168
|
const plugin_instances = new Map<string, BasePlugin>();
|
|
174
169
|
|
|
175
|
-
const runtime = createAgentRuntime({
|
|
176
|
-
agent_id: id,
|
|
177
|
-
project_root: path,
|
|
178
|
-
logger,
|
|
179
|
-
config,
|
|
180
|
-
env,
|
|
181
|
-
systems: instruction,
|
|
182
|
-
plugin_instances,
|
|
183
|
-
get_session_port: this.get_session_port,
|
|
184
|
-
list_cached_sessions: this.list_cached_sessions,
|
|
185
|
-
});
|
|
186
|
-
|
|
187
170
|
this.register_plugins(plugin_instances, this.options.plugins || []);
|
|
188
171
|
|
|
189
172
|
// 关键点(中文)
|
|
@@ -204,20 +187,27 @@ export class AgentAssemblyService {
|
|
|
204
187
|
tools.plugin_call = tools.plugin_call || plugin_tools.plugin_call;
|
|
205
188
|
}
|
|
206
189
|
const resolve_session_model = this.resolve_session_model;
|
|
190
|
+
const paths = createAgentPathRuntime(path, id);
|
|
191
|
+
const pluginConfig = createAgentPluginConfigRuntime(path);
|
|
207
192
|
agent_context = new AgentContext({
|
|
208
|
-
agent: runtime,
|
|
209
193
|
cwd: path,
|
|
210
194
|
rootPath: path,
|
|
211
195
|
logger,
|
|
212
196
|
config,
|
|
213
197
|
env,
|
|
214
198
|
systems: instruction,
|
|
215
|
-
paths
|
|
216
|
-
pluginConfig
|
|
199
|
+
paths,
|
|
200
|
+
pluginConfig,
|
|
201
|
+
pluginInstances: plugin_instances,
|
|
217
202
|
session: {
|
|
218
203
|
get: (session_id) => this.get_session_port(session_id),
|
|
219
|
-
listExecutingSessionIds: () =>
|
|
220
|
-
|
|
204
|
+
listExecutingSessionIds: () =>
|
|
205
|
+
this.list_cached_sessions()
|
|
206
|
+
.filter((session) => session.isExecuting())
|
|
207
|
+
.map((session) => session.id),
|
|
208
|
+
getExecutingSessionCount: () =>
|
|
209
|
+
this.list_cached_sessions()
|
|
210
|
+
.filter((session) => session.isExecuting()).length,
|
|
221
211
|
resolveModel: async (session_id) =>
|
|
222
212
|
await resolve_session_model(session_id),
|
|
223
213
|
},
|
|
@@ -251,7 +241,6 @@ export class AgentAssemblyService {
|
|
|
251
241
|
plugin_instances,
|
|
252
242
|
plugin_registry,
|
|
253
243
|
plugins,
|
|
254
|
-
runtime,
|
|
255
244
|
agent_context: agent_context!,
|
|
256
245
|
...(shell ? { shell } : {}),
|
|
257
246
|
};
|
|
@@ -13,6 +13,11 @@ import type { Tool } from "ai";
|
|
|
13
13
|
import type { Logger } from "@/utils/logger/Logger.js";
|
|
14
14
|
import type {
|
|
15
15
|
AgentCreateSessionInput,
|
|
16
|
+
AgentArchiveSessionInput,
|
|
17
|
+
AgentArchiveSessionsInput,
|
|
18
|
+
AgentArchiveSessionResult,
|
|
19
|
+
AgentArchiveSessionsResult,
|
|
20
|
+
AgentCleanArchiveResult,
|
|
16
21
|
AgentListSessionsInput,
|
|
17
22
|
AgentManagedSession,
|
|
18
23
|
AgentModel,
|
|
@@ -24,16 +29,26 @@ import type {
|
|
|
24
29
|
} from "@/types/agent/AgentTypes.js";
|
|
25
30
|
import { Session } from "@/session/Session.js";
|
|
26
31
|
import {
|
|
32
|
+
getSdkAgentArchivedSessionDirPath,
|
|
33
|
+
getSdkAgentArchivedSessionsDirPath,
|
|
27
34
|
getSdkAgentSessionDirPath,
|
|
35
|
+
listArchivedAgentSessionSummaryPage,
|
|
28
36
|
listAgentSessionSummaryPage,
|
|
29
37
|
} from "@/session/index.js";
|
|
30
|
-
import type { AgentRuntime } from "@/types/runtime/agent/AgentRuntime.js";
|
|
31
38
|
import type { AgentContext } from "@/types/runtime/agent/AgentContext.js";
|
|
32
39
|
import type { SessionPort } from "@/types/runtime/agent/AgentContext.js";
|
|
33
40
|
import type { BasePlugin } from "@/plugin/core/BasePlugin.js";
|
|
34
41
|
import { isPluginEnabled } from "@/plugin/core/Activation.js";
|
|
35
42
|
import { createInstructionSystemBlocks } from "@/agent/local/AgentInstructions.js";
|
|
36
43
|
|
|
44
|
+
function decodeMaybe(input: string): string {
|
|
45
|
+
try {
|
|
46
|
+
return decodeURIComponent(input);
|
|
47
|
+
} catch {
|
|
48
|
+
return input;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
37
52
|
type AgentSessionManagerOptions = {
|
|
38
53
|
/**
|
|
39
54
|
* 当前 agent 稳定标识。
|
|
@@ -55,13 +70,11 @@ type AgentSessionManagerOptions = {
|
|
|
55
70
|
*/
|
|
56
71
|
logger: Logger;
|
|
57
72
|
|
|
58
|
-
/**
|
|
59
|
-
* 当前 agent runtime。
|
|
60
|
-
*/
|
|
61
|
-
runtime: AgentRuntime;
|
|
62
|
-
|
|
63
73
|
/**
|
|
64
74
|
* 延迟读取当前 agent context。
|
|
75
|
+
*
|
|
76
|
+
* 关键点(中文)
|
|
77
|
+
* - 装配期通过 getter 拿到 context,避免循环引用。
|
|
65
78
|
*/
|
|
66
79
|
get_agent_context: () => AgentContext;
|
|
67
80
|
|
|
@@ -94,7 +107,6 @@ export class AgentSessionManager {
|
|
|
94
107
|
private readonly project_root: string;
|
|
95
108
|
private readonly tools: Record<string, Tool>;
|
|
96
109
|
private readonly logger: Logger;
|
|
97
|
-
private readonly runtime: AgentRuntime;
|
|
98
110
|
private readonly get_agent_context: AgentSessionManagerOptions["get_agent_context"];
|
|
99
111
|
private readonly get_instruction: AgentSessionManagerOptions["get_instruction"];
|
|
100
112
|
private readonly plugin_instances: Map<string, BasePlugin>;
|
|
@@ -109,16 +121,18 @@ export class AgentSessionManager {
|
|
|
109
121
|
this.project_root = options.project_root;
|
|
110
122
|
this.tools = options.tools;
|
|
111
123
|
this.logger = options.logger;
|
|
112
|
-
this.runtime = options.runtime;
|
|
113
124
|
this.get_agent_context = options.get_agent_context;
|
|
114
125
|
this.get_instruction = options.get_instruction;
|
|
115
126
|
this.plugin_instances = options.plugin_instances;
|
|
116
127
|
this.default_model = options.default_model;
|
|
117
128
|
this.SessionClass = options.SessionClass || Session;
|
|
118
129
|
this.session_collection = {
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
130
|
+
create_session: async (input) => await this.create_session(input),
|
|
131
|
+
get_session: async (session_id) => await this.get_session(session_id),
|
|
132
|
+
list_sessions: async (input) => await this.list_sessions(input),
|
|
133
|
+
archive_session: async (input) => await this.archive_session(input),
|
|
134
|
+
archive_sessions: async (input) => await this.archive_sessions(input),
|
|
135
|
+
clean_archive: async () => await this.clean_archive(),
|
|
122
136
|
};
|
|
123
137
|
}
|
|
124
138
|
|
|
@@ -180,7 +194,7 @@ export class AgentSessionManager {
|
|
|
180
194
|
async get_session(session_id: string): Promise<AgentSession> {
|
|
181
195
|
const resolved_session_id = String(session_id || "").trim();
|
|
182
196
|
if (!resolved_session_id) {
|
|
183
|
-
throw new Error("
|
|
197
|
+
throw new Error("get_session requires a non-empty sessionId");
|
|
184
198
|
}
|
|
185
199
|
const session_dir_path = getSdkAgentSessionDirPath(
|
|
186
200
|
this.project_root,
|
|
@@ -211,10 +225,110 @@ export class AgentSessionManager {
|
|
|
211
225
|
projectRoot: this.project_root,
|
|
212
226
|
agentId: this.agent_id,
|
|
213
227
|
input,
|
|
214
|
-
executingSessionIds: new Set(this.
|
|
228
|
+
executingSessionIds: new Set(this.get_agent_context().listExecutingSessionIds()),
|
|
215
229
|
});
|
|
216
230
|
}
|
|
217
231
|
|
|
232
|
+
/**
|
|
233
|
+
* 归档单个 session。
|
|
234
|
+
*/
|
|
235
|
+
async archive_session(
|
|
236
|
+
input: AgentArchiveSessionInput,
|
|
237
|
+
): Promise<AgentArchiveSessionResult> {
|
|
238
|
+
const session_id = String(input?.id || "").trim();
|
|
239
|
+
if (!session_id) {
|
|
240
|
+
throw new Error("archive_session requires a non-empty id");
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const executing_session_ids = new Set(
|
|
244
|
+
this.get_agent_context().listExecutingSessionIds(),
|
|
245
|
+
);
|
|
246
|
+
if (executing_session_ids.has(session_id)) {
|
|
247
|
+
throw new Error(`Session "${session_id}" is currently executing`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const source_path = getSdkAgentSessionDirPath(
|
|
251
|
+
this.project_root,
|
|
252
|
+
this.agent_id,
|
|
253
|
+
session_id,
|
|
254
|
+
);
|
|
255
|
+
if (!(await fs.pathExists(source_path))) {
|
|
256
|
+
throw new Error(`Session "${session_id}" not found`);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
const target_path = getSdkAgentArchivedSessionDirPath(
|
|
260
|
+
this.project_root,
|
|
261
|
+
this.agent_id,
|
|
262
|
+
session_id,
|
|
263
|
+
);
|
|
264
|
+
if (await fs.pathExists(target_path)) {
|
|
265
|
+
throw new Error(`Archived session "${session_id}" already exists`);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
await fs.ensureDir(getSdkAgentArchivedSessionsDirPath(
|
|
269
|
+
this.project_root,
|
|
270
|
+
this.agent_id,
|
|
271
|
+
));
|
|
272
|
+
await fs.move(source_path, target_path);
|
|
273
|
+
|
|
274
|
+
// 关键点(中文):归档后清理缓存,避免后续操作访问已移动目录。
|
|
275
|
+
this.sessions_by_id.delete(session_id);
|
|
276
|
+
this.configured_session_ids.delete(session_id);
|
|
277
|
+
|
|
278
|
+
return {
|
|
279
|
+
sessionId: session_id,
|
|
280
|
+
archivedAt: Date.now(),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* 列出当前 agent 的已归档 session 摘要页。
|
|
286
|
+
*/
|
|
287
|
+
async archive_sessions(
|
|
288
|
+
input?: AgentArchiveSessionsInput,
|
|
289
|
+
): Promise<AgentArchiveSessionsResult> {
|
|
290
|
+
return await listArchivedAgentSessionSummaryPage({
|
|
291
|
+
projectRoot: this.project_root,
|
|
292
|
+
agentId: this.agent_id,
|
|
293
|
+
input,
|
|
294
|
+
});
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 永久清空已归档 session。
|
|
299
|
+
*/
|
|
300
|
+
async clean_archive(): Promise<AgentCleanArchiveResult> {
|
|
301
|
+
const archived_root = getSdkAgentArchivedSessionsDirPath(
|
|
302
|
+
this.project_root,
|
|
303
|
+
this.agent_id,
|
|
304
|
+
);
|
|
305
|
+
if (!(await fs.pathExists(archived_root))) {
|
|
306
|
+
return {
|
|
307
|
+
removedSessionIds: [],
|
|
308
|
+
};
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
const entries = await fs.readdir(archived_root, { withFileTypes: true });
|
|
312
|
+
const removed_session_ids: string[] = [];
|
|
313
|
+
|
|
314
|
+
for (const entry of entries) {
|
|
315
|
+
if (!entry.isDirectory()) continue;
|
|
316
|
+
const session_id = decodeMaybe(entry.name);
|
|
317
|
+
if (!session_id) continue;
|
|
318
|
+
const session_path = getSdkAgentArchivedSessionDirPath(
|
|
319
|
+
this.project_root,
|
|
320
|
+
this.agent_id,
|
|
321
|
+
session_id,
|
|
322
|
+
);
|
|
323
|
+
await fs.remove(session_path);
|
|
324
|
+
removed_session_ids.push(session_id);
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
return {
|
|
328
|
+
removedSessionIds: removed_session_ids,
|
|
329
|
+
};
|
|
330
|
+
}
|
|
331
|
+
|
|
218
332
|
private get_or_create_session(input?: {
|
|
219
333
|
/**
|
|
220
334
|
* 可选指定 session id。
|
|
@@ -8,9 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import type {
|
|
11
|
-
|
|
12
|
-
AgentListSessionsInput,
|
|
13
|
-
AgentSessionSummaryPage,
|
|
11
|
+
AgentSessionCollection,
|
|
14
12
|
RemoteAgentPluginActionInput,
|
|
15
13
|
RemoteAgentPluginActionResult,
|
|
16
14
|
RemoteAgentOptions,
|
|
@@ -42,37 +40,6 @@ export class RemoteAgent {
|
|
|
42
40
|
this.transport = create_remote_agent_transport(url, options.token);
|
|
43
41
|
}
|
|
44
42
|
|
|
45
|
-
/**
|
|
46
|
-
* 新建一个远程 session。
|
|
47
|
-
*/
|
|
48
|
-
async createSession(
|
|
49
|
-
input?: AgentCreateSessionInput,
|
|
50
|
-
): Promise<RemoteAgentSession> {
|
|
51
|
-
const info = await this.transport.create_session(input);
|
|
52
|
-
return new RemoteSession(this.transport, info.sessionId);
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
/**
|
|
56
|
-
* 获取一个已存在的远程 session。
|
|
57
|
-
*/
|
|
58
|
-
async getSession(sessionId: string): Promise<RemoteAgentSession> {
|
|
59
|
-
const resolved_session_id = String(sessionId || "").trim();
|
|
60
|
-
if (!resolved_session_id) {
|
|
61
|
-
throw new Error("getSession requires a non-empty sessionId");
|
|
62
|
-
}
|
|
63
|
-
const info = await this.transport.get_info(resolved_session_id);
|
|
64
|
-
return new RemoteSession(this.transport, info.sessionId);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* 列出远程 agent 的 session 摘要页。
|
|
69
|
-
*/
|
|
70
|
-
async listSessions(
|
|
71
|
-
input?: AgentListSessionsInput,
|
|
72
|
-
): Promise<AgentSessionSummaryPage> {
|
|
73
|
-
return await this.transport.list_sessions(input);
|
|
74
|
-
}
|
|
75
|
-
|
|
76
43
|
/**
|
|
77
44
|
* 执行远程 Agent runtime 内的 plugin action。
|
|
78
45
|
*
|
|
@@ -153,4 +120,28 @@ export class RemoteAgent {
|
|
|
153
120
|
async close(): Promise<void> {
|
|
154
121
|
await this.transport.close?.();
|
|
155
122
|
}
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* 返回当前远程 session collection 入口。
|
|
126
|
+
*
|
|
127
|
+
* 关键点(中文)
|
|
128
|
+
* - `RemoteAgentTransport` 只负责协议传输,不直接等价于 `AgentSessionCollection`。
|
|
129
|
+
* - 这里把 transport 包装为 `AgentSessionCollection`,`create_session` / `get_session` 返回 `RemoteSession` 实例。
|
|
130
|
+
*/
|
|
131
|
+
session_collection(): AgentSessionCollection {
|
|
132
|
+
return {
|
|
133
|
+
create_session: async (input) => {
|
|
134
|
+
const info = await this.transport.create_session(input);
|
|
135
|
+
return new RemoteSession(this.transport, info);
|
|
136
|
+
},
|
|
137
|
+
get_session: async (session_id) => {
|
|
138
|
+
const info = await this.transport.get_info(session_id);
|
|
139
|
+
return new RemoteSession(this.transport, info);
|
|
140
|
+
},
|
|
141
|
+
list_sessions: async (input) => await this.transport.list_sessions(input),
|
|
142
|
+
archive_session: async (input) => await this.transport.archive_session(input),
|
|
143
|
+
archive_sessions: async (input) => await this.transport.archive_sessions(input),
|
|
144
|
+
clean_archive: async () => await this.transport.clean_archive(),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
156
147
|
}
|
|
@@ -7,14 +7,16 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import type {
|
|
10
|
+
AgentSession,
|
|
11
|
+
AgentSessionConfigSnapshot,
|
|
10
12
|
AgentSessionForkInput,
|
|
11
13
|
AgentSessionHistoryInput,
|
|
12
14
|
AgentSessionHistoryPage,
|
|
13
15
|
AgentSessionInfo,
|
|
14
16
|
AgentSessionSetInput,
|
|
15
17
|
AgentSessionSystemSnapshot,
|
|
16
|
-
RemoteAgentSession,
|
|
17
18
|
} from "@/types/agent/AgentTypes.js";
|
|
19
|
+
import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
|
|
18
20
|
import type {
|
|
19
21
|
AgentSessionEvent,
|
|
20
22
|
AgentSessionSubscriber,
|
|
@@ -50,8 +52,10 @@ type RemoteTurnLifecycle = {
|
|
|
50
52
|
/**
|
|
51
53
|
* 远程 Session 客户端。
|
|
52
54
|
*/
|
|
53
|
-
export class RemoteSession implements
|
|
55
|
+
export class RemoteSession implements AgentSession {
|
|
54
56
|
readonly id: string;
|
|
57
|
+
readonly agentId: string;
|
|
58
|
+
readonly config: AgentSessionConfigSnapshot;
|
|
55
59
|
|
|
56
60
|
private readonly transport: RemoteSessionTransport;
|
|
57
61
|
private readonly event_hub = new SessionEventHub();
|
|
@@ -62,9 +66,12 @@ export class RemoteSession implements RemoteAgentSession {
|
|
|
62
66
|
private event_subscriber_count = 0;
|
|
63
67
|
private event_subscription: TransportSubscription | null = null;
|
|
64
68
|
|
|
65
|
-
constructor(transport: RemoteSessionTransport,
|
|
69
|
+
constructor(transport: RemoteSessionTransport, info: AgentSessionInfo) {
|
|
66
70
|
this.transport = transport;
|
|
67
|
-
this.id =
|
|
71
|
+
this.id = info.sessionId;
|
|
72
|
+
this.agentId = info.agentId;
|
|
73
|
+
// 远程 session 不暴露服务端模型实例,config 返回空快照。
|
|
74
|
+
this.config = {} as AgentSessionConfigSnapshot;
|
|
68
75
|
}
|
|
69
76
|
|
|
70
77
|
/**
|
|
@@ -98,6 +105,14 @@ export class RemoteSession implements RemoteAgentSession {
|
|
|
98
105
|
return create_turn_handle(lifecycle);
|
|
99
106
|
}
|
|
100
107
|
|
|
108
|
+
/**
|
|
109
|
+
* 停止当前远程 session turn,并取消未吸收队列。
|
|
110
|
+
*/
|
|
111
|
+
async stop(): Promise<AgentSessionStopResult> {
|
|
112
|
+
await this.ensure_event_pump();
|
|
113
|
+
return await this.transport.stop(this.id);
|
|
114
|
+
}
|
|
115
|
+
|
|
101
116
|
/**
|
|
102
117
|
* 订阅当前远程 session 的 future 事件。
|
|
103
118
|
*/
|
|
@@ -134,9 +149,9 @@ export class RemoteSession implements RemoteAgentSession {
|
|
|
134
149
|
/**
|
|
135
150
|
* 分叉远程 session。
|
|
136
151
|
*/
|
|
137
|
-
async fork(input?: AgentSessionForkInput | string): Promise<
|
|
152
|
+
async fork(input?: AgentSessionForkInput | string): Promise<AgentSession> {
|
|
138
153
|
const info = await this.transport.fork(this.id, input);
|
|
139
|
-
return new RemoteSession(this.transport, info
|
|
154
|
+
return new RemoteSession(this.transport, info);
|
|
140
155
|
}
|
|
141
156
|
|
|
142
157
|
private async ensure_event_pump(): Promise<void> {
|