@downcity/agent 1.1.320 → 1.1.324
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/tools/ask/AskQuestionsTool.js +1 -1
- package/bin/tools/ask/AskQuestionsTool.js.map +1 -1
- package/bin/tools/ask/AskQuestionsToolSchemas.d.ts.map +1 -1
- package/bin/tools/ask/AskQuestionsToolSchemas.js +4 -3
- package/bin/tools/ask/AskQuestionsToolSchemas.js.map +1 -1
- 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/tools/ask/AskQuestionsTool.ts +1 -1
- package/src/tools/ask/AskQuestionsToolSchemas.ts +8 -3
- 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
|
@@ -19,6 +19,28 @@ export type SessionUserMessagePart = UIMessagePart<UIDataTypes, UITools>;
|
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Session prompt 输入。
|
|
22
|
+
*
|
|
23
|
+
* 输入示例(中文):
|
|
24
|
+
* ```ts
|
|
25
|
+
* session.prompt({
|
|
26
|
+
* query: [
|
|
27
|
+
* { type: "text", text: "请分析这个附件" },
|
|
28
|
+
* {
|
|
29
|
+
* type: "file",
|
|
30
|
+
* mediaType: "image/png",
|
|
31
|
+
* url: "data:image/png;base64,...",
|
|
32
|
+
* filename: "image.png",
|
|
33
|
+
* },
|
|
34
|
+
* ],
|
|
35
|
+
* });
|
|
36
|
+
* ```
|
|
37
|
+
*
|
|
38
|
+
* 生命周期说明(中文):
|
|
39
|
+
* - `string` 会直接成为新的文本 User Message。
|
|
40
|
+
* - `file` part 如果携带 Data URL,会在 Message 入库前解码到 Session 的 attachments 目录。
|
|
41
|
+
* - 持久化 Message 里的 `url` 变成相对 Workspace 根目录的文件路径,而不是 Base64 内容。
|
|
42
|
+
* - 模型执行前,Executor 再按该路径读取文件,并转换成 AI SDK / Provider 可消费的 ModelMessage。
|
|
43
|
+
* - 远程 Session 中,Data URL 在服务端落盘;调用方本地路径必须对服务端可访问。
|
|
22
44
|
*/
|
|
23
45
|
export interface AgentSessionPromptInput {
|
|
24
46
|
/**
|
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { JsonObject } from "@/types/common/Json.js";
|
|
8
8
|
import type { SessionRecordV1 } from "@/executor/types/SessionRecords.js";
|
|
9
9
|
import type { SessionMessageStore } from "@/types/store/SessionStore.js";
|
|
10
|
+
import type { SessionAttachmentStore } from "@/types/store/SessionAttachmentStore.js";
|
|
10
11
|
import type { AgentSessionPromptInput } from "@/types/sdk/AgentSessionPrompt.js";
|
|
11
12
|
import type {
|
|
12
13
|
SessionAssistantMessagePart,
|
|
@@ -20,6 +21,8 @@ export interface SessionMessagesOptions {
|
|
|
20
21
|
session_id: string;
|
|
21
22
|
/** Message 快照持久化 Store。 */
|
|
22
23
|
store: SessionMessageStore;
|
|
24
|
+
/** 当前 Session 的附件持久化能力。 */
|
|
25
|
+
attachment_store: SessionAttachmentStore;
|
|
23
26
|
/** 持久化成功后的实时 Mutation 发布函数。 */
|
|
24
27
|
publish: (mutation: SessionMutation) => void;
|
|
25
28
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session 附件持久化能力。
|
|
3
|
+
*
|
|
4
|
+
* Session Message 只保存附件引用,附件内容由该 Store 在 Session 生命周期内持有。
|
|
5
|
+
*
|
|
6
|
+
* 数据边界(中文):
|
|
7
|
+
* - 输入边界:`session.prompt()` 接收 `type: "file"`、`mediaType` 和 Data URL。
|
|
8
|
+
* - 存储边界:Store 解码 Data URL,写入 `.downcity/.../attachments/att_<id>.<ext>`。
|
|
9
|
+
* - Message 边界:Session Message 只记录相对 Workspace 根目录的附件路径。
|
|
10
|
+
* - 模型边界:模型转换器读取该路径,在内存中恢复成模型需要的文件内容。
|
|
11
|
+
*
|
|
12
|
+
* 因此,Data URL 不会直接进入 JSONL Message;Session Message 是附件路径的唯一引用。
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/** 可持久化的 Data URL 附件。 */
|
|
16
|
+
export interface SessionAttachmentStore {
|
|
17
|
+
/**
|
|
18
|
+
* 保存 Data URL,并返回相对 Workspace 根目录的稳定文件路径。
|
|
19
|
+
* 调用成功后,返回路径对应的附件文件必须已经完整落盘。
|
|
20
|
+
*/
|
|
21
|
+
persist_data_url(input: {
|
|
22
|
+
/** 完整的 Data URL。 */
|
|
23
|
+
data_url: string;
|
|
24
|
+
/** 调用侧声明的 MIME 类型。 */
|
|
25
|
+
media_type: string;
|
|
26
|
+
/** 可选的原始文件名,仅用于推导扩展名。 */
|
|
27
|
+
filename?: string;
|
|
28
|
+
}): Promise<string>;
|
|
29
|
+
}
|
|
@@ -17,6 +17,7 @@ import type {
|
|
|
17
17
|
SessionSegmentSnapshot,
|
|
18
18
|
SessionSegmentSummary,
|
|
19
19
|
} from "@/types/session/SessionSegment.js";
|
|
20
|
+
import type { SessionAttachmentStore } from "@/types/store/SessionAttachmentStore.js";
|
|
20
21
|
|
|
21
22
|
/** 在 Message Store 写事务中创建新消息所需的稳定状态。 */
|
|
22
23
|
export interface SessionMessageCommitState {
|
|
@@ -88,6 +89,9 @@ export interface SessionStore {
|
|
|
88
89
|
/** 当前 Session 的 Message 持久化能力。 */
|
|
89
90
|
readonly messages: SessionMessageStore;
|
|
90
91
|
|
|
92
|
+
/** 当前 Session 的附件持久化能力。 */
|
|
93
|
+
readonly attachments: SessionAttachmentStore;
|
|
94
|
+
|
|
91
95
|
/** 读取 Session Metadata;不存在时返回规范化的初始值。 */
|
|
92
96
|
read_metadata(): Promise<SessionHistoryMetaV1>;
|
|
93
97
|
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Workspace 内 Session 附件的本地实现。
|
|
3
|
+
*
|
|
4
|
+
* Data URL 在这里解码为文件;Message 层只接收相对 Workspace 根目录的文件引用。
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import path from "node:path";
|
|
8
|
+
import { generate_id } from "@/utils/Id.js";
|
|
9
|
+
import type { FileSystem } from "@/types/workspace/FileSystem.js";
|
|
10
|
+
import type { SessionAttachmentStore } from "@/types/store/SessionAttachmentStore.js";
|
|
11
|
+
|
|
12
|
+
const DATA_URL_PATTERN = /^data:([^;,]+)?((?:;[^;,]+)*),(.*)$/s;
|
|
13
|
+
|
|
14
|
+
const MEDIA_TYPE_EXTENSIONS: Record<string, string> = {
|
|
15
|
+
"image/png": ".png",
|
|
16
|
+
"image/jpeg": ".jpg",
|
|
17
|
+
"image/webp": ".webp",
|
|
18
|
+
"image/gif": ".gif",
|
|
19
|
+
"video/mp4": ".mp4",
|
|
20
|
+
"video/quicktime": ".mov",
|
|
21
|
+
"video/webm": ".webm",
|
|
22
|
+
"application/pdf": ".pdf",
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export interface LocalSessionAttachmentStoreOptions {
|
|
26
|
+
/** Workspace 文件能力。 */
|
|
27
|
+
files: FileSystem;
|
|
28
|
+
/** 当前 Session 附件目录的绝对路径。 */
|
|
29
|
+
attachments_dir_path: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** Session 附件本地持久化实现。 */
|
|
33
|
+
export class LocalSessionAttachmentStore implements SessionAttachmentStore {
|
|
34
|
+
private readonly files: FileSystem;
|
|
35
|
+
private readonly attachments_dir_path: string;
|
|
36
|
+
|
|
37
|
+
constructor(options: LocalSessionAttachmentStoreOptions) {
|
|
38
|
+
this.files = options.files;
|
|
39
|
+
this.attachments_dir_path = path.resolve(options.attachments_dir_path);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async persist_data_url(input: {
|
|
43
|
+
data_url: string;
|
|
44
|
+
media_type: string;
|
|
45
|
+
filename?: string;
|
|
46
|
+
}): Promise<string> {
|
|
47
|
+
const parsed = parse_data_url(input.data_url, input.media_type);
|
|
48
|
+
const extension = resolve_extension(parsed.media_type, input.filename);
|
|
49
|
+
const attachment_name = `att_${generate_id()}${extension}`;
|
|
50
|
+
const absolute_path = path.join(this.attachments_dir_path, attachment_name);
|
|
51
|
+
await this.files.ensure_directory(this.attachments_dir_path);
|
|
52
|
+
await this.files.write_file_atomically(absolute_path, parsed.bytes);
|
|
53
|
+
return path.relative(this.files.root_path, absolute_path);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function parse_data_url(data_url: string, fallback_media_type: string): {
|
|
58
|
+
media_type: string;
|
|
59
|
+
bytes: Buffer;
|
|
60
|
+
} {
|
|
61
|
+
const match = DATA_URL_PATTERN.exec(String(data_url || "").trim());
|
|
62
|
+
if (!match) throw new Error("Invalid data URL");
|
|
63
|
+
|
|
64
|
+
const media_type = String(match[1] || fallback_media_type || "").trim();
|
|
65
|
+
if (!media_type || !media_type.includes("/")) {
|
|
66
|
+
throw new Error("Data URL requires a valid media type");
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const metadata = String(match[2] || "");
|
|
70
|
+
const payload = String(match[3] || "");
|
|
71
|
+
try {
|
|
72
|
+
const normalized_payload = payload.replace(/\s/g, "");
|
|
73
|
+
if (
|
|
74
|
+
metadata.includes(";base64") &&
|
|
75
|
+
(!/^[A-Za-z0-9+/]*={0,2}$/.test(normalized_payload) ||
|
|
76
|
+
normalized_payload.length % 4 === 1)
|
|
77
|
+
) {
|
|
78
|
+
throw new Error("invalid base64 payload");
|
|
79
|
+
}
|
|
80
|
+
return {
|
|
81
|
+
media_type,
|
|
82
|
+
bytes: metadata.includes(";base64")
|
|
83
|
+
? Buffer.from(normalized_payload, "base64")
|
|
84
|
+
: Buffer.from(decodeURIComponent(payload), "utf8"),
|
|
85
|
+
};
|
|
86
|
+
} catch (error) {
|
|
87
|
+
throw new Error(`Invalid data URL payload: ${error instanceof Error ? error.message : String(error)}`);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function resolve_extension(media_type: string, filename?: string): string {
|
|
92
|
+
const known = MEDIA_TYPE_EXTENSIONS[media_type.toLowerCase()];
|
|
93
|
+
if (known) return known;
|
|
94
|
+
const filename_extension = path.extname(String(filename || "")).toLowerCase();
|
|
95
|
+
return /^[.][a-z0-9]{1,10}$/.test(filename_extension) ? filename_extension : ".bin";
|
|
96
|
+
}
|
|
@@ -12,12 +12,14 @@ import {
|
|
|
12
12
|
get_sdk_agent_session_meta_path,
|
|
13
13
|
get_sdk_agent_session_assistant_message_path,
|
|
14
14
|
get_sdk_agent_session_messages_path,
|
|
15
|
+
get_sdk_agent_session_attachments_dir_path,
|
|
15
16
|
} from "@/workspace/store/LocalStorePaths.js";
|
|
16
17
|
import { normalize_session_metadata } from "@/session/storage/Metadata.js";
|
|
17
18
|
import type { SessionHistoryMetaV1 } from "@/executor/types/SessionHistoryMeta.js";
|
|
18
19
|
import type { SessionStore } from "@/types/store/SessionStore.js";
|
|
19
20
|
import type { FileSystem } from "@/types/workspace/FileSystem.js";
|
|
20
21
|
import type { LocalSessionStoreOptions } from "@/types/store/LocalStore.js";
|
|
22
|
+
import { LocalSessionAttachmentStore } from "@/workspace/store/LocalSessionAttachmentStore.js";
|
|
21
23
|
|
|
22
24
|
/** 本地 Session Store。 */
|
|
23
25
|
export class LocalSessionStore implements SessionStore {
|
|
@@ -27,6 +29,9 @@ export class LocalSessionStore implements SessionStore {
|
|
|
27
29
|
/** 当前 Session 的 JSONL Message Store。 */
|
|
28
30
|
readonly messages: JsonlSessionMessageStore;
|
|
29
31
|
|
|
32
|
+
/** 当前 Session 的附件持久化能力。 */
|
|
33
|
+
readonly attachments: LocalSessionAttachmentStore;
|
|
34
|
+
|
|
30
35
|
/** 当前 Store 与 WorkspaceTools 共用的 Workspace 文件能力。 */
|
|
31
36
|
private readonly files: FileSystem;
|
|
32
37
|
|
|
@@ -51,6 +56,14 @@ export class LocalSessionStore implements SessionStore {
|
|
|
51
56
|
this.session_id,
|
|
52
57
|
),
|
|
53
58
|
});
|
|
59
|
+
this.attachments = new LocalSessionAttachmentStore({
|
|
60
|
+
files: this.files,
|
|
61
|
+
attachments_dir_path: get_sdk_agent_session_attachments_dir_path(
|
|
62
|
+
this.files.root_path,
|
|
63
|
+
this.agent_id,
|
|
64
|
+
this.session_id,
|
|
65
|
+
),
|
|
66
|
+
});
|
|
54
67
|
}
|
|
55
68
|
|
|
56
69
|
/** 读取规范化 Session Metadata。 */
|
|
@@ -161,6 +161,22 @@ export function get_sdk_agent_session_messages_dir_path(
|
|
|
161
161
|
);
|
|
162
162
|
}
|
|
163
163
|
|
|
164
|
+
/**
|
|
165
|
+
* 单个 Session 的附件目录路径。
|
|
166
|
+
*
|
|
167
|
+
* 附件与 Session 目录同生命周期,归档或删除 Session 时随目录一起迁移或清理。
|
|
168
|
+
*/
|
|
169
|
+
export function get_sdk_agent_session_attachments_dir_path(
|
|
170
|
+
workspace_path: string,
|
|
171
|
+
agent_id: string,
|
|
172
|
+
session_id: string,
|
|
173
|
+
): string {
|
|
174
|
+
return path.join(
|
|
175
|
+
get_sdk_agent_session_dir_path(workspace_path, agent_id, session_id),
|
|
176
|
+
"attachments",
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
|
|
164
180
|
/**
|
|
165
181
|
* 单个 session 的 Active JSONL 文件路径。
|
|
166
182
|
*/
|