@downcity/agent 1.1.215 → 1.1.217

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (81) hide show
  1. package/bin/agent/local/Agent.d.ts +2 -0
  2. package/bin/agent/local/Agent.d.ts.map +1 -1
  3. package/bin/agent/local/Agent.js +6 -0
  4. package/bin/agent/local/Agent.js.map +1 -1
  5. package/bin/agent/local/services/AgentSessions.d.ts +6 -0
  6. package/bin/agent/local/services/AgentSessions.d.ts.map +1 -1
  7. package/bin/agent/local/services/AgentSessions.js +11 -1
  8. package/bin/agent/local/services/AgentSessions.js.map +1 -1
  9. package/bin/agent/remote/RemoteSession.d.ts +2 -4
  10. package/bin/agent/remote/RemoteSession.d.ts.map +1 -1
  11. package/bin/agent/remote/RemoteSession.js +17 -7
  12. package/bin/agent/remote/RemoteSession.js.map +1 -1
  13. package/bin/agent/remote/RemoteTransport.d.ts +3 -1
  14. package/bin/agent/remote/RemoteTransport.d.ts.map +1 -1
  15. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts +2 -1
  16. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
  17. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +13 -0
  18. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
  19. package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts +2 -1
  20. package/bin/agent/remote/transports/RpcRemoteAgentTransport.d.ts.map +1 -1
  21. package/bin/agent/remote/transports/RpcRemoteAgentTransport.js +3 -0
  22. package/bin/agent/remote/transports/RpcRemoteAgentTransport.js.map +1 -1
  23. package/bin/executor/types/SessionHistoryMeta.d.ts +2 -0
  24. package/bin/executor/types/SessionHistoryMeta.d.ts.map +1 -1
  25. package/bin/rpc/Client.d.ts +8 -1
  26. package/bin/rpc/Client.d.ts.map +1 -1
  27. package/bin/rpc/Client.js +11 -0
  28. package/bin/rpc/Client.js.map +1 -1
  29. package/bin/session/Session.d.ts.map +1 -1
  30. package/bin/session/Session.js +1 -0
  31. package/bin/session/Session.js.map +1 -1
  32. package/bin/session/browse/Browse.d.ts.map +1 -1
  33. package/bin/session/browse/Browse.js +3 -0
  34. package/bin/session/browse/Browse.js.map +1 -1
  35. package/bin/session/services/SessionStateService.d.ts +8 -0
  36. package/bin/session/services/SessionStateService.d.ts.map +1 -1
  37. package/bin/session/services/SessionStateService.js +34 -5
  38. package/bin/session/services/SessionStateService.js.map +1 -1
  39. package/bin/session/services/SessionViewService.d.ts.map +1 -1
  40. package/bin/session/services/SessionViewService.js +3 -0
  41. package/bin/session/services/SessionViewService.js.map +1 -1
  42. package/bin/session/storage/Metadata.d.ts +2 -0
  43. package/bin/session/storage/Metadata.d.ts.map +1 -1
  44. package/bin/session/storage/Metadata.js +10 -0
  45. package/bin/session/storage/Metadata.js.map +1 -1
  46. package/bin/session/storage/Persistence.d.ts.map +1 -1
  47. package/bin/session/storage/Persistence.js +5 -0
  48. package/bin/session/storage/Persistence.js.map +1 -1
  49. package/bin/types/agent/AgentOptions.d.ts +10 -0
  50. package/bin/types/agent/AgentOptions.d.ts.map +1 -1
  51. package/bin/types/agent/SessionActor.d.ts +2 -0
  52. package/bin/types/agent/SessionActor.d.ts.map +1 -1
  53. package/bin/types/agent/SessionTypes.d.ts +13 -1
  54. package/bin/types/agent/SessionTypes.d.ts.map +1 -1
  55. package/bin/types/rpc/RpcProtocol.d.ts +13 -1
  56. package/bin/types/rpc/RpcProtocol.d.ts.map +1 -1
  57. package/bin/types/session/SessionOptions.d.ts +3 -0
  58. package/bin/types/session/SessionOptions.d.ts.map +1 -1
  59. package/package.json +3 -3
  60. package/scripts/remote-session-reconnect.test.mjs +33 -0
  61. package/scripts/session-action-message.test.mjs +50 -0
  62. package/src/agent/local/Agent.ts +6 -0
  63. package/src/agent/local/services/AgentSessions.ts +16 -1
  64. package/src/agent/remote/RemoteSession.ts +17 -9
  65. package/src/agent/remote/RemoteTransport.ts +3 -0
  66. package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +22 -0
  67. package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +8 -0
  68. package/src/executor/types/SessionHistoryMeta.ts +2 -0
  69. package/src/rpc/Client.ts +18 -0
  70. package/src/session/Session.ts +1 -0
  71. package/src/session/browse/Browse.ts +3 -0
  72. package/src/session/services/SessionStateService.ts +39 -5
  73. package/src/session/services/SessionViewService.ts +3 -0
  74. package/src/session/storage/Metadata.ts +13 -0
  75. package/src/session/storage/Persistence.ts +5 -0
  76. package/src/types/agent/AgentOptions.ts +12 -0
  77. package/src/types/agent/SessionActor.ts +3 -0
  78. package/src/types/agent/SessionTypes.ts +13 -1
  79. package/src/types/rpc/RpcProtocol.ts +14 -0
  80. package/src/types/session/SessionOptions.ts +4 -0
  81. package/tsconfig.tsbuildinfo +1 -1
