@downcity/agent 1.1.203 → 1.1.204
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/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +2 -0
- package/bin/session/Session.js.map +1 -1
- package/bin/session/SessionTitle.d.ts +9 -0
- package/bin/session/SessionTitle.d.ts.map +1 -1
- package/bin/session/SessionTitle.js +68 -3
- package/bin/session/SessionTitle.js.map +1 -1
- package/bin/session/services/SessionStateService.d.ts +6 -0
- package/bin/session/services/SessionStateService.d.ts.map +1 -1
- package/bin/session/services/SessionStateService.js +6 -0
- package/bin/session/services/SessionStateService.js.map +1 -1
- package/bin/session/services/SessionViewService.d.ts +6 -0
- package/bin/session/services/SessionViewService.d.ts.map +1 -1
- package/bin/session/services/SessionViewService.js +3 -0
- package/bin/session/services/SessionViewService.js.map +1 -1
- package/package.json +1 -1
- package/scripts/session-title-event.test.mjs +66 -0
- package/src/session/Session.ts +2 -0
- package/src/session/SessionTitle.ts +135 -3
- package/src/session/services/SessionStateService.ts +12 -0
- package/src/session/services/SessionViewService.ts +9 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -11,6 +11,7 @@ import { generateText, type LanguageModel } from "ai";
|
|
|
11
11
|
import type { SessionHistoryMetaV1 } from "@/executor/types/SessionHistoryMeta.js";
|
|
12
12
|
import type { SessionRecordV1 } from "@/executor/types/SessionRecords.js";
|
|
13
13
|
import { is_session_message_record } from "@/executor/types/SessionRecords.js";
|
|
14
|
+
import type { Logger } from "@/utils/logger/Logger.js";
|
|
14
15
|
import {
|
|
15
16
|
normalizeSessionTitle,
|
|
16
17
|
readSessionMetadata,
|
|
@@ -48,6 +49,16 @@ export interface EnsureSessionTitleParams {
|
|
|
48
49
|
*/
|
|
49
50
|
model?: LanguageModel;
|
|
50
51
|
|
|
52
|
+
/**
|
|
53
|
+
* 当前模型展示标签;仅用于排障日志,不参与生成逻辑。
|
|
54
|
+
*/
|
|
55
|
+
modelLabel?: string;
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* 当前 session 运行日志器;标题生成失败时仅记录摘要,不影响主流程。
|
|
59
|
+
*/
|
|
60
|
+
logger?: Logger;
|
|
61
|
+
|
|
51
62
|
/**
|
|
52
63
|
* 是否允许调用模型生成标题。
|
|
53
64
|
*/
|
|
@@ -102,16 +113,95 @@ function normalizeGeneratedTitle(input: string): string | undefined {
|
|
|
102
113
|
: undefined;
|
|
103
114
|
}
|
|
104
115
|
|
|
116
|
+
function summarizeTitleError(error: unknown): {
|
|
117
|
+
/**
|
|
118
|
+
* 错误对象名称。
|
|
119
|
+
*/
|
|
120
|
+
name: string | null;
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* 错误消息摘要。
|
|
124
|
+
*/
|
|
125
|
+
message: string | null;
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* 字符串化后的错误摘要。
|
|
129
|
+
*/
|
|
130
|
+
error: string;
|
|
131
|
+
} {
|
|
132
|
+
const record =
|
|
133
|
+
error && typeof error === "object" && !Array.isArray(error)
|
|
134
|
+
? (error as Record<string, unknown>)
|
|
135
|
+
: {};
|
|
136
|
+
return {
|
|
137
|
+
name: typeof record.name === "string" ? record.name : null,
|
|
138
|
+
message: typeof record.message === "string" ? record.message : null,
|
|
139
|
+
error: String(error),
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function logSessionTitleDiagnostic(input: {
|
|
144
|
+
/**
|
|
145
|
+
* 当前 session 标识。
|
|
146
|
+
*/
|
|
147
|
+
sessionId: string;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* 日志级别。
|
|
151
|
+
*/
|
|
152
|
+
level: "debug" | "warn";
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* 日志消息。
|
|
156
|
+
*/
|
|
157
|
+
message: string;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* 结构化日志字段。
|
|
161
|
+
*/
|
|
162
|
+
details: Record<string, string | number | boolean | null | undefined>;
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* 当前 session 运行日志器。
|
|
166
|
+
*/
|
|
167
|
+
logger?: Logger;
|
|
168
|
+
}): Promise<void> {
|
|
169
|
+
if (!input.logger) return;
|
|
170
|
+
try {
|
|
171
|
+
await input.logger.log(input.level, input.message, {
|
|
172
|
+
sessionId: input.sessionId,
|
|
173
|
+
...input.details,
|
|
174
|
+
});
|
|
175
|
+
} catch {
|
|
176
|
+
// 关键点(中文):标题诊断日志失败不能影响 session 主流程。
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
105
180
|
async function generateSessionTitle(input: {
|
|
106
181
|
/**
|
|
107
182
|
* 当前模型实例。
|
|
108
183
|
*/
|
|
109
184
|
model: LanguageModel;
|
|
110
185
|
|
|
186
|
+
/**
|
|
187
|
+
* 当前 session 标识。
|
|
188
|
+
*/
|
|
189
|
+
sessionId: string;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* 当前模型展示标签;仅用于排障日志。
|
|
193
|
+
*/
|
|
194
|
+
modelLabel?: string;
|
|
195
|
+
|
|
111
196
|
/**
|
|
112
197
|
* 首条用户消息文本。
|
|
113
198
|
*/
|
|
114
199
|
firstUserText: string;
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* 当前 session 运行日志器。
|
|
203
|
+
*/
|
|
204
|
+
logger?: Logger;
|
|
115
205
|
}): Promise<string | undefined> {
|
|
116
206
|
try {
|
|
117
207
|
const result = await generateText({
|
|
@@ -125,8 +215,33 @@ async function generateSessionTitle(input: {
|
|
|
125
215
|
input.firstUserText,
|
|
126
216
|
].join("\n"),
|
|
127
217
|
});
|
|
128
|
-
|
|
129
|
-
|
|
218
|
+
const generatedTitle = normalizeGeneratedTitle(result.text);
|
|
219
|
+
if (!generatedTitle) {
|
|
220
|
+
await logSessionTitleDiagnostic({
|
|
221
|
+
logger: input.logger,
|
|
222
|
+
sessionId: input.sessionId,
|
|
223
|
+
level: "warn",
|
|
224
|
+
message: "[agent] session_title.empty",
|
|
225
|
+
details: {
|
|
226
|
+
modelLabel: input.modelLabel || null,
|
|
227
|
+
firstUserTextLength: input.firstUserText.length,
|
|
228
|
+
rawTitleLength: String(result.text || "").length,
|
|
229
|
+
},
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
return generatedTitle;
|
|
233
|
+
} catch (error) {
|
|
234
|
+
await logSessionTitleDiagnostic({
|
|
235
|
+
logger: input.logger,
|
|
236
|
+
sessionId: input.sessionId,
|
|
237
|
+
level: "warn",
|
|
238
|
+
message: "[agent] session_title.generate_failed",
|
|
239
|
+
details: {
|
|
240
|
+
modelLabel: input.modelLabel || null,
|
|
241
|
+
firstUserTextLength: input.firstUserText.length,
|
|
242
|
+
...summarizeTitleError(error),
|
|
243
|
+
},
|
|
244
|
+
});
|
|
130
245
|
// 关键点(中文):标题生成失败不能影响 session 主流程。
|
|
131
246
|
return undefined;
|
|
132
247
|
}
|
|
@@ -142,13 +257,30 @@ export async function ensureSessionTitle(
|
|
|
142
257
|
if (current.title) return current;
|
|
143
258
|
|
|
144
259
|
const firstUserText = resolveFirstUserText(input.messages);
|
|
145
|
-
if (input.generate !== true
|
|
260
|
+
if (input.generate !== true) {
|
|
261
|
+
return current;
|
|
262
|
+
}
|
|
263
|
+
if (!input.model || !firstUserText) {
|
|
264
|
+
await logSessionTitleDiagnostic({
|
|
265
|
+
logger: input.logger,
|
|
266
|
+
sessionId: input.sessionId,
|
|
267
|
+
level: "debug",
|
|
268
|
+
message: "[agent] session_title.skipped",
|
|
269
|
+
details: {
|
|
270
|
+
reason: !input.model ? "missing_model" : "missing_first_user_text",
|
|
271
|
+
modelLabel: input.modelLabel || null,
|
|
272
|
+
messageCount: input.messages.length,
|
|
273
|
+
},
|
|
274
|
+
});
|
|
146
275
|
return current;
|
|
147
276
|
}
|
|
148
277
|
|
|
149
278
|
const generatedTitle = await generateSessionTitle({
|
|
150
279
|
model: input.model,
|
|
280
|
+
sessionId: input.sessionId,
|
|
281
|
+
modelLabel: input.modelLabel,
|
|
151
282
|
firstUserText,
|
|
283
|
+
logger: input.logger,
|
|
152
284
|
});
|
|
153
285
|
if (!generatedTitle) return current;
|
|
154
286
|
|
|
@@ -41,6 +41,7 @@ import type {
|
|
|
41
41
|
import { to_session_action_record } from "@/executor/types/SessionRecords.js";
|
|
42
42
|
import type { SessionLocalState } from "@/types/session/SessionLocalState.js";
|
|
43
43
|
import { generateId } from "@/utils/Id.js";
|
|
44
|
+
import type { Logger } from "@/utils/logger/Logger.js";
|
|
44
45
|
|
|
45
46
|
type SessionStateServiceOptions = {
|
|
46
47
|
/**
|
|
@@ -73,6 +74,11 @@ type SessionStateServiceOptions = {
|
|
|
73
74
|
*/
|
|
74
75
|
state: SessionLocalState;
|
|
75
76
|
|
|
77
|
+
/**
|
|
78
|
+
* 当前 session 运行日志器。
|
|
79
|
+
*/
|
|
80
|
+
logger: Logger;
|
|
81
|
+
|
|
76
82
|
/**
|
|
77
83
|
* 在执行前补齐宿主级配置。
|
|
78
84
|
*/
|
|
@@ -107,6 +113,7 @@ export class SessionStateService {
|
|
|
107
113
|
private readonly history_store: JsonlSessionHistoryStore;
|
|
108
114
|
private readonly executor: Executor;
|
|
109
115
|
private readonly state: SessionLocalState;
|
|
116
|
+
private readonly logger: Logger;
|
|
110
117
|
private readonly ensure_configured_hook?: SessionStateServiceOptions["ensure_configured_hook"];
|
|
111
118
|
private readonly publish_event: SessionStateServiceOptions["publish_event"];
|
|
112
119
|
|
|
@@ -117,6 +124,7 @@ export class SessionStateService {
|
|
|
117
124
|
this.history_store = options.history_store;
|
|
118
125
|
this.executor = options.executor;
|
|
119
126
|
this.state = options.state;
|
|
127
|
+
this.logger = options.logger;
|
|
120
128
|
this.ensure_configured_hook = options.ensure_configured_hook;
|
|
121
129
|
this.publish_event = options.publish_event;
|
|
122
130
|
}
|
|
@@ -340,6 +348,10 @@ export class SessionStateService {
|
|
|
340
348
|
sessionId: this.session_id,
|
|
341
349
|
messages,
|
|
342
350
|
...(input?.generate ? { model: this.state.sessionConfig.model } : {}),
|
|
351
|
+
...(this.state.sessionConfig.modelLabel
|
|
352
|
+
? { modelLabel: this.state.sessionConfig.modelLabel }
|
|
353
|
+
: {}),
|
|
354
|
+
logger: this.logger,
|
|
343
355
|
generate: input?.generate === true,
|
|
344
356
|
});
|
|
345
357
|
const next_title = String(next_metadata.title || "").trim();
|
|
@@ -31,6 +31,7 @@ import type {
|
|
|
31
31
|
import type { SessionRecordV1 } from "@/executor/types/SessionRecords.js";
|
|
32
32
|
import { SessionStateService } from "@/session/services/SessionStateService.js";
|
|
33
33
|
import type { SessionRunContext } from "@/types/executor/SessionRunContext.js";
|
|
34
|
+
import type { Logger } from "@/utils/logger/Logger.js";
|
|
34
35
|
|
|
35
36
|
type SessionViewServiceOptions<TSession extends Pick<AgentSession, "set">> = {
|
|
36
37
|
/**
|
|
@@ -58,6 +59,11 @@ type SessionViewServiceOptions<TSession extends Pick<AgentSession, "set">> = {
|
|
|
58
59
|
*/
|
|
59
60
|
state_service: SessionStateService;
|
|
60
61
|
|
|
62
|
+
/**
|
|
63
|
+
* 当前 session 运行日志器。
|
|
64
|
+
*/
|
|
65
|
+
logger: Logger;
|
|
66
|
+
|
|
61
67
|
/**
|
|
62
68
|
* 判断当前 session 是否正在执行。
|
|
63
69
|
*/
|
|
@@ -106,6 +112,7 @@ export class SessionViewService<TSession extends Pick<AgentSession, "set">> {
|
|
|
106
112
|
private readonly session_id: string;
|
|
107
113
|
private readonly history_store: JsonlSessionHistoryStore;
|
|
108
114
|
private readonly state_service: SessionStateService;
|
|
115
|
+
private readonly logger: Logger;
|
|
109
116
|
private readonly is_executing: SessionViewServiceOptions<TSession>["is_executing"];
|
|
110
117
|
private readonly get_instruction_system_blocks: SessionViewServiceOptions<TSession>["get_instruction_system_blocks"];
|
|
111
118
|
private readonly get_managed_plugin_system_blocks: SessionViewServiceOptions<TSession>["get_managed_plugin_system_blocks"];
|
|
@@ -119,6 +126,7 @@ export class SessionViewService<TSession extends Pick<AgentSession, "set">> {
|
|
|
119
126
|
this.session_id = options.session_id;
|
|
120
127
|
this.history_store = options.history_store;
|
|
121
128
|
this.state_service = options.state_service;
|
|
129
|
+
this.logger = options.logger;
|
|
122
130
|
this.is_executing = options.is_executing;
|
|
123
131
|
this.get_instruction_system_blocks = options.get_instruction_system_blocks;
|
|
124
132
|
this.get_managed_plugin_system_blocks =
|
|
@@ -164,6 +172,7 @@ export class SessionViewService<TSession extends Pick<AgentSession, "set">> {
|
|
|
164
172
|
agentId: this.agent_id,
|
|
165
173
|
sessionId: this.session_id,
|
|
166
174
|
messages: input.messages,
|
|
175
|
+
logger: this.logger,
|
|
167
176
|
});
|
|
168
177
|
return buildSessionInfo({
|
|
169
178
|
projectRoot: this.project_root,
|