@downcity/agent 1.1.228 → 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 +6 -1
- package/bin/agent/local/Agent.d.ts.map +1 -1
- package/bin/agent/local/Agent.js +30 -3
- package/bin/agent/local/Agent.js.map +1 -1
- package/bin/agent/local/services/AgentSessions.d.ts +23 -0
- package/bin/agent/local/services/AgentSessions.d.ts.map +1 -1
- package/bin/agent/local/services/AgentSessions.js +44 -0
- 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 -7
- 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 +43 -0
- package/bin/executor/Executor.d.ts.map +1 -1
- package/bin/executor/Executor.js +127 -7
- package/bin/executor/Executor.js.map +1 -1
- package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts +10 -0
- package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts +0 -10
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js +4 -34
- 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 +15 -0
- package/bin/executor/core-engine/CoreEngineRunner.d.ts.map +1 -1
- package/bin/executor/core-engine/CoreEngineRunner.js +150 -35
- package/bin/executor/core-engine/CoreEngineRunner.js.map +1 -1
- package/bin/executor/store/history/SessionHistoryStore.d.ts +7 -23
- 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 +2 -4
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js.map +1 -1
- package/bin/executor/tools/plugin/PluginToolBridge.d.ts.map +1 -1
- package/bin/executor/tools/plugin/PluginToolBridge.js +4 -2
- package/bin/executor/tools/plugin/PluginToolBridge.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 +3 -2
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +2 -2
- package/bin/index.js.map +1 -1
- package/bin/model/CityModelAdapter.d.ts +6 -0
- package/bin/model/CityModelAdapter.d.ts.map +1 -1
- package/bin/model/CityModelAdapter.js +13 -0
- package/bin/model/CityModelAdapter.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 +41 -2
- package/bin/plugin/core/PluginRegistry.d.ts.map +1 -1
- package/bin/plugin/core/PluginRegistry.js +145 -6
- 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 +15 -1
- package/bin/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +83 -11
- 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/browse/Browse.d.ts +0 -8
- package/bin/session/browse/Browse.d.ts.map +1 -1
- package/bin/session/browse/Browse.js +72 -57
- package/bin/session/browse/Browse.js.map +1 -1
- package/bin/session/recorder/JsonlSessionMessageStore.d.ts +69 -16
- package/bin/session/recorder/JsonlSessionMessageStore.d.ts.map +1 -1
- package/bin/session/recorder/JsonlSessionMessageStore.js +283 -60
- package/bin/session/recorder/JsonlSessionMessageStore.js.map +1 -1
- package/bin/session/recorder/SessionRecorder.d.ts +20 -2
- package/bin/session/recorder/SessionRecorder.d.ts.map +1 -1
- package/bin/session/recorder/SessionRecorder.js +91 -46
- package/bin/session/recorder/SessionRecorder.js.map +1 -1
- package/bin/session/recorder/SessionRecorderCompaction.d.ts +3 -3
- package/bin/session/recorder/SessionRecorderCompaction.d.ts.map +1 -1
- package/bin/session/recorder/SessionRecorderCompaction.js +118 -88
- package/bin/session/recorder/SessionRecorderCompaction.js.map +1 -1
- package/bin/session/recorder/SessionRecorderHistoryStore.d.ts +2 -2
- package/bin/session/recorder/SessionRecorderHistoryStore.d.ts.map +1 -1
- package/bin/session/recorder/SessionRecorderHistoryStore.js +31 -36
- package/bin/session/recorder/SessionRecorderHistoryStore.js.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.d.ts +21 -1
- package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.js +65 -8
- package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
- package/bin/session/services/SessionStateService.d.ts +19 -1
- package/bin/session/services/SessionStateService.d.ts.map +1 -1
- package/bin/session/services/SessionStateService.js +70 -40
- package/bin/session/services/SessionStateService.js.map +1 -1
- package/bin/session/services/SessionTurnService.d.ts +15 -0
- package/bin/session/services/SessionTurnService.d.ts.map +1 -1
- package/bin/session/services/SessionTurnService.js +104 -7
- package/bin/session/services/SessionTurnService.js.map +1 -1
- package/bin/session/services/SessionViewService.d.ts +1 -5
- package/bin/session/services/SessionViewService.d.ts.map +1 -1
- package/bin/session/services/SessionViewService.js +4 -34
- package/bin/session/services/SessionViewService.js.map +1 -1
- package/bin/session/storage/Paths.d.ts +3 -15
- package/bin/session/storage/Paths.d.ts.map +1 -1
- package/bin/session/storage/Paths.js +5 -21
- package/bin/session/storage/Paths.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/agent/SessionTypes.d.ts +2 -24
- package/bin/types/agent/SessionTypes.d.ts.map +1 -1
- package/bin/types/executor/SessionRunContext.d.ts +29 -0
- package/bin/types/executor/SessionRunContext.d.ts.map +1 -1
- package/bin/types/plugin/PluginRuntime.d.ts +59 -2
- 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 +17 -5
- package/bin/types/runtime/agent/AgentContext.d.ts.map +1 -1
- package/bin/types/runtime/agent/AgentContext.js +29 -6
- package/bin/types/runtime/agent/AgentContext.js.map +1 -1
- package/bin/types/session/SessionLocalState.d.ts +8 -0
- package/bin/types/session/SessionLocalState.d.ts.map +1 -1
- package/bin/types/session/SessionMessage.d.ts +16 -12
- package/bin/types/session/SessionMessage.d.ts.map +1 -1
- package/bin/types/session/SessionMutation.d.ts +1 -1
- package/bin/types/session/SessionMutation.js +1 -1
- package/bin/types/session/SessionOptions.d.ts +16 -0
- 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/types/session/SessionSegment.d.ts +57 -0
- package/bin/types/session/SessionSegment.d.ts.map +1 -0
- package/bin/types/session/SessionSegment.js +8 -0
- package/bin/types/session/SessionSegment.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/city-model-tool-loop.test.mjs +32 -0
- 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-action-message.test.mjs +59 -13
- package/scripts/session-compaction-context-window.test.mjs +96 -0
- package/scripts/session-config-turn-boundary.test.mjs +278 -0
- package/scripts/session-list-title.test.mjs +21 -3
- package/scripts/session-prompt-runtime.test.mjs +93 -0
- package/scripts/session-recorder.test.mjs +335 -15
- package/src/agent/local/Agent.ts +34 -3
- package/src/agent/local/services/AgentSessions.ts +61 -0
- package/src/agent/remote/RemoteSession.ts +5 -0
- package/src/agent/remote/RemoteTransport.ts +2 -0
- package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +15 -5
- package/src/agent/remote/transports/RpcRemoteAgentTransport.ts +4 -0
- package/src/executor/Executor.ts +161 -7
- package/src/executor/composer/compaction/SessionCompactionComposer.ts +12 -0
- package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.ts +4 -55
- 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 +211 -43
- package/src/executor/store/history/SessionHistoryStore.ts +7 -26
- package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +2 -4
- package/src/executor/tools/plugin/PluginToolBridge.ts +4 -2
- package/src/executor/types/SessionRun.ts +9 -0
- package/src/index.ts +9 -1
- package/src/model/CityModelAdapter.ts +15 -0
- package/src/plugin/core/PluginLocalExecution.ts +3 -3
- package/src/plugin/core/PluginRegistry.ts +187 -6
- package/src/rpc/Client.ts +12 -0
- package/src/session/Session.ts +97 -11
- package/src/session/SessionSystemBuilder.ts +4 -1
- package/src/session/browse/Browse.ts +73 -69
- package/src/session/recorder/JsonlSessionMessageStore.ts +378 -60
- package/src/session/recorder/SessionRecorder.ts +116 -49
- package/src/session/recorder/SessionRecorderCompaction.ts +131 -108
- package/src/session/recorder/SessionRecorderHistoryStore.ts +29 -49
- package/src/session/runtime/SessionPromptRuntime.ts +86 -10
- package/src/session/services/SessionStateService.ts +89 -45
- package/src/session/services/SessionTurnService.ts +112 -7
- package/src/session/services/SessionViewService.ts +4 -50
- package/src/session/storage/Paths.ts +5 -38
- package/src/types/agent/SessionActor.ts +9 -0
- package/src/types/agent/SessionTypes.ts +2 -24
- package/src/types/executor/SessionRunContext.ts +34 -0
- package/src/types/plugin/PluginRuntime.ts +63 -2
- package/src/types/plugin/PluginState.ts +10 -0
- package/src/types/rpc/RpcProtocol.ts +11 -0
- package/src/types/runtime/agent/AgentContext.ts +32 -7
- package/src/types/session/SessionLocalState.ts +9 -0
- package/src/types/session/SessionMessage.ts +16 -12
- package/src/types/session/SessionMutation.ts +1 -1
- package/src/types/session/SessionOptions.ts +21 -0
- package/src/types/session/SessionQueueCommand.ts +78 -0
- package/src/types/session/SessionSegment.ts +62 -0
- package/src/utils/logger/Logger.ts +10 -10
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -16,6 +16,7 @@ import type {
|
|
|
16
16
|
import type { SessionMutation } from "@/types/session/SessionMutation.js";
|
|
17
17
|
import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
|
|
18
18
|
import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
|
|
19
|
+
import type { SessionQueueCommand } from "@/types/session/SessionQueueCommand.js";
|
|
19
20
|
import type {
|
|
20
21
|
AgentSessionTurnHandle,
|
|
21
22
|
AgentSessionTurnResult,
|
|
@@ -25,7 +26,10 @@ const TURN_STOPPED_MESSAGE = "Turn stopped";
|
|
|
25
26
|
const QUEUED_PROMPT_CANCELLED_MESSAGE =
|
|
26
27
|
"Prompt cancelled because session was stopped";
|
|
27
28
|
|
|
28
|
-
type
|
|
29
|
+
type SessionQueuePrompt = {
|
|
30
|
+
/** 当前队列项固定为 prompt。 */
|
|
31
|
+
type: "prompt";
|
|
32
|
+
|
|
29
33
|
/**
|
|
30
34
|
* 当前排队中的 prompt 输入。
|
|
31
35
|
*/
|
|
@@ -37,6 +41,11 @@ type QueuedPrompt = {
|
|
|
37
41
|
deferredHandle: Deferred<AgentSessionTurnHandle>;
|
|
38
42
|
};
|
|
39
43
|
|
|
44
|
+
/**
|
|
45
|
+
* Session queue 的统一有序输入。
|
|
46
|
+
*/
|
|
47
|
+
type SessionQueueInput = SessionQueuePrompt | SessionQueueCommand;
|
|
48
|
+
|
|
40
49
|
/**
|
|
41
50
|
* Promise 延迟控制器。
|
|
42
51
|
*/
|
|
@@ -139,7 +148,7 @@ export class SessionPromptRuntime {
|
|
|
139
148
|
private readonly appendErrorMessage: SessionPromptRuntimeOptions["appendErrorMessage"];
|
|
140
149
|
private readonly executeTurn: SessionPromptRuntimeOptions["executeTurn"];
|
|
141
150
|
private readonly stopTurn: SessionPromptRuntimeOptions["stopTurn"];
|
|
142
|
-
private readonly queue:
|
|
151
|
+
private readonly queue: SessionQueueInput[] = [];
|
|
143
152
|
private processingPromise: Promise<void> | null = null;
|
|
144
153
|
private activeTurn: ActiveTurnState | null = null;
|
|
145
154
|
|
|
@@ -161,6 +170,7 @@ export class SessionPromptRuntime {
|
|
|
161
170
|
prompt(input: AgentSessionPromptInput): Promise<AgentSessionTurnHandle> {
|
|
162
171
|
const deferredHandle = createDeferred<AgentSessionTurnHandle>();
|
|
163
172
|
this.queue.push({
|
|
173
|
+
type: "prompt",
|
|
164
174
|
input,
|
|
165
175
|
deferredHandle,
|
|
166
176
|
});
|
|
@@ -168,6 +178,27 @@ export class SessionPromptRuntime {
|
|
|
168
178
|
return deferredHandle.promise;
|
|
169
179
|
}
|
|
170
180
|
|
|
181
|
+
/**
|
|
182
|
+
* 把一次已成功创建的 command 加入统一输入队列。
|
|
183
|
+
*/
|
|
184
|
+
enqueue_command(command: SessionQueueCommand): void {
|
|
185
|
+
this.queue.push(command);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* 判断是否存在等待并入下一 Session step 的 prompt。
|
|
190
|
+
*/
|
|
191
|
+
has_pending_prompt(): boolean {
|
|
192
|
+
return this.queue.some((item) => item.type === "prompt");
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
/**
|
|
196
|
+
* 判断是否存在等待在下一 Session step 检查点执行的 command。
|
|
197
|
+
*/
|
|
198
|
+
has_pending_command(): boolean {
|
|
199
|
+
return this.queue.some((item) => item.type === "command");
|
|
200
|
+
}
|
|
201
|
+
|
|
171
202
|
/**
|
|
172
203
|
* 返回当前 actor prompt 调度器是否仍处于活跃态。
|
|
173
204
|
*
|
|
@@ -176,7 +207,7 @@ export class SessionPromptRuntime {
|
|
|
176
207
|
* - Session 会用它阻止内部 direct execution 与 actor 模式并发混用。
|
|
177
208
|
*/
|
|
178
209
|
isActive(): boolean {
|
|
179
|
-
return this.processingPromise !== null || this.
|
|
210
|
+
return this.processingPromise !== null || this.has_pending_prompt();
|
|
180
211
|
}
|
|
181
212
|
|
|
182
213
|
/**
|
|
@@ -211,16 +242,19 @@ export class SessionPromptRuntime {
|
|
|
211
242
|
if (this.processingPromise) return;
|
|
212
243
|
this.processingPromise = this.processLoop().finally(() => {
|
|
213
244
|
this.processingPromise = null;
|
|
214
|
-
if (this.
|
|
245
|
+
if (this.has_pending_prompt()) {
|
|
215
246
|
this.ensureProcessing();
|
|
216
247
|
}
|
|
217
248
|
});
|
|
218
249
|
}
|
|
219
250
|
|
|
220
251
|
private async processLoop(): Promise<void> {
|
|
221
|
-
while (this.
|
|
252
|
+
while (this.has_pending_prompt()) {
|
|
253
|
+
const prompt_index = this.queue.findIndex((item) => item.type === "prompt");
|
|
254
|
+
if (prompt_index < 0) break;
|
|
255
|
+
const queued_before_prompt = this.queue.splice(0, prompt_index);
|
|
222
256
|
const current = this.queue.shift();
|
|
223
|
-
if (!current) break;
|
|
257
|
+
if (!current || current.type !== "prompt") break;
|
|
224
258
|
|
|
225
259
|
const turnId = `turn:${this.sessionId}:${Date.now()}:${nanoid(6)}`;
|
|
226
260
|
const activeTurn = createActiveTurnState(turnId);
|
|
@@ -237,12 +271,18 @@ export class SessionPromptRuntime {
|
|
|
237
271
|
current.deferredHandle.resolve(createTurnHandle(activeTurn));
|
|
238
272
|
|
|
239
273
|
try {
|
|
274
|
+
await this.execute_commands(
|
|
275
|
+
queued_before_prompt.filter(
|
|
276
|
+
(item): item is SessionQueueCommand => item.type === "command",
|
|
277
|
+
),
|
|
278
|
+
activeTurn,
|
|
279
|
+
);
|
|
240
280
|
await this.createAndPersistUserMessage(current.input, turnId, "prompt");
|
|
241
281
|
const result = await this.executeTurn({
|
|
242
282
|
turnId,
|
|
243
283
|
promptInput: current.input,
|
|
244
284
|
onStepMerge: async () => {
|
|
245
|
-
return await this.
|
|
285
|
+
return await this.drain_queued_inputs(activeTurn);
|
|
246
286
|
},
|
|
247
287
|
abortSignal: activeTurn.abortController.signal,
|
|
248
288
|
});
|
|
@@ -317,8 +357,12 @@ export class SessionPromptRuntime {
|
|
|
317
357
|
}
|
|
318
358
|
|
|
319
359
|
private cancelQueuedPrompts(): number {
|
|
320
|
-
|
|
321
|
-
|
|
360
|
+
const cancelled = this.queue.filter(
|
|
361
|
+
(item): item is SessionQueuePrompt => item.type === "prompt",
|
|
362
|
+
);
|
|
363
|
+
if (cancelled.length <= 0) return 0;
|
|
364
|
+
const retained = this.queue.filter((item) => item.type === "command");
|
|
365
|
+
this.queue.splice(0, this.queue.length, ...retained);
|
|
322
366
|
for (const item of cancelled) {
|
|
323
367
|
const turnId = `turn:${this.sessionId}:cancelled:${Date.now()}:${nanoid(6)}`;
|
|
324
368
|
const cancelledTurn = createActiveTurnState(turnId);
|
|
@@ -359,7 +403,10 @@ export class SessionPromptRuntime {
|
|
|
359
403
|
return cancelled.length;
|
|
360
404
|
}
|
|
361
405
|
|
|
362
|
-
|
|
406
|
+
/**
|
|
407
|
+
* 在下一 Session step 检查点按入队顺序提交配置并持久化 steer。
|
|
408
|
+
*/
|
|
409
|
+
private async drain_queued_inputs(
|
|
363
410
|
activeTurn: ActiveTurnState,
|
|
364
411
|
): Promise<SessionUserMessageV1[]> {
|
|
365
412
|
if (this.queue.length <= 0) return [];
|
|
@@ -368,6 +415,16 @@ export class SessionPromptRuntime {
|
|
|
368
415
|
|
|
369
416
|
for (let index = 0; index < drained.length; index += 1) {
|
|
370
417
|
const item = drained[index];
|
|
418
|
+
if (item.type === "command") {
|
|
419
|
+
try {
|
|
420
|
+
await item.execute({
|
|
421
|
+
turn_id: activeTurn.turnId,
|
|
422
|
+
});
|
|
423
|
+
} catch {
|
|
424
|
+
// command 自己负责失败观测;单条失败不能吞掉后续 steer 或 command。
|
|
425
|
+
}
|
|
426
|
+
continue;
|
|
427
|
+
}
|
|
371
428
|
try {
|
|
372
429
|
const message = await this.createAndPersistUserMessage(
|
|
373
430
|
item.input,
|
|
@@ -386,6 +443,25 @@ export class SessionPromptRuntime {
|
|
|
386
443
|
|
|
387
444
|
return merged;
|
|
388
445
|
}
|
|
446
|
+
|
|
447
|
+
/**
|
|
448
|
+
* 执行一组已经从统一队列截取的 command。
|
|
449
|
+
*/
|
|
450
|
+
private async execute_commands(
|
|
451
|
+
commands: SessionQueueCommand[],
|
|
452
|
+
active_turn: ActiveTurnState,
|
|
453
|
+
): Promise<void> {
|
|
454
|
+
if (commands.length <= 0) return;
|
|
455
|
+
for (const command of commands) {
|
|
456
|
+
try {
|
|
457
|
+
await command.execute({
|
|
458
|
+
turn_id: active_turn.turnId,
|
|
459
|
+
});
|
|
460
|
+
} catch {
|
|
461
|
+
// command 自己负责失败观测;单条失败不能阻断当前 prompt。
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
}
|
|
389
465
|
}
|
|
390
466
|
|
|
391
467
|
function buildPromptRuntimeErrorAssistantMessage(input: {
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
import {
|
|
11
11
|
inferAgentModelLabel,
|
|
12
12
|
normalizeAgentModel,
|
|
13
|
+
read_agent_model_context_window,
|
|
13
14
|
} from "@/model/CityModelAdapter.js";
|
|
14
15
|
import {
|
|
15
16
|
patchSessionModelLabel,
|
|
@@ -36,6 +37,7 @@ import type {
|
|
|
36
37
|
SessionUserMessageV1,
|
|
37
38
|
} from "@/executor/types/SessionRecords.js";
|
|
38
39
|
import type { SessionLocalState } from "@/types/session/SessionLocalState.js";
|
|
40
|
+
import type { SessionQueueCommand } from "@/types/session/SessionQueueCommand.js";
|
|
39
41
|
import { generateId } from "@/utils/Id.js";
|
|
40
42
|
import type { Logger } from "@/utils/logger/Logger.js";
|
|
41
43
|
import type { AgentModel } from "@/model/CityModelAdapter.js";
|
|
@@ -46,8 +48,6 @@ import {
|
|
|
46
48
|
from_ui_user_parts,
|
|
47
49
|
to_executor_ui_message,
|
|
48
50
|
} from "@/session/recorder/SessionMessageCodec.js";
|
|
49
|
-
import fs from "fs-extra";
|
|
50
|
-
import { getSdkAgentSessionMessagesPath } from "@/session/storage/Paths.js";
|
|
51
51
|
import type { SessionMessage } from "@/types/session/SessionMessage.js";
|
|
52
52
|
|
|
53
53
|
type SessionStateServiceOptions = {
|
|
@@ -116,6 +116,20 @@ type SessionSetOptions = {
|
|
|
116
116
|
|
|
117
117
|
type EmitActionInput = SessionActionRecordInputV1 | SessionActionRecordV1;
|
|
118
118
|
|
|
119
|
+
/**
|
|
120
|
+
* Session 配置成功写入后的队列提交结果。
|
|
121
|
+
*/
|
|
122
|
+
export interface SessionConfiguredCommandResult {
|
|
123
|
+
/**
|
|
124
|
+
* 等待在下一 Session step 检查点执行的 command。
|
|
125
|
+
*
|
|
126
|
+
* 说明(中文)
|
|
127
|
+
* - 输入未产生实际配置变化时为空。
|
|
128
|
+
* - fork 等内部初始化路径可以选择立即提交而不返回 command。
|
|
129
|
+
*/
|
|
130
|
+
command?: SessionQueueCommand;
|
|
131
|
+
}
|
|
132
|
+
|
|
119
133
|
/**
|
|
120
134
|
* 本地 Session 状态与持久化服务。
|
|
121
135
|
*/
|
|
@@ -209,6 +223,9 @@ export class SessionStateService {
|
|
|
209
223
|
...(metadata.modelId ? { modelId: metadata.modelId } : {}),
|
|
210
224
|
};
|
|
211
225
|
await this.restore_persisted_model();
|
|
226
|
+
this.state.effective_session_config = {
|
|
227
|
+
...this.state.sessionConfig,
|
|
228
|
+
};
|
|
212
229
|
})();
|
|
213
230
|
await this.state.initializePromise;
|
|
214
231
|
}
|
|
@@ -247,7 +264,10 @@ export class SessionStateService {
|
|
|
247
264
|
/**
|
|
248
265
|
* 写入当前 session 配置。
|
|
249
266
|
*/
|
|
250
|
-
async set(
|
|
267
|
+
async set(
|
|
268
|
+
input: AgentSessionSetInput,
|
|
269
|
+
options?: SessionSetOptions,
|
|
270
|
+
): Promise<SessionConfiguredCommandResult> {
|
|
251
271
|
const should_emit_action = options?.emit_action !== false;
|
|
252
272
|
const previous_model_label = this.state.sessionConfig.modelLabel;
|
|
253
273
|
const previous_model_id = String(
|
|
@@ -273,47 +293,59 @@ export class SessionStateService {
|
|
|
273
293
|
const next_model_name = next_model_label || next_model_id;
|
|
274
294
|
const action_id = `model-switching:${this.session_id}:${Date.now()}:${generateId()}`;
|
|
275
295
|
|
|
276
|
-
if (should_emit_model_switch_action) {
|
|
277
|
-
await this.emit_action_event({
|
|
278
|
-
id: action_id,
|
|
279
|
-
title: `Switching session model from ${previous_model_name} to ${next_model_name}`,
|
|
280
|
-
state: "running",
|
|
281
|
-
});
|
|
282
|
-
}
|
|
283
|
-
|
|
284
296
|
try {
|
|
297
|
+
const next_config: AgentSessionConfigSnapshot = {
|
|
298
|
+
...this.state.sessionConfig,
|
|
299
|
+
};
|
|
285
300
|
if (next_model) {
|
|
286
|
-
|
|
287
|
-
|
|
301
|
+
next_config.model = normalizeAgentModel(next_model);
|
|
302
|
+
next_config.modelLabel = next_model_label;
|
|
303
|
+
next_config.model_context_window =
|
|
304
|
+
read_agent_model_context_window(next_model);
|
|
288
305
|
}
|
|
289
306
|
if (next_model_id) {
|
|
290
|
-
|
|
307
|
+
next_config.modelId = next_model_id;
|
|
291
308
|
}
|
|
292
309
|
await patchSessionModelLabel({
|
|
293
310
|
projectRoot: this.project_root,
|
|
294
311
|
agentId: this.agent_id,
|
|
295
312
|
sessionId: this.session_id,
|
|
296
|
-
model:
|
|
297
|
-
modelId:
|
|
313
|
+
model: next_config.model,
|
|
314
|
+
modelId: next_config.modelId,
|
|
298
315
|
});
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
316
|
+
this.state.sessionConfig = next_config;
|
|
317
|
+
|
|
318
|
+
const apply_effective_config = async (turn_id?: string): Promise<void> => {
|
|
319
|
+
this.state.effective_session_config = {
|
|
320
|
+
...next_config,
|
|
321
|
+
};
|
|
322
|
+
if (!should_emit_model_switch_action) return;
|
|
323
|
+
await this.emit_config_action_event({
|
|
302
324
|
id: action_id,
|
|
303
|
-
title: `Session model
|
|
304
|
-
|
|
305
|
-
|
|
325
|
+
title: `Session model switched from ${previous_model_name} to ${next_model_name}`,
|
|
326
|
+
state: "completed",
|
|
327
|
+
...(turn_id ? { turnId: turn_id } : {}),
|
|
306
328
|
});
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
if (options?.emit_action === false) {
|
|
332
|
+
await apply_effective_config();
|
|
333
|
+
return {};
|
|
307
334
|
}
|
|
308
|
-
throw error;
|
|
309
|
-
}
|
|
310
335
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
336
|
+
const command_id = generateId();
|
|
337
|
+
return {
|
|
338
|
+
command: {
|
|
339
|
+
type: "command",
|
|
340
|
+
command_id,
|
|
341
|
+
scope: "session",
|
|
342
|
+
execute: async ({ turn_id }) => {
|
|
343
|
+
await apply_effective_config(turn_id);
|
|
344
|
+
},
|
|
345
|
+
},
|
|
346
|
+
};
|
|
347
|
+
} catch (error) {
|
|
348
|
+
throw error;
|
|
317
349
|
}
|
|
318
350
|
}
|
|
319
351
|
|
|
@@ -324,6 +356,8 @@ export class SessionStateService {
|
|
|
324
356
|
const model = await this.resolve_model_by_id(model_id);
|
|
325
357
|
this.state.sessionConfig.model = normalizeAgentModel(model);
|
|
326
358
|
this.state.sessionConfig.modelLabel = inferAgentModelLabel(model);
|
|
359
|
+
this.state.sessionConfig.model_context_window =
|
|
360
|
+
read_agent_model_context_window(model);
|
|
327
361
|
}
|
|
328
362
|
|
|
329
363
|
/** 使用宿主 resolver 解析稳定模型 ID。 */
|
|
@@ -384,28 +418,17 @@ export class SessionStateService {
|
|
|
384
418
|
* 仅刷新当前 session metadata。
|
|
385
419
|
*/
|
|
386
420
|
async touch_metadata(): Promise<void> {
|
|
387
|
-
const
|
|
388
|
-
limit: 500,
|
|
389
|
-
include_internal: true,
|
|
390
|
-
});
|
|
391
|
-
const messages_path = getSdkAgentSessionMessagesPath(
|
|
392
|
-
this.project_root,
|
|
393
|
-
this.agent_id,
|
|
394
|
-
this.session_id,
|
|
395
|
-
);
|
|
396
|
-
const history_bytes = await fs.stat(messages_path)
|
|
397
|
-
.then((file_stat) => file_stat.size)
|
|
398
|
-
.catch(() => 0);
|
|
421
|
+
const stats = await this.recorder.storage_stats();
|
|
399
422
|
const preview_text = resolve_message_preview(
|
|
400
|
-
|
|
423
|
+
stats.latest_message || undefined,
|
|
401
424
|
).slice(0, 180);
|
|
402
425
|
await touchSessionMetadata({
|
|
403
426
|
projectRoot: this.project_root,
|
|
404
427
|
agentId: this.agent_id,
|
|
405
428
|
sessionId: this.session_id,
|
|
406
429
|
sessionConfig: this.state.sessionConfig,
|
|
407
|
-
message_count:
|
|
408
|
-
history_bytes,
|
|
430
|
+
message_count: stats.message_count,
|
|
431
|
+
history_bytes: stats.history_bytes,
|
|
409
432
|
...(preview_text ? { preview_text } : {}),
|
|
410
433
|
});
|
|
411
434
|
}
|
|
@@ -512,6 +535,27 @@ export class SessionStateService {
|
|
|
512
535
|
});
|
|
513
536
|
}
|
|
514
537
|
|
|
538
|
+
/**
|
|
539
|
+
* 尽力写入配置生效 action,不让 timeline 故障改变 effective state。
|
|
540
|
+
*/
|
|
541
|
+
async emit_config_action_event(input: EmitActionInput): Promise<boolean> {
|
|
542
|
+
try {
|
|
543
|
+
await this.emit_action_event(input);
|
|
544
|
+
return true;
|
|
545
|
+
} catch (error) {
|
|
546
|
+
try {
|
|
547
|
+
await this.logger.log("warn", "[agent] config action persistence failed", {
|
|
548
|
+
sessionId: this.session_id,
|
|
549
|
+
actionId: String(input.id || ""),
|
|
550
|
+
error: error instanceof Error ? error.message : String(error),
|
|
551
|
+
});
|
|
552
|
+
} catch {
|
|
553
|
+
// 配置已经提交,日志失败也不能反向改变 effective state。
|
|
554
|
+
}
|
|
555
|
+
return false;
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
|
|
515
559
|
/**
|
|
516
560
|
* 构造并持久化一条 prompt user 消息。
|
|
517
561
|
*/
|
|
@@ -14,6 +14,7 @@ import { isAgentSessionPromptInputEmpty } from "@/types/sdk/AgentSessionPrompt.j
|
|
|
14
14
|
import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
|
|
15
15
|
import type { AgentSessionStopResult } from "@/types/sdk/AgentSessionStop.js";
|
|
16
16
|
import type { AgentSessionTurnHandle } from "@/types/sdk/AgentSessionTurn.js";
|
|
17
|
+
import type { SessionRunResult } from "@/executor/types/SessionRun.js";
|
|
17
18
|
import type {
|
|
18
19
|
SessionRecordV1,
|
|
19
20
|
SessionMessageRecordV1,
|
|
@@ -22,12 +23,14 @@ import type {
|
|
|
22
23
|
import { SessionEventHub } from "@/session/runtime/SessionEventHub.js";
|
|
23
24
|
import { SessionPromptRuntime } from "@/session/runtime/SessionPromptRuntime.js";
|
|
24
25
|
import type { SessionRunContext } from "@/types/executor/SessionRunContext.js";
|
|
26
|
+
import type { SessionQueueCommand } from "@/types/session/SessionQueueCommand.js";
|
|
25
27
|
import { SessionStateService } from "@/session/services/SessionStateService.js";
|
|
26
28
|
import {
|
|
27
29
|
SessionAssistantMessageWriter,
|
|
28
30
|
SessionRecorder,
|
|
29
31
|
} from "@/session/recorder/SessionRecorder.js";
|
|
30
32
|
import { from_ui_assistant_parts } from "@/session/recorder/SessionMessageCodec.js";
|
|
33
|
+
import { generateId } from "@/utils/Id.js";
|
|
31
34
|
|
|
32
35
|
type SessionTurnServiceOptions = {
|
|
33
36
|
/**
|
|
@@ -70,6 +73,8 @@ export class SessionTurnService {
|
|
|
70
73
|
private readonly event_hub: SessionEventHub;
|
|
71
74
|
private readonly recorder: SessionRecorder;
|
|
72
75
|
private readonly prompt_runtime: SessionPromptRuntime;
|
|
76
|
+
private active_run_context: SessionRunContext | null = null;
|
|
77
|
+
private request_active_history_reload: (() => void) | null = null;
|
|
73
78
|
|
|
74
79
|
constructor(options: SessionTurnServiceOptions) {
|
|
75
80
|
this.session_id = options.session_id;
|
|
@@ -136,6 +141,52 @@ export class SessionTurnService {
|
|
|
136
141
|
return await this.prompt_runtime.prompt(input);
|
|
137
142
|
}
|
|
138
143
|
|
|
144
|
+
/**
|
|
145
|
+
* 把 configured state command 加入当前 Session 的统一输入队列。
|
|
146
|
+
*/
|
|
147
|
+
enqueue_command(command: SessionQueueCommand): void {
|
|
148
|
+
this.prompt_runtime.enqueue_command(command);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* 把一次显式历史压缩加入当前 Session 的统一输入队列。
|
|
153
|
+
*/
|
|
154
|
+
async compact(): Promise<void> {
|
|
155
|
+
await this.state_service.ensure_runnable();
|
|
156
|
+
const command_id = generateId();
|
|
157
|
+
this.enqueue_command({
|
|
158
|
+
type: "command",
|
|
159
|
+
command_id,
|
|
160
|
+
scope: "session",
|
|
161
|
+
execute: async ({ turn_id }) => {
|
|
162
|
+
const active_run_context = this.active_run_context;
|
|
163
|
+
const run_context = active_run_context ||
|
|
164
|
+
this.create_compaction_run_context(turn_id);
|
|
165
|
+
const result = await this.executor.compact_history(run_context);
|
|
166
|
+
if (
|
|
167
|
+
result.compacted &&
|
|
168
|
+
active_run_context !== null &&
|
|
169
|
+
active_run_context === this.active_run_context
|
|
170
|
+
) {
|
|
171
|
+
this.request_active_history_reload?.();
|
|
172
|
+
}
|
|
173
|
+
await this.state_service.touch_metadata();
|
|
174
|
+
if (result.compacted) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (!result.compacted && result.reason === "nothing_to_compact") {
|
|
178
|
+
await this.state_service.emit_action_event({
|
|
179
|
+
id: `compacting:${this.session_id}:${command_id}`,
|
|
180
|
+
title: "Session records already compact",
|
|
181
|
+
description: "The Session has no active records to compact.",
|
|
182
|
+
state: "completed",
|
|
183
|
+
turnId: turn_id,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
},
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
|
|
139
190
|
/**
|
|
140
191
|
* 停止当前 turn,并取消尚未被吸收的排队 prompt。
|
|
141
192
|
*/
|
|
@@ -161,6 +212,7 @@ export class SessionTurnService {
|
|
|
161
212
|
current: SessionAssistantMessageWriter | null;
|
|
162
213
|
} = { current: null };
|
|
163
214
|
let assistant_segment_index = 0;
|
|
215
|
+
let history_reload_requested = false;
|
|
164
216
|
const ensure_assistant_writer = async (): Promise<SessionAssistantMessageWriter> => {
|
|
165
217
|
if (assistant_writer_ref.current) return assistant_writer_ref.current;
|
|
166
218
|
assistant_segment_index += 1;
|
|
@@ -175,6 +227,13 @@ export class SessionTurnService {
|
|
|
175
227
|
sessionId: this.session_id,
|
|
176
228
|
projectRoot: this.project_root,
|
|
177
229
|
onStepCallback: async () => {
|
|
230
|
+
if (
|
|
231
|
+
this.prompt_runtime.has_pending_command() &&
|
|
232
|
+
assistant_writer_ref.current
|
|
233
|
+
) {
|
|
234
|
+
await assistant_writer_ref.current.complete();
|
|
235
|
+
assistant_writer_ref.current = null;
|
|
236
|
+
}
|
|
178
237
|
const merged = await input.onStepMerge();
|
|
179
238
|
if (merged.length > 0 && assistant_writer_ref.current) {
|
|
180
239
|
await assistant_writer_ref.current.complete();
|
|
@@ -182,6 +241,12 @@ export class SessionTurnService {
|
|
|
182
241
|
}
|
|
183
242
|
return merged;
|
|
184
243
|
},
|
|
244
|
+
hasPendingStepInput: () => this.prompt_runtime.has_pending_prompt(),
|
|
245
|
+
consume_history_reload: () => {
|
|
246
|
+
const requested = history_reload_requested;
|
|
247
|
+
history_reload_requested = false;
|
|
248
|
+
return requested;
|
|
249
|
+
},
|
|
185
250
|
onUiMessageChunkCallback: async (chunk) => {
|
|
186
251
|
if (!is_assistant_content_chunk(chunk.type)) return;
|
|
187
252
|
const writer = await ensure_assistant_writer();
|
|
@@ -197,12 +262,32 @@ export class SessionTurnService {
|
|
|
197
262
|
};
|
|
198
263
|
const query = input.promptInput.query;
|
|
199
264
|
const executor_query = typeof query === "string" ? query : extractTextFromParts(query);
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
}
|
|
265
|
+
this.active_run_context = run_context;
|
|
266
|
+
this.request_active_history_reload = () => {
|
|
267
|
+
history_reload_requested = true;
|
|
268
|
+
};
|
|
269
|
+
let result: SessionRunResult;
|
|
270
|
+
try {
|
|
271
|
+
result = await this.executor.run({
|
|
272
|
+
query: executor_query,
|
|
273
|
+
runContext: run_context,
|
|
274
|
+
});
|
|
275
|
+
} finally {
|
|
276
|
+
if (this.active_run_context === run_context) {
|
|
277
|
+
this.active_run_context = null;
|
|
278
|
+
this.request_active_history_reload = null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
const final_assistant_parts = result.assistantMessage
|
|
282
|
+
? from_ui_assistant_parts(result.assistantMessage.parts)
|
|
283
|
+
: [];
|
|
204
284
|
const final_assistant_writer = assistant_writer_ref.current;
|
|
205
285
|
if (final_assistant_writer) {
|
|
286
|
+
for (const part of final_assistant_parts) {
|
|
287
|
+
if (part.type === "file") {
|
|
288
|
+
await final_assistant_writer.append_file_part(part);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
206
291
|
if (input.abortSignal?.aborted) {
|
|
207
292
|
await final_assistant_writer.stop();
|
|
208
293
|
} else if (result.success) {
|
|
@@ -211,14 +296,13 @@ export class SessionTurnService {
|
|
|
211
296
|
await final_assistant_writer.fail(result.error);
|
|
212
297
|
}
|
|
213
298
|
} else if (result.assistantMessage) {
|
|
214
|
-
|
|
215
|
-
if (parts.length > 0) {
|
|
299
|
+
if (final_assistant_parts.length > 0) {
|
|
216
300
|
assistant_segment_index += 1;
|
|
217
301
|
const fallback_writer = await this.recorder.open_assistant_message({
|
|
218
302
|
turn_id: input.turnId,
|
|
219
303
|
segment_index: assistant_segment_index,
|
|
220
304
|
});
|
|
221
|
-
for (const part of
|
|
305
|
+
for (const part of final_assistant_parts) await fallback_writer.upsert_part(part);
|
|
222
306
|
if (result.success) await fallback_writer.complete();
|
|
223
307
|
else await fallback_writer.fail(result.error);
|
|
224
308
|
}
|
|
@@ -236,6 +320,10 @@ export class SessionTurnService {
|
|
|
236
320
|
await this.state_service.persist_deferred_user_messages(
|
|
237
321
|
result.deferredPersistedUserMessages,
|
|
238
322
|
);
|
|
323
|
+
if (result.compact_required) {
|
|
324
|
+
await this.executor.compact_history(run_context);
|
|
325
|
+
await this.state_service.touch_metadata();
|
|
326
|
+
}
|
|
239
327
|
return {
|
|
240
328
|
text: result.assistantMessage
|
|
241
329
|
? extractTextFromUiMessage(result.assistantMessage)
|
|
@@ -247,6 +335,23 @@ export class SessionTurnService {
|
|
|
247
335
|
...(result.error ? { error: result.error } : {}),
|
|
248
336
|
};
|
|
249
337
|
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* 为 turn 开始前执行的 compact command 构造最小运行上下文。
|
|
341
|
+
*/
|
|
342
|
+
private create_compaction_run_context(turn_id: string): SessionRunContext {
|
|
343
|
+
return {
|
|
344
|
+
turnId: turn_id,
|
|
345
|
+
sessionId: this.session_id,
|
|
346
|
+
projectRoot: this.project_root,
|
|
347
|
+
onActionCallback: async (event) => {
|
|
348
|
+
await this.state_service.persist_action_event(event);
|
|
349
|
+
},
|
|
350
|
+
injectedUserMessages: [],
|
|
351
|
+
deferredPersistedUserMessages: [],
|
|
352
|
+
pendingAssistantFileParts: [],
|
|
353
|
+
};
|
|
354
|
+
}
|
|
250
355
|
}
|
|
251
356
|
|
|
252
357
|
function is_assistant_content_chunk(type: string): boolean {
|