@downcity/agent 1.1.232 → 1.1.236
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 +1 -1
- package/bin/agent/local/Agent.js +2 -2
- package/bin/agent/local/Agent.js.map +1 -1
- package/bin/agent/local/services/AgentSessions.d.ts +4 -4
- package/bin/agent/local/services/AgentSessions.d.ts.map +1 -1
- package/bin/agent/local/services/AgentSessions.js +8 -8
- package/bin/agent/local/services/AgentSessions.js.map +1 -1
- package/bin/agent/remote/RemoteSession.d.ts +2 -0
- package/bin/agent/remote/RemoteSession.d.ts.map +1 -1
- package/bin/agent/remote/RemoteSession.js +4 -0
- package/bin/agent/remote/RemoteSession.js.map +1 -1
- package/bin/agent/remote/RemoteTransport.d.ts +2 -0
- package/bin/agent/remote/RemoteTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts +1 -0
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +11 -0
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts +1 -0
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.js +3 -0
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
- package/bin/executor/Executor.d.ts +20 -2
- package/bin/executor/Executor.d.ts.map +1 -1
- package/bin/executor/Executor.js +102 -18
- package/bin/executor/Executor.js.map +1 -1
- package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts +6 -0
- package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts +0 -9
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js +4 -38
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.d.ts +1 -3
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.d.ts.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.js +7 -59
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.js.map +1 -1
- package/bin/executor/core-engine/CoreEngineContextCompaction.d.ts +38 -0
- package/bin/executor/core-engine/CoreEngineContextCompaction.d.ts.map +1 -0
- package/bin/executor/core-engine/CoreEngineContextCompaction.js +392 -0
- package/bin/executor/core-engine/CoreEngineContextCompaction.js.map +1 -0
- package/bin/executor/core-engine/CoreEngineMessageState.d.ts +15 -0
- package/bin/executor/core-engine/CoreEngineMessageState.d.ts.map +1 -1
- package/bin/executor/core-engine/CoreEngineMessageState.js +20 -0
- package/bin/executor/core-engine/CoreEngineMessageState.js.map +1 -1
- package/bin/executor/core-engine/CoreEngineRunner.d.ts +9 -5
- package/bin/executor/core-engine/CoreEngineRunner.d.ts.map +1 -1
- package/bin/executor/core-engine/CoreEngineRunner.js +146 -35
- package/bin/executor/core-engine/CoreEngineRunner.js.map +1 -1
- package/bin/executor/store/history/SessionHistoryStore.d.ts +4 -10
- package/bin/executor/store/history/SessionHistoryStore.d.ts.map +1 -1
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.d.ts.map +1 -1
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js +1 -3
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js.map +1 -1
- package/bin/executor/types/SessionRun.d.ts +8 -0
- package/bin/executor/types/SessionRun.d.ts.map +1 -1
- package/bin/index.d.ts +1 -1
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +1 -1
- package/bin/index.js.map +1 -1
- package/bin/plugin/core/PluginLocalExecution.js +3 -3
- package/bin/plugin/core/PluginLocalExecution.js.map +1 -1
- package/bin/plugin/core/PluginRegistry.d.ts +20 -2
- package/bin/plugin/core/PluginRegistry.d.ts.map +1 -1
- package/bin/plugin/core/PluginRegistry.js +100 -3
- package/bin/plugin/core/PluginRegistry.js.map +1 -1
- package/bin/rpc/Client.d.ts +4 -0
- package/bin/rpc/Client.d.ts.map +1 -1
- package/bin/rpc/Client.js +11 -0
- package/bin/rpc/Client.js.map +1 -1
- package/bin/session/Session.d.ts +7 -3
- package/bin/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +26 -19
- package/bin/session/Session.js.map +1 -1
- package/bin/session/SessionSystemBuilder.d.ts.map +1 -1
- package/bin/session/SessionSystemBuilder.js +3 -1
- package/bin/session/SessionSystemBuilder.js.map +1 -1
- package/bin/session/recorder/SessionRecorderCompaction.d.ts.map +1 -1
- package/bin/session/recorder/SessionRecorderCompaction.js +102 -60
- package/bin/session/recorder/SessionRecorderCompaction.js.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.d.ts +11 -7
- package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.js +22 -24
- package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
- package/bin/session/services/SessionStateService.d.ts +10 -6
- package/bin/session/services/SessionStateService.d.ts.map +1 -1
- package/bin/session/services/SessionStateService.js +29 -20
- package/bin/session/services/SessionStateService.js.map +1 -1
- package/bin/session/services/SessionTurnService.d.ts +13 -3
- package/bin/session/services/SessionTurnService.d.ts.map +1 -1
- package/bin/session/services/SessionTurnService.js +90 -7
- package/bin/session/services/SessionTurnService.js.map +1 -1
- package/bin/types/agent/SessionActor.d.ts +8 -0
- package/bin/types/agent/SessionActor.d.ts.map +1 -1
- package/bin/types/executor/SessionRunContext.d.ts +16 -8
- package/bin/types/executor/SessionRunContext.d.ts.map +1 -1
- package/bin/types/plugin/PluginRuntime.d.ts +36 -4
- package/bin/types/plugin/PluginRuntime.d.ts.map +1 -1
- package/bin/types/plugin/PluginState.d.ts +10 -0
- package/bin/types/plugin/PluginState.d.ts.map +1 -1
- package/bin/types/rpc/RpcProtocol.d.ts +10 -0
- package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
- package/bin/types/runtime/agent/AgentContext.d.ts +2 -2
- package/bin/types/runtime/agent/AgentContext.js +1 -1
- package/bin/types/session/SessionLocalState.d.ts +2 -2
- package/bin/types/session/SessionOptions.d.ts +5 -5
- package/bin/types/session/SessionOptions.d.ts.map +1 -1
- package/bin/types/session/SessionQueueCommand.d.ts +68 -0
- package/bin/types/session/SessionQueueCommand.d.ts.map +1 -0
- package/bin/types/session/SessionQueueCommand.js +10 -0
- package/bin/types/session/SessionQueueCommand.js.map +1 -0
- package/bin/utils/logger/Logger.d.ts +7 -9
- package/bin/utils/logger/Logger.d.ts.map +1 -1
- package/bin/utils/logger/Logger.js +11 -9
- package/bin/utils/logger/Logger.js.map +1 -1
- package/package.json +3 -3
- package/scripts/core-engine-context-compaction.test.mjs +411 -0
- package/scripts/logger-isolation.test.mjs +55 -0
- package/scripts/multi-agent-plugin-isolation.test.mjs +289 -0
- package/scripts/plugin-tool-bridge.test.mjs +52 -0
- package/scripts/remote-session-reconnect.test.mjs +18 -0
- package/scripts/session-compaction-context-window.test.mjs +27 -26
- package/scripts/session-config-turn-boundary.test.mjs +80 -26
- package/scripts/session-prompt-runtime.test.mjs +23 -15
- package/scripts/session-recorder.test.mjs +15 -24
- package/src/agent/local/Agent.ts +2 -2
- package/src/agent/local/services/AgentSessions.ts +10 -10
- package/src/agent/remote/RemoteSession.ts +5 -0
- package/src/agent/remote/RemoteTransport.ts +2 -0
- package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +15 -0
- package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +4 -0
- package/src/executor/Executor.ts +111 -19
- package/src/executor/composer/compaction/SessionCompactionComposer.ts +7 -0
- package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.ts +4 -57
- package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionExecutor.ts +8 -66
- package/src/executor/core-engine/CoreEngineContextCompaction.ts +477 -0
- package/src/executor/core-engine/CoreEngineMessageState.ts +29 -0
- package/src/executor/core-engine/CoreEngineRunner.ts +200 -48
- package/src/executor/store/history/SessionHistoryStore.ts +4 -12
- package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +1 -3
- package/src/executor/types/SessionRun.ts +9 -0
- package/src/index.ts +1 -1
- package/src/plugin/core/PluginLocalExecution.ts +3 -3
- package/src/plugin/core/PluginRegistry.ts +116 -3
- package/src/rpc/Client.ts +12 -0
- package/src/session/Session.ts +28 -20
- package/src/session/SessionSystemBuilder.ts +4 -1
- package/src/session/recorder/SessionRecorderCompaction.ts +113 -65
- package/src/session/runtime/SessionPromptRuntime.ts +31 -46
- package/src/session/services/SessionStateService.ts +34 -25
- package/src/session/services/SessionTurnService.ts +97 -8
- package/src/types/agent/SessionActor.ts +9 -0
- package/src/types/executor/SessionRunContext.ts +17 -8
- package/src/types/plugin/PluginRuntime.ts +38 -4
- package/src/types/plugin/PluginState.ts +10 -0
- package/src/types/rpc/RpcProtocol.ts +11 -0
- package/src/types/runtime/agent/AgentContext.ts +2 -2
- package/src/types/session/SessionLocalState.ts +2 -2
- package/src/types/session/SessionOptions.ts +6 -6
- package/src/types/session/SessionQueueCommand.ts +78 -0
- package/src/utils/logger/Logger.ts +10 -10
- package/tsconfig.tsbuildinfo +1 -1
- package/bin/types/session/SessionConfigMutation.d.ts +0 -68
- package/bin/types/session/SessionConfigMutation.d.ts.map +0 -1
- package/bin/types/session/SessionConfigMutation.js +0 -10
- package/bin/types/session/SessionConfigMutation.js.map +0 -1
- package/src/types/session/SessionConfigMutation.ts +0 -78
|
@@ -139,8 +139,9 @@ test("SessionPromptRuntime merges queued prompts at the next step boundary", asy
|
|
|
139
139
|
);
|
|
140
140
|
});
|
|
141
141
|
|
|
142
|
-
test("SessionPromptRuntime applies config and steer in queue order
|
|
142
|
+
test("SessionPromptRuntime applies config and steer in queue order at the same step checkpoint", async () => {
|
|
143
143
|
const lifecycle = [];
|
|
144
|
+
const applied_turn_ids = [];
|
|
144
145
|
const execution_finished = createDeferred();
|
|
145
146
|
let step_merge = null;
|
|
146
147
|
|
|
@@ -164,20 +165,24 @@ test("SessionPromptRuntime applies config and steer in queue order before the ne
|
|
|
164
165
|
stopTurn: () => false,
|
|
165
166
|
});
|
|
166
167
|
|
|
167
|
-
runtime.
|
|
168
|
-
|
|
168
|
+
runtime.enqueue_command({
|
|
169
|
+
type: "command",
|
|
170
|
+
command_id: "config-1",
|
|
169
171
|
scope: "agent",
|
|
170
|
-
|
|
171
|
-
|
|
172
|
+
execute: async ({ turn_id }) => {
|
|
173
|
+
applied_turn_ids.push(turn_id);
|
|
174
|
+
lifecycle.push("config:1");
|
|
172
175
|
},
|
|
173
176
|
});
|
|
174
177
|
const first_turn = await runtime.prompt({ query: "first" });
|
|
175
178
|
const merge = await waitUntil(() => step_merge);
|
|
176
|
-
runtime.
|
|
177
|
-
|
|
179
|
+
runtime.enqueue_command({
|
|
180
|
+
type: "command",
|
|
181
|
+
command_id: "config-2",
|
|
178
182
|
scope: "session",
|
|
179
|
-
|
|
180
|
-
|
|
183
|
+
execute: async ({ turn_id }) => {
|
|
184
|
+
applied_turn_ids.push(turn_id);
|
|
185
|
+
lifecycle.push("config:2");
|
|
181
186
|
},
|
|
182
187
|
});
|
|
183
188
|
const second_turn_promise = runtime.prompt({ query: "second" });
|
|
@@ -191,17 +196,18 @@ test("SessionPromptRuntime applies config and steer in queue order before the ne
|
|
|
191
196
|
["second"],
|
|
192
197
|
);
|
|
193
198
|
assert.deepEqual(lifecycle, [
|
|
194
|
-
"config:1
|
|
199
|
+
"config:1",
|
|
195
200
|
"prompt:first",
|
|
196
|
-
"config:2
|
|
201
|
+
"config:2",
|
|
197
202
|
"steer:second",
|
|
198
203
|
]);
|
|
204
|
+
assert.deepEqual(applied_turn_ids, [first_turn.id, first_turn.id]);
|
|
199
205
|
|
|
200
206
|
execution_finished.resolve();
|
|
201
207
|
await first_turn.finished;
|
|
202
208
|
});
|
|
203
209
|
|
|
204
|
-
test("SessionPromptRuntime keeps
|
|
210
|
+
test("SessionPromptRuntime keeps command queued without starting a turn", async () => {
|
|
205
211
|
let apply_count = 0;
|
|
206
212
|
const runtime = new SessionPromptRuntime({
|
|
207
213
|
sessionId: "test",
|
|
@@ -212,15 +218,17 @@ test("SessionPromptRuntime keeps config queued without starting a turn", async (
|
|
|
212
218
|
stopTurn: () => false,
|
|
213
219
|
});
|
|
214
220
|
|
|
215
|
-
runtime.
|
|
216
|
-
|
|
221
|
+
runtime.enqueue_command({
|
|
222
|
+
type: "command",
|
|
223
|
+
command_id: "config-only",
|
|
217
224
|
scope: "agent",
|
|
218
|
-
|
|
225
|
+
execute: async () => {
|
|
219
226
|
apply_count += 1;
|
|
220
227
|
},
|
|
221
228
|
});
|
|
222
229
|
|
|
223
230
|
assert.equal(runtime.isActive(), false);
|
|
231
|
+
assert.equal(runtime.has_pending_command(), true);
|
|
224
232
|
assert.equal(apply_count, 0);
|
|
225
233
|
});
|
|
226
234
|
|
|
@@ -308,23 +308,21 @@ test("compact 把 Active 前缀关闭为带累计 Summary 的 Segment", async ()
|
|
|
308
308
|
const result = await history_store.compact({
|
|
309
309
|
model,
|
|
310
310
|
system: [],
|
|
311
|
-
|
|
312
|
-
maxInputTokensApprox: 1,
|
|
313
|
-
compactRatio: 0.5,
|
|
311
|
+
force: true,
|
|
314
312
|
});
|
|
315
313
|
assert.equal(result.compacted, true);
|
|
316
314
|
assert.equal((await fs.readFile(file_path, "utf8")).includes("message 1"), false);
|
|
317
315
|
const active = await recorder.list_messages({ include_internal: true });
|
|
318
|
-
assert.deepEqual(active.items.map((message) => message.sequence), [
|
|
316
|
+
assert.deepEqual(active.items.map((message) => message.sequence), []);
|
|
319
317
|
assert.equal(active.source, "active");
|
|
320
318
|
assert.equal(active.has_more, true);
|
|
321
319
|
const segment = await recorder.list_messages({
|
|
322
|
-
before_sequence:
|
|
320
|
+
before_sequence: 7,
|
|
323
321
|
include_internal: true,
|
|
324
322
|
});
|
|
325
|
-
assert.deepEqual(segment.items.map((message) => message.sequence), [1, 2, 3]);
|
|
323
|
+
assert.deepEqual(segment.items.map((message) => message.sequence), [1, 2, 3, 4, 5, 6]);
|
|
326
324
|
assert.equal(segment.source, "segment");
|
|
327
|
-
assert.equal((await history_store.list_records()).length,
|
|
325
|
+
assert.equal((await history_store.list_records()).length, 1);
|
|
328
326
|
await assert.rejects(
|
|
329
327
|
recorder.list_messages({ before_sequence: 0 }),
|
|
330
328
|
/before_sequence must be a positive integer/,
|
|
@@ -469,9 +467,7 @@ test("连续 Compact 生成按 sequence 连续的 Segment 与累计 Summary", as
|
|
|
469
467
|
const compact = async () => await history_store.compact({
|
|
470
468
|
model,
|
|
471
469
|
system: [],
|
|
472
|
-
|
|
473
|
-
maxInputTokensApprox: 1,
|
|
474
|
-
compactRatio: 0.5,
|
|
470
|
+
force: true,
|
|
475
471
|
});
|
|
476
472
|
|
|
477
473
|
await append_messages(1, 8);
|
|
@@ -483,19 +479,19 @@ test("连续 Compact 生成按 sequence 连续的 Segment 与累计 Summary", as
|
|
|
483
479
|
assert.equal(prompts[1].includes("<previous-summary>"), true);
|
|
484
480
|
assert.equal(prompts[1].includes("Summary 1"), true);
|
|
485
481
|
const active = await recorder.list_messages();
|
|
486
|
-
assert.deepEqual(active.items.map((message) => message.sequence), [
|
|
487
|
-
const latest_segment = await recorder.list_messages({ before_sequence:
|
|
488
|
-
assert.deepEqual(latest_segment.items.map((message) => message.sequence), [
|
|
482
|
+
assert.deepEqual(active.items.map((message) => message.sequence), []);
|
|
483
|
+
const latest_segment = await recorder.list_messages({ before_sequence: 13 });
|
|
484
|
+
assert.deepEqual(latest_segment.items.map((message) => message.sequence), [9, 10, 11, 12]);
|
|
489
485
|
assert.equal(latest_segment.has_more, true);
|
|
490
486
|
const earliest_segment = await recorder.list_messages({
|
|
491
487
|
before_sequence: latest_segment.start_sequence,
|
|
492
488
|
});
|
|
493
|
-
assert.deepEqual(earliest_segment.items.map((message) => message.sequence), [1, 2, 3, 4]);
|
|
489
|
+
assert.deepEqual(earliest_segment.items.map((message) => message.sequence), [1, 2, 3, 4, 5, 6, 7, 8]);
|
|
494
490
|
const context = await history_store.list_records();
|
|
495
491
|
assert.equal(context[0].parts[0]?.text, "Summary 2");
|
|
496
492
|
});
|
|
497
493
|
|
|
498
|
-
test("Summary
|
|
494
|
+
test("Summary 生成失败时使用确定性 Summary 完成归档", async () => {
|
|
499
495
|
const session_id = "compact-summary-failure-test";
|
|
500
496
|
const { recorder, file_path } = await create_recorder(session_id);
|
|
501
497
|
for (let index = 1; index <= 6; index += 1) {
|
|
@@ -520,18 +516,13 @@ test("Summary 生成失败时保留完整 Active 且不创建 Segment", async ()
|
|
|
520
516
|
const result = await history_store.compact({
|
|
521
517
|
model,
|
|
522
518
|
system: [],
|
|
523
|
-
|
|
524
|
-
maxInputTokensApprox: 1,
|
|
525
|
-
compactRatio: 0.5,
|
|
519
|
+
force: true,
|
|
526
520
|
});
|
|
527
521
|
|
|
528
|
-
assert.deepEqual(result, { compacted:
|
|
529
|
-
assert.deepEqual(
|
|
530
|
-
(await recorder.list_messages()).items.map((message) => message.sequence),
|
|
531
|
-
[1, 2, 3, 4, 5, 6],
|
|
532
|
-
);
|
|
522
|
+
assert.deepEqual(result, { compacted: true });
|
|
523
|
+
assert.deepEqual((await recorder.list_messages()).items, []);
|
|
533
524
|
const segment_entries = await fs.readdir(path.join(path.dirname(file_path), "segments"));
|
|
534
|
-
assert.
|
|
525
|
+
assert.equal(segment_entries.length, 1);
|
|
535
526
|
});
|
|
536
527
|
|
|
537
528
|
test("内部上下文读取完整快照并保留第 500 条之后的最新消息", async () => {
|
package/src/agent/local/Agent.ts
CHANGED
|
@@ -101,7 +101,7 @@ export class Agent {
|
|
|
101
101
|
plugin_registry.set_change_listener(({ type, plugin_name }) => {
|
|
102
102
|
const verb = type === "register" ? "registered" : "unregistered";
|
|
103
103
|
this.local_sessions.broadcast_plugins({
|
|
104
|
-
|
|
104
|
+
command_id: generateId(),
|
|
105
105
|
title: `Agent plugin ${plugin_name} ${verb}`,
|
|
106
106
|
plugins: plugin_registry.execution_view(),
|
|
107
107
|
});
|
|
@@ -171,7 +171,7 @@ export class Agent {
|
|
|
171
171
|
*
|
|
172
172
|
* 关键点(中文)
|
|
173
173
|
* - `null` / `undefined` 表示删除该 key;其他值会强制转字符串后写入。
|
|
174
|
-
* - configured env 原地更新;已有 Session
|
|
174
|
+
* - configured env 原地更新;已有 Session 在下一 step 检查点提交 effective env。
|
|
175
175
|
*/
|
|
176
176
|
patchEnv(patch: Record<string, string | null | undefined>): void {
|
|
177
177
|
this.apply_env_patch(patch);
|
|
@@ -91,7 +91,7 @@ type AgentSessionsOptions = {
|
|
|
91
91
|
/** 延迟读取当前 Agent configured env。 */
|
|
92
92
|
get_agent_env: () => Record<string, string>;
|
|
93
93
|
|
|
94
|
-
/** 创建当前 configured Plugin registry
|
|
94
|
+
/** 创建当前 configured Plugin registry 的 Session step 执行视图。 */
|
|
95
95
|
get_agent_plugins: () => AgentPluginExecutionRuntime;
|
|
96
96
|
|
|
97
97
|
/**
|
|
@@ -164,15 +164,15 @@ export class AgentSessions implements AgentSessionsApi<AgentSession> {
|
|
|
164
164
|
/**
|
|
165
165
|
* 把 Agent instruction 修改广播到已有 Session 的统一输入队列。
|
|
166
166
|
*/
|
|
167
|
-
broadcast_instruction(instruction: string[],
|
|
167
|
+
broadcast_instruction(instruction: string[], command_id: string): void {
|
|
168
168
|
const instruction_blocks = createInstructionSystemBlocks(
|
|
169
169
|
instruction,
|
|
170
170
|
this.project_root,
|
|
171
171
|
);
|
|
172
172
|
for (const session of this.sessions_by_id.values()) {
|
|
173
|
-
session.
|
|
173
|
+
session.enqueue_agent_command({
|
|
174
174
|
type: "instruction",
|
|
175
|
-
|
|
175
|
+
command_id,
|
|
176
176
|
instruction_blocks,
|
|
177
177
|
});
|
|
178
178
|
}
|
|
@@ -181,11 +181,11 @@ export class AgentSessions implements AgentSessionsApi<AgentSession> {
|
|
|
181
181
|
/**
|
|
182
182
|
* 把 Agent env 修改广播到已有 Session 的统一输入队列。
|
|
183
183
|
*/
|
|
184
|
-
broadcast_env(env: Record<string, string>,
|
|
184
|
+
broadcast_env(env: Record<string, string>, command_id: string): void {
|
|
185
185
|
for (const session of this.sessions_by_id.values()) {
|
|
186
|
-
session.
|
|
186
|
+
session.enqueue_agent_command({
|
|
187
187
|
type: "env",
|
|
188
|
-
|
|
188
|
+
command_id,
|
|
189
189
|
env: { ...env },
|
|
190
190
|
});
|
|
191
191
|
}
|
|
@@ -195,14 +195,14 @@ export class AgentSessions implements AgentSessionsApi<AgentSession> {
|
|
|
195
195
|
* 把 Plugin registry 修改广播到已有 Session 的统一输入队列。
|
|
196
196
|
*/
|
|
197
197
|
broadcast_plugins(input: {
|
|
198
|
-
|
|
198
|
+
command_id: string;
|
|
199
199
|
title: string;
|
|
200
200
|
plugins: AgentPluginExecutionRuntime;
|
|
201
201
|
}): void {
|
|
202
202
|
for (const session of this.sessions_by_id.values()) {
|
|
203
|
-
session.
|
|
203
|
+
session.enqueue_agent_command({
|
|
204
204
|
type: "plugins",
|
|
205
|
-
|
|
205
|
+
command_id: input.command_id,
|
|
206
206
|
title: input.title,
|
|
207
207
|
plugins: input.plugins,
|
|
208
208
|
});
|
|
@@ -123,6 +123,11 @@ export class RemoteSession implements RemoteAgentSession {
|
|
|
123
123
|
return await this.transport.stop(this.id);
|
|
124
124
|
}
|
|
125
125
|
|
|
126
|
+
/** 把一次显式历史压缩加入当前远程 Session 的有序输入队列。 */
|
|
127
|
+
async compact(): Promise<void> {
|
|
128
|
+
await this.transport.compact(this.id);
|
|
129
|
+
}
|
|
130
|
+
|
|
126
131
|
/** 订阅当前 Session 的全部未来 Mutation。 */
|
|
127
132
|
subscribe(subscriber: SessionMutationSubscriber): SessionMutationUnsubscribe {
|
|
128
133
|
this.event_subscriber_count += 1;
|
|
@@ -62,6 +62,8 @@ export type RemoteSessionTransport = {
|
|
|
62
62
|
): Promise<{ id: string }>;
|
|
63
63
|
/** 停止当前 session turn,并取消未吸收队列。 */
|
|
64
64
|
stop(session_id: string): Promise<AgentSessionStopResult>;
|
|
65
|
+
/** 把一次显式历史压缩加入远程 Session 的有序输入队列。 */
|
|
66
|
+
compact(session_id: string): Promise<void>;
|
|
65
67
|
/** 订阅 session 事件。 */
|
|
66
68
|
subscribe(params: {
|
|
67
69
|
session_id: string;
|
|
@@ -166,6 +166,21 @@ export class HttpRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
166
166
|
return payload.result;
|
|
167
167
|
}
|
|
168
168
|
|
|
169
|
+
async compact(session_id: string): Promise<void> {
|
|
170
|
+
const payload = await read_http_json<{
|
|
171
|
+
success?: boolean;
|
|
172
|
+
error?: string;
|
|
173
|
+
}>(`${this.base_url}/api/sdk/sessions/${encodeURIComponent(session_id)}/compact`, {
|
|
174
|
+
method: "POST",
|
|
175
|
+
headers: this.headers({
|
|
176
|
+
"Content-Type": "application/json",
|
|
177
|
+
}),
|
|
178
|
+
});
|
|
179
|
+
if (!payload.success) {
|
|
180
|
+
throw new Error(String(payload.error || "Remote session compact failed"));
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
169
184
|
async subscribe(params: {
|
|
170
185
|
session_id: string;
|
|
171
186
|
on_ready: () => void;
|
|
@@ -83,6 +83,10 @@ export class RpcRemoteAgentTransport implements RemoteAgentTransport {
|
|
|
83
83
|
return await this.client.stop_session(session_id);
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
+
async compact(session_id: string): Promise<void> {
|
|
87
|
+
await this.client.compact_session(session_id);
|
|
88
|
+
}
|
|
89
|
+
|
|
86
90
|
async subscribe(params: {
|
|
87
91
|
session_id: string;
|
|
88
92
|
on_ready: () => void;
|
package/src/executor/Executor.ts
CHANGED
|
@@ -342,6 +342,7 @@ export class Executor implements SessionExecutor {
|
|
|
342
342
|
);
|
|
343
343
|
return result;
|
|
344
344
|
} finally {
|
|
345
|
+
await this.release_step_plugins(run_context);
|
|
345
346
|
this.recovery_policy.reset_run_state();
|
|
346
347
|
if (this.abort_controller === abort_controller) {
|
|
347
348
|
this.abort_controller = null;
|
|
@@ -364,6 +365,7 @@ export class Executor implements SessionExecutor {
|
|
|
364
365
|
throw new Error("Executor.run requires historyComposer.sessionId");
|
|
365
366
|
}
|
|
366
367
|
|
|
368
|
+
await this.refresh_step_runtime(run_context);
|
|
367
369
|
const composed_context = await this.contextComposer.compose(run_context);
|
|
368
370
|
const tools = this.bind_run_scope_to_tools(composed_context.tools, run_context);
|
|
369
371
|
const system = await this.systemComposer.resolve(run_context);
|
|
@@ -386,14 +388,17 @@ export class Executor implements SessionExecutor {
|
|
|
386
388
|
};
|
|
387
389
|
const context_window = this.get_model_context_window?.();
|
|
388
390
|
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
391
|
+
if (retry_count > 0) {
|
|
392
|
+
await this.compactionComposer.run({
|
|
393
|
+
historyStore: this.historyStore,
|
|
394
|
+
model,
|
|
395
|
+
...(context_window !== undefined ? { context_window } : {}),
|
|
396
|
+
system,
|
|
397
|
+
retryCount: retry_count,
|
|
398
|
+
force: true,
|
|
399
|
+
onAction: emit_compaction_action,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
397
402
|
} catch (error) {
|
|
398
403
|
await this.emitAction(run_context, {
|
|
399
404
|
type: "action",
|
|
@@ -440,31 +445,79 @@ export class Executor implements SessionExecutor {
|
|
|
440
445
|
execute_input: input,
|
|
441
446
|
model,
|
|
442
447
|
run_context,
|
|
443
|
-
|
|
444
|
-
await this.
|
|
448
|
+
resolve_step_inputs: async () =>
|
|
449
|
+
await this.resolve_step_inputs(run_context),
|
|
445
450
|
});
|
|
446
451
|
}
|
|
447
452
|
|
|
448
453
|
/**
|
|
449
|
-
*
|
|
454
|
+
* 在 Assistant writer 完成后归档当前 canonical 历史。
|
|
455
|
+
*
|
|
456
|
+
* 关键点(中文):持久化 compact 不能发生在流式 Assistant 草稿仍打开时,
|
|
457
|
+
* 因此由 SessionTurnService 在 writer complete/fail 之后显式调用。
|
|
458
|
+
*/
|
|
459
|
+
async compact_history(run_context: SessionRunContext): Promise<{
|
|
460
|
+
compacted: boolean;
|
|
461
|
+
reason?: string;
|
|
462
|
+
}> {
|
|
463
|
+
let compaction_action_id = "";
|
|
464
|
+
try {
|
|
465
|
+
await this.refresh_step_runtime(run_context);
|
|
466
|
+
const model = this.resolveModelOrThrow();
|
|
467
|
+
const system = await this.systemComposer.resolve(run_context);
|
|
468
|
+
const emit_compaction_action = async (
|
|
469
|
+
action: SessionActionRecordV1,
|
|
470
|
+
): Promise<void> => {
|
|
471
|
+
if (action.state === "running") compaction_action_id = action.id;
|
|
472
|
+
await this.emitAction(run_context, action);
|
|
473
|
+
};
|
|
474
|
+
const context_window = this.get_model_context_window?.();
|
|
475
|
+
return await this.compactionComposer.run({
|
|
476
|
+
historyStore: this.historyStore,
|
|
477
|
+
model,
|
|
478
|
+
...(context_window !== undefined ? { context_window } : {}),
|
|
479
|
+
system,
|
|
480
|
+
retryCount: 0,
|
|
481
|
+
force: true,
|
|
482
|
+
onAction: emit_compaction_action,
|
|
483
|
+
});
|
|
484
|
+
} catch (error) {
|
|
485
|
+
await this.emitAction(run_context, {
|
|
486
|
+
type: "action",
|
|
487
|
+
id:
|
|
488
|
+
compaction_action_id ||
|
|
489
|
+
`compacting:${this.sessionId}:failed:${Date.now()}:${generateId()}`,
|
|
490
|
+
title: "Session records compact failed",
|
|
491
|
+
description: error instanceof Error ? error.message : String(error),
|
|
492
|
+
state: "failed",
|
|
493
|
+
metadata: {
|
|
494
|
+
v: 1,
|
|
495
|
+
ts: Date.now(),
|
|
496
|
+
sessionId: this.sessionId,
|
|
497
|
+
},
|
|
498
|
+
});
|
|
499
|
+
return { compacted: false, reason: "compact_failed" };
|
|
500
|
+
} finally {
|
|
501
|
+
await this.release_step_plugins(run_context);
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
/**
|
|
506
|
+
* 解析下一 Session step 实际使用的运行配置。
|
|
450
507
|
*
|
|
451
508
|
* 关键点(中文)
|
|
452
509
|
* - 调用方必须先提交 Session 统一输入队列,再调用本方法。
|
|
453
510
|
* - 每次调用只读取一次 model、system 与 tools,并把它们传给同一个 `streamText()`。
|
|
454
511
|
*/
|
|
455
|
-
private async
|
|
512
|
+
private async resolve_step_inputs(run_context: SessionRunContext): Promise<{
|
|
456
513
|
model: LanguageModel;
|
|
457
514
|
system: SessionExecuteInput["system"];
|
|
458
515
|
tools: SessionExecuteInput["tools"];
|
|
516
|
+
context_window?: number;
|
|
459
517
|
}> {
|
|
460
|
-
|
|
461
|
-
if (this.get_systems) {
|
|
462
|
-
run_context.agentSystems = Object.freeze([...this.get_systems()]);
|
|
463
|
-
}
|
|
464
|
-
if (this.get_plugins) {
|
|
465
|
-
run_context.agentPlugins = this.get_plugins();
|
|
466
|
-
}
|
|
518
|
+
await this.refresh_step_runtime(run_context);
|
|
467
519
|
const composed_context = await this.contextComposer.compose(run_context);
|
|
520
|
+
const context_window = this.get_model_context_window?.();
|
|
468
521
|
return {
|
|
469
522
|
model: this.resolveModelOrThrow(),
|
|
470
523
|
system: await this.systemComposer.resolve(run_context),
|
|
@@ -472,9 +525,42 @@ export class Executor implements SessionExecutor {
|
|
|
472
525
|
composed_context.tools,
|
|
473
526
|
run_context,
|
|
474
527
|
),
|
|
528
|
+
...(context_window !== undefined ? { context_window } : {}),
|
|
475
529
|
};
|
|
476
530
|
}
|
|
477
531
|
|
|
532
|
+
/**
|
|
533
|
+
* 刷新当前 Session step 的 effective Agent 运行视图。
|
|
534
|
+
*/
|
|
535
|
+
private async refresh_step_runtime(
|
|
536
|
+
run_context: SessionRunContext,
|
|
537
|
+
): Promise<void> {
|
|
538
|
+
await this.release_step_plugins(run_context);
|
|
539
|
+
run_context.agentEnv = Object.freeze({ ...this.getEnv() });
|
|
540
|
+
if (this.get_systems) {
|
|
541
|
+
run_context.agentSystems = Object.freeze([...this.get_systems()]);
|
|
542
|
+
} else {
|
|
543
|
+
delete run_context.agentSystems;
|
|
544
|
+
}
|
|
545
|
+
if (this.get_plugins) {
|
|
546
|
+
run_context.agentPlugins = this.get_plugins().acquire();
|
|
547
|
+
} else {
|
|
548
|
+
delete run_context.agentPlugins;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* 释放当前 Session step 持有的 Plugin execution lease。
|
|
554
|
+
*/
|
|
555
|
+
private async release_step_plugins(
|
|
556
|
+
run_context: SessionRunContext,
|
|
557
|
+
): Promise<void> {
|
|
558
|
+
const plugins = run_context.agentPlugins;
|
|
559
|
+
if (!plugins) return;
|
|
560
|
+
delete run_context.agentPlugins;
|
|
561
|
+
await plugins.release();
|
|
562
|
+
}
|
|
563
|
+
|
|
478
564
|
/**
|
|
479
565
|
* 为所有 tool 的 execute callback 绑定 ShellRunScope。
|
|
480
566
|
*
|
|
@@ -532,6 +618,12 @@ export class Executor implements SessionExecutor {
|
|
|
532
618
|
...(typeof input?.onStepCallback === "function"
|
|
533
619
|
? { onStepCallback: input.onStepCallback }
|
|
534
620
|
: {}),
|
|
621
|
+
...(typeof input?.hasPendingStepInput === "function"
|
|
622
|
+
? { hasPendingStepInput: input.hasPendingStepInput }
|
|
623
|
+
: {}),
|
|
624
|
+
...(typeof input?.consume_history_reload === "function"
|
|
625
|
+
? { consume_history_reload: input.consume_history_reload }
|
|
626
|
+
: {}),
|
|
535
627
|
...(typeof input?.onAssistantStepCallback === "function"
|
|
536
628
|
? { onAssistantStepCallback: input.onAssistantStepCallback }
|
|
537
629
|
: {}),
|
|
@@ -47,6 +47,13 @@ export type SessionCompactionInput = {
|
|
|
47
47
|
* - 回调由上层 Session 负责转成订阅事件与 action record。
|
|
48
48
|
*/
|
|
49
49
|
onAction?: (action: SessionActionRecordV1) => Promise<void>;
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* 是否明确要求持久化 deep compact。
|
|
53
|
+
*
|
|
54
|
+
* 普通模型调用不应因为历史字符数量而触发 compact;仅 usage 反馈或超限恢复会设置此值。
|
|
55
|
+
*/
|
|
56
|
+
force?: boolean;
|
|
50
57
|
};
|
|
51
58
|
|
|
52
59
|
/**
|
|
@@ -11,76 +11,23 @@ import type {
|
|
|
11
11
|
SessionCompactionInput,
|
|
12
12
|
} from "@executor/composer/compaction/SessionCompactionComposer.js";
|
|
13
13
|
|
|
14
|
-
type JsonlSessionCompactionComposerOptions = {
|
|
15
|
-
keepLastMessages?: number;
|
|
16
|
-
maxInputTokensApprox?: number;
|
|
17
|
-
compactRatio?: number;
|
|
18
|
-
};
|
|
19
|
-
|
|
20
14
|
/**
|
|
21
15
|
* JsonlSessionCompactionComposer 默认实现。
|
|
22
16
|
*/
|
|
23
17
|
export class JsonlSessionCompactionComposer implements SessionCompactionComposer {
|
|
24
18
|
readonly name = "summary_compaction_composer";
|
|
25
|
-
private readonly options: JsonlSessionCompactionComposerOptions;
|
|
26
|
-
|
|
27
|
-
constructor(options?: JsonlSessionCompactionComposerOptions) {
|
|
28
|
-
this.options = options || {};
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
private resolvePolicy(retryCount: number, context_window?: number): {
|
|
32
|
-
keepLastMessages: number;
|
|
33
|
-
maxInputTokensApprox: number;
|
|
34
|
-
compactRatio: number;
|
|
35
|
-
} {
|
|
36
|
-
const baseKeepLastMessages =
|
|
37
|
-
typeof this.options.keepLastMessages === "number"
|
|
38
|
-
? Math.max(6, Math.min(5000, Math.floor(this.options.keepLastMessages)))
|
|
39
|
-
: 30;
|
|
40
|
-
const configured_max_input_tokens =
|
|
41
|
-
typeof this.options.maxInputTokensApprox === "number"
|
|
42
|
-
&& Number.isFinite(this.options.maxInputTokensApprox)
|
|
43
|
-
? Math.max(2000, Math.floor(this.options.maxInputTokensApprox))
|
|
44
|
-
: undefined;
|
|
45
|
-
const model_context_window =
|
|
46
|
-
Number.isSafeInteger(context_window) && Number(context_window) > 0
|
|
47
|
-
? Number(context_window)
|
|
48
|
-
: undefined;
|
|
49
|
-
const baseMaxInputTokensApprox = configured_max_input_tokens
|
|
50
|
-
?? (model_context_window !== undefined
|
|
51
|
-
? Math.max(1, Math.floor(model_context_window * 0.8))
|
|
52
|
-
: 128000);
|
|
53
|
-
const retryFactor = Math.max(1, Math.pow(2, retryCount));
|
|
54
|
-
const keepLastMessages = Math.max(6, Math.floor(baseKeepLastMessages / retryFactor));
|
|
55
|
-
const minimum_input_tokens = Math.min(2000, baseMaxInputTokensApprox);
|
|
56
|
-
const maxInputTokensApprox = Math.max(
|
|
57
|
-
minimum_input_tokens,
|
|
58
|
-
Math.floor(baseMaxInputTokensApprox / retryFactor),
|
|
59
|
-
);
|
|
60
|
-
const compactRatioRaw =
|
|
61
|
-
typeof this.options.compactRatio === "number" &&
|
|
62
|
-
Number.isFinite(this.options.compactRatio)
|
|
63
|
-
? this.options.compactRatio
|
|
64
|
-
: 0.5;
|
|
65
|
-
const compactRatio = Math.max(0.1, Math.min(0.9, compactRatioRaw));
|
|
66
|
-
return {
|
|
67
|
-
keepLastMessages,
|
|
68
|
-
maxInputTokensApprox,
|
|
69
|
-
compactRatio,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
19
|
|
|
73
20
|
async run(input: SessionCompactionInput): Promise<{
|
|
74
21
|
compacted: boolean;
|
|
75
22
|
reason?: string;
|
|
76
23
|
}> {
|
|
77
|
-
|
|
24
|
+
if (!input.force && input.retryCount <= 0) {
|
|
25
|
+
return { compacted: false, reason: "not_requested" };
|
|
26
|
+
}
|
|
78
27
|
return await input.historyStore.compact({
|
|
79
28
|
model: input.model,
|
|
80
29
|
system: input.system,
|
|
81
|
-
|
|
82
|
-
maxInputTokensApprox: policy.maxInputTokensApprox,
|
|
83
|
-
compactRatio: policy.compactRatio,
|
|
30
|
+
force: true,
|
|
84
31
|
...(input.onAction ? { onAction: input.onAction } : {}),
|
|
85
32
|
});
|
|
86
33
|
}
|