@@ -453,6 +453,7 @@ export function buildSessionInfo(
453
453
  ...(input.metadata.modelLabel
454
454
  ? { modelLabel: input.metadata.modelLabel }
455
455
  : {}),
456
+ ...(input.metadata.modelId ? { modelId: input.metadata.modelId } : {}),
456
457
  ...(typeof input.metadata.timezone === "string" && input.metadata.timezone.trim()
457
458
  ? { timezone: input.metadata.timezone.trim() }
458
459
  : {}),
@@ -632,6 +633,7 @@ export async function listAgentSessionSummaryPage(params: {
632
633
  ...(typeof info.createdAt === "number" ? { createdAt: info.createdAt } : {}),
633
634
  ...(typeof info.updatedAt === "number" ? { updatedAt: info.updatedAt } : {}),
634
635
  ...(info.modelLabel ? { modelLabel: info.modelLabel } : {}),
636
+ ...(info.modelId ? { modelId: info.modelId } : {}),
635
637
  ...(info.executing ? { executing: true } : {}),
636
638
  };
637
639
 
@@ -732,6 +734,7 @@ export async function listArchivedAgentSessionSummaryPage(params: {
732
734
  ...(typeof info.createdAt === "number" ? { createdAt: info.createdAt } : {}),
733
735
  ...(typeof info.updatedAt === "number" ? { updatedAt: info.updatedAt } : {}),
734
736
  ...(info.modelLabel ? { modelLabel: info.modelLabel } : {}),
737
+ ...(info.modelId ? { modelId: info.modelId } : {}),
735
738
  };
736
739
 
737
740
  if (query) {
@@ -42,6 +42,7 @@ import { to_session_action_record } from "@/executor/types/SessionRecords.js";
42
42
  import type { SessionLocalState } from "@/types/session/SessionLocalState.js";
43
43
  import { generateId } from "@/utils/Id.js";
44
44
  import type { Logger } from "@/utils/logger/Logger.js";
45
+ import type { AgentModel } from "@/model/CityModelAdapter.js";
45
46
 
46
47
  type SessionStateServiceOptions = {
47
48
  /**
@@ -84,6 +85,9 @@ type SessionStateServiceOptions = {
84
85
  */
85
86
  ensure_configured_hook?: () => Promise<void>;
86
87
 
88
+ /** 按稳定 ID 解析当前 Session 的运行时模型。 */
89
+ resolve_model?: (model_id: string) => Promise<AgentModel>;
90
+
87
91
  /**
88
92
  * 发布 session 事件。
89
93
  */
@@ -115,6 +119,7 @@ export class SessionStateService {
115
119
  private readonly state: SessionLocalState;
116
120
  private readonly logger: Logger;
117
121
  private readonly ensure_configured_hook?: SessionStateServiceOptions["ensure_configured_hook"];
122
+ private readonly resolve_model?: SessionStateServiceOptions["resolve_model"];
118
123
  private readonly publish_event: SessionStateServiceOptions["publish_event"];
119
124
 
120
125
  constructor(options: SessionStateServiceOptions) {
@@ -126,6 +131,7 @@ export class SessionStateService {
126
131
  this.state = options.state;
127
132
  this.logger = options.logger;
128
133
  this.ensure_configured_hook = options.ensure_configured_hook;
134
+ this.resolve_model = options.resolve_model;
129
135
  this.publish_event = options.publish_event;
130
136
  }
131
137
 
@@ -189,6 +195,7 @@ export class SessionStateService {
189
195
  ...(metadata.modelLabel
190
196
  ? { modelLabel: metadata.modelLabel }
191
197
  : {}),
198
+ ...(metadata.modelId ? { modelId: metadata.modelId } : {}),
192
199
  };
193
200
  })();
194
201
  await this.state.initializePromise;
@@ -199,6 +206,7 @@ export class SessionStateService {
199
206
  */
200
207
  async ensure_ready_for_execution(): Promise<void> {
201
208
  await this.initialize();
209
+ await this.restore_persisted_model();
202
210
  if (this.state.ensureConfiguredPromise) {
203
211
  await this.state.ensureConfiguredPromise;
204
212
  return;
@@ -231,11 +239,16 @@ export class SessionStateService {
231
239
  async set(input: AgentSessionSetInput, options?: SessionSetOptions): Promise<void> {
232
240
  const should_emit_action = options?.emit_action !== false;
233
241
  const previous_model_label = this.state.sessionConfig.modelLabel;
234
- const next_model_label = input.model
235
- ? inferAgentModelLabel(input.model)
242
+ const requested_model_id = String(input.modelId || "").trim();
243
+ const next_model = input.model || (requested_model_id
244
+ ? await this.resolve_model_by_id(requested_model_id)
245
+ : undefined);
246
+ const next_model_label = next_model
247
+ ? inferAgentModelLabel(next_model)
236
248
  : undefined;
249
+ const next_model_id = requested_model_id || next_model_label || "";
237
250
  const should_emit_model_switch_action = Boolean(
238
- input.model &&
251
+ next_model &&
239
252
  should_emit_action &&
240
253
  this.state.sessionConfig.model &&
241
254
  previous_model_label &&
@@ -256,15 +269,19 @@ export class SessionStateService {
256
269
  }
257
270
 
258
271
  try {
259
- if (input.model) {
260
- this.state.sessionConfig.model = normalizeAgentModel(input.model);
272
+ if (next_model) {
273
+ this.state.sessionConfig.model = normalizeAgentModel(next_model);
261
274
  this.state.sessionConfig.modelLabel = next_model_label;
262
275
  }
276
+ if (next_model_id) {
277
+ this.state.sessionConfig.modelId = next_model_id;
278
+ }
263
279
  await patchSessionModelLabel({
264
280
  projectRoot: this.project_root,
265
281
  agentId: this.agent_id,
266
282
  sessionId: this.session_id,
267
283
  model: this.state.sessionConfig.model,
284
+ modelId: this.state.sessionConfig.modelId,
268
285
  });
269
286
  } catch (error) {
270
287
  if (should_emit_model_switch_action) {
@@ -290,6 +307,23 @@ export class SessionStateService {
290
307
  }
291
308
  }
292
309
 
310
+ /** 恢复 metadata 中持久化的模型覆盖。 */
311
+ private async restore_persisted_model(): Promise<void> {
312
+ const model_id = String(this.state.sessionConfig.modelId || "").trim();
313
+ if (!model_id || this.state.sessionConfig.model) return;
314
+ const model = await this.resolve_model_by_id(model_id);
315
+ this.state.sessionConfig.model = normalizeAgentModel(model);
316
+ this.state.sessionConfig.modelLabel = inferAgentModelLabel(model);
317
+ }
318
+
319
+ /** 使用宿主 resolver 解析稳定模型 ID。 */
320
+ private async resolve_model_by_id(model_id: string): Promise<AgentModel> {
321
+ if (!this.resolve_model) {
322
+ throw new Error("Session model resolver is not configured.");
323
+ }
324
+ return await this.resolve_model(model_id);
325
+ }
326
+
293
327
  /**
294
328
  * 追加一条 user 消息并刷新标题与 metadata。
295
329
  */
@@ -306,6 +306,9 @@ export class SessionViewService<TSession extends Pick<AgentSession, "set">> {
306
306
  await forked_bundle.state_service.set(
307
307
  {
308
308
  model: session_config.model,
309
+ ...(session_config.modelId
310
+ ? { modelId: session_config.modelId }
311
+ : {}),
309
312
  },
310
313
  { emit_action: false },
311
314
  );
@@ -33,6 +33,11 @@ function normalizeModelLabel(input: unknown): string | undefined {
33
33
  return label || undefined;
34
34
  }
35
35
 
36
+ function normalize_model_id(input: unknown): string | undefined {
37
+ const model_id = typeof input === "string" ? input.trim() : "";
38
+ return model_id || undefined;
39
+ }
40
+
36
41
  /**
37
42
  * 归一化 session 标题。
38
43
  */
@@ -153,6 +158,9 @@ export async function readSessionMetadataFromPath(input: {
153
158
  ...(normalizeModelLabel(raw.modelLabel)
154
159
  ? { modelLabel: normalizeModelLabel(raw.modelLabel) }
155
160
  : {}),
161
+ ...(normalize_model_id(raw.modelId)
162
+ ? { modelId: normalize_model_id(raw.modelId) }
163
+ : {}),
156
164
  ...(normalize_message_count(raw.messageCount) !== undefined
157
165
  ? { messageCount: normalize_message_count(raw.messageCount) }
158
166
  : {}),
@@ -204,6 +212,8 @@ export async function patchSessionModelLabel(
204
212
  * 当前模型实例。
205
213
  */
206
214
  model?: LanguageModel;
215
+ /** 当前模型稳定 ID。 */
216
+ modelId?: string;
207
217
  },
208
218
  ): Promise<SessionHistoryMetaV1> {
209
219
  const current = await readSessionMetadata(input);
@@ -220,6 +230,9 @@ export async function patchSessionModelLabel(
220
230
  modelLabel,
221
231
  }
222
232
  : {}),
233
+ ...(normalize_model_id(input.modelId)
234
+ ? { modelId: normalize_model_id(input.modelId) }
235
+ : {}),
223
236
  };
224
237
  await writeSessionMetadata({
225
238
  ...input,
@@ -93,6 +93,11 @@ export async function touchSessionMetadata(
93
93
  modelLabel: params.sessionConfig.modelLabel,
94
94
  }
95
95
  : {}),
96
+ ...(params.sessionConfig.modelId
97
+ ? {
98
+ modelId: params.sessionConfig.modelId,
99
+ }
100
+ : {}),
96
101
  };
97
102
  await writeSessionMetadata({
98
103
  projectRoot: params.projectRoot,
@@ -82,6 +82,18 @@ export interface AgentOptions {
82
82
  */
83
83
  model?: AgentModel;
84
84
 
85
+ /** 当前 Agent 默认模型对应的稳定 ID。 */
86
+ model_id?: string;
87
+
88
+ /**
89
+ * 按稳定 ID 解析 Session 运行时模型。
90
+ *
91
+ * 关键点(中文)
92
+ * - Session 热切换与进程重启后的恢复统一使用该 resolver。
93
+ * - SDK 不维护模型目录,具体目录与鉴权由宿主负责。
94
+ */
95
+ resolve_model?: (model_id: string) => Promise<AgentModel>;
96
+
85
97
  /**
86
98
  * 当前 agent 显式持有的插件实例集合。
87
99
  *
@@ -101,6 +101,9 @@ export interface AgentSession extends AgentSessionActor {
101
101
  * 远程 Agent 返回的公开 session 接口。
102
102
  */
103
103
  export interface RemoteAgentSession extends AgentSessionActor {
104
+ /** 按稳定模型 ID 更新远程 Session 模型。 */
105
+ set(input: AgentSessionSetInput): Promise<void>;
106
+
104
107
  /** 从当前远程 session 创建一个分叉会话。 */
105
108
  fork(input?: AgentSessionForkInput | string): Promise<RemoteAgentSession>;
106
109
  }
@@ -67,9 +67,17 @@ export interface AgentSessionSetInput {
67
67
  *
68
68
  * 关键点(中文)
69
69
  * - 这里接受运行中的模型实例,而不是模型 ID。
70
- * - 由于模型实例通常不可序列化,落盘只保留轻量可读标签用于展示。
70
+ * - 未同时提供 `modelId` 时,SDK 会从模型实例推导稳定 ID 并持久化。
71
71
  */
72
72
  model?: AgentModel;
73
+ /**
74
+ * 当前 session 使用的稳定模型 ID。
75
+ *
76
+ * 关键点(中文)
77
+ * - 由 Agent 宿主提供的模型 resolver 解析为运行时模型实例。
78
+ * - 该值会持久化到 session metadata,进程重启后可以恢复。
79
+ */
80
+ modelId?: string;
73
81
  }
74
82
 
75
83
  /**
@@ -80,6 +88,8 @@ export interface AgentSessionConfigSnapshot {
80
88
  model?: LanguageModel;
81
89
  /** 当前模型的轻量可读标签。 */
82
90
  modelLabel?: string;
91
+ /** 当前 session 绑定的稳定模型 ID。 */
92
+ modelId?: string;
83
93
  }
84
94
 
85
95
  /**
@@ -253,6 +263,8 @@ export interface AgentSessionSummary {
253
263
  messageCount: number;
254
264
  /** 当前 session 绑定模型的可读标签。 */
255
265
  modelLabel?: string;
266
+ /** 当前 session 绑定的稳定模型 ID。 */
267
+ modelId?: string;
256
268
  /** 当前 session 是否处于执行中。 */
257
269
  executing?: boolean;
258
270
  }
@@ -17,6 +17,7 @@ import type {
17
17
  AgentCleanArchiveResult,
18
18
  AgentSessionRecordsInput,
19
19
  AgentSessionSystemSnapshot,
20
+ AgentSessionSetInput,
20
21
  } from "@/types/agent/SessionTypes.js";
21
22
  import type { AgentSessionEvent } from "@/types/sdk/AgentSessionEvent.js";
22
23
  import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
@@ -60,6 +61,19 @@ export type RpcRequest =
60
61
  sessionId: string;
61
62
  };
62
63
  }
64
+ | {
65
+ /** 请求 id,用于匹配响应。 */
66
+ id: string;
67
+ /** 更新 session 模型。 */
68
+ method: "sdk.sessions.set";
69
+ /** Session 配置更新参数。 */
70
+ params: {
71
+ /** 目标 session id。 */
72
+ sessionId: string;
73
+ /** 仅包含稳定 modelId 的配置输入。 */
74
+ input: AgentSessionSetInput;
75
+ };
76
+ }
63
77
  | {
64
78
  /** 请求 id,用于匹配响应。 */
65
79
  id: string;
@@ -13,6 +13,7 @@ import type { SessionPort } from "@/types/runtime/agent/AgentContext.js";
13
13
  import type { AgentSessionSystemBlock } from "@/types/agent/SessionTypes.js";
14
14
  import type { SessionComposerOptions } from "@/types/session/SessionComposerOptions.js";
15
15
  import type { Logger } from "@/utils/logger/Logger.js";
16
+ import type { AgentModel } from "@/model/CityModelAdapter.js";
16
17
 
17
18
  /**
18
19
  * Agent 可管理的本地 Session 实例。
@@ -83,6 +84,9 @@ export interface SessionOptions {
83
84
  */
84
85
  ensureConfigured?: (session: AgentManagedSession) => Promise<void>;
85
86
 
87
+ /** 按稳定模型 ID 解析当前 Session 的运行时模型。 */
88
+ resolve_model?: (model_id: string) => Promise<AgentModel>;
89
+
86
90
  /**
87
91
  * 当前 session 的 Composer 覆盖项。
88
92
  */