@downcity/agent 1.1.228 → 1.1.232

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 (157) hide show
  1. package/bin/agent/local/Agent.d.ts +6 -1
  2. package/bin/agent/local/Agent.d.ts.map +1 -1
  3. package/bin/agent/local/Agent.js +30 -3
  4. package/bin/agent/local/Agent.js.map +1 -1
  5. package/bin/agent/local/services/AgentSessions.d.ts +23 -0
  6. package/bin/agent/local/services/AgentSessions.d.ts.map +1 -1
  7. package/bin/agent/local/services/AgentSessions.js +44 -0
  8. package/bin/agent/local/services/AgentSessions.js.map +1 -1
  9. package/bin/agent/remote/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
  10. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +0 -7
  11. package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
  12. package/bin/executor/Executor.d.ts +25 -0
  13. package/bin/executor/Executor.d.ts.map +1 -1
  14. package/bin/executor/Executor.js +36 -0
  15. package/bin/executor/Executor.js.map +1 -1
  16. package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts +4 -0
  17. package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts.map +1 -1
  18. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts +0 -1
  19. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts.map +1 -1
  20. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js +15 -11
  21. package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js.map +1 -1
  22. package/bin/executor/core-engine/CoreEngineRunner.d.ts +11 -0
  23. package/bin/executor/core-engine/CoreEngineRunner.d.ts.map +1 -1
  24. package/bin/executor/core-engine/CoreEngineRunner.js +12 -8
  25. package/bin/executor/core-engine/CoreEngineRunner.js.map +1 -1
  26. package/bin/executor/store/history/SessionHistoryStore.d.ts +3 -13
  27. package/bin/executor/store/history/SessionHistoryStore.d.ts.map +1 -1
  28. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js +1 -1
  29. package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js.map +1 -1
  30. package/bin/executor/tools/plugin/PluginToolBridge.d.ts.map +1 -1
  31. package/bin/executor/tools/plugin/PluginToolBridge.js +4 -2
  32. package/bin/executor/tools/plugin/PluginToolBridge.js.map +1 -1
  33. package/bin/index.d.ts +2 -1
  34. package/bin/index.d.ts.map +1 -1
  35. package/bin/index.js +1 -1
  36. package/bin/index.js.map +1 -1
  37. package/bin/model/CityModelAdapter.d.ts +6 -0
  38. package/bin/model/CityModelAdapter.d.ts.map +1 -1
  39. package/bin/model/CityModelAdapter.js +13 -0
  40. package/bin/model/CityModelAdapter.js.map +1 -1
  41. package/bin/plugin/core/PluginRegistry.d.ts +22 -1
  42. package/bin/plugin/core/PluginRegistry.d.ts.map +1 -1
  43. package/bin/plugin/core/PluginRegistry.js +46 -4
  44. package/bin/plugin/core/PluginRegistry.js.map +1 -1
  45. package/bin/session/Session.d.ts +11 -1
  46. package/bin/session/Session.d.ts.map +1 -1
  47. package/bin/session/Session.js +76 -11
  48. package/bin/session/Session.js.map +1 -1
  49. package/bin/session/browse/Browse.d.ts +0 -8
  50. package/bin/session/browse/Browse.d.ts.map +1 -1
  51. package/bin/session/browse/Browse.js +72 -57
  52. package/bin/session/browse/Browse.js.map +1 -1
  53. package/bin/session/recorder/JsonlSessionMessageStore.d.ts +69 -16
  54. package/bin/session/recorder/JsonlSessionMessageStore.d.ts.map +1 -1
  55. package/bin/session/recorder/JsonlSessionMessageStore.js +283 -60
  56. package/bin/session/recorder/JsonlSessionMessageStore.js.map +1 -1
  57. package/bin/session/recorder/SessionRecorder.d.ts +20 -2
  58. package/bin/session/recorder/SessionRecorder.d.ts.map +1 -1
  59. package/bin/session/recorder/SessionRecorder.js +91 -46
  60. package/bin/session/recorder/SessionRecorder.js.map +1 -1
  61. package/bin/session/recorder/SessionRecorderCompaction.d.ts +3 -3
  62. package/bin/session/recorder/SessionRecorderCompaction.d.ts.map +1 -1
  63. package/bin/session/recorder/SessionRecorderCompaction.js +50 -62
  64. package/bin/session/recorder/SessionRecorderCompaction.js.map +1 -1
  65. package/bin/session/recorder/SessionRecorderHistoryStore.d.ts +2 -2
  66. package/bin/session/recorder/SessionRecorderHistoryStore.d.ts.map +1 -1
  67. package/bin/session/recorder/SessionRecorderHistoryStore.js +31 -36
  68. package/bin/session/recorder/SessionRecorderHistoryStore.js.map +1 -1
  69. package/bin/session/runtime/SessionPromptRuntime.d.ts +17 -1
  70. package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
  71. package/bin/session/runtime/SessionPromptRuntime.js +67 -8
  72. package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
  73. package/bin/session/services/SessionStateService.d.ts +15 -1
  74. package/bin/session/services/SessionStateService.d.ts.map +1 -1
  75. package/bin/session/services/SessionStateService.js +60 -39
  76. package/bin/session/services/SessionStateService.js.map +1 -1
  77. package/bin/session/services/SessionTurnService.d.ts +5 -0
  78. package/bin/session/services/SessionTurnService.d.ts.map +1 -1
  79. package/bin/session/services/SessionTurnService.js +17 -3
  80. package/bin/session/services/SessionTurnService.js.map +1 -1
  81. package/bin/session/services/SessionViewService.d.ts +1 -5
  82. package/bin/session/services/SessionViewService.d.ts.map +1 -1
  83. package/bin/session/services/SessionViewService.js +4 -34
  84. package/bin/session/services/SessionViewService.js.map +1 -1
  85. package/bin/session/storage/Paths.d.ts +3 -15
  86. package/bin/session/storage/Paths.d.ts.map +1 -1
  87. package/bin/session/storage/Paths.js +5 -21
  88. package/bin/session/storage/Paths.js.map +1 -1
  89. package/bin/types/agent/SessionTypes.d.ts +2 -24
  90. package/bin/types/agent/SessionTypes.d.ts.map +1 -1
  91. package/bin/types/executor/SessionRunContext.d.ts +21 -0
  92. package/bin/types/executor/SessionRunContext.d.ts.map +1 -1
  93. package/bin/types/plugin/PluginRuntime.d.ts +25 -0
  94. package/bin/types/plugin/PluginRuntime.d.ts.map +1 -1
  95. package/bin/types/runtime/agent/AgentContext.d.ts +17 -5
  96. package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
  97. package/bin/types/runtime/agent/AgentContext.js +29 -6
  98. package/bin/types/runtime/agent/AgentContext.js.map +1 -1
  99. package/bin/types/session/SessionConfigMutation.d.ts +68 -0
  100. package/bin/types/session/SessionConfigMutation.d.ts.map +1 -0
  101. package/bin/types/session/SessionConfigMutation.js +10 -0
  102. package/bin/types/session/SessionConfigMutation.js.map +1 -0
  103. package/bin/types/session/SessionLocalState.d.ts +8 -0
  104. package/bin/types/session/SessionLocalState.d.ts.map +1 -1
  105. package/bin/types/session/SessionMessage.d.ts +16 -12
  106. package/bin/types/session/SessionMessage.d.ts.map +1 -1
  107. package/bin/types/session/SessionMutation.d.ts +1 -1
  108. package/bin/types/session/SessionMutation.js +1 -1
  109. package/bin/types/session/SessionOptions.d.ts +16 -0
  110. package/bin/types/session/SessionOptions.d.ts.map +1 -1
  111. package/bin/types/session/SessionSegment.d.ts +57 -0
  112. package/bin/types/session/SessionSegment.d.ts.map +1 -0
  113. package/bin/types/session/SessionSegment.js +8 -0
  114. package/bin/types/session/SessionSegment.js.map +1 -0
  115. package/package.json +3 -3
  116. package/scripts/city-model-tool-loop.test.mjs +32 -0
  117. package/scripts/session-action-message.test.mjs +59 -13
  118. package/scripts/session-compaction-context-window.test.mjs +95 -0
  119. package/scripts/session-config-turn-boundary.test.mjs +224 -0
  120. package/scripts/session-list-title.test.mjs +21 -3
  121. package/scripts/session-prompt-runtime.test.mjs +85 -0
  122. package/scripts/session-recorder.test.mjs +340 -11
  123. package/src/agent/local/Agent.ts +34 -3
  124. package/src/agent/local/services/AgentSessions.ts +61 -0
  125. package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +0 -5
  126. package/src/executor/Executor.ts +62 -0
  127. package/src/executor/composer/compaction/SessionCompactionComposer.ts +5 -0
  128. package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.ts +16 -14
  129. package/src/executor/core-engine/CoreEngineRunner.ts +24 -8
  130. package/src/executor/store/history/SessionHistoryStore.ts +3 -14
  131. package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +1 -1
  132. package/src/executor/tools/plugin/PluginToolBridge.ts +4 -2
  133. package/src/index.ts +8 -0
  134. package/src/model/CityModelAdapter.ts +15 -0
  135. package/src/plugin/core/PluginRegistry.ts +72 -4
  136. package/src/session/Session.ts +89 -11
  137. package/src/session/browse/Browse.ts +73 -69
  138. package/src/session/recorder/JsonlSessionMessageStore.ts +378 -60
  139. package/src/session/recorder/SessionRecorder.ts +116 -49
  140. package/src/session/recorder/SessionRecorderCompaction.ts +55 -80
  141. package/src/session/recorder/SessionRecorderHistoryStore.ts +29 -49
  142. package/src/session/runtime/SessionPromptRuntime.ts +100 -9
  143. package/src/session/services/SessionStateService.ts +79 -44
  144. package/src/session/services/SessionTurnService.ts +19 -3
  145. package/src/session/services/SessionViewService.ts +4 -50
  146. package/src/session/storage/Paths.ts +5 -38
  147. package/src/types/agent/SessionTypes.ts +2 -24
  148. package/src/types/executor/SessionRunContext.ts +25 -0
  149. package/src/types/plugin/PluginRuntime.ts +27 -0
  150. package/src/types/runtime/agent/AgentContext.ts +32 -7
  151. package/src/types/session/SessionConfigMutation.ts +78 -0
  152. package/src/types/session/SessionLocalState.ts +9 -0
  153. package/src/types/session/SessionMessage.ts +16 -12
  154. package/src/types/session/SessionMutation.ts +1 -1
  155. package/src/types/session/SessionOptions.ts +21 -0
  156. package/src/types/session/SessionSegment.ts +62 -0
  157. package/tsconfig.tsbuildinfo +1 -1
