@yefengr/pi-reach 0.0.6 → 0.0.8
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/README.md +62 -48
- package/dist/files/content-type.d.ts +16 -0
- package/dist/files/content-type.js +98 -0
- package/dist/files/content-type.js.map +1 -0
- package/dist/files/image-structure.d.ts +21 -0
- package/dist/files/image-structure.js +269 -0
- package/dist/files/image-structure.js.map +1 -0
- package/dist/files/publications.d.ts +26 -0
- package/dist/files/publications.js +136 -0
- package/dist/files/publications.js.map +1 -0
- package/dist/files/publish-tool.d.ts +23 -0
- package/dist/files/publish-tool.js +70 -0
- package/dist/files/publish-tool.js.map +1 -0
- package/dist/files/reader.d.ts +66 -0
- package/dist/files/reader.js +416 -0
- package/dist/files/reader.js.map +1 -0
- package/dist/files/safe-open.d.ts +22 -0
- package/dist/files/safe-open.js +119 -0
- package/dist/files/safe-open.js.map +1 -0
- package/dist/index.js +12 -17
- package/dist/index.js.map +1 -1
- package/dist/runtime/file_binding.d.ts +25 -0
- package/dist/runtime/file_binding.js +49 -0
- package/dist/runtime/file_binding.js.map +1 -0
- package/dist/runtime/session_frame_router.d.ts +11 -0
- package/dist/runtime/session_frame_router.js +25 -0
- package/dist/runtime/session_frame_router.js.map +1 -0
- package/dist/timeline/runtime.d.ts +1 -1
- package/dist/timeline/runtime.js +26 -54
- package/dist/timeline/runtime.js.map +1 -1
- package/dist/timeline/system_event.d.ts +7 -0
- package/dist/timeline/system_event.js +58 -0
- package/dist/timeline/system_event.js.map +1 -0
- package/dist/timeline/tool_lifecycle.js +8 -2
- package/dist/timeline/tool_lifecycle.js.map +1 -1
- package/dist/timeline/v2_service.js +3 -0
- package/dist/timeline/v2_service.js.map +1 -1
- package/dist/vendor/protocol/session/files.d.ts +83 -0
- package/dist/vendor/protocol/session/files.js +162 -0
- package/dist/vendor/protocol/session/frames.d.ts +293 -18
- package/dist/vendor/protocol/session/frames.js +52 -2
- package/dist/vendor/protocol/session/index.d.ts +1 -0
- package/dist/vendor/protocol/session/index.js +1 -0
- package/dist/vendor/protocol/session/schema.d.ts +2 -2
- package/dist/vendor/protocol/session/schema.js +2 -0
- package/package.json +5 -4
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import { readFileSync, statSync } from "node:fs";
|
|
2
|
+
import { isAbsolute, resolve } from "node:path";
|
|
3
|
+
import { isDeepStrictEqual } from "node:util";
|
|
4
|
+
import { parseSessionEntries } from "@earendil-works/pi-coding-agent";
|
|
5
|
+
import { idSchema, PUBLISHED_FILE_TYPE, publishedFileMetadataSchema } from "../vendor/protocol/session/index.js";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
export const PUBLISHED_FILE_TOOL_NAME = "publish_file";
|
|
8
|
+
/** 只存于原生 custom;不能把此 schema 当作 wire payload。 */
|
|
9
|
+
export const publicationDataSchema = publishedFileMetadataSchema.extend({
|
|
10
|
+
source_path: z.string().min(1).refine((path) => !path.includes("\0") && isAbsolute(path) && resolve(path) === path),
|
|
11
|
+
group_id: idSchema.optional(),
|
|
12
|
+
}).strict();
|
|
13
|
+
const resultDetailsSchema = z.object({ publication_id: idSchema }).strict();
|
|
14
|
+
// 一个 manager 最多保留一个文件的解析结果;manager 回收时缓存随之回收。
|
|
15
|
+
const diskProofs = new WeakMap();
|
|
16
|
+
function sameFile(left, right) {
|
|
17
|
+
return left.dev === right.dev && left.ino === right.ino && left.size === right.size
|
|
18
|
+
&& left.mtimeNs === right.mtimeNs && left.ctimeNs === right.ctimeNs;
|
|
19
|
+
}
|
|
20
|
+
function diskEntries(manager) {
|
|
21
|
+
try {
|
|
22
|
+
const path = manager.getSessionFile();
|
|
23
|
+
if (!path)
|
|
24
|
+
return null;
|
|
25
|
+
const before = statSync(path, { bigint: true });
|
|
26
|
+
if (!before.isFile())
|
|
27
|
+
return null;
|
|
28
|
+
const cached = diskProofs.get(manager);
|
|
29
|
+
if (cached?.path === path && sameFile(cached.stat, before))
|
|
30
|
+
return cached.entries;
|
|
31
|
+
const entries = parseSessionEntries(readFileSync(path, "utf8"));
|
|
32
|
+
const after = statSync(path, { bigint: true });
|
|
33
|
+
if (!after.isFile() || !sameFile(before, after))
|
|
34
|
+
return null;
|
|
35
|
+
diskProofs.set(manager, { path, stat: after, entries });
|
|
36
|
+
return entries;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
diskProofs.delete(manager);
|
|
40
|
+
return null;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
function record(value) {
|
|
44
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
45
|
+
}
|
|
46
|
+
/** JSONL 不存 undefined 字段;比较完整原生记录而非只比较 publication_id。 */
|
|
47
|
+
function samePersistedEntry(current, persisted) {
|
|
48
|
+
return isDeepStrictEqual(JSON.parse(JSON.stringify(current)), persisted);
|
|
49
|
+
}
|
|
50
|
+
function addByCall(map, callId, value) {
|
|
51
|
+
if (typeof callId !== "string")
|
|
52
|
+
return;
|
|
53
|
+
const matches = map.get(callId) ?? [];
|
|
54
|
+
matches.push(value);
|
|
55
|
+
map.set(callId, matches);
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* 原生成功 toolResult 是确认点,且 custom/result 两者均须已落盘。
|
|
59
|
+
* 失败 append 留下的内部孤立记录无资格;不尝试回滚 SDK 的 append-only 树。
|
|
60
|
+
*/
|
|
61
|
+
export function collectPublications(manager) {
|
|
62
|
+
const confirmed = new Map();
|
|
63
|
+
try {
|
|
64
|
+
const persisted = diskEntries(manager);
|
|
65
|
+
if (!persisted)
|
|
66
|
+
return confirmed;
|
|
67
|
+
const headers = persisted.filter((entry) => record(entry) && entry.type === "session");
|
|
68
|
+
const header = manager.getHeader();
|
|
69
|
+
if (headers.length !== 1 || persisted[0] !== headers[0] || !header
|
|
70
|
+
|| header.id !== manager.getSessionId() || !samePersistedEntry(header, headers[0]))
|
|
71
|
+
return confirmed;
|
|
72
|
+
const diskById = new Map();
|
|
73
|
+
for (const entry of persisted) {
|
|
74
|
+
if (!record(entry) || !idSchema.safeParse(entry.id).success || diskById.has(entry.id))
|
|
75
|
+
return confirmed;
|
|
76
|
+
diskById.set(entry.id, entry);
|
|
77
|
+
}
|
|
78
|
+
const branch = manager.getBranch();
|
|
79
|
+
const customs = new Map();
|
|
80
|
+
const results = new Map();
|
|
81
|
+
const branchIds = new Set();
|
|
82
|
+
const persistedPrefixes = [];
|
|
83
|
+
let persistedPrefix = true;
|
|
84
|
+
for (let index = 0; index < branch.length; index++) {
|
|
85
|
+
const entry = branch[index];
|
|
86
|
+
if (branchIds.has(entry.id) || entry.parentId !== (index === 0 ? null : branch[index - 1].id))
|
|
87
|
+
return confirmed;
|
|
88
|
+
branchIds.add(entry.id);
|
|
89
|
+
const diskEntry = diskById.get(entry.id);
|
|
90
|
+
persistedPrefix = persistedPrefix && diskEntry?.type === entry.type
|
|
91
|
+
&& "parentId" in diskEntry && diskEntry.parentId === entry.parentId;
|
|
92
|
+
persistedPrefixes.push(persistedPrefix);
|
|
93
|
+
if (entry.type === "custom" && entry.customType === PUBLISHED_FILE_TYPE) {
|
|
94
|
+
addByCall(customs, record(entry.data) ? entry.data.tool_call_id : undefined, { entry, index });
|
|
95
|
+
}
|
|
96
|
+
else if (entry.type === "message" && entry.message.role === "toolResult" && entry.message.toolName === PUBLISHED_FILE_TOOL_NAME) {
|
|
97
|
+
addByCall(results, entry.message.toolCallId, { entry, index });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const candidates = [];
|
|
101
|
+
for (const [callId, matches] of customs) {
|
|
102
|
+
const tools = results.get(callId);
|
|
103
|
+
if (matches.length !== 1 || tools?.length !== 1)
|
|
104
|
+
continue;
|
|
105
|
+
const custom = matches[0];
|
|
106
|
+
const result = tools[0];
|
|
107
|
+
const data = publicationDataSchema.safeParse(custom.entry.data);
|
|
108
|
+
const message = result.entry.message;
|
|
109
|
+
if (!data.success || message.role !== "toolResult" || message.isError !== false
|
|
110
|
+
|| result.index <= custom.index || !persistedPrefixes[result.index])
|
|
111
|
+
continue;
|
|
112
|
+
const details = resultDetailsSchema.safeParse(message.details);
|
|
113
|
+
const timestamp = Date.parse(custom.entry.timestamp);
|
|
114
|
+
if (!details.success || details.data.publication_id !== custom.entry.id || !Number.isFinite(timestamp) || timestamp < 0)
|
|
115
|
+
continue;
|
|
116
|
+
if (!samePersistedEntry(custom.entry, diskById.get(custom.entry.id))
|
|
117
|
+
|| !samePersistedEntry(result.entry, diskById.get(result.entry.id)))
|
|
118
|
+
continue;
|
|
119
|
+
const { source_path, group_id, ...metadata } = data.data;
|
|
120
|
+
candidates.push({
|
|
121
|
+
id: custom.entry.id, sourcePath: source_path, metadata, timestamp,
|
|
122
|
+
...(group_id === undefined ? {} : { groupId: group_id }),
|
|
123
|
+
branchPosition: result.index + 0.5,
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
candidates.sort((left, right) => left.branchPosition - right.branchPosition);
|
|
127
|
+
for (const candidate of candidates)
|
|
128
|
+
confirmed.set(candidate.id, candidate);
|
|
129
|
+
return confirmed;
|
|
130
|
+
}
|
|
131
|
+
catch {
|
|
132
|
+
// 不可信或无法读取的原生记录只能拒绝资格,不能降级为内存证明。
|
|
133
|
+
return new Map();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
//# sourceMappingURL=publications.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publications.js","sourceRoot":"","sources":["../../src/files/publications.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { SessionManager, ToolDefinition } from "@earendil-works/pi-coding-agent";
|
|
2
|
+
import { Type } from "typebox";
|
|
3
|
+
declare const parameters: Type.TObject<{
|
|
4
|
+
path: Type.TString;
|
|
5
|
+
}>;
|
|
6
|
+
export type PublishFileToolOptions = {
|
|
7
|
+
getManager: () => SessionManager | null;
|
|
8
|
+
isCurrent: () => boolean;
|
|
9
|
+
getGeneration: () => number;
|
|
10
|
+
getGroupId: () => string | null;
|
|
11
|
+
/** 仅在共享资源账目中完成 inspection 并关闭句柄后 resolve。 */
|
|
12
|
+
inspect: (absolutePath: string) => Promise<{
|
|
13
|
+
sourcePath: string;
|
|
14
|
+
fileName: string;
|
|
15
|
+
mimeType: string;
|
|
16
|
+
byteLength: number;
|
|
17
|
+
}>;
|
|
18
|
+
};
|
|
19
|
+
export type PublishFileToolDetails = {
|
|
20
|
+
publication_id: string;
|
|
21
|
+
};
|
|
22
|
+
export declare function createPublishFileTool(options: PublishFileToolOptions): ToolDefinition<typeof parameters, PublishFileToolDetails>;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
import { resolve } from "node:path";
|
|
2
|
+
import { statSync } from "node:fs";
|
|
3
|
+
import { PUBLISHED_FILE_TYPE } from "../vendor/protocol/session/index.js";
|
|
4
|
+
import { Type } from "typebox";
|
|
5
|
+
import { publicationDataSchema, PUBLISHED_FILE_TOOL_NAME } from "./publications.js";
|
|
6
|
+
const parameters = Type.Object({ path: Type.String({ minLength: 1 }) }, { additionalProperties: false });
|
|
7
|
+
const FAILURE_TEXT = "文件发布失败";
|
|
8
|
+
function storedSession(manager, file) {
|
|
9
|
+
try {
|
|
10
|
+
return manager.isPersisted() && !!file && statSync(file).isFile();
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return false;
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function createPublishFileTool(options) {
|
|
17
|
+
// SDK sequential 是调度约束;此门禁也阻止意外重入或嵌套直接调用。
|
|
18
|
+
let executing = false;
|
|
19
|
+
return {
|
|
20
|
+
name: PUBLISHED_FILE_TOOL_NAME,
|
|
21
|
+
label: "发布文件",
|
|
22
|
+
description: "仅发布面向用户交付的成果,或用户明确要求的文件;不要自动发布所有读取、修改或临时文件。已授权 PWA 可能自动获取受支持的小图片。path 相对当前工作目录或为绝对路径,允许项目外的普通文件;只保存引用,不复制原件。",
|
|
23
|
+
parameters,
|
|
24
|
+
executionMode: "sequential",
|
|
25
|
+
async execute(toolCallId, args, signal, _onUpdate, ctx) {
|
|
26
|
+
if (executing)
|
|
27
|
+
throw new Error(FAILURE_TEXT);
|
|
28
|
+
executing = true;
|
|
29
|
+
try {
|
|
30
|
+
const manager = options.getManager();
|
|
31
|
+
const generation = options.getGeneration();
|
|
32
|
+
const groupId = options.getGroupId();
|
|
33
|
+
if (!manager)
|
|
34
|
+
throw new Error(FAILURE_TEXT);
|
|
35
|
+
const sessionId = manager.getSessionId();
|
|
36
|
+
const sessionFile = manager.getSessionFile();
|
|
37
|
+
const valid = () => options.isCurrent() && !signal?.aborted && !ctx.signal?.aborted
|
|
38
|
+
&& options.getGeneration() === generation && options.getManager() === manager
|
|
39
|
+
&& ctx.sessionManager === manager && ctx.sessionManager.getSessionId() === sessionId
|
|
40
|
+
&& manager.getSessionId() === sessionId && manager.getSessionFile() === sessionFile
|
|
41
|
+
&& storedSession(manager, sessionFile);
|
|
42
|
+
if (!valid() || typeof args.path !== "string" || !args.path || args.path.includes("\0"))
|
|
43
|
+
throw new Error(FAILURE_TEXT);
|
|
44
|
+
const inspected = await options.inspect(resolve(ctx.cwd, args.path));
|
|
45
|
+
const data = publicationDataSchema.parse({
|
|
46
|
+
source_path: inspected.sourcePath,
|
|
47
|
+
file_name: inspected.fileName,
|
|
48
|
+
mime_type: inspected.mimeType,
|
|
49
|
+
byte_length: inspected.byteLength,
|
|
50
|
+
tool_call_id: toolCallId,
|
|
51
|
+
...(groupId === null ? {} : { group_id: groupId }),
|
|
52
|
+
});
|
|
53
|
+
if (!valid())
|
|
54
|
+
throw new Error(FAILURE_TEXT);
|
|
55
|
+
const text = `已发布文件引用:${data.file_name} · ${data.mime_type} · ${data.byte_length} B`;
|
|
56
|
+
const publicationId = manager.appendCustomEntry(PUBLISHED_FILE_TYPE, data);
|
|
57
|
+
// append 后没有可失败的 await;正式资格由随后落盘的成功原生 toolResult 建立。
|
|
58
|
+
return { content: [{ type: "text", text }], details: { publication_id: publicationId } };
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
// SDK 会将 throw 转为 isError;不把内部路径或 I/O 异常暴露给消息与日志。
|
|
62
|
+
throw new Error(FAILURE_TEXT);
|
|
63
|
+
}
|
|
64
|
+
finally {
|
|
65
|
+
executing = false;
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
//# sourceMappingURL=publish-tool.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"publish-tool.js","sourceRoot":"","sources":["../../src/files/publish-tool.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { type ClientFrame, type ServerFrame } from "../vendor/protocol/session/index.js";
|
|
2
|
+
export type FileScope = {
|
|
3
|
+
ownerId: string;
|
|
4
|
+
channelId: string;
|
|
5
|
+
runtimeId: string;
|
|
6
|
+
sessionId: string;
|
|
7
|
+
generation: number;
|
|
8
|
+
};
|
|
9
|
+
type Request = Extract<ClientFrame, {
|
|
10
|
+
type: "file_open" | "file_read" | "file_close";
|
|
11
|
+
}>;
|
|
12
|
+
type Response = Extract<ServerFrame, {
|
|
13
|
+
type: "file_opened" | "file_chunk" | "file_closed" | "file_error";
|
|
14
|
+
}>;
|
|
15
|
+
type Options = {
|
|
16
|
+
resolve: (scope: FileScope, publicationId: string) => {
|
|
17
|
+
sourcePath: string;
|
|
18
|
+
} | null | Promise<{
|
|
19
|
+
sourcePath: string;
|
|
20
|
+
} | null>;
|
|
21
|
+
isCurrent: (scope: FileScope) => boolean;
|
|
22
|
+
idleMs?: number;
|
|
23
|
+
maxResources?: number;
|
|
24
|
+
};
|
|
25
|
+
/** 所有异步任务共享额度;失效不等于内核 I/O 已停止或句柄已关闭。 */
|
|
26
|
+
export declare class FileReaderRuntime {
|
|
27
|
+
private readonly options;
|
|
28
|
+
private readonly resources;
|
|
29
|
+
private readonly opens;
|
|
30
|
+
private readonly idleMs;
|
|
31
|
+
private readonly maxResources;
|
|
32
|
+
private timer?;
|
|
33
|
+
private timerDeadline?;
|
|
34
|
+
private disposed;
|
|
35
|
+
constructor(options: Options);
|
|
36
|
+
get resourceCount(): number;
|
|
37
|
+
private reserve;
|
|
38
|
+
private schedule;
|
|
39
|
+
private invalidate;
|
|
40
|
+
private assertCurrent;
|
|
41
|
+
private eligible;
|
|
42
|
+
private checked;
|
|
43
|
+
private release;
|
|
44
|
+
private closeHandle;
|
|
45
|
+
private cleanup;
|
|
46
|
+
private finish;
|
|
47
|
+
private reply;
|
|
48
|
+
private failure;
|
|
49
|
+
handle(frame: Request, suppliedScope: FileScope): Promise<Response>;
|
|
50
|
+
private open;
|
|
51
|
+
private openResponse;
|
|
52
|
+
private openTask;
|
|
53
|
+
private verify;
|
|
54
|
+
private readTask;
|
|
55
|
+
private read;
|
|
56
|
+
inspectForPublication(path: string): Promise<{
|
|
57
|
+
sourcePath: string;
|
|
58
|
+
fileName: string;
|
|
59
|
+
mimeType: string;
|
|
60
|
+
byteLength: number;
|
|
61
|
+
}>;
|
|
62
|
+
private inspectTask;
|
|
63
|
+
closeWhere(predicate: (scope: FileScope) => boolean): Promise<void>;
|
|
64
|
+
dispose(): Promise<void>;
|
|
65
|
+
}
|
|
66
|
+
export {};
|