@downcity/agent 1.1.320 → 1.1.323
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/Agent.d.ts.map +1 -1
- package/bin/agent/Agent.js +1 -0
- package/bin/agent/Agent.js.map +1 -1
- package/bin/agent/AgentSessions.d.ts +2 -0
- package/bin/agent/AgentSessions.d.ts.map +1 -1
- package/bin/agent/AgentSessions.js +9 -0
- package/bin/agent/AgentSessions.js.map +1 -1
- package/bin/executor/messages/SessionAttachmentMapper.d.ts +15 -7
- package/bin/executor/messages/SessionAttachmentMapper.d.ts.map +1 -1
- package/bin/executor/messages/SessionAttachmentMapper.js +22 -32
- package/bin/executor/messages/SessionAttachmentMapper.js.map +1 -1
- package/bin/index.d.ts +1 -0
- package/bin/index.d.ts.map +1 -1
- package/bin/index.js.map +1 -1
- package/bin/session/Session.d.ts +2 -0
- package/bin/session/Session.d.ts.map +1 -1
- package/bin/session/Session.js +9 -4
- package/bin/session/Session.js.map +1 -1
- package/bin/session/SessionLoop.js +2 -2
- package/bin/session/SessionLoop.js.map +1 -1
- package/bin/session/SessionMessages.d.ts +2 -1
- package/bin/session/SessionMessages.d.ts.map +1 -1
- package/bin/session/SessionMessages.js +14 -8
- package/bin/session/SessionMessages.js.map +1 -1
- package/bin/session/SessionState.d.ts +10 -9
- package/bin/session/SessionState.d.ts.map +1 -1
- package/bin/session/SessionState.js +108 -54
- package/bin/session/SessionState.js.map +1 -1
- package/bin/session/SessionTitle.d.ts +4 -0
- package/bin/session/SessionTitle.d.ts.map +1 -1
- package/bin/session/SessionTitle.js +5 -1
- package/bin/session/SessionTitle.js.map +1 -1
- package/bin/session/messages/SessionAssistantMessageWriter.d.ts +1 -1
- package/bin/session/messages/SessionAssistantMessageWriter.d.ts.map +1 -1
- package/bin/session/messages/SessionAssistantMessageWriter.js +4 -4
- package/bin/session/messages/SessionAssistantMessageWriter.js.map +1 -1
- package/bin/session/runtime/SessionTitleTask.d.ts +35 -0
- package/bin/session/runtime/SessionTitleTask.d.ts.map +1 -0
- package/bin/session/runtime/SessionTitleTask.js +72 -0
- package/bin/session/runtime/SessionTitleTask.js.map +1 -0
- package/bin/types/sdk/AgentSessionPrompt.d.ts +22 -0
- package/bin/types/sdk/AgentSessionPrompt.d.ts.map +1 -1
- package/bin/types/sdk/AgentSessionPrompt.js.map +1 -1
- package/bin/types/session/SessionMessages.d.ts +3 -0
- package/bin/types/session/SessionMessages.d.ts.map +1 -1
- package/bin/types/session/SessionOptions.d.ts +2 -0
- package/bin/types/session/SessionOptions.d.ts.map +1 -1
- package/bin/types/store/SessionAttachmentStore.d.ts +29 -0
- package/bin/types/store/SessionAttachmentStore.d.ts.map +1 -0
- package/bin/types/store/SessionAttachmentStore.js +15 -0
- package/bin/types/store/SessionAttachmentStore.js.map +1 -0
- package/bin/types/store/SessionStore.d.ts +3 -0
- package/bin/types/store/SessionStore.d.ts.map +1 -1
- package/bin/workspace/store/LocalSessionAttachmentStore.d.ts +25 -0
- package/bin/workspace/store/LocalSessionAttachmentStore.d.ts.map +1 -0
- package/bin/workspace/store/LocalSessionAttachmentStore.js +72 -0
- package/bin/workspace/store/LocalSessionAttachmentStore.js.map +1 -0
- package/bin/workspace/store/LocalSessionStore.d.ts +3 -0
- package/bin/workspace/store/LocalSessionStore.d.ts.map +1 -1
- package/bin/workspace/store/LocalSessionStore.js +8 -1
- package/bin/workspace/store/LocalSessionStore.js.map +1 -1
- package/bin/workspace/store/LocalStorePaths.d.ts +6 -0
- package/bin/workspace/store/LocalStorePaths.d.ts.map +1 -1
- package/bin/workspace/store/LocalStorePaths.js +8 -0
- package/bin/workspace/store/LocalStorePaths.js.map +1 -1
- package/package.json +1 -1
- package/scripts/session-attachments.test.mjs +101 -0
- package/scripts/session-list-title.test.mjs +6 -0
- package/scripts/session-title-event.test.mjs +67 -3
- package/scripts/session-turn-failure.test.mjs +34 -1
- package/src/agent/Agent.ts +1 -0
- package/src/agent/AgentSessions.ts +10 -0
- package/src/executor/messages/SessionAttachmentMapper.ts +24 -37
- package/src/index.ts +1 -0
- package/src/session/Session.ts +10 -4
- package/src/session/SessionLoop.ts +2 -2
- package/src/session/SessionMessages.ts +17 -7
- package/src/session/SessionState.ts +118 -61
- package/src/session/SessionTitle.ts +13 -1
- package/src/session/messages/SessionAssistantMessageWriter.ts +7 -3
- package/src/session/runtime/SessionTitleTask.ts +86 -0
- package/src/types/sdk/AgentSessionPrompt.ts +22 -0
- package/src/types/session/SessionMessages.ts +3 -0
- package/src/types/session/SessionOptions.ts +3 -0
- package/src/types/store/SessionAttachmentStore.ts +29 -0
- package/src/types/store/SessionStore.ts +4 -0
- package/src/workspace/store/LocalSessionAttachmentStore.ts +96 -0
- package/src/workspace/store/LocalSessionStore.ts +13 -0
- package/src/workspace/store/LocalStorePaths.ts +16 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace 内 Session 附件的本地实现。
|
|
3
|
+
*
|
|
4
|
+
* Data URL 在这里解码为文件;Message 层只接收相对 Workspace 根目录的文件引用。
|
|
5
|
+
*/
|
|
6
|
+
import type { FileSystem } from "../../types/workspace/FileSystem.js";
|
|
7
|
+
import type { SessionAttachmentStore } from "../../types/store/SessionAttachmentStore.js";
|
|
8
|
+
export interface LocalSessionAttachmentStoreOptions {
|
|
9
|
+
/** Workspace 文件能力。 */
|
|
10
|
+
files: FileSystem;
|
|
11
|
+
/** 当前 Session 附件目录的绝对路径。 */
|
|
12
|
+
attachments_dir_path: string;
|
|
13
|
+
}
|
|
14
|
+
/** Session 附件本地持久化实现。 */
|
|
15
|
+
export declare class LocalSessionAttachmentStore implements SessionAttachmentStore {
|
|
16
|
+
private readonly files;
|
|
17
|
+
private readonly attachments_dir_path;
|
|
18
|
+
constructor(options: LocalSessionAttachmentStoreOptions);
|
|
19
|
+
persist_data_url(input: {
|
|
20
|
+
data_url: string;
|
|
21
|
+
media_type: string;
|
|
22
|
+
filename?: string;
|
|
23
|
+
}): Promise<string>;
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=LocalSessionAttachmentStore.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalSessionAttachmentStore.d.ts","sourceRoot":"","sources":["../../../src/workspace/store/LocalSessionAttachmentStore.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iCAAiC,CAAC;AAClE,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,yCAAyC,CAAC;AAetF,MAAM,WAAW,kCAAkC;IACjD,sBAAsB;IACtB,KAAK,EAAE,UAAU,CAAC;IAClB,4BAA4B;IAC5B,oBAAoB,EAAE,MAAM,CAAC;CAC9B;AAED,yBAAyB;AACzB,qBAAa,2BAA4B,YAAW,sBAAsB;IACxE,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IACnC,OAAO,CAAC,QAAQ,CAAC,oBAAoB,CAAS;gBAElC,OAAO,EAAE,kCAAkC;IAKjD,gBAAgB,CAAC,KAAK,EAAE;QAC5B,QAAQ,EAAE,MAAM,CAAC;QACjB,UAAU,EAAE,MAAM,CAAC;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,GAAG,OAAO,CAAC,MAAM,CAAC;CASpB"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace 内 Session 附件的本地实现。
|
|
3
|
+
*
|
|
4
|
+
* Data URL 在这里解码为文件;Message 层只接收相对 Workspace 根目录的文件引用。
|
|
5
|
+
*/
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import { generate_id } from "../../utils/Id.js";
|
|
8
|
+
const DATA_URL_PATTERN = /^data:([^;,]+)?((?:;[^;,]+)*),(.*)$/s;
|
|
9
|
+
const MEDIA_TYPE_EXTENSIONS = {
|
|
10
|
+
"image/png": ".png",
|
|
11
|
+
"image/jpeg": ".jpg",
|
|
12
|
+
"image/webp": ".webp",
|
|
13
|
+
"image/gif": ".gif",
|
|
14
|
+
"video/mp4": ".mp4",
|
|
15
|
+
"video/quicktime": ".mov",
|
|
16
|
+
"video/webm": ".webm",
|
|
17
|
+
"application/pdf": ".pdf",
|
|
18
|
+
};
|
|
19
|
+
/** Session 附件本地持久化实现。 */
|
|
20
|
+
export class LocalSessionAttachmentStore {
|
|
21
|
+
files;
|
|
22
|
+
attachments_dir_path;
|
|
23
|
+
constructor(options) {
|
|
24
|
+
this.files = options.files;
|
|
25
|
+
this.attachments_dir_path = path.resolve(options.attachments_dir_path);
|
|
26
|
+
}
|
|
27
|
+
async persist_data_url(input) {
|
|
28
|
+
const parsed = parse_data_url(input.data_url, input.media_type);
|
|
29
|
+
const extension = resolve_extension(parsed.media_type, input.filename);
|
|
30
|
+
const attachment_name = `att_${generate_id()}${extension}`;
|
|
31
|
+
const absolute_path = path.join(this.attachments_dir_path, attachment_name);
|
|
32
|
+
await this.files.ensure_directory(this.attachments_dir_path);
|
|
33
|
+
await this.files.write_file_atomically(absolute_path, parsed.bytes);
|
|
34
|
+
return path.relative(this.files.root_path, absolute_path);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
function parse_data_url(data_url, fallback_media_type) {
|
|
38
|
+
const match = DATA_URL_PATTERN.exec(String(data_url || "").trim());
|
|
39
|
+
if (!match)
|
|
40
|
+
throw new Error("Invalid data URL");
|
|
41
|
+
const media_type = String(match[1] || fallback_media_type || "").trim();
|
|
42
|
+
if (!media_type || !media_type.includes("/")) {
|
|
43
|
+
throw new Error("Data URL requires a valid media type");
|
|
44
|
+
}
|
|
45
|
+
const metadata = String(match[2] || "");
|
|
46
|
+
const payload = String(match[3] || "");
|
|
47
|
+
try {
|
|
48
|
+
const normalized_payload = payload.replace(/\s/g, "");
|
|
49
|
+
if (metadata.includes(";base64") &&
|
|
50
|
+
(!/^[A-Za-z0-9+/]*={0,2}$/.test(normalized_payload) ||
|
|
51
|
+
normalized_payload.length % 4 === 1)) {
|
|
52
|
+
throw new Error("invalid base64 payload");
|
|
53
|
+
}
|
|
54
|
+
return {
|
|
55
|
+
media_type,
|
|
56
|
+
bytes: metadata.includes(";base64")
|
|
57
|
+
? Buffer.from(normalized_payload, "base64")
|
|
58
|
+
: Buffer.from(decodeURIComponent(payload), "utf8"),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
throw new Error(`Invalid data URL payload: ${error instanceof Error ? error.message : String(error)}`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function resolve_extension(media_type, filename) {
|
|
66
|
+
const known = MEDIA_TYPE_EXTENSIONS[media_type.toLowerCase()];
|
|
67
|
+
if (known)
|
|
68
|
+
return known;
|
|
69
|
+
const filename_extension = path.extname(String(filename || "")).toLowerCase();
|
|
70
|
+
return /^[.][a-z0-9]{1,10}$/.test(filename_extension) ? filename_extension : ".bin";
|
|
71
|
+
}
|
|
72
|
+
//# sourceMappingURL=LocalSessionAttachmentStore.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"LocalSessionAttachmentStore.js","sourceRoot":"","sources":["../../../src/workspace/store/LocalSessionAttachmentStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -9,12 +9,15 @@ import { JsonlSessionMessageStore } from "../../workspace/store/JsonlSessionMess
|
|
|
9
9
|
import type { SessionHistoryMetaV1 } from "../../executor/types/SessionHistoryMeta.js";
|
|
10
10
|
import type { SessionStore } from "../../types/store/SessionStore.js";
|
|
11
11
|
import type { LocalSessionStoreOptions } from "../../types/store/LocalStore.js";
|
|
12
|
+
import { LocalSessionAttachmentStore } from "../../workspace/store/LocalSessionAttachmentStore.js";
|
|
12
13
|
/** 本地 Session Store。 */
|
|
13
14
|
export declare class LocalSessionStore implements SessionStore {
|
|
14
15
|
/** 当前 Session 的稳定标识。 */
|
|
15
16
|
readonly session_id: string;
|
|
16
17
|
/** 当前 Session 的 JSONL Message Store。 */
|
|
17
18
|
readonly messages: JsonlSessionMessageStore;
|
|
19
|
+
/** 当前 Session 的附件持久化能力。 */
|
|
20
|
+
readonly attachments: LocalSessionAttachmentStore;
|
|
18
21
|
/** 当前 Store 与 WorkspaceTools 共用的 Workspace 文件能力。 */
|
|
19
22
|
private readonly files;
|
|
20
23
|
/** 当前 Agent 的稳定标识。 */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalSessionStore.d.ts","sourceRoot":"","sources":["../../../src/workspace/store/LocalSessionStore.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,+CAA+C,CAAC;
|
|
1
|
+
{"version":3,"file":"LocalSessionStore.d.ts","sourceRoot":"","sources":["../../../src/workspace/store/LocalSessionStore.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,+CAA+C,CAAC;AASzF,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,wCAAwC,CAAC;AACnF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAElE,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,6BAA6B,CAAC;AAC5E,OAAO,EAAE,2BAA2B,EAAE,MAAM,kDAAkD,CAAC;AAE/F,wBAAwB;AACxB,qBAAa,iBAAkB,YAAW,YAAY;IACpD,wBAAwB;IACxB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B,wCAAwC;IACxC,QAAQ,CAAC,QAAQ,EAAE,wBAAwB,CAAC;IAE5C,2BAA2B;IAC3B,QAAQ,CAAC,WAAW,EAAE,2BAA2B,CAAC;IAElD,oDAAoD;IACpD,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IAEnC,sBAAsB;IACtB,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;gBAEtB,OAAO,EAAE,wBAAwB;IA4B7C,8BAA8B;IACxB,aAAa,IAAI,OAAO,CAAC,oBAAoB,CAAC;IAWpD,6BAA6B;IACvB,cAAc,CAAC,QAAQ,EAAE,oBAAoB,GAAG,OAAO,CAAC,IAAI,CAAC;IAOnE,0BAA0B;IACpB,eAAe,IAAI,OAAO,CAAC,OAAO,CAAC;IAIzC,sBAAsB;IAChB,gBAAgB,IAAI,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC;IAKhD,sBAAsB;IAChB,iBAAiB,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAS3D,kDAAkD;IAClD,OAAO,CAAC,gBAAgB;IAQxB,6CAA6C;IAC7C,OAAO,CAAC,aAAa;CAOtB"}
|
|
@@ -6,14 +6,17 @@
|
|
|
6
6
|
* - Session 领域只持有本对象,不再自行拼接任何存储路径。
|
|
7
7
|
*/
|
|
8
8
|
import { JsonlSessionMessageStore } from "../../workspace/store/JsonlSessionMessageStore.js";
|
|
9
|
-
import { get_sdk_agent_session_instruction_path, get_sdk_agent_session_meta_path, get_sdk_agent_session_assistant_message_path, get_sdk_agent_session_messages_path, } from "../../workspace/store/LocalStorePaths.js";
|
|
9
|
+
import { get_sdk_agent_session_instruction_path, get_sdk_agent_session_meta_path, get_sdk_agent_session_assistant_message_path, get_sdk_agent_session_messages_path, get_sdk_agent_session_attachments_dir_path, } from "../../workspace/store/LocalStorePaths.js";
|
|
10
10
|
import { normalize_session_metadata } from "../../session/storage/Metadata.js";
|
|
11
|
+
import { LocalSessionAttachmentStore } from "../../workspace/store/LocalSessionAttachmentStore.js";
|
|
11
12
|
/** 本地 Session Store。 */
|
|
12
13
|
export class LocalSessionStore {
|
|
13
14
|
/** 当前 Session 的稳定标识。 */
|
|
14
15
|
session_id;
|
|
15
16
|
/** 当前 Session 的 JSONL Message Store。 */
|
|
16
17
|
messages;
|
|
18
|
+
/** 当前 Session 的附件持久化能力。 */
|
|
19
|
+
attachments;
|
|
17
20
|
/** 当前 Store 与 WorkspaceTools 共用的 Workspace 文件能力。 */
|
|
18
21
|
files;
|
|
19
22
|
/** 当前 Agent 的稳定标识。 */
|
|
@@ -28,6 +31,10 @@ export class LocalSessionStore {
|
|
|
28
31
|
file_path: get_sdk_agent_session_messages_path(this.files.root_path, this.agent_id, this.session_id),
|
|
29
32
|
assistant_message_file_path: get_sdk_agent_session_assistant_message_path(this.files.root_path, this.agent_id, this.session_id),
|
|
30
33
|
});
|
|
34
|
+
this.attachments = new LocalSessionAttachmentStore({
|
|
35
|
+
files: this.files,
|
|
36
|
+
attachments_dir_path: get_sdk_agent_session_attachments_dir_path(this.files.root_path, this.agent_id, this.session_id),
|
|
37
|
+
});
|
|
31
38
|
}
|
|
32
39
|
/** 读取规范化 Session Metadata。 */
|
|
33
40
|
async read_metadata() {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalSessionStore.js","sourceRoot":"","sources":["../../../src/workspace/store/LocalSessionStore.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,EAAE,wBAAwB,EAAE,MAAM,+CAA+C,CAAC;AACzF,OAAO,EACL,sCAAsC,EACtC,+BAA+B,EAC/B,4CAA4C,EAC5C,mCAAmC,
|
|
1
|
+
{"version":3,"file":"LocalSessionStore.js","sourceRoot":"","sources":["../../../src/workspace/store/LocalSessionStore.ts"],"names":[],"mappings":"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"}
|
|
@@ -61,6 +61,12 @@ export declare function get_sdk_agent_session_instruction_path(workspace_path: s
|
|
|
61
61
|
* 单个 session 的消息目录路径。
|
|
62
62
|
*/
|
|
63
63
|
export declare function get_sdk_agent_session_messages_dir_path(workspace_path: string, agent_id: string, session_id: string): string;
|
|
64
|
+
/**
|
|
65
|
+
* 单个 Session 的附件目录路径。
|
|
66
|
+
*
|
|
67
|
+
* 附件与 Session 目录同生命周期,归档或删除 Session 时随目录一起迁移或清理。
|
|
68
|
+
*/
|
|
69
|
+
export declare function get_sdk_agent_session_attachments_dir_path(workspace_path: string, agent_id: string, session_id: string): string;
|
|
64
70
|
/**
|
|
65
71
|
* 单个 session 的 Active JSONL 文件路径。
|
|
66
72
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalStorePaths.d.ts","sourceRoot":"","sources":["../../../src/workspace/store/LocalStorePaths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAExE;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,oCAAoC,CAClD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,wCAAwC,CACtD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CACrD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,gDAAgD,CAC9D,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,4CAA4C,CAC1D,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,wCAAwC,CACtD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;;;;;GAMG;AACH,wBAAgB,sCAAsC,CACpD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CACrD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,mCAAmC,CACjD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAC7C,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;;;;;;GAOG;AACH,wBAAgB,4CAA4C,CAC1D,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR"}
|
|
1
|
+
{"version":3,"file":"LocalStorePaths.d.ts","sourceRoot":"","sources":["../../../src/workspace/store/LocalStorePaths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAExE;AAED;;GAEG;AACH,wBAAgB,4BAA4B,CAAC,cAAc,EAAE,MAAM,GAAG,MAAM,CAE3E;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,oCAAoC,CAClD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED;;;;;;GAMG;AACH,wBAAgB,wCAAwC,CACtD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,GACf,MAAM,CAER;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CACrD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,gDAAgD,CAC9D,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,4CAA4C,CAC1D,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,wCAAwC,CACtD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,8BAA8B,CAC5C,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;;;;;GAMG;AACH,wBAAgB,sCAAsC,CACpD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,uCAAuC,CACrD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;;;GAIG;AACH,wBAAgB,0CAA0C,CACxD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,mCAAmC,CACjD,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;GAEG;AACH,wBAAgB,+BAA+B,CAC7C,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR;AAED;;;;;;;GAOG;AACH,wBAAgB,4CAA4C,CAC1D,cAAc,EAAE,MAAM,EACtB,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,GACjB,MAAM,CAKR"}
|
|
@@ -86,6 +86,14 @@ export function get_sdk_agent_session_instruction_path(workspace_path, agent_id,
|
|
|
86
86
|
export function get_sdk_agent_session_messages_dir_path(workspace_path, agent_id, session_id) {
|
|
87
87
|
return path.join(get_sdk_agent_session_dir_path(workspace_path, agent_id, session_id), "messages");
|
|
88
88
|
}
|
|
89
|
+
/**
|
|
90
|
+
* 单个 Session 的附件目录路径。
|
|
91
|
+
*
|
|
92
|
+
* 附件与 Session 目录同生命周期,归档或删除 Session 时随目录一起迁移或清理。
|
|
93
|
+
*/
|
|
94
|
+
export function get_sdk_agent_session_attachments_dir_path(workspace_path, agent_id, session_id) {
|
|
95
|
+
return path.join(get_sdk_agent_session_dir_path(workspace_path, agent_id, session_id), "attachments");
|
|
96
|
+
}
|
|
89
97
|
/**
|
|
90
98
|
* 单个 session 的 Active JSONL 文件路径。
|
|
91
99
|
*/
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LocalStorePaths.js","sourceRoot":"","sources":["../../../src/workspace/store/LocalStorePaths.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"LocalStorePaths.js","sourceRoot":"","sources":["../../../src/workspace/store/LocalStorePaths.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;GAEG;AACH,MAAM,UAAU,yBAAyB,CAAC,cAAsB;IAC9D,OAAO,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,WAAW,CAAC,CAAC;AAChD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,4BAA4B,CAAC,cAAsB;IACjE,OAAO,IAAI,CAAC,IAAI,CAAC,yBAAyB,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC;AACxE,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,cAAsB,EACtB,QAAgB;IAEhB,OAAO,IAAI,CAAC,IAAI,CACd,4BAA4B,CAAC,cAAc,CAAC,EAC5C,kBAAkB,CAAC,MAAM,CAAC,QAAQ,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAClD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,oCAAoC,CAClD,cAAsB,EACtB,QAAgB;IAEhB,OAAO,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,cAAc,EAAE,QAAQ,CAAC,EAAE,UAAU,CAAC,CAAC;AACjF,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,wCAAwC,CACtD,cAAsB,EACtB,QAAgB;IAEhB,OAAO,IAAI,CAAC,IAAI,CAAC,sBAAsB,CAAC,cAAc,EAAE,QAAQ,CAAC,EAAE,mBAAmB,CAAC,CAAC;AAC1F,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uCAAuC,CACrD,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,wCAAwC,CAAC,cAAc,EAAE,QAAQ,CAAC,EAClE,kBAAkB,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CACpD,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,gDAAgD,CAC9D,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,uCAAuC,CAAC,cAAc,EAAE,QAAQ,EAAE,UAAU,CAAC,EAC7E,UAAU,CACX,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,4CAA4C,CAC1D,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,gDAAgD,CAAC,cAAc,EAAE,QAAQ,EAAE,UAAU,CAAC,EACtF,cAAc,CACf,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,wCAAwC,CACtD,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,gDAAgD,CAAC,cAAc,EAAE,QAAQ,EAAE,UAAU,CAAC,EACtF,WAAW,CACZ,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,8BAA8B,CAC5C,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,oCAAoC,CAAC,cAAc,EAAE,QAAQ,CAAC,EAC9D,kBAAkB,CAAC,MAAM,CAAC,UAAU,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CACpD,CAAC;AACJ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,sCAAsC,CACpD,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,8BAA8B,CAAC,cAAc,EAAE,QAAQ,EAAE,UAAU,CAAC,EACpE,gBAAgB,CACjB,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,uCAAuC,CACrD,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,8BAA8B,CAAC,cAAc,EAAE,QAAQ,EAAE,UAAU,CAAC,EACpE,UAAU,CACX,CAAC;AACJ,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,0CAA0C,CACxD,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,8BAA8B,CAAC,cAAc,EAAE,QAAQ,EAAE,UAAU,CAAC,EACpE,aAAa,CACd,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,mCAAmC,CACjD,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,uCAAuC,CAAC,cAAc,EAAE,QAAQ,EAAE,UAAU,CAAC,EAC7E,cAAc,CACf,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,+BAA+B,CAC7C,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,uCAAuC,CAAC,cAAc,EAAE,QAAQ,EAAE,UAAU,CAAC,EAC7E,WAAW,CACZ,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,4CAA4C,CAC1D,cAAsB,EACtB,QAAgB,EAChB,UAAkB;IAElB,OAAO,IAAI,CAAC,IAAI,CACd,uCAAuC,CAAC,cAAc,EAAE,QAAQ,EAAE,UAAU,CAAC,EAC7E,wBAAwB,CACzB,CAAC;AACJ,CAAC"}
|
package/package.json
CHANGED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file 验证 Session prompt 的 Data URL 附件会先落盘,再以路径持久化。
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import assert from "node:assert/strict";
|
|
6
|
+
import fs from "node:fs/promises";
|
|
7
|
+
import os from "node:os";
|
|
8
|
+
import path from "node:path";
|
|
9
|
+
import test from "node:test";
|
|
10
|
+
import { LocalFileSystem } from "../bin/workspace/LocalFileSystem.js";
|
|
11
|
+
import { LocalSessionStore } from "../bin/workspace/store/LocalSessionStore.js";
|
|
12
|
+
import { SessionMessages } from "../bin/session/SessionMessages.js";
|
|
13
|
+
|
|
14
|
+
async function create_harness() {
|
|
15
|
+
const root_path = await fs.mkdtemp(path.join(os.tmpdir(), "downcity-session-attachment-"));
|
|
16
|
+
const files = new LocalFileSystem(root_path);
|
|
17
|
+
const store = new LocalSessionStore({
|
|
18
|
+
files,
|
|
19
|
+
agent_id: "attachment-test-agent",
|
|
20
|
+
session_id: "attachment-test-session",
|
|
21
|
+
});
|
|
22
|
+
const messages = new SessionMessages({
|
|
23
|
+
session_id: "attachment-test-session",
|
|
24
|
+
store: store.messages,
|
|
25
|
+
attachment_store: store.attachments,
|
|
26
|
+
publish: () => {},
|
|
27
|
+
});
|
|
28
|
+
await messages.initialize();
|
|
29
|
+
return { root_path, store, messages };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
test("Data URL 先保存为 Session 附件,Message 只保存相对路径", async () => {
|
|
33
|
+
const harness = await create_harness();
|
|
34
|
+
const result = await harness.messages.append_prompt_message({
|
|
35
|
+
turn_id: "turn-1",
|
|
36
|
+
input_type: "prompt",
|
|
37
|
+
prompt: {
|
|
38
|
+
query: [{
|
|
39
|
+
type: "file",
|
|
40
|
+
mediaType: "image/png",
|
|
41
|
+
url: "data:image/png;base64,aGVsbG8=",
|
|
42
|
+
filename: "diagram.png",
|
|
43
|
+
}],
|
|
44
|
+
},
|
|
45
|
+
project_root: harness.root_path,
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
const part = result.parts[0];
|
|
49
|
+
assert.equal(part.type, "file");
|
|
50
|
+
assert.match(part.url, /^\.downcity\/agents\/attachment-test-agent\/sessions\/attachment-test-session\/attachments\/att_/);
|
|
51
|
+
assert.equal(part.url.endsWith(".png"), true);
|
|
52
|
+
assert.equal(
|
|
53
|
+
(await fs.readFile(path.join(harness.root_path, part.url))).toString(),
|
|
54
|
+
"hello",
|
|
55
|
+
);
|
|
56
|
+
assert.equal(part.url.includes("base64"), false);
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
test("视频 Data URL 使用 MIME 类型和扩展名落盘", async () => {
|
|
60
|
+
const harness = await create_harness();
|
|
61
|
+
const result = await harness.messages.append_prompt_message({
|
|
62
|
+
turn_id: "turn-1",
|
|
63
|
+
input_type: "prompt",
|
|
64
|
+
prompt: {
|
|
65
|
+
query: [{
|
|
66
|
+
type: "file",
|
|
67
|
+
mediaType: "video/mp4",
|
|
68
|
+
url: "data:video/mp4;base64,AQID",
|
|
69
|
+
filename: "demo.mp4",
|
|
70
|
+
}],
|
|
71
|
+
},
|
|
72
|
+
project_root: harness.root_path,
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
const part = result.parts[0];
|
|
76
|
+
assert.equal(part.url.endsWith(".mp4"), true);
|
|
77
|
+
assert.deepEqual(
|
|
78
|
+
[...await fs.readFile(path.join(harness.root_path, part.url))],
|
|
79
|
+
[1, 2, 3],
|
|
80
|
+
);
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
test("无效 Data URL 不会写入 Session Message", async () => {
|
|
84
|
+
const harness = await create_harness();
|
|
85
|
+
await assert.rejects(
|
|
86
|
+
harness.messages.append_prompt_message({
|
|
87
|
+
turn_id: "turn-1",
|
|
88
|
+
input_type: "prompt",
|
|
89
|
+
prompt: {
|
|
90
|
+
query: [{
|
|
91
|
+
type: "file",
|
|
92
|
+
mediaType: "image/png",
|
|
93
|
+
url: "data:image/png;base64",
|
|
94
|
+
}],
|
|
95
|
+
},
|
|
96
|
+
project_root: harness.root_path,
|
|
97
|
+
}),
|
|
98
|
+
/Invalid data URL/,
|
|
99
|
+
);
|
|
100
|
+
assert.deepEqual(await harness.store.messages.list_messages(), []);
|
|
101
|
+
});
|
|
@@ -72,6 +72,12 @@ async function create_agent_with_titled_session(input) {
|
|
|
72
72
|
await session.append_user_message({
|
|
73
73
|
text: input.first_user_text,
|
|
74
74
|
});
|
|
75
|
+
const title_deadline = Date.now() + 1000;
|
|
76
|
+
while (Date.now() < title_deadline) {
|
|
77
|
+
if ((await session.get_info()).title === input.title) break;
|
|
78
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
79
|
+
}
|
|
80
|
+
assert.equal((await session.get_info()).title, input.title);
|
|
75
81
|
|
|
76
82
|
return {
|
|
77
83
|
agent,
|
|
@@ -79,6 +79,30 @@ function create_failing_title_model() {
|
|
|
79
79
|
});
|
|
80
80
|
}
|
|
81
81
|
|
|
82
|
+
function create_delayed_title_model(title_text) {
|
|
83
|
+
let resolve_started;
|
|
84
|
+
let resolve_release;
|
|
85
|
+
const started = new Promise((resolve) => {
|
|
86
|
+
resolve_started = resolve;
|
|
87
|
+
});
|
|
88
|
+
const released = new Promise((resolve) => {
|
|
89
|
+
resolve_release = resolve;
|
|
90
|
+
});
|
|
91
|
+
const model = new MockLanguageModelV3({
|
|
92
|
+
modelId: "mock-session-title-delayed-model",
|
|
93
|
+
doStream: async () => {
|
|
94
|
+
resolve_started();
|
|
95
|
+
await released;
|
|
96
|
+
return create_stream_text_result(title_text);
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
return {
|
|
100
|
+
model,
|
|
101
|
+
started,
|
|
102
|
+
release: () => resolve_release(),
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
82
106
|
async function read_log_lines(agent_path) {
|
|
83
107
|
const logs_path = path.join(agent_path, ".downcity", "logs");
|
|
84
108
|
const entries = await fs.readdir(logs_path);
|
|
@@ -91,6 +115,15 @@ async function read_log_lines(agent_path) {
|
|
|
91
115
|
return lines;
|
|
92
116
|
}
|
|
93
117
|
|
|
118
|
+
async function wait_for_title(session, expected_title) {
|
|
119
|
+
const deadline = Date.now() + 1000;
|
|
120
|
+
while (Date.now() < deadline) {
|
|
121
|
+
if ((await session.get_info()).title === expected_title) return;
|
|
122
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
123
|
+
}
|
|
124
|
+
assert.equal((await session.get_info()).title, expected_title);
|
|
125
|
+
}
|
|
126
|
+
|
|
94
127
|
test("Session keeps title empty when no model is available", async () => {
|
|
95
128
|
const agent_path = await fs.mkdtemp(
|
|
96
129
|
path.join(os.tmpdir(), "downcity-agent-session-title-"),
|
|
@@ -123,6 +156,29 @@ test("Session keeps title empty when no model is available", async () => {
|
|
|
123
156
|
}
|
|
124
157
|
});
|
|
125
158
|
|
|
159
|
+
test("Session title generation does not block user message append", async () => {
|
|
160
|
+
const agent_path = await fs.mkdtemp(
|
|
161
|
+
path.join(os.tmpdir(), "downcity-agent-session-title-async-"),
|
|
162
|
+
);
|
|
163
|
+
const delayed = create_delayed_title_model("异步标题");
|
|
164
|
+
const agent = new Agent({
|
|
165
|
+
id: "title_async_agent",
|
|
166
|
+
workspace: new Workspace({ path: agent_path }),
|
|
167
|
+
model: delayed.model,
|
|
168
|
+
});
|
|
169
|
+
const session = await agent.sessions.create();
|
|
170
|
+
|
|
171
|
+
try {
|
|
172
|
+
await session.append_user_message({ text: "标题生成不应阻塞消息写入" });
|
|
173
|
+
await delayed.started;
|
|
174
|
+
assert.equal((await session.get_info()).title, undefined);
|
|
175
|
+
delayed.release();
|
|
176
|
+
await wait_for_title(session, "异步标题");
|
|
177
|
+
} finally {
|
|
178
|
+
await agent.dispose();
|
|
179
|
+
}
|
|
180
|
+
});
|
|
181
|
+
|
|
126
182
|
test("Session logs title generation failure without blocking the session", async () => {
|
|
127
183
|
const agent_path = await fs.mkdtemp(
|
|
128
184
|
path.join(os.tmpdir(), "downcity-agent-session-title-log-"),
|
|
@@ -142,8 +198,16 @@ test("Session logs title generation failure without blocking the session", async
|
|
|
142
198
|
|
|
143
199
|
assert.equal((await session.get_info()).title, undefined);
|
|
144
200
|
|
|
145
|
-
|
|
146
|
-
const
|
|
201
|
+
let log_lines = [];
|
|
202
|
+
const log_deadline = Date.now() + 1000;
|
|
203
|
+
while (Date.now() < log_deadline) {
|
|
204
|
+
await agent.get_logger().save_all_logs();
|
|
205
|
+
log_lines = await read_log_lines(agent_path);
|
|
206
|
+
if (log_lines.some((line) => line.includes("session_title.generate_failed"))) {
|
|
207
|
+
break;
|
|
208
|
+
}
|
|
209
|
+
await new Promise((resolve) => setTimeout(resolve, 10));
|
|
210
|
+
}
|
|
147
211
|
const title_failure_log = log_lines
|
|
148
212
|
.map((line) => JSON.parse(line))
|
|
149
213
|
.find((entry) => entry.message.includes("session_title.generate_failed"));
|
|
@@ -196,7 +260,7 @@ test("Session retries title generation after model becomes available", async ()
|
|
|
196
260
|
text: "Need another prompt to trigger the retry path",
|
|
197
261
|
});
|
|
198
262
|
|
|
199
|
-
|
|
263
|
+
await wait_for_title(session, "排查 session 标题");
|
|
200
264
|
} finally {
|
|
201
265
|
unsubscribe();
|
|
202
266
|
await agent.dispose();
|
|
@@ -45,8 +45,9 @@ async function create_turn_harness(execute_turn) {
|
|
|
45
45
|
compact_history: async () => ({ compacted: false, reason: "nothing_to_compact" }),
|
|
46
46
|
state: {
|
|
47
47
|
ensure_runnable: async () => {},
|
|
48
|
-
|
|
48
|
+
schedule_title_generation: () => {},
|
|
49
49
|
touch_metadata: async () => {},
|
|
50
|
+
touch_metadata_in_background: () => {},
|
|
50
51
|
},
|
|
51
52
|
messages,
|
|
52
53
|
events: new SessionEventHub(),
|
|
@@ -137,6 +138,38 @@ test("Provider 在部分输出后失败时保留 failed Assistant 并追加 Erro
|
|
|
137
138
|
assert.equal(page.items[2].message, "stream interrupted");
|
|
138
139
|
});
|
|
139
140
|
|
|
141
|
+
test("Assistant 失败收口时不会遗留 input-streaming Tool Part", async () => {
|
|
142
|
+
const { messages, turn } = await create_turn_harness(async (turn_context) => {
|
|
143
|
+
await turn_context.output.assistant.begin_step();
|
|
144
|
+
await turn_context.output.assistant.write_chunk({
|
|
145
|
+
type: "tool-input-start",
|
|
146
|
+
toolCallId: "call-1",
|
|
147
|
+
toolName: "shell_exec",
|
|
148
|
+
});
|
|
149
|
+
await turn_context.output.assistant.write_chunk({
|
|
150
|
+
type: "tool-input-delta",
|
|
151
|
+
toolCallId: "call-1",
|
|
152
|
+
inputTextDelta: '{"cmd":"pwd"}',
|
|
153
|
+
});
|
|
154
|
+
return {
|
|
155
|
+
success: false,
|
|
156
|
+
text: "",
|
|
157
|
+
error: "stream interrupted",
|
|
158
|
+
deferred_persisted_user_messages: [],
|
|
159
|
+
};
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
const handle = await turn.prompt({ query: "hello" });
|
|
163
|
+
await handle.finished;
|
|
164
|
+
const page = await messages.list_messages();
|
|
165
|
+
const assistant = page.items.find((message) => message.type === "assistant");
|
|
166
|
+
|
|
167
|
+
assert.equal(assistant?.status, "failed");
|
|
168
|
+
assert.equal(assistant?.parts[0]?.type, "tool");
|
|
169
|
+
assert.equal(assistant?.parts[0]?.state, "failed");
|
|
170
|
+
assert.equal(assistant?.parts[0]?.error, "stream interrupted");
|
|
171
|
+
});
|
|
172
|
+
|
|
140
173
|
test("Turn 使用 step canonical chunks 保持 Tool 与最终正文顺序", async () => {
|
|
141
174
|
const { messages, turn } = await create_turn_harness(async (turn_context) => {
|
|
142
175
|
await turn_context.output.assistant.begin_step();
|
package/src/agent/Agent.ts
CHANGED
|
@@ -133,6 +133,13 @@ export class AgentSessions implements AgentSessionsContract<AgentSession> {
|
|
|
133
133
|
return this.list_executing_session_ids().length;
|
|
134
134
|
}
|
|
135
135
|
|
|
136
|
+
/** 释放全部缓存 Session 的标题后台任务。 */
|
|
137
|
+
dispose_title_generation(): void {
|
|
138
|
+
for (const session of this.sessions_by_id.values()) {
|
|
139
|
+
session.dispose_title_generation?.();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
136
143
|
/**
|
|
137
144
|
* 把 Agent env 修改广播到已有 Session 的统一输入队列。
|
|
138
145
|
*/
|
|
@@ -228,6 +235,7 @@ export class AgentSessions implements AgentSessionsContract<AgentSession> {
|
|
|
228
235
|
if (cached?.is_executing()) {
|
|
229
236
|
await cached.stop();
|
|
230
237
|
}
|
|
238
|
+
cached?.dispose_title_generation?.();
|
|
231
239
|
const existed = await this.store.remove_session(resolved_session_id);
|
|
232
240
|
this.sessions_by_id.delete(resolved_session_id);
|
|
233
241
|
return existed;
|
|
@@ -245,6 +253,7 @@ export class AgentSessions implements AgentSessionsContract<AgentSession> {
|
|
|
245
253
|
if (cached?.is_executing()) {
|
|
246
254
|
throw new Error(`Session "${resolved_session_id}" is currently executing`);
|
|
247
255
|
}
|
|
256
|
+
cached?.dispose_title_generation?.();
|
|
248
257
|
const existed = await this.store.clear_session_messages(resolved_session_id);
|
|
249
258
|
this.sessions_by_id.delete(resolved_session_id);
|
|
250
259
|
return existed;
|
|
@@ -279,6 +288,7 @@ export class AgentSessions implements AgentSessionsContract<AgentSession> {
|
|
|
279
288
|
}
|
|
280
289
|
|
|
281
290
|
const result = await this.store.archive_session(session_id);
|
|
291
|
+
this.sessions_by_id.get(session_id)?.dispose_title_generation?.();
|
|
282
292
|
this.sessions_by_id.delete(session_id);
|
|
283
293
|
return result;
|
|
284
294
|
}
|