@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.
- 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/transports/HttpRemoteAgentTransport.d.ts.map +1 -1
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js +0 -7
- package/bin/agent/remote/transports/HttpRemoteAgentTransport.js.map +1 -1
- package/bin/executor/Executor.d.ts +25 -0
- package/bin/executor/Executor.d.ts.map +1 -1
- package/bin/executor/Executor.js +36 -0
- package/bin/executor/Executor.js.map +1 -1
- package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts +4 -0
- package/bin/executor/composer/compaction/SessionCompactionComposer.d.ts.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts +0 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.d.ts.map +1 -1
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js +15 -11
- package/bin/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.js.map +1 -1
- package/bin/executor/core-engine/CoreEngineRunner.d.ts +11 -0
- package/bin/executor/core-engine/CoreEngineRunner.d.ts.map +1 -1
- package/bin/executor/core-engine/CoreEngineRunner.js +12 -8
- package/bin/executor/core-engine/CoreEngineRunner.js.map +1 -1
- package/bin/executor/store/history/SessionHistoryStore.d.ts +3 -13
- package/bin/executor/store/history/SessionHistoryStore.d.ts.map +1 -1
- package/bin/executor/store/history/jsonl/JsonlSessionHistoryStore.js +1 -1
- 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/index.d.ts +2 -1
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js +1 -1
- 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/PluginRegistry.d.ts +22 -1
- package/bin/plugin/core/PluginRegistry.d.ts.map +1 -1
- package/bin/plugin/core/PluginRegistry.js +46 -4
- package/bin/plugin/core/PluginRegistry.js.map +1 -1
- package/bin/session/Session.d.ts +11 -1
- package/bin/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +76 -11
- package/bin/session/Session.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 +50 -62
- 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 +17 -1
- package/bin/session/runtime/SessionPromptRuntime.d.ts.map +1 -1
- package/bin/session/runtime/SessionPromptRuntime.js +67 -8
- package/bin/session/runtime/SessionPromptRuntime.js.map +1 -1
- package/bin/session/services/SessionStateService.d.ts +15 -1
- package/bin/session/services/SessionStateService.d.ts.map +1 -1
- package/bin/session/services/SessionStateService.js +60 -39
- package/bin/session/services/SessionStateService.js.map +1 -1
- package/bin/session/services/SessionTurnService.d.ts +5 -0
- package/bin/session/services/SessionTurnService.d.ts.map +1 -1
- package/bin/session/services/SessionTurnService.js +17 -3
- 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/SessionTypes.d.ts +2 -24
- package/bin/types/agent/SessionTypes.d.ts.map +1 -1
- package/bin/types/executor/SessionRunContext.d.ts +21 -0
- package/bin/types/executor/SessionRunContext.d.ts.map +1 -1
- package/bin/types/plugin/PluginRuntime.d.ts +25 -0
- package/bin/types/plugin/PluginRuntime.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/SessionConfigMutation.d.ts +68 -0
- package/bin/types/session/SessionConfigMutation.d.ts.map +1 -0
- package/bin/types/session/SessionConfigMutation.js +10 -0
- package/bin/types/session/SessionConfigMutation.js.map +1 -0
- 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/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/package.json +3 -3
- package/scripts/city-model-tool-loop.test.mjs +32 -0
- package/scripts/session-action-message.test.mjs +59 -13
- package/scripts/session-compaction-context-window.test.mjs +95 -0
- package/scripts/session-config-turn-boundary.test.mjs +224 -0
- package/scripts/session-list-title.test.mjs +21 -3
- package/scripts/session-prompt-runtime.test.mjs +85 -0
- package/scripts/session-recorder.test.mjs +340 -11
- package/src/agent/local/Agent.ts +34 -3
- package/src/agent/local/services/AgentSessions.ts +61 -0
- package/src/agent/remote/transports/HttpRemoteAgentTransport.ts +0 -5
- package/src/executor/Executor.ts +62 -0
- package/src/executor/composer/compaction/SessionCompactionComposer.ts +5 -0
- package/src/executor/composer/compaction/jsonl/JsonlSessionCompactionComposer.ts +16 -14
- package/src/executor/core-engine/CoreEngineRunner.ts +24 -8
- package/src/executor/store/history/SessionHistoryStore.ts +3 -14
- package/src/executor/store/history/jsonl/JsonlSessionHistoryStore.ts +1 -1
- package/src/executor/tools/plugin/PluginToolBridge.ts +4 -2
- package/src/index.ts +8 -0
- package/src/model/CityModelAdapter.ts +15 -0
- package/src/plugin/core/PluginRegistry.ts +72 -4
- package/src/session/Session.ts +89 -11
- 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 +55 -80
- package/src/session/recorder/SessionRecorderHistoryStore.ts +29 -49
- package/src/session/runtime/SessionPromptRuntime.ts +100 -9
- package/src/session/services/SessionStateService.ts +79 -44
- package/src/session/services/SessionTurnService.ts +19 -3
- package/src/session/services/SessionViewService.ts +4 -50
- package/src/session/storage/Paths.ts +5 -38
- package/src/types/agent/SessionTypes.ts +2 -24
- package/src/types/executor/SessionRunContext.ts +25 -0
- package/src/types/plugin/PluginRuntime.ts +27 -0
- package/src/types/runtime/agent/AgentContext.ts +32 -7
- package/src/types/session/SessionConfigMutation.ts +78 -0
- 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/SessionSegment.ts +62 -0
- package/tsconfig.tsbuildinfo +1 -1
package/src/session/Session.ts
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* 关键点(中文)
|
|
5
5
|
* - 面向 `new Agent(...)` 的本地会话使用场景。
|
|
6
6
|
* - 对外保留稳定 Session facade,把状态、turn、view 逻辑下沉到独立 service。
|
|
7
|
-
* -
|
|
7
|
+
* - 内部使用 `SessionRecorder` 统一管理 Active、Segment 与流式 Assistant 草稿。
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { Executor } from "@executor/Executor.js";
|
|
@@ -55,6 +55,7 @@ import { SessionStateService } from "@/session/services/SessionStateService.js";
|
|
|
55
55
|
import { SessionTurnService } from "@/session/services/SessionTurnService.js";
|
|
56
56
|
import { SessionViewService } from "@/session/services/SessionViewService.js";
|
|
57
57
|
import type { SessionLocalState } from "@/types/session/SessionLocalState.js";
|
|
58
|
+
import type { AgentSessionConfigMutation } from "@/types/session/SessionConfigMutation.js";
|
|
58
59
|
import type {
|
|
59
60
|
SessionComposerFactoryContext,
|
|
60
61
|
SessionComposerInput,
|
|
@@ -65,6 +66,7 @@ import type { SessionContextComposer } from "@/executor/composer/context/Session
|
|
|
65
66
|
import type { SessionHistoryComposer } from "@/executor/composer/history/SessionHistoryComposer.js";
|
|
66
67
|
import type { SessionSystemComposer } from "@/executor/composer/system/SessionSystemComposer.js";
|
|
67
68
|
import type { SessionOptions } from "@/types/session/SessionOptions.js";
|
|
69
|
+
import type { AgentPluginExecutionRuntime } from "@/types/plugin/PluginRuntime.js";
|
|
68
70
|
|
|
69
71
|
/**
|
|
70
72
|
* SDK 本地 Session。
|
|
@@ -92,6 +94,11 @@ export class Session implements AgentSession {
|
|
|
92
94
|
private readonly set_approval_mode_hook: SessionOptions["set_approval_mode"];
|
|
93
95
|
private readonly resolve_approval_hook: SessionOptions["resolve_approval"];
|
|
94
96
|
private readonly localState: SessionLocalState;
|
|
97
|
+
private readonly getAgentEnv: SessionOptions["getAgentEnv"];
|
|
98
|
+
private readonly get_agent_plugins: SessionOptions["get_agent_plugins"];
|
|
99
|
+
private effective_instruction_system_blocks: AgentSessionSystemBlock[];
|
|
100
|
+
private effective_agent_env: Record<string, string>;
|
|
101
|
+
private effective_agent_plugins: AgentPluginExecutionRuntime;
|
|
95
102
|
private readonly stateService: SessionStateService;
|
|
96
103
|
private readonly turnService: SessionTurnService;
|
|
97
104
|
private readonly viewService: SessionViewService<this>;
|
|
@@ -104,6 +111,13 @@ export class Session implements AgentSession {
|
|
|
104
111
|
this.tools = options.tools;
|
|
105
112
|
this.logger = options.logger;
|
|
106
113
|
this.getInstructionSystemBlocks = options.getInstructionSystemBlocks;
|
|
114
|
+
this.getAgentEnv = options.getAgentEnv;
|
|
115
|
+
this.get_agent_plugins = options.get_agent_plugins;
|
|
116
|
+
this.effective_instruction_system_blocks = options
|
|
117
|
+
.getInstructionSystemBlocks()
|
|
118
|
+
.map((block) => ({ ...block }));
|
|
119
|
+
this.effective_agent_env = { ...options.getAgentEnv() };
|
|
120
|
+
this.effective_agent_plugins = options.get_agent_plugins();
|
|
107
121
|
this.getManagedPluginSystemBlocks = options.getManagedPluginSystemBlocks;
|
|
108
122
|
this.getPluginSystemBlocks = options.getPluginSystemBlocks;
|
|
109
123
|
this.ensureConfiguredHook = options.ensureConfigured;
|
|
@@ -187,9 +201,11 @@ export class Session implements AgentSession {
|
|
|
187
201
|
state_service: this.stateService,
|
|
188
202
|
logger: this.logger,
|
|
189
203
|
is_executing: () => this.isExecuting(),
|
|
190
|
-
get_instruction_system_blocks:
|
|
204
|
+
get_instruction_system_blocks: () =>
|
|
205
|
+
this.effective_instruction_system_blocks.map((block) => ({ ...block })),
|
|
191
206
|
get_managed_plugin_system_blocks: this.getManagedPluginSystemBlocks,
|
|
192
|
-
get_plugin_system_blocks:
|
|
207
|
+
get_plugin_system_blocks: async () =>
|
|
208
|
+
await this.effective_agent_plugins.systemBlocks(),
|
|
193
209
|
...(this.composers?.systemComposer
|
|
194
210
|
? { custom_system_composer: system_composer }
|
|
195
211
|
: {}),
|
|
@@ -226,7 +242,10 @@ export class Session implements AgentSession {
|
|
|
226
242
|
* 写入当前 session 默认配置。
|
|
227
243
|
*/
|
|
228
244
|
async set(input: AgentSessionSetInput): Promise<void> {
|
|
229
|
-
await this.stateService.set(input);
|
|
245
|
+
const configured = await this.stateService.set(input);
|
|
246
|
+
if (configured.mutation) {
|
|
247
|
+
this.turnService.enqueue_config(configured.mutation);
|
|
248
|
+
}
|
|
230
249
|
}
|
|
231
250
|
|
|
232
251
|
/**
|
|
@@ -243,6 +262,47 @@ export class Session implements AgentSession {
|
|
|
243
262
|
return await this.turnService.stop();
|
|
244
263
|
}
|
|
245
264
|
|
|
265
|
+
/**
|
|
266
|
+
* 把 Agent configured state 修改加入当前 Session 的统一输入队列。
|
|
267
|
+
*/
|
|
268
|
+
enqueue_agent_config(mutation: AgentSessionConfigMutation): void {
|
|
269
|
+
this.turnService.enqueue_config({
|
|
270
|
+
mutation_id: mutation.mutation_id,
|
|
271
|
+
scope: "agent",
|
|
272
|
+
apply: async ({ turn_id }) => {
|
|
273
|
+
if (mutation.type === "instruction") {
|
|
274
|
+
this.effective_instruction_system_blocks = mutation
|
|
275
|
+
.instruction_blocks
|
|
276
|
+
.map((block) => ({ ...block }));
|
|
277
|
+
await this.stateService.emit_action_event({
|
|
278
|
+
id: `agent-instruction:${this.id}:${mutation.mutation_id}`,
|
|
279
|
+
title: "Agent instruction updated",
|
|
280
|
+
state: "completed",
|
|
281
|
+
turnId: turn_id,
|
|
282
|
+
});
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
if (mutation.type === "env") {
|
|
286
|
+
this.effective_agent_env = { ...mutation.env };
|
|
287
|
+
await this.stateService.emit_action_event({
|
|
288
|
+
id: `agent-env:${this.id}:${mutation.mutation_id}`,
|
|
289
|
+
title: "Agent environment updated",
|
|
290
|
+
state: "completed",
|
|
291
|
+
turnId: turn_id,
|
|
292
|
+
});
|
|
293
|
+
return;
|
|
294
|
+
}
|
|
295
|
+
this.effective_agent_plugins = mutation.plugins;
|
|
296
|
+
await this.stateService.emit_action_event({
|
|
297
|
+
id: `agent-plugins:${this.id}:${mutation.mutation_id}`,
|
|
298
|
+
title: mutation.title,
|
|
299
|
+
state: "completed",
|
|
300
|
+
turnId: turn_id,
|
|
301
|
+
});
|
|
302
|
+
},
|
|
303
|
+
});
|
|
304
|
+
}
|
|
305
|
+
|
|
246
306
|
/**
|
|
247
307
|
* 订阅当前 Session 的未来事件。
|
|
248
308
|
*/
|
|
@@ -388,8 +448,11 @@ export class Session implements AgentSession {
|
|
|
388
448
|
tools: this.tools,
|
|
389
449
|
logger: this.logger,
|
|
390
450
|
getInstructionSystemBlocks: this.getInstructionSystemBlocks,
|
|
451
|
+
getAgentEnv: this.getAgentEnv,
|
|
452
|
+
get_agent_plugins: this.get_agent_plugins,
|
|
391
453
|
getManagedPluginSystemBlocks: this.getManagedPluginSystemBlocks,
|
|
392
|
-
getPluginSystemBlocks:
|
|
454
|
+
getPluginSystemBlocks: async () =>
|
|
455
|
+
await this.effective_agent_plugins.systemBlocks(),
|
|
393
456
|
ensureConfigured: this.ensureConfiguredHook,
|
|
394
457
|
composers: this.composers,
|
|
395
458
|
list_approvals: this.list_approvals,
|
|
@@ -422,7 +485,7 @@ export class Session implements AgentSession {
|
|
|
422
485
|
const messages_dir_path = `${session_dir_path}/messages`;
|
|
423
486
|
return new JsonlSessionMessageStore({
|
|
424
487
|
session_id: this.id,
|
|
425
|
-
file_path: `${messages_dir_path}/
|
|
488
|
+
file_path: `${messages_dir_path}/active.jsonl`,
|
|
426
489
|
assistant_message_file_path: getSdkAgentSessionAssistantMessagePath(
|
|
427
490
|
this.projectRoot,
|
|
428
491
|
this.agentId,
|
|
@@ -441,6 +504,7 @@ export class Session implements AgentSession {
|
|
|
441
504
|
private create_local_state(): SessionLocalState {
|
|
442
505
|
return {
|
|
443
506
|
sessionConfig: {},
|
|
507
|
+
effective_session_config: {},
|
|
444
508
|
createdAt: Date.now(),
|
|
445
509
|
timezone: resolveSystemTimezone(),
|
|
446
510
|
initializePromise: null,
|
|
@@ -455,9 +519,11 @@ export class Session implements AgentSession {
|
|
|
455
519
|
sessionId: this.id,
|
|
456
520
|
historyStore: this.historyStore,
|
|
457
521
|
getTools: () => this.tools,
|
|
458
|
-
getInstructionSystemBlocks:
|
|
522
|
+
getInstructionSystemBlocks: () =>
|
|
523
|
+
this.effective_instruction_system_blocks.map((block) => ({ ...block })),
|
|
459
524
|
getManagedPluginSystemBlocks: this.getManagedPluginSystemBlocks,
|
|
460
|
-
getPluginSystemBlocks:
|
|
525
|
+
getPluginSystemBlocks: async () =>
|
|
526
|
+
await this.effective_agent_plugins.systemBlocks(),
|
|
461
527
|
getSessionCreatedAt: () => this.localState.createdAt,
|
|
462
528
|
getSessionTimezone: () => this.localState.timezone,
|
|
463
529
|
};
|
|
@@ -482,9 +548,11 @@ export class Session implements AgentSession {
|
|
|
482
548
|
projectRoot: this.projectRoot,
|
|
483
549
|
getSessionCreatedAt: () => this.localState.createdAt,
|
|
484
550
|
getSessionTimezone: () => this.localState.timezone,
|
|
485
|
-
getInstructionSystemBlocks:
|
|
551
|
+
getInstructionSystemBlocks: () =>
|
|
552
|
+
this.effective_instruction_system_blocks.map((block) => ({ ...block })),
|
|
486
553
|
getManagedPluginSystemBlocks: this.getManagedPluginSystemBlocks,
|
|
487
|
-
getPluginSystemBlocks:
|
|
554
|
+
getPluginSystemBlocks: async () =>
|
|
555
|
+
await this.effective_agent_plugins.systemBlocks(),
|
|
488
556
|
}),
|
|
489
557
|
);
|
|
490
558
|
}
|
|
@@ -516,10 +584,20 @@ export class Session implements AgentSession {
|
|
|
516
584
|
sessionId: this.id,
|
|
517
585
|
historyStore: this.historyStore,
|
|
518
586
|
historyComposer: this.historyComposer,
|
|
519
|
-
getModel: () =>
|
|
587
|
+
getModel: () =>
|
|
588
|
+
this.localState.effective_session_config.model ||
|
|
589
|
+
this.localState.sessionConfig.model,
|
|
590
|
+
get_model_context_window: () =>
|
|
591
|
+
this.localState.effective_session_config.model
|
|
592
|
+
? this.localState.effective_session_config.model_context_window
|
|
593
|
+
: this.localState.sessionConfig.model_context_window,
|
|
520
594
|
logger: this.logger,
|
|
521
595
|
systemComposer: system_composer,
|
|
522
596
|
getTools: () => this.tools,
|
|
597
|
+
getEnv: () => ({ ...this.effective_agent_env }),
|
|
598
|
+
get_systems: () =>
|
|
599
|
+
this.effective_instruction_system_blocks.map((block) => block.content),
|
|
600
|
+
get_plugins: () => this.effective_agent_plugins,
|
|
523
601
|
...(context_composer ? { contextComposer: context_composer } : {}),
|
|
524
602
|
...(compaction_composer
|
|
525
603
|
? { compactionComposer: compaction_composer }
|
|
@@ -94,13 +94,6 @@ type SessionBrowseBaseInput = {
|
|
|
94
94
|
executing?: boolean;
|
|
95
95
|
};
|
|
96
96
|
|
|
97
|
-
type SessionArchiveFileV1 = {
|
|
98
|
-
v?: unknown;
|
|
99
|
-
sessionId?: unknown;
|
|
100
|
-
archivedAt?: unknown;
|
|
101
|
-
messages?: unknown;
|
|
102
|
-
};
|
|
103
|
-
|
|
104
97
|
function decodeMaybe(input: string): string {
|
|
105
98
|
try {
|
|
106
99
|
return decodeURIComponent(input);
|
|
@@ -140,11 +133,6 @@ function encodeCursor(offset: number): string | undefined {
|
|
|
140
133
|
return String(Math.floor(offset));
|
|
141
134
|
}
|
|
142
135
|
|
|
143
|
-
function normalizeArchiveId(input: unknown): string | undefined {
|
|
144
|
-
const value = String(input || "").trim();
|
|
145
|
-
return value || undefined;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
136
|
function stringifyForDisplay(input: unknown, maxChars = 2400): string {
|
|
149
137
|
if (input === undefined) return "";
|
|
150
138
|
if (input === null) return "null";
|
|
@@ -344,7 +332,7 @@ export async function loadSessionMessagesFromPath(
|
|
|
344
332
|
}
|
|
345
333
|
}
|
|
346
334
|
|
|
347
|
-
const inflight_path =
|
|
335
|
+
const inflight_path = path.join(path.dirname(filePath), "assistant_message.json");
|
|
348
336
|
if (await fs.pathExists(inflight_path)) {
|
|
349
337
|
try {
|
|
350
338
|
const message = (await fs.readJson(inflight_path)) as SessionMessage;
|
|
@@ -395,52 +383,12 @@ function project_canonical_message_record(message: SessionMessage): SessionRecor
|
|
|
395
383
|
}];
|
|
396
384
|
}
|
|
397
385
|
|
|
398
|
-
/**
|
|
399
|
-
* 读取 compact archive 文件中的消息。
|
|
400
|
-
*
|
|
401
|
-
* 关键点(中文)
|
|
402
|
-
* - archive 是 compact 时被覆盖的“上一层历史”,不是当前运行态事实源。
|
|
403
|
-
* - 这里不读取 inflight,因为 archive 层永远是已完成的历史快照。
|
|
404
|
-
*/
|
|
405
|
-
export async function loadSessionArchiveMessagesFromPath(
|
|
406
|
-
filePath: string,
|
|
407
|
-
): Promise<SessionRecordV1[]> {
|
|
408
|
-
try {
|
|
409
|
-
const raw = (await fs.readJson(filePath)) as SessionArchiveFileV1 | null;
|
|
410
|
-
const items = Array.isArray(raw?.messages) ? raw.messages : [];
|
|
411
|
-
return items.filter((item): item is SessionRecordV1 => {
|
|
412
|
-
if (!item || typeof item !== "object") return false;
|
|
413
|
-
const candidate = item as { type?: unknown; role?: unknown; parts?: unknown };
|
|
414
|
-
return (
|
|
415
|
-
(
|
|
416
|
-
candidate.type === "action" ||
|
|
417
|
-
candidate.role === "user" ||
|
|
418
|
-
candidate.role === "assistant"
|
|
419
|
-
) &&
|
|
420
|
-
(candidate.type === "action" || Array.isArray(candidate.parts))
|
|
421
|
-
);
|
|
422
|
-
});
|
|
423
|
-
} catch {
|
|
424
|
-
return [];
|
|
425
|
-
}
|
|
426
|
-
}
|
|
427
|
-
|
|
428
386
|
function isCompactSummaryMessage(message: SessionRecordV1): boolean {
|
|
429
387
|
if (!is_session_message_record(message)) return false;
|
|
430
388
|
const metadata = (message.metadata || null) as SessionMetadataV1 | null;
|
|
431
389
|
return metadata?.source === "compact" || metadata?.kind === "summary";
|
|
432
390
|
}
|
|
433
391
|
|
|
434
|
-
function resolvePreviousArchiveId(messages: SessionRecordV1[]): string | undefined {
|
|
435
|
-
for (const message of messages) {
|
|
436
|
-
if (!isCompactSummaryMessage(message)) continue;
|
|
437
|
-
const metadata = (message.metadata || null) as SessionMetadataV1 | null;
|
|
438
|
-
const archiveId = normalizeArchiveId(metadata?.archiveId);
|
|
439
|
-
if (archiveId) return archiveId;
|
|
440
|
-
}
|
|
441
|
-
return undefined;
|
|
442
|
-
}
|
|
443
|
-
|
|
444
392
|
function filterUserVisibleHistoryMessages(
|
|
445
393
|
messages: SessionRecordV1[],
|
|
446
394
|
): SessionRecordV1[] {
|
|
@@ -460,9 +408,11 @@ export function buildSessionInfo(
|
|
|
460
408
|
180,
|
|
461
409
|
)
|
|
462
410
|
: input.metadata.previewText;
|
|
463
|
-
const message_count =
|
|
464
|
-
?
|
|
465
|
-
:
|
|
411
|
+
const message_count = typeof input.metadata.messageCount === "number"
|
|
412
|
+
? input.metadata.messageCount
|
|
413
|
+
: messages
|
|
414
|
+
? filterUserVisibleHistoryMessages(messages).length
|
|
415
|
+
: 0;
|
|
466
416
|
const title =
|
|
467
417
|
typeof input.metadata.title === "string" && input.metadata.title.trim()
|
|
468
418
|
? input.metadata.title.trim()
|
|
@@ -494,7 +444,7 @@ export function buildSessionInfo(
|
|
|
494
444
|
* 读取列表所需的轻量 session 摘要。
|
|
495
445
|
*
|
|
496
446
|
* 关键点(中文)
|
|
497
|
-
* - 新记录直接使用 metadata 摘要,不扫描
|
|
447
|
+
* - 新记录直接使用 metadata 摘要,不扫描 active.jsonl。
|
|
498
448
|
* - 旧记录或执行中记录回退读取一次完整历史,并把摘要补写回 metadata。
|
|
499
449
|
*/
|
|
500
450
|
async function resolve_session_summary_metadata(input: {
|
|
@@ -507,10 +457,9 @@ async function resolve_session_summary_metadata(input: {
|
|
|
507
457
|
/** 是否强制刷新摘要。 */
|
|
508
458
|
refresh: boolean;
|
|
509
459
|
}): Promise<SessionHistoryMetaV1> {
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
const inflight_path = `${input.messagesPath.slice(0, -"messages.jsonl".length)}assistant_message.json`;
|
|
460
|
+
const storage_stats = await resolve_session_disk_stats(input.messagesPath);
|
|
461
|
+
const history_bytes = storage_stats.history_bytes;
|
|
462
|
+
const inflight_path = path.join(path.dirname(input.messagesPath), "assistant_message.json");
|
|
514
463
|
const has_inflight = await fs.pathExists(inflight_path);
|
|
515
464
|
if (
|
|
516
465
|
!input.refresh &&
|
|
@@ -529,9 +478,11 @@ async function resolve_session_summary_metadata(input: {
|
|
|
529
478
|
void _previous_preview;
|
|
530
479
|
const next_metadata: SessionHistoryMetaV1 = {
|
|
531
480
|
...metadata_without_preview,
|
|
532
|
-
messageCount:
|
|
481
|
+
messageCount: storage_stats.message_count,
|
|
533
482
|
historyBytes: history_bytes,
|
|
534
|
-
...(preview_text
|
|
483
|
+
...(preview_text || input.metadata.previewText
|
|
484
|
+
? { previewText: preview_text || input.metadata.previewText }
|
|
485
|
+
: {}),
|
|
535
486
|
};
|
|
536
487
|
const temp_path = `${input.metaPath}.${process.pid}.${Date.now()}.tmp`;
|
|
537
488
|
await fs.ensureDir(path.dirname(input.metaPath));
|
|
@@ -540,6 +491,65 @@ async function resolve_session_summary_metadata(input: {
|
|
|
540
491
|
return next_metadata;
|
|
541
492
|
}
|
|
542
493
|
|
|
494
|
+
/**
|
|
495
|
+
* 只读取 Active 和 Segment 文件索引,计算列表所需的持久化统计。
|
|
496
|
+
*
|
|
497
|
+
* 关键点(中文)
|
|
498
|
+
* - Segment 的结束 sequence 直接来自文件名,不解析历史正文。
|
|
499
|
+
* - Active 需要逐行读取,以同时覆盖 revision 行与运行中 Assistant sequence。
|
|
500
|
+
*/
|
|
501
|
+
async function resolve_session_disk_stats(messages_path: string) {
|
|
502
|
+
const messages_dir_path = path.dirname(messages_path);
|
|
503
|
+
const segments_dir_path = path.join(messages_dir_path, "segments");
|
|
504
|
+
const segment_entries = await fs.readdir(segments_dir_path, { withFileTypes: true })
|
|
505
|
+
.catch(() => []);
|
|
506
|
+
const segment_files = segment_entries.flatMap((entry) => {
|
|
507
|
+
if (!entry.isFile()) return [];
|
|
508
|
+
const match = /^(\d+)-(\d+)\.jsonl$/.exec(entry.name);
|
|
509
|
+
if (!match) return [];
|
|
510
|
+
return [{
|
|
511
|
+
file_path: path.join(segments_dir_path, entry.name),
|
|
512
|
+
end_sequence: Number(match[2]),
|
|
513
|
+
}];
|
|
514
|
+
});
|
|
515
|
+
const active_raw = await fs.readFile(messages_path, "utf8").catch(() => "");
|
|
516
|
+
let latest_active_sequence = 0;
|
|
517
|
+
for (const line of active_raw.split("\n")) {
|
|
518
|
+
if (!line.trim()) continue;
|
|
519
|
+
try {
|
|
520
|
+
const message = JSON.parse(line) as Partial<SessionMessage>;
|
|
521
|
+
if (Number.isInteger(message.sequence)) {
|
|
522
|
+
latest_active_sequence = Math.max(latest_active_sequence, Number(message.sequence));
|
|
523
|
+
}
|
|
524
|
+
} catch {
|
|
525
|
+
// 单行损坏不阻断 Session 列表,其正文读取时会按既有规则忽略。
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
const inflight_path = path.join(messages_dir_path, "assistant_message.json");
|
|
529
|
+
try {
|
|
530
|
+
const inflight = await fs.readJson(inflight_path) as Partial<SessionMessage>;
|
|
531
|
+
if (Number.isInteger(inflight.sequence)) {
|
|
532
|
+
latest_active_sequence = Math.max(latest_active_sequence, Number(inflight.sequence));
|
|
533
|
+
}
|
|
534
|
+
} catch {
|
|
535
|
+
// 草稿不存在或损坏时只统计已完成历史。
|
|
536
|
+
}
|
|
537
|
+
const segment_sizes = await Promise.all(
|
|
538
|
+
segment_files.map(({ file_path }) => fs.stat(file_path)
|
|
539
|
+
.then((file_stat) => file_stat.size)
|
|
540
|
+
.catch(() => 0)),
|
|
541
|
+
);
|
|
542
|
+
const latest_segment_sequence = segment_files.reduce(
|
|
543
|
+
(latest, segment) => Math.max(latest, segment.end_sequence),
|
|
544
|
+
0,
|
|
545
|
+
);
|
|
546
|
+
return {
|
|
547
|
+
history_bytes: Buffer.byteLength(active_raw, "utf8") +
|
|
548
|
+
segment_sizes.reduce((total, size) => total + size, 0),
|
|
549
|
+
message_count: Math.max(latest_segment_sequence, latest_active_sequence),
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
|
|
543
553
|
/**
|
|
544
554
|
* 基于完整消息列表构建 session records 分页结果。
|
|
545
555
|
*/
|
|
@@ -552,8 +562,6 @@ export function buildSessionRecordsPage(params: {
|
|
|
552
562
|
const order = params.input?.order || "asc";
|
|
553
563
|
const limit = normalizeLimit(params.input?.limit, 50, 500);
|
|
554
564
|
const cursor = normalizeCursor(params.input?.cursor);
|
|
555
|
-
const archive_id = normalizeArchiveId(params.input?.archive_id);
|
|
556
|
-
const previous_archive_id = resolvePreviousArchiveId(params.messages);
|
|
557
565
|
const visibleMessages = filterUserVisibleHistoryMessages(params.messages);
|
|
558
566
|
|
|
559
567
|
if (view === "timeline") {
|
|
@@ -570,8 +578,6 @@ export function buildSessionRecordsPage(params: {
|
|
|
570
578
|
? { next_cursor: encodeCursor(nextOffset) }
|
|
571
579
|
: {}),
|
|
572
580
|
has_more: nextOffset < orderedEvents.length,
|
|
573
|
-
...(archive_id ? { archive_id } : {}),
|
|
574
|
-
...(previous_archive_id ? { previous_archive_id } : {}),
|
|
575
581
|
};
|
|
576
582
|
}
|
|
577
583
|
|
|
@@ -588,8 +594,6 @@ export function buildSessionRecordsPage(params: {
|
|
|
588
594
|
? { next_cursor: encodeCursor(nextOffset) }
|
|
589
595
|
: {}),
|
|
590
596
|
has_more: nextOffset < orderedMessages.length,
|
|
591
|
-
...(archive_id ? { archive_id } : {}),
|
|
592
|
-
...(previous_archive_id ? { previous_archive_id } : {}),
|
|
593
597
|
};
|
|
594
598
|
}
|
|
595
599
|
|