@@ -32,6 +32,7 @@ import type {
32
32
  import { to_session_action_record } from "@/executor/types/SessionRecords.js";
33
33
  import type { SessionExecutor } from "@/executor/types/SessionExecutor.js";
34
34
  import type { SessionRunContext } from "@/types/executor/SessionRunContext.js";
35
+ import type { AgentPluginExecutionRuntime } from "@/types/plugin/PluginRuntime.js";
35
36
  import type {
36
37
  SessionExecuteInput,
37
38
  SessionRunResult,
@@ -61,6 +62,11 @@ type ExecutorOptions = {
61
62
  */
62
63
  getModel: () => LanguageModel | undefined;
63
64
 
65
+ /**
66
+ * 读取当前 session 模型支持的总上下文窗口长度。
67
+ */
68
+ get_model_context_window?: () => number | undefined;
69
+
64
70
  /**
65
71
  * 统一日志器。
66
72
  */
@@ -81,6 +87,17 @@ type ExecutorOptions = {
81
87
  */
82
88
  getTools: () => Record<string, Tool>;
83
89
 
90
+ /**
91
+ * 读取当前 Session effective Agent env。
92
+ */
93
+ getEnv: () => Record<string, string>;
94
+
95
+ /** 读取当前 Session effective Agent instruction 文本。 */
96
+ get_systems?: () => string[];
97
+
98
+ /** 创建当前 Session effective Plugin 执行视图。 */
99
+ get_plugins?: () => AgentPluginExecutionRuntime;
100
+
84
101
  /**
85
102
  * 可选自定义 context Composer。
86
103
  */
@@ -100,6 +117,10 @@ export class Executor implements SessionExecutor {
100
117
  private readonly historyComposer: SessionHistoryComposer;
101
118
  private readonly historyStore: SessionHistoryStore;
102
119
  private readonly getModel: ExecutorOptions["getModel"];
120
+ private readonly getEnv: ExecutorOptions["getEnv"];
121
+ private readonly get_systems: ExecutorOptions["get_systems"];
122
+ private readonly get_plugins: ExecutorOptions["get_plugins"];
123
+ private readonly get_model_context_window?: ExecutorOptions["get_model_context_window"];
103
124
  private readonly logger: Logger;
104
125
  private readonly compactionComposer: SessionCompactionComposer;
105
126
  private readonly systemComposer: SessionSystemComposer;
@@ -122,6 +143,10 @@ export class Executor implements SessionExecutor {
122
143
  this.historyStore = options.historyStore;
123
144
  this.historyComposer = options.historyComposer;
124
145
  this.getModel = options.getModel;
146
+ this.getEnv = options.getEnv;
147
+ this.get_systems = options.get_systems;
148
+ this.get_plugins = options.get_plugins;
149
+ this.get_model_context_window = options.get_model_context_window;
125
150
  this.logger = options.logger;
126
151
  this.compactionComposer =
127
152
  options.compactionComposer || new JsonlSessionCompactionComposer();
@@ -359,10 +384,12 @@ export class Executor implements SessionExecutor {
359
384
  }
360
385
  await this.emitAction(run_context, action);
361
386
  };
387
+ const context_window = this.get_model_context_window?.();
362
388
 
363
389
  await this.compactionComposer.run({
364
390
  historyStore: this.historyStore,
365
391
  model,
392
+ ...(context_window !== undefined ? { context_window } : {}),
366
393
  system,
367
394
  retryCount: retry_count,
368
395
  onAction: emit_compaction_action,
@@ -413,9 +440,41 @@ export class Executor implements SessionExecutor {
413
440
  execute_input: input,
414
441
  model,
415
442
  run_context,
443
+ resolve_model_turn: async () =>
444
+ await this.resolve_model_turn(run_context),
416
445
  });
417
446
  }
418
447
 
448
+ /**
449
+ * 解析下一次 provider 请求实际使用的运行配置。
450
+ *
451
+ * 关键点(中文)
452
+ * - 调用方必须先提交 Session 统一输入队列,再调用本方法。
453
+ * - 每次调用只读取一次 model、system 与 tools,并把它们传给同一个 `streamText()`。
454
+ */
455
+ private async resolve_model_turn(run_context: SessionRunContext): Promise<{
456
+ model: LanguageModel;
457
+ system: SessionExecuteInput["system"];
458
+ tools: SessionExecuteInput["tools"];
459
+ }> {
460
+ run_context.agentEnv = Object.freeze({ ...this.getEnv() });
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
+ }
467
+ const composed_context = await this.contextComposer.compose(run_context);
468
+ return {
469
+ model: this.resolveModelOrThrow(),
470
+ system: await this.systemComposer.resolve(run_context),
471
+ tools: this.bind_run_scope_to_tools(
472
+ composed_context.tools,
473
+ run_context,
474
+ ),
475
+ };
476
+ }
477
+
419
478
  /**
420
479
  * 为所有 tool 的 execute callback 绑定 ShellRunScope。
421
480
  *
@@ -446,6 +505,9 @@ export class Executor implements SessionExecutor {
446
505
  run_context: {
447
506
  ...(session_id ? { session_id } : {}),
448
507
  ...(turn_id ? { turn_id } : {}),
508
+ ...(run_context.agentEnv
509
+ ? { env: run_context.agentEnv }
510
+ : {}),
449
511
  },
450
512
  },
451
513
  async () => await original_execute(args, options),
@@ -24,6 +24,11 @@ export type SessionCompactionInput = {
24
24
  */
25
25
  model: LanguageModel;
26
26
 
27
+ /**
28
+ * 当前模型支持的总上下文窗口长度,单位为 token。
29
+ */
30
+ context_window?: number;
31
+
27
32
  /**
28
33
  * 当前轮 system messages。
29
34
  */
@@ -14,7 +14,6 @@ import type {
14
14
  type JsonlSessionCompactionComposerOptions = {
15
15
  keepLastMessages?: number;
16
16
  maxInputTokensApprox?: number;
17
- archiveOnCompact?: boolean;
18
17
  compactRatio?: number;
19
18
  };
20
19
 
@@ -29,30 +28,35 @@ export class JsonlSessionCompactionComposer implements SessionCompactionComposer
29
28
  this.options = options || {};
30
29
  }
31
30
 
32
- private resolvePolicy(retryCount: number): {
31
+ private resolvePolicy(retryCount: number, context_window?: number): {
33
32
  keepLastMessages: number;
34
33
  maxInputTokensApprox: number;
35
- archiveOnCompact: boolean;
36
34
  compactRatio: number;
37
35
  } {
38
36
  const baseKeepLastMessages =
39
37
  typeof this.options.keepLastMessages === "number"
40
38
  ? Math.max(6, Math.min(5000, Math.floor(this.options.keepLastMessages)))
41
39
  : 30;
42
- const baseMaxInputTokensApprox =
40
+ const configured_max_input_tokens =
43
41
  typeof this.options.maxInputTokensApprox === "number"
44
- ? Math.max(2000, Math.min(200_000, Math.floor(this.options.maxInputTokensApprox)))
45
- : 128000;
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);
46
53
  const retryFactor = Math.max(1, Math.pow(2, retryCount));
47
54
  const keepLastMessages = Math.max(6, Math.floor(baseKeepLastMessages / retryFactor));
55
+ const minimum_input_tokens = Math.min(2000, baseMaxInputTokensApprox);
48
56
  const maxInputTokensApprox = Math.max(
49
- 2000,
57
+ minimum_input_tokens,
50
58
  Math.floor(baseMaxInputTokensApprox / retryFactor),
51
59
  );
52
- const archiveOnCompact =
53
- this.options.archiveOnCompact === undefined
54
- ? true
55
- : Boolean(this.options.archiveOnCompact);
56
60
  const compactRatioRaw =
57
61
  typeof this.options.compactRatio === "number" &&
58
62
  Number.isFinite(this.options.compactRatio)
@@ -62,7 +66,6 @@ export class JsonlSessionCompactionComposer implements SessionCompactionComposer
62
66
  return {
63
67
  keepLastMessages,
64
68
  maxInputTokensApprox,
65
- archiveOnCompact,
66
69
  compactRatio,
67
70
  };
68
71
  }
@@ -71,13 +74,12 @@ export class JsonlSessionCompactionComposer implements SessionCompactionComposer
71
74
  compacted: boolean;
72
75
  reason?: string;
73
76
  }> {
74
- const policy = this.resolvePolicy(input.retryCount);
77
+ const policy = this.resolvePolicy(input.retryCount, input.context_window);
75
78
  return await input.historyStore.compact({
76
79
  model: input.model,
77
80
  system: input.system,
78
81
  keepLastMessages: policy.keepLastMessages,
79
82
  maxInputTokensApprox: policy.maxInputTokensApprox,
80
- archiveOnCompact: policy.archiveOnCompact,
81
83
  compactRatio: policy.compactRatio,
82
84
  ...(input.onAction ? { onAction: input.onAction } : {}),
83
85
  });
@@ -125,6 +125,18 @@ interface CoreEngineRunInput {
125
125
  * 当前显式运行上下文。
126
126
  */
127
127
  run_context: SessionRunContext;
128
+
129
+ /**
130
+ * 在统一输入队列提交后解析当前模型 turn 的 effective 配置。
131
+ */
132
+ resolve_model_turn: () => Promise<{
133
+ /** 当前模型 turn 使用的模型。 */
134
+ model: LanguageModel;
135
+ /** 当前模型 turn 使用的 system messages。 */
136
+ system: SessionExecuteInput["system"];
137
+ /** 当前模型 turn 使用的工具集合。 */
138
+ tools: SessionExecuteInput["tools"];
139
+ }>;
128
140
  }
129
141
 
130
142
  /**
@@ -149,10 +161,10 @@ export class CoreEngineRunner {
149
161
  async run(input: CoreEngineRunInput): Promise<SessionRunResult> {
150
162
  const start_time = Date.now();
151
163
  const session_id = String(this.history_store.sessionId || "").trim();
152
- const system = Array.isArray(input.execute_input.system)
164
+ let system = Array.isArray(input.execute_input.system)
153
165
  ? input.execute_input.system
154
166
  : [];
155
- const tools = input.execute_input.tools;
167
+ let tools = input.execute_input.tools;
156
168
  let last_observed_stream_error: unknown = undefined;
157
169
  let final_assistant_ui_message: SessionMessageRecordV1 | null = null;
158
170
 
@@ -188,7 +200,7 @@ export class CoreEngineRunner {
188
200
  await context_composer_on_step_finish(step_result);
189
201
  };
190
202
 
191
- const prepare_step = this.context_composer.createPrepareStepHandler({
203
+ const prepare_model_turn_inputs = this.context_composer.createPrepareStepHandler({
192
204
  system,
193
205
  appendMergedUserMessages: append_merged_user_messages,
194
206
  runContext: input.run_context,
@@ -198,11 +210,16 @@ export class CoreEngineRunner {
198
210
  let incomplete_response_recovery_count = 0;
199
211
 
200
212
  while (step_count < MAX_TOOL_LOOP_STEPS) {
213
+ // 关键点(中文):steer 与配置 mutation 必须在选择 model/system/tools 前提交。
214
+ // 这样运行中写入的配置只影响下一次 provider 请求,不会改变当前流或 tool callback。
215
+ await prepare_model_turn_inputs({ messages: [] });
216
+ const model_turn = await input.resolve_model_turn();
217
+ system = Array.isArray(model_turn.system) ? model_turn.system : [];
218
+ tools = model_turn.tools;
201
219
  const result = streamText({
202
- model: input.model,
220
+ model: model_turn.model,
203
221
  system,
204
222
  onStepFinish: on_step_finish,
205
- prepareStep: prepare_step,
206
223
  messages: message_state.modelMessages,
207
224
  tools,
208
225
  abortSignal: input.run_context.abortSignal,
@@ -350,9 +367,8 @@ export class CoreEngineRunner {
350
367
  }
351
368
 
352
369
  // 关键点(中文):stop 前做 tail merge,覆盖最后一个 step 后才入队的新 user 消息。
353
- const tail_prepared = await prepare_step({ messages: [] });
354
- const tail_merged_message_count = Array.isArray(tail_prepared.messages)
355
- ? tail_prepared.messages.length
370
+ const tail_merged_message_count = input.run_context.hasPendingStepInput?.()
371
+ ? 1
356
372
  : 0;
357
373
  if (
358
374
  shouldContinueForTailMergedUserMessages({
@@ -39,11 +39,6 @@ export type SessionHistoryCompactInput = {
39
39
  */
40
40
  maxInputTokensApprox: number;
41
41
 
42
- /**
43
- * compact 时是否归档旧消息。
44
- */
45
- archiveOnCompact: boolean;
46
-
47
42
  /**
48
43
  * 前段压缩比例(0-1)。
49
44
  */
@@ -88,19 +83,13 @@ export interface SessionHistoryStore {
88
83
  */
89
84
  finalize_inflight(message?: SessionRecordV1 | null): Promise<void>;
90
85
 
91
- /**
92
- * 读取完整 session records。
93
- */
86
+ /** 读取当前模型上下文所需的 session records。 */
94
87
  list_records(): Promise<SessionRecordV1[]>;
95
88
 
96
- /**
97
- * 读取 session record 区间 [start, end)。
98
- */
89
+ /** 读取当前模型上下文 record 区间 [start, end)。 */
99
90
  slice_records(start: number, end: number): Promise<SessionRecordV1[]>;
100
91
 
101
- /**
102
- * 读取 session record 总条数。
103
- */
92
+ /** 读取当前模型上下文 record 总条数。 */
104
93
  record_count(): Promise<number>;
105
94
 
106
95
  /**
@@ -667,7 +667,7 @@ export class JsonlSessionHistoryStore implements SessionHistoryStore {
667
667
  system: this.normalizeSystem(input.system),
668
668
  keepLastMessages: input.keepLastMessages,
669
669
  maxInputTokensApprox: input.maxInputTokensApprox,
670
- archiveOnCompact: input.archiveOnCompact,
670
+ archiveOnCompact: false,
671
671
  compactRatio: input.compactRatio,
672
672
  ...(input.onAction ? { onAction: input.onAction } : {}),
673
673
  },
@@ -138,7 +138,8 @@ export async function invokePluginCallTool(
138
138
  }
139
139
 
140
140
  try {
141
- const result = await params.plugins.runAction({
141
+ const plugins = getSessionRunContext()?.agentPlugins || params.plugins;
142
+ const result = await plugins.runAction({
142
143
  plugin,
143
144
  action,
144
145
  payload,
@@ -191,7 +192,8 @@ export async function invokePluginReadTool(
191
192
  ): Promise<PluginReadToolResult> {
192
193
  const input: PluginReadInput = params.input;
193
194
  try {
194
- const data = params.plugins.read({
195
+ const plugins = getSessionRunContext()?.agentPlugins || params.plugins;
196
+ const data = plugins.read({
195
197
  plugin: typeof input.plugin === "string" ? input.plugin : undefined,
196
198
  action: typeof input.action === "string" ? input.action : undefined,
197
199
  });
package/src/index.ts CHANGED
@@ -15,6 +15,7 @@ export type { SessionOptions } from "./types/session/SessionOptions.js";
15
15
  export {
16
16
  inferAgentModelLabel,
17
17
  normalizeAgentModel,
18
+ read_agent_model_context_window,
18
19
  } from "./model/CityModelAdapter.js";
19
20
  export type { AgentModel } from "./model/CityModelAdapter.js";
20
21
  export type {
@@ -48,6 +49,13 @@ export type {
48
49
  SessionUserMessage,
49
50
  SessionUserMessagePart,
50
51
  } from "./types/session/SessionMessage.js";
52
+ export type {
53
+ SessionContextSnapshot,
54
+ SessionMessageStorageStats,
55
+ SessionSegmentRange,
56
+ SessionSegmentSnapshot,
57
+ SessionSegmentSummary,
58
+ } from "./types/session/SessionSegment.js";
51
59
  export {
52
60
  is_session_mutation,
53
61
  } from "./types/session/SessionMutation.js";
@@ -44,6 +44,21 @@ export function normalizeAgentModel(model: AgentModel): LanguageModel {
44
44
  return model;
45
45
  }
46
46
 
47
+ /**
48
+ * 从 Agent 模型输入读取已声明的上下文窗口。
49
+ *
50
+ * 只有 CityModel 携带可信的模型目录元数据;普通 AI SDK LanguageModel 未声明时返回空。
51
+ */
52
+ export function read_agent_model_context_window(
53
+ model: AgentModel | undefined,
54
+ ): number | undefined {
55
+ if (!model || !isCityModel(model)) return undefined;
56
+ const context_window = model.context_window;
57
+ return Number.isSafeInteger(context_window) && Number(context_window) > 0
58
+ ? Number(context_window)
59
+ : undefined;
60
+ }
61
+
47
62
  /**
48
63
  * 从 Agent 模型输入推导展示标签。
49
64
  */
@@ -13,6 +13,7 @@ import type { Plugin } from "@/types/plugin/PluginDefinition.js";
13
13
  import type { PluginActionResult } from "@/types/plugin/PluginAction.js";
14
14
  import type {
15
15
  AgentPlugins,
16
+ AgentPluginExecutionRuntime,
16
17
  PluginAvailability,
17
18
  PluginActionReadView,
18
19
  PluginReadView,
@@ -101,6 +102,13 @@ export class PluginRegistry implements AgentPlugins {
101
102
 
102
103
  private readonly records = new Map<string, PluginRuntimeRecord>();
103
104
 
105
+ private change_listener?: (input: {
106
+ /** 当前修改是注册还是卸载。 */
107
+ type: "register" | "unregister";
108
+ /** 当前修改的 plugin 名称。 */
109
+ plugin_name: string;
110
+ }) => void;
111
+
104
112
  constructor(params: {
105
113
  contextResolver: ContextResolver;
106
114
  hookRegistry: HookRegistry;
@@ -111,6 +119,13 @@ export class PluginRegistry implements AgentPlugins {
111
119
  this.pluginInstances = params.pluginInstances;
112
120
  }
113
121
 
122
+ /**
123
+ * 设置 Agent 内部配置修改监听器。
124
+ */
125
+ set_change_listener(listener: PluginRegistry["change_listener"]): void {
126
+ this.change_listener = listener;
127
+ }
128
+
114
129
  /**
115
130
  * 注册单个 plugin。
116
131
  *
@@ -134,6 +149,7 @@ export class PluginRegistry implements AgentPlugins {
134
149
 
135
150
  try {
136
151
  await this.start_record(record);
152
+ this.change_listener?.({ type: "register", plugin_name: key });
137
153
  return to_plugin_snapshot(record);
138
154
  } catch (error) {
139
155
  this.unregister_hooks(key);
@@ -179,6 +195,7 @@ export class PluginRegistry implements AgentPlugins {
179
195
  this.unregister_hooks(key);
180
196
  this.records.delete(key);
181
197
  this.pluginInstances.delete(key);
198
+ this.change_listener?.({ type: "unregister", plugin_name: key });
182
199
  return true;
183
200
  }
184
201
 
@@ -381,11 +398,25 @@ export class PluginRegistry implements AgentPlugins {
381
398
  plugin?: string;
382
399
  action?: string;
383
400
  }): PluginReadView | { plugins: PluginView[] } {
401
+ return this.read_from_records(this.records, params);
402
+ }
403
+
404
+ /**
405
+ * 从指定记录视图读取 plugin/action metadata。
406
+ */
407
+ private read_from_records(
408
+ records: ReadonlyMap<string, PluginRuntimeRecord>,
409
+ params: { plugin?: string; action?: string },
410
+ ): PluginReadView | { plugins: PluginView[] } {
384
411
  const pluginName = normalize_plugin_name(params.plugin || "");
385
412
  if (!pluginName) {
386
- return { plugins: this.list() };
413
+ return {
414
+ plugins: Array.from(records.values())
415
+ .map((record) => toPluginView(record.plugin))
416
+ .sort((left, right) => left.name.localeCompare(right.name)),
417
+ };
387
418
  }
388
- const plugin = this.get(pluginName);
419
+ const plugin = records.get(pluginName)?.plugin || null;
389
420
  if (!plugin) {
390
421
  return {
391
422
  name: pluginName,
@@ -470,8 +501,22 @@ export class PluginRegistry implements AgentPlugins {
470
501
  action: string;
471
502
  payload?: JsonValue;
472
503
  }): Promise<PluginActionResult<JsonValue>> {
504
+ return await this.run_action_from_records(this.records, params);
505
+ }
506
+
507
+ /**
508
+ * 从指定记录视图运行 plugin action。
509
+ */
510
+ private async run_action_from_records(
511
+ records: ReadonlyMap<string, PluginRuntimeRecord>,
512
+ params: {
513
+ plugin: string;
514
+ action: string;
515
+ payload?: JsonValue;
516
+ },
517
+ ): Promise<PluginActionResult<JsonValue>> {
473
518
  const key = normalize_plugin_name(params.plugin);
474
- const record = this.records.get(key);
519
+ const record = records.get(key);
475
520
  if (!record) {
476
521
  return {
477
522
  success: false,
@@ -536,9 +581,18 @@ export class PluginRegistry implements AgentPlugins {
536
581
  * 读取当前生效的 plugin system blocks。
537
582
  */
538
583
  async systemBlocks(): Promise<AgentSessionSystemBlock[]> {
584
+ return await this.system_blocks_from_records(this.records);
585
+ }
586
+
587
+ /**
588
+ * 从指定记录视图解析 plugin system blocks。
589
+ */
590
+ private async system_blocks_from_records(
591
+ records: ReadonlyMap<string, PluginRuntimeRecord>,
592
+ ): Promise<AgentSessionSystemBlock[]> {
539
593
  const context = this.contextResolver();
540
594
  const out: AgentSessionSystemBlock[] = [];
541
- for (const record of this.records.values()) {
595
+ for (const record of records.values()) {
542
596
  const plugin = record.plugin;
543
597
  if (record.state !== "ready") continue;
544
598
  if (typeof plugin.system !== "function") continue;
@@ -560,4 +614,18 @@ export class PluginRegistry implements AgentPlugins {
560
614
  }
561
615
  return out;
562
616
  }
617
+
618
+ /**
619
+ * 创建当前 configured registry 的模型 turn 执行视图。
620
+ */
621
+ execution_view(): AgentPluginExecutionRuntime {
622
+ const records = new Map(this.records);
623
+ return {
624
+ read: (params) => this.read_from_records(records, params),
625
+ runAction: async (params) =>
626
+ await this.run_action_from_records(records, params),
627
+ systemBlocks: async () =>
628
+ await this.system_blocks_from_records(records),
629
+ };
630
+ }
563
631
  }