@downcity/agent 1.1.216 → 1.1.222
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 +2 -0
- package/bin/agent/local/Agent.d.ts.map +1 -1
- package/bin/agent/local/Agent.js +6 -0
- package/bin/agent/local/Agent.js.map +1 -1
- package/bin/agent/local/services/AgentSessions.d.ts +6 -0
- package/bin/agent/local/services/AgentSessions.d.ts.map +1 -1
- package/bin/agent/local/services/AgentSessions.js +8 -1
- package/bin/agent/local/services/AgentSessions.js.map +1 -1
- package/bin/agent/remote/RemoteSession.d.ts +11 -7
- package/bin/agent/remote/RemoteSession.d.ts.map +1 -1
- package/bin/agent/remote/RemoteSession.js +36 -11
- package/bin/agent/remote/RemoteSession.js.map +1 -1
- package/bin/agent/remote/RemoteTransport.d.ts +9 -3
- package/bin/agent/remote/RemoteTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts +6 -2
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +38 -11
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts +6 -2
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.js +8 -2
- package/bin/agent/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
- package/bin/executor/types/SessionHistoryMeta.d.ts +2 -0
- package/bin/executor/types/SessionHistoryMeta.d.ts.map +1 -1
- package/bin/executor/types/SessionRecords.d.ts +2 -0
- package/bin/executor/types/SessionRecords.d.ts.map +1 -1
- package/bin/executor/types/SessionRecords.js.map +1 -1
- package/bin/index.d.ts +5 -2
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +1 -0
- package/bin/index.js.map +1 -1
- package/bin/rpc/Client.d.ts +18 -4
- package/bin/rpc/Client.d.ts.map +1 -1
- package/bin/rpc/Client.js +25 -3
- package/bin/rpc/Client.js.map +1 -1
- package/bin/session/Session.d.ts +13 -3
- package/bin/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +48 -18
- package/bin/session/Session.js.map +1 -1
- package/bin/session/browse/Browse.d.ts.map +1 -1
- package/bin/session/browse/Browse.js +3 -0
- package/bin/session/browse/Browse.js.map +1 -1
- package/bin/session/recorder/JsonlSessionMessageStore.d.ts +45 -0
- package/bin/session/recorder/JsonlSessionMessageStore.d.ts.map +1 -0
- package/bin/session/recorder/JsonlSessionMessageStore.js +127 -0
- package/bin/session/recorder/JsonlSessionMessageStore.js.map +1 -0
- package/bin/session/recorder/SessionMessageCodec.d.ts +20 -0
- package/bin/session/recorder/SessionMessageCodec.d.ts.map +1 -0
- package/bin/session/recorder/SessionMessageCodec.js +252 -0
- package/bin/session/recorder/SessionMessageCodec.js.map +1 -0
- package/bin/session/recorder/SessionMessageReducer.d.ts +12 -0
- package/bin/session/recorder/SessionMessageReducer.d.ts.map +1 -0
- package/bin/session/recorder/SessionMessageReducer.js +109 -0
- package/bin/session/recorder/SessionMessageReducer.js.map +1 -0
- package/bin/session/recorder/SessionRecorder.d.ts +177 -0
- package/bin/session/recorder/SessionRecorder.d.ts.map +1 -0
- package/bin/session/recorder/SessionRecorder.js +610 -0
- package/bin/session/recorder/SessionRecorder.js.map +1 -0
- package/bin/session/recorder/SessionRecorderCompaction.d.ts +18 -0
- package/bin/session/recorder/SessionRecorderCompaction.d.ts.map +1 -0
- package/bin/session/recorder/SessionRecorderCompaction.js +141 -0
- package/bin/session/recorder/SessionRecorderCompaction.js.map +1 -0
- package/bin/session/recorder/SessionRecorderHistoryStore.d.ts +66 -0
- package/bin/session/recorder/SessionRecorderHistoryStore.d.ts.map +1 -0
- package/bin/session/recorder/SessionRecorderHistoryStore.js +195 -0
- package/bin/session/recorder/SessionRecorderHistoryStore.js.map +1 -0
- package/bin/session/runtime/SessionMessageEventHub.d.ts +15 -0
- package/bin/session/runtime/SessionMessageEventHub.d.ts.map +1 -0
- package/bin/session/runtime/SessionMessageEventHub.js +28 -0
- package/bin/session/runtime/SessionMessageEventHub.js.map +1 -0
- package/bin/session/runtime/SessionPromptRuntime.d.ts +7 -13
- package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.js +13 -13
- package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
- package/bin/session/services/SessionStateService.d.ts +15 -3
- package/bin/session/services/SessionStateService.d.ts.map +1 -1
- package/bin/session/services/SessionStateService.js +165 -76
- package/bin/session/services/SessionStateService.js.map +1 -1
- package/bin/session/services/SessionTurnService.d.ts +4 -0
- package/bin/session/services/SessionTurnService.d.ts.map +1 -1
- package/bin/session/services/SessionTurnService.js +82 -52
- package/bin/session/services/SessionTurnService.js.map +1 -1
- package/bin/session/services/SessionViewService.d.ts +8 -3
- package/bin/session/services/SessionViewService.d.ts.map +1 -1
- package/bin/session/services/SessionViewService.js +13 -5
- package/bin/session/services/SessionViewService.js.map +1 -1
- package/bin/session/storage/Metadata.d.ts +2 -0
- package/bin/session/storage/Metadata.d.ts.map +1 -1
- package/bin/session/storage/Metadata.js +10 -0
- package/bin/session/storage/Metadata.js.map +1 -1
- package/bin/session/storage/Persistence.d.ts +6 -0
- package/bin/session/storage/Persistence.d.ts.map +1 -1
- package/bin/session/storage/Persistence.js +12 -0
- package/bin/session/storage/Persistence.js.map +1 -1
- package/bin/session/storage/RuntimeSessionPort.d.ts +3 -2
- package/bin/session/storage/RuntimeSessionPort.d.ts.map +1 -1
- package/bin/session/storage/RuntimeSessionPort.js.map +1 -1
- package/bin/types/agent/AgentOptions.d.ts +10 -0
- package/bin/types/agent/AgentOptions.d.ts.map +1 -1
- package/bin/types/agent/SessionActor.d.ts +12 -4
- package/bin/types/agent/SessionActor.d.ts.map +1 -1
- package/bin/types/agent/SessionTypes.d.ts +13 -1
- package/bin/types/agent/SessionTypes.d.ts.map +1 -1
- package/bin/types/rpc/RpcProtocol.d.ts +32 -6
- package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
- package/bin/types/runtime/agent/AgentContext.d.ts +4 -3
- package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
- package/bin/types/runtime/agent/AgentContext.js.map +1 -1
- package/bin/types/sdk/AgentSessionEvent.d.ts +2 -1
- package/bin/types/sdk/AgentSessionEvent.d.ts.map +1 -1
- package/bin/types/session/SessionMessage.d.ts +217 -0
- package/bin/types/session/SessionMessage.d.ts.map +1 -0
- package/bin/types/session/SessionMessage.js +8 -0
- package/bin/types/session/SessionMessage.js.map +1 -0
- package/bin/types/session/SessionMessageMutation.d.ts +92 -0
- package/bin/types/session/SessionMessageMutation.d.ts.map +1 -0
- package/bin/types/session/SessionMessageMutation.js +21 -0
- package/bin/types/session/SessionMessageMutation.js.map +1 -0
- package/bin/types/session/SessionOptions.d.ts +3 -0
- package/bin/types/session/SessionOptions.d.ts.map +1 -1
- package/package.json +3 -3
- package/scripts/agent-env.test.mjs +4 -4
- package/scripts/remote-session-reconnect.test.mjs +33 -0
- package/scripts/session-action-message.test.mjs +148 -27
- package/scripts/session-list-title.test.mjs +6 -19
- package/scripts/session-prompt-runtime.test.mjs +4 -16
- package/scripts/session-recorder.test.mjs +245 -0
- package/scripts/session-title-event.test.mjs +10 -18
- package/src/agent/local/Agent.ts +6 -0
- package/src/agent/local/services/AgentSessions.ts +14 -1
- package/src/agent/remote/RemoteSession.ts +52 -16
- package/src/agent/remote/RemoteTransport.ts +20 -6
- package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +67 -13
- package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +27 -6
- package/src/executor/types/SessionHistoryMeta.ts +2 -0
- package/src/executor/types/SessionRecords.ts +2 -0
- package/src/index.ts +23 -5
- package/src/rpc/Client.ts +47 -8
- package/src/session/Session.ts +66 -31
- package/src/session/browse/Browse.ts +3 -0
- package/src/session/recorder/JsonlSessionMessageStore.ts +162 -0
- package/src/session/recorder/SessionMessageCodec.ts +279 -0
- package/src/session/recorder/SessionMessageReducer.ts +155 -0
- package/src/session/recorder/SessionRecorder.ts +812 -0
- package/src/session/recorder/SessionRecorderCompaction.ts +187 -0
- package/src/session/recorder/SessionRecorderHistoryStore.ts +276 -0
- package/src/session/runtime/SessionMessageEventHub.ts +35 -0
- package/src/session/runtime/SessionPromptRuntime.ts +23 -24
- package/src/session/services/SessionStateService.ts +189 -84
- package/src/session/services/SessionTurnService.ts +88 -59
- package/src/session/services/SessionViewService.ts +28 -13
- package/src/session/storage/Metadata.ts +13 -0
- package/src/session/storage/Persistence.ts +18 -0
- package/src/session/storage/RuntimeSessionPort.ts +6 -4
- package/src/types/agent/AgentOptions.ts +12 -0
- package/src/types/agent/SessionActor.ts +28 -5
- package/src/types/agent/SessionTypes.ts +13 -1
- package/src/types/rpc/RpcProtocol.ts +34 -6
- package/src/types/runtime/agent/AgentContext.ts +8 -2
- package/src/types/sdk/AgentSessionEvent.ts +2 -0
- package/src/types/session/SessionMessage.ts +247 -0
- package/src/types/session/SessionMessageMutation.ts +121 -0
- package/src/types/session/SessionOptions.ts +4 -0
- package/tsconfig.tsbuildinfo +1 -1
- package/bin/session/SessionEventMapper.d.ts +0 -32
- package/bin/session/SessionEventMapper.d.ts.map +0 -1
- package/bin/session/SessionEventMapper.js +0 -131
- package/bin/session/SessionEventMapper.js.map +0 -1
- package/bin/types/sdk/InternalUiChunkEvent.d.ts +0 -100
- package/bin/types/sdk/InternalUiChunkEvent.d.ts.map +0 -1
- package/bin/types/sdk/InternalUiChunkEvent.js +0 -9
- package/bin/types/sdk/InternalUiChunkEvent.js.map +0 -1
- package/scripts/session-history-archive.test.mjs +0 -188
- package/src/session/SessionEventMapper.ts +0 -148
- package/src/types/sdk/InternalUiChunkEvent.ts +0 -108
|
@@ -12,6 +12,7 @@ import type { SessionPort } from "../../types/runtime/agent/AgentContext.js";
|
|
|
12
12
|
import type { AgentSessionSystemBlock } from "../../types/agent/SessionTypes.js";
|
|
13
13
|
import type { SessionComposerOptions } from "../../types/session/SessionComposerOptions.js";
|
|
14
14
|
import type { Logger } from "../../utils/logger/Logger.js";
|
|
15
|
+
import type { AgentModel } from "../../model/CityModelAdapter.js";
|
|
15
16
|
/**
|
|
16
17
|
* Agent 可管理的本地 Session 实例。
|
|
17
18
|
*/
|
|
@@ -69,6 +70,8 @@ export interface SessionOptions {
|
|
|
69
70
|
* 在执行前确保当前 session 已完成宿主侧默认配置。
|
|
70
71
|
*/
|
|
71
72
|
ensureConfigured?: (session: AgentManagedSession) => Promise<void>;
|
|
73
|
+
/** 按稳定模型 ID 解析当前 Session 的运行时模型。 */
|
|
74
|
+
resolve_model?: (model_id: string) => Promise<AgentModel>;
|
|
72
75
|
/**
|
|
73
76
|
* 当前 session 的 Composer 覆盖项。
|
|
74
77
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SessionOptions.d.ts","sourceRoot":"","sources":["../../../src/types/session/SessionOptions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"SessionOptions.d.ts","sourceRoot":"","sources":["../../../src/types/session/SessionOptions.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,IAAI,CAAC;AAC/B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAClE,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,uCAAuC,CAAC;AACzE,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AAC7E,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,2CAA2C,CAAC;AACxF,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,0BAA0B,CAAC;AACvD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAE9D;;GAEG;AACH,MAAM,WAAW,mBAAoB,SAAQ,YAAY;IACvD;;OAEG;IACH,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IAE5B;;OAEG;IACH,cAAc,IAAI,WAAW,CAAC;IAE9B;;OAEG;IACH,WAAW,IAAI,OAAO,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B;;OAEG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;OAEG;IACH,SAAS,EAAE,MAAM,CAAC;IAElB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAE5B;;OAEG;IACH,MAAM,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,0BAA0B,EAAE,MAAM,uBAAuB,EAAE,CAAC;IAE5D;;OAEG;IACH,4BAA4B,EAAE,MAAM,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAAC;IAEvE;;OAEG;IACH,qBAAqB,EAAE,MAAM,OAAO,CAAC,uBAAuB,EAAE,CAAC,CAAC;IAEhE;;OAEG;IACH,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IAEnE,oCAAoC;IACpC,aAAa,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,UAAU,CAAC,CAAC;IAE1D;;OAEG;IACH,SAAS,CAAC,EAAE,sBAAsB,CAAC;CACpC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@downcity/agent",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.222",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Downcity Agent 运行时 — 单 Agent 执行壳与本机 RPC 能力",
|
|
6
6
|
"main": "./bin/index.js",
|
|
@@ -13,8 +13,8 @@
|
|
|
13
13
|
},
|
|
14
14
|
"dependencies": {
|
|
15
15
|
"@ai-sdk/openai-compatible": "^2.0.48",
|
|
16
|
-
"@downcity/shell": "^0.1.
|
|
17
|
-
"@downcity/type": "0.1.
|
|
16
|
+
"@downcity/shell": "^0.1.85",
|
|
17
|
+
"@downcity/type": "0.1.118",
|
|
18
18
|
"ai": "^6.0.193",
|
|
19
19
|
"commander": "^15.0.0",
|
|
20
20
|
"dotenv": "^17.4.2",
|
|
@@ -33,7 +33,7 @@ test("项目 .env 覆盖宿主环境且不修改 process.env", () => {
|
|
|
33
33
|
assert.equal(env.PROJECT_ONLY, "value");
|
|
34
34
|
assert.equal(process.env.DOWNCITY_ENV_TEST, original_value);
|
|
35
35
|
} finally {
|
|
36
|
-
fs.rmSync(project_root, { recursive: true, force: true });
|
|
36
|
+
fs.rmSync(project_root, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
37
37
|
}
|
|
38
38
|
});
|
|
39
39
|
|
|
@@ -50,7 +50,7 @@ test("SDK 未传 config 时不读取 downcity.json", async () => {
|
|
|
50
50
|
await agent.ready();
|
|
51
51
|
await agent.dispose();
|
|
52
52
|
} finally {
|
|
53
|
-
fs.rmSync(project_root, { recursive: true, force: true });
|
|
53
|
+
fs.rmSync(project_root, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
54
54
|
}
|
|
55
55
|
});
|
|
56
56
|
|
|
@@ -65,7 +65,7 @@ test("SDK 未注入 plugin_config 时明确拒绝持久化", async () => {
|
|
|
65
65
|
await agent.ready();
|
|
66
66
|
await agent.dispose();
|
|
67
67
|
} finally {
|
|
68
|
-
fs.rmSync(project_root, { recursive: true, force: true });
|
|
68
|
+
fs.rmSync(project_root, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
69
69
|
}
|
|
70
70
|
});
|
|
71
71
|
|
|
@@ -96,6 +96,6 @@ test("项目初始化只创建 .env、Skills 和运行目录并返回真实结
|
|
|
96
96
|
assert.equal(second.skippedFiles.includes(".agents/skills/"), true);
|
|
97
97
|
assert.equal(second.skippedFiles.includes(".downcity/"), true);
|
|
98
98
|
} finally {
|
|
99
|
-
fs.rmSync(project_root, { recursive: true, force: true });
|
|
99
|
+
fs.rmSync(project_root, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
|
|
100
100
|
}
|
|
101
101
|
});
|
|
@@ -65,3 +65,36 @@ test("RemoteSession reconnects the event pump after transport close", async () =
|
|
|
65
65
|
});
|
|
66
66
|
assert.equal((await second_turn.finished).text, "done");
|
|
67
67
|
});
|
|
68
|
+
|
|
69
|
+
test("RemoteSession switches models by stable id without local model instances", async () => {
|
|
70
|
+
const updates = [];
|
|
71
|
+
const transport = {
|
|
72
|
+
async set(_session_id, input) {
|
|
73
|
+
updates.push(input);
|
|
74
|
+
return {
|
|
75
|
+
agentId: "agent_test",
|
|
76
|
+
sessionId: "session_test",
|
|
77
|
+
messageCount: 0,
|
|
78
|
+
modelId: input.modelId,
|
|
79
|
+
modelLabel: input.modelId,
|
|
80
|
+
};
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
const session = new RemoteSession(transport, {
|
|
84
|
+
agentId: "agent_test",
|
|
85
|
+
sessionId: "session_test",
|
|
86
|
+
messageCount: 0,
|
|
87
|
+
modelId: "old-model",
|
|
88
|
+
modelLabel: "old-model",
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
await session.set({ modelId: "next-model" });
|
|
92
|
+
|
|
93
|
+
assert.deepEqual(updates, [{ modelId: "next-model" }]);
|
|
94
|
+
assert.equal(session.config.modelId, "next-model");
|
|
95
|
+
assert.equal(session.config.modelLabel, "next-model");
|
|
96
|
+
await assert.rejects(
|
|
97
|
+
session.set({ model: { modelId: "local-model", provider: "test" } }),
|
|
98
|
+
/does not accept a local model instance/,
|
|
99
|
+
);
|
|
100
|
+
});
|
|
@@ -113,12 +113,15 @@ test("session.set only persists and publishes model-switching actions when model
|
|
|
113
113
|
});
|
|
114
114
|
const events = [];
|
|
115
115
|
const unsubscribe = session.subscribe((event) => {
|
|
116
|
-
if (
|
|
116
|
+
if (
|
|
117
|
+
event.variant === "message" && event.type === "action"
|
|
118
|
+
) events.push(event.message);
|
|
117
119
|
});
|
|
118
120
|
|
|
119
121
|
await session.set({
|
|
122
|
+
modelId: "test-model",
|
|
120
123
|
model: {
|
|
121
|
-
modelId: "test-model",
|
|
124
|
+
modelId: "test-model-label-v1",
|
|
122
125
|
provider: "test",
|
|
123
126
|
},
|
|
124
127
|
});
|
|
@@ -126,8 +129,9 @@ test("session.set only persists and publishes model-switching actions when model
|
|
|
126
129
|
assert.deepEqual(events, []);
|
|
127
130
|
|
|
128
131
|
await session.set({
|
|
132
|
+
modelId: "test-model",
|
|
129
133
|
model: {
|
|
130
|
-
modelId: "test-model",
|
|
134
|
+
modelId: "test-model-label-v2",
|
|
131
135
|
provider: "test",
|
|
132
136
|
},
|
|
133
137
|
});
|
|
@@ -135,26 +139,32 @@ test("session.set only persists and publishes model-switching actions when model
|
|
|
135
139
|
assert.deepEqual(events, []);
|
|
136
140
|
|
|
137
141
|
await session.set({
|
|
142
|
+
modelId: "next-test-model",
|
|
138
143
|
model: {
|
|
139
|
-
modelId: "next-test-model",
|
|
144
|
+
modelId: "next-test-model-label",
|
|
140
145
|
provider: "test",
|
|
141
146
|
},
|
|
142
147
|
});
|
|
143
148
|
unsubscribe();
|
|
144
149
|
|
|
145
150
|
assert.deepEqual(
|
|
146
|
-
events.map((event) => `${event.title}:${event.
|
|
147
|
-
[
|
|
151
|
+
events.map((event) => `${event.title}:${event.status}`),
|
|
152
|
+
[
|
|
153
|
+
"Switching session model from test-model-label-v2 to next-test-model-label:running",
|
|
154
|
+
"Session model switched from test-model-label-v2 to next-test-model-label:completed",
|
|
155
|
+
],
|
|
148
156
|
);
|
|
149
|
-
assert.equal(new Set(events.map((event) => event.
|
|
157
|
+
assert.equal(new Set(events.map((event) => event.message_id)).size, 1);
|
|
150
158
|
|
|
151
|
-
const
|
|
152
|
-
const action_events =
|
|
153
|
-
(item) => item.
|
|
159
|
+
const messages = await session.messages();
|
|
160
|
+
const action_events = messages.items.filter(
|
|
161
|
+
(item) => item.type === "action",
|
|
154
162
|
);
|
|
155
163
|
assert.deepEqual(
|
|
156
|
-
action_events.map((event) => `${event.
|
|
157
|
-
[
|
|
164
|
+
action_events.map((event) => `${event.title}:${event.status}`),
|
|
165
|
+
[
|
|
166
|
+
"Session model switched from test-model-label-v2 to next-test-model-label:completed",
|
|
167
|
+
],
|
|
158
168
|
);
|
|
159
169
|
} finally {
|
|
160
170
|
await agent.dispose();
|
|
@@ -177,9 +187,9 @@ test("default model initialization does not persist model-switching actions", as
|
|
|
177
187
|
const session = await agent.sessions.create({
|
|
178
188
|
sessionId: "model_init_action_session",
|
|
179
189
|
});
|
|
180
|
-
const
|
|
181
|
-
const action_events =
|
|
182
|
-
(item) => item.
|
|
190
|
+
const messages = await session.messages();
|
|
191
|
+
const action_events = messages.items.filter(
|
|
192
|
+
(item) => item.type === "action",
|
|
183
193
|
);
|
|
184
194
|
assert.deepEqual(action_events, []);
|
|
185
195
|
} finally {
|
|
@@ -187,6 +197,117 @@ test("default model initialization does not persist model-switching actions", as
|
|
|
187
197
|
}
|
|
188
198
|
});
|
|
189
199
|
|
|
200
|
+
test("session model id persists and restores through the host resolver", async () => {
|
|
201
|
+
const agent_path = await fs.mkdtemp(
|
|
202
|
+
path.join(os.tmpdir(), "downcity-agent-session-model-id-"),
|
|
203
|
+
);
|
|
204
|
+
const resolved_model_ids = [];
|
|
205
|
+
const resolve_model = async (model_id) => {
|
|
206
|
+
resolved_model_ids.push(model_id);
|
|
207
|
+
return {
|
|
208
|
+
modelId: model_id,
|
|
209
|
+
provider: "test",
|
|
210
|
+
};
|
|
211
|
+
};
|
|
212
|
+
const first_agent = new Agent({
|
|
213
|
+
id: "session_model_id_agent",
|
|
214
|
+
path: agent_path,
|
|
215
|
+
model: await resolve_model("default-model"),
|
|
216
|
+
model_id: "default-model",
|
|
217
|
+
resolve_model,
|
|
218
|
+
});
|
|
219
|
+
try {
|
|
220
|
+
const session = await first_agent.sessions.create({
|
|
221
|
+
sessionId: "persisted-model-session",
|
|
222
|
+
});
|
|
223
|
+
await session.set({ modelId: "session-model" });
|
|
224
|
+
const info = await session.get_info();
|
|
225
|
+
assert.equal(info.modelId, "session-model");
|
|
226
|
+
assert.equal(session.config.modelId, "session-model");
|
|
227
|
+
} finally {
|
|
228
|
+
await first_agent.dispose();
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const restored_agent = new Agent({
|
|
232
|
+
id: "session_model_id_agent",
|
|
233
|
+
path: agent_path,
|
|
234
|
+
model: await resolve_model("other-default-model"),
|
|
235
|
+
model_id: "other-default-model",
|
|
236
|
+
resolve_model,
|
|
237
|
+
});
|
|
238
|
+
try {
|
|
239
|
+
const restored_session = await restored_agent.sessions.get(
|
|
240
|
+
"persisted-model-session",
|
|
241
|
+
);
|
|
242
|
+
assert.equal(restored_session.config.modelId, "session-model");
|
|
243
|
+
assert.equal(restored_session.config.model.modelId, "session-model");
|
|
244
|
+
assert.equal(resolved_model_ids.at(-1), "session-model");
|
|
245
|
+
} finally {
|
|
246
|
+
await restored_agent.dispose();
|
|
247
|
+
}
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
test("runtime restoration does not publish a model switch when model id is unchanged", async () => {
|
|
251
|
+
const agent_path = await fs.mkdtemp(
|
|
252
|
+
path.join(os.tmpdir(), "downcity-agent-session-model-runtime-restore-"),
|
|
253
|
+
);
|
|
254
|
+
const first_agent = new Agent({
|
|
255
|
+
id: "session_model_runtime_restore_agent",
|
|
256
|
+
path: agent_path,
|
|
257
|
+
model: {
|
|
258
|
+
modelId: "default-model",
|
|
259
|
+
provider: "test",
|
|
260
|
+
},
|
|
261
|
+
model_id: "default-model",
|
|
262
|
+
resolve_model: async (model_id) => ({
|
|
263
|
+
modelId: model_id,
|
|
264
|
+
provider: "test",
|
|
265
|
+
}),
|
|
266
|
+
});
|
|
267
|
+
try {
|
|
268
|
+
const session = await first_agent.sessions.create({
|
|
269
|
+
sessionId: "runtime-restored-model-session",
|
|
270
|
+
});
|
|
271
|
+
await session.set({ modelId: "session-model" });
|
|
272
|
+
} finally {
|
|
273
|
+
await first_agent.dispose();
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
let resolve_count = 0;
|
|
277
|
+
const restored_agent = new Agent({
|
|
278
|
+
id: "session_model_runtime_restore_agent",
|
|
279
|
+
path: agent_path,
|
|
280
|
+
model: {
|
|
281
|
+
modelId: "other-default-model",
|
|
282
|
+
provider: "test",
|
|
283
|
+
},
|
|
284
|
+
model_id: "other-default-model",
|
|
285
|
+
resolve_model: async (model_id) => {
|
|
286
|
+
resolve_count += 1;
|
|
287
|
+
return {
|
|
288
|
+
modelId: model_id,
|
|
289
|
+
provider: "test",
|
|
290
|
+
};
|
|
291
|
+
},
|
|
292
|
+
});
|
|
293
|
+
try {
|
|
294
|
+
const session_port = restored_agent
|
|
295
|
+
.getContext()
|
|
296
|
+
.session.get("runtime-restored-model-session");
|
|
297
|
+
const events = [];
|
|
298
|
+
const unsubscribe = session_port.subscribe((event) => {
|
|
299
|
+
if (event.type === "action") events.push(event);
|
|
300
|
+
});
|
|
301
|
+
await session_port.stop();
|
|
302
|
+
unsubscribe();
|
|
303
|
+
|
|
304
|
+
assert.equal(resolve_count, 1);
|
|
305
|
+
assert.deepEqual(events, []);
|
|
306
|
+
} finally {
|
|
307
|
+
await restored_agent.dispose();
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
|
|
190
311
|
test("session.fork persists and publishes history-forking actions on source session", async () => {
|
|
191
312
|
const agent_path = await fs.mkdtemp(
|
|
192
313
|
path.join(os.tmpdir(), "downcity-agent-history-forking-"),
|
|
@@ -209,32 +330,32 @@ test("session.fork persists and publishes history-forking actions on source sess
|
|
|
209
330
|
|
|
210
331
|
const events = [];
|
|
211
332
|
const unsubscribe = session.subscribe((event) => {
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
333
|
+
if (
|
|
334
|
+
event.variant === "message" && event.type === "action"
|
|
335
|
+
) events.push(event.message);
|
|
215
336
|
});
|
|
216
337
|
const forked = await session.fork();
|
|
217
338
|
unsubscribe();
|
|
218
339
|
|
|
219
340
|
assert.deepEqual(
|
|
220
|
-
events.map((event) => `${event.title}:${event.
|
|
341
|
+
events.map((event) => `${event.title}:${event.status}`),
|
|
221
342
|
["Forking session records:running", "Session records forked:completed"],
|
|
222
343
|
);
|
|
223
|
-
assert.equal(new Set(events.map((event) => event.
|
|
344
|
+
assert.equal(new Set(events.map((event) => event.message_id)).size, 1);
|
|
224
345
|
assert.notEqual(forked.id, session.id);
|
|
225
346
|
|
|
226
|
-
const
|
|
227
|
-
const source_fork_actions =
|
|
228
|
-
(item) => item.
|
|
347
|
+
const source_messages = await session.messages();
|
|
348
|
+
const source_fork_actions = source_messages.items.filter(
|
|
349
|
+
(item) => item.type === "action" && item.title === "Session records forked",
|
|
229
350
|
);
|
|
230
351
|
assert.deepEqual(
|
|
231
|
-
source_fork_actions.map((event) => event.
|
|
352
|
+
source_fork_actions.map((event) => event.status),
|
|
232
353
|
["completed"],
|
|
233
354
|
);
|
|
234
355
|
|
|
235
|
-
const
|
|
236
|
-
const forked_model_actions =
|
|
237
|
-
(item) => item.
|
|
356
|
+
const forked_messages = await forked.messages();
|
|
357
|
+
const forked_model_actions = forked_messages.items.filter(
|
|
358
|
+
(item) => item.type === "action" && item.title.startsWith("Session model switched from "),
|
|
238
359
|
);
|
|
239
360
|
assert.equal(forked_model_actions.length, 0);
|
|
240
361
|
} finally {
|
|
@@ -141,7 +141,7 @@ test("list_sessions returns persisted title from active session metadata", async
|
|
|
141
141
|
}
|
|
142
142
|
});
|
|
143
143
|
|
|
144
|
-
test("list_sessions
|
|
144
|
+
test("list_sessions reflects canonical Recorder history changes", async () => {
|
|
145
145
|
const { agent, collection, session } = await create_agent_with_titled_session({
|
|
146
146
|
tmp_prefix: "downcity-agent-session-summary-repair-",
|
|
147
147
|
agent_id: "summary_repair_agent",
|
|
@@ -162,31 +162,18 @@ test("list_sessions rebuilds stale metadata after external history changes", asy
|
|
|
162
162
|
const meta_path = path.join(messages_dir, "meta.json");
|
|
163
163
|
|
|
164
164
|
try {
|
|
165
|
-
await
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
id: "external_assistant",
|
|
169
|
-
role: "assistant",
|
|
170
|
-
metadata: {
|
|
171
|
-
v: 1,
|
|
172
|
-
ts: Date.now(),
|
|
173
|
-
sessionId: session.id,
|
|
174
|
-
source: "egress",
|
|
175
|
-
kind: "normal",
|
|
176
|
-
},
|
|
177
|
-
parts: [{ type: "text", text: "Externally appended history" }],
|
|
178
|
-
})}\n`,
|
|
179
|
-
"utf8",
|
|
180
|
-
);
|
|
165
|
+
await session.append_assistant_message({
|
|
166
|
+
text: "Recorder appended history",
|
|
167
|
+
});
|
|
181
168
|
|
|
182
169
|
const page = await collection.list();
|
|
183
170
|
const repaired_meta = JSON.parse(await fs.readFile(meta_path, "utf8"));
|
|
184
171
|
const history_stat = await fs.stat(messages_path);
|
|
185
172
|
|
|
186
173
|
assert.equal(page.items[0].messageCount, 2);
|
|
187
|
-
assert.equal(page.items[0].previewText, "
|
|
174
|
+
assert.equal(page.items[0].previewText, "Recorder appended history");
|
|
188
175
|
assert.equal(repaired_meta.messageCount, 2);
|
|
189
|
-
assert.equal(repaired_meta.previewText, "
|
|
176
|
+
assert.equal(repaired_meta.previewText, "Recorder appended history");
|
|
190
177
|
assert.equal(repaired_meta.historyBytes, history_stat.size);
|
|
191
178
|
} finally {
|
|
192
179
|
await agent.dispose();
|
|
@@ -76,7 +76,6 @@ async function isSettled(promise) {
|
|
|
76
76
|
test("SessionPromptRuntime merges queued prompts at the next step boundary", async () => {
|
|
77
77
|
const events = [];
|
|
78
78
|
const persisted = [];
|
|
79
|
-
const steer_actions = [];
|
|
80
79
|
const merge_lifecycle = [];
|
|
81
80
|
const executionFinished = createDeferred();
|
|
82
81
|
let stepMerge = null;
|
|
@@ -92,10 +91,7 @@ test("SessionPromptRuntime merges queued prompts at the next step boundary", asy
|
|
|
92
91
|
merge_lifecycle.push(`message:${input.query}`);
|
|
93
92
|
return message;
|
|
94
93
|
},
|
|
95
|
-
|
|
96
|
-
steer_actions.push(input);
|
|
97
|
-
merge_lifecycle.push(`action:${input.message.parts[0]?.text}`);
|
|
98
|
-
},
|
|
94
|
+
appendErrorMessage: async () => {},
|
|
99
95
|
executeTurn: async (input) => {
|
|
100
96
|
stepMerge = input.onStepMerge;
|
|
101
97
|
await executionFinished.promise;
|
|
@@ -126,17 +122,9 @@ test("SessionPromptRuntime merges queued prompts at the next step boundary", asy
|
|
|
126
122
|
persisted.map((message) => message.parts[0]?.text),
|
|
127
123
|
["first", "second"],
|
|
128
124
|
);
|
|
129
|
-
assert.deepEqual(
|
|
130
|
-
steer_actions.map((action) => ({
|
|
131
|
-
turn_id: action.turn_id,
|
|
132
|
-
text: action.message.parts[0]?.text,
|
|
133
|
-
})),
|
|
134
|
-
[{ turn_id: firstTurn.id, text: "second" }],
|
|
135
|
-
);
|
|
136
125
|
assert.deepEqual(merge_lifecycle, [
|
|
137
126
|
"message:first",
|
|
138
127
|
"message:second",
|
|
139
|
-
"action:second",
|
|
140
128
|
]);
|
|
141
129
|
|
|
142
130
|
executionFinished.resolve();
|
|
@@ -163,7 +151,7 @@ test("SessionPromptRuntime moves unmerged prompts into the next turn", async ()
|
|
|
163
151
|
createAndPersistUserMessage: async (input) => {
|
|
164
152
|
return createUserMessage(input.query, events.length + 1);
|
|
165
153
|
},
|
|
166
|
-
|
|
154
|
+
appendErrorMessage: async () => {},
|
|
167
155
|
executeTurn: async (input) => {
|
|
168
156
|
const deferred = createDeferred();
|
|
169
157
|
finishQueue.push({
|
|
@@ -221,7 +209,7 @@ test("SessionPromptRuntime stops current turn and cancels unmerged queued prompt
|
|
|
221
209
|
createAndPersistUserMessage: async (input) => {
|
|
222
210
|
return createUserMessage(input.query, events.length + 1);
|
|
223
211
|
},
|
|
224
|
-
|
|
212
|
+
appendErrorMessage: async () => {},
|
|
225
213
|
executeTurn: async (input) => {
|
|
226
214
|
await new Promise((resolve) => {
|
|
227
215
|
input.abortSignal.addEventListener("abort", resolve, { once: true });
|
|
@@ -278,7 +266,7 @@ test("SessionPromptRuntime does not synthesize assistant text when stopped befor
|
|
|
278
266
|
createAndPersistUserMessage: async (input) => {
|
|
279
267
|
return createUserMessage(input.query, 1);
|
|
280
268
|
},
|
|
281
|
-
|
|
269
|
+
appendErrorMessage: async () => {},
|
|
282
270
|
executeTurn: async (input) => {
|
|
283
271
|
await new Promise((resolve) => {
|
|
284
272
|
input.abortSignal.addEventListener("abort", resolve, { once: true });
|