@agentex/agent 0.0.28 → 0.0.30
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/CHANGELOG.md +42 -0
- package/README.md +40 -1
- package/dist/history/fs.d.ts +36 -0
- package/dist/history/fs.d.ts.map +1 -0
- package/dist/history/fs.js +266 -0
- package/dist/history/fs.js.map +1 -0
- package/dist/history/index.d.ts +3 -0
- package/dist/history/index.d.ts.map +1 -0
- package/dist/history/index.js +2 -0
- package/dist/history/index.js.map +1 -0
- package/dist/history/types.d.ts +75 -0
- package/dist/history/types.d.ts.map +1 -0
- package/dist/history/types.js +9 -0
- package/dist/history/types.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/providers/claude/history.d.ts +3 -0
- package/dist/providers/claude/history.d.ts.map +1 -0
- package/dist/providers/claude/history.js +248 -0
- package/dist/providers/claude/history.js.map +1 -0
- package/dist/providers/claude/index.d.ts.map +1 -1
- package/dist/providers/claude/index.js +28 -0
- package/dist/providers/claude/index.js.map +1 -1
- package/dist/providers/codex/attach.d.ts +2 -2
- package/dist/providers/codex/attach.js +4 -4
- package/dist/providers/codex/attach.js.map +1 -1
- package/dist/providers/codex/history.d.ts +3 -0
- package/dist/providers/codex/history.d.ts.map +1 -0
- package/dist/providers/codex/history.js +350 -0
- package/dist/providers/codex/history.js.map +1 -0
- package/dist/providers/codex/index.d.ts.map +1 -1
- package/dist/providers/codex/index.js +28 -0
- package/dist/providers/codex/index.js.map +1 -1
- package/dist/providers/codex/session.d.ts.map +1 -1
- package/dist/providers/codex/session.js +6 -4
- package/dist/providers/codex/session.js.map +1 -1
- package/dist/providers/codex/transcript-normalize.d.ts +3 -2
- package/dist/providers/codex/transcript-normalize.d.ts.map +1 -1
- package/dist/providers/codex/transcript-normalize.js +4 -3
- package/dist/providers/codex/transcript-normalize.js.map +1 -1
- package/dist/types.d.ts +5 -1
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/package.json +7 -1
- package/src/history/fs.ts +307 -0
- package/src/history/index.ts +18 -0
- package/src/history/types.ts +104 -0
- package/src/index.ts +18 -0
- package/src/providers/claude/history.ts +290 -0
- package/src/providers/claude/index.ts +28 -0
- package/src/providers/codex/attach.ts +4 -4
- package/src/providers/codex/history.ts +399 -0
- package/src/providers/codex/index.ts +28 -0
- package/src/providers/codex/session.ts +5 -4
- package/src/providers/codex/transcript-normalize.ts +4 -3
- package/src/types.ts +5 -1
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { readdir, stat, open as openFile, type FileHandle } from "node:fs/promises";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import * as readline from "node:readline";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
LocalHistoryError,
|
|
8
|
+
type LocalHistorySourceFingerprint,
|
|
9
|
+
} from "./types.js";
|
|
10
|
+
|
|
11
|
+
export const UUID_JSONL_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}\.jsonl$/i;
|
|
12
|
+
export const ROLLOUT_UUID_RE = /-([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i;
|
|
13
|
+
|
|
14
|
+
export interface JsonlRecord {
|
|
15
|
+
raw: Record<string, unknown>;
|
|
16
|
+
text: string;
|
|
17
|
+
lineStartOffset: number;
|
|
18
|
+
nextOffset: number;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface StableReadOptions {
|
|
22
|
+
/** Reject a completed read when the opened source changed while streaming. */
|
|
23
|
+
rejectChanges?: boolean;
|
|
24
|
+
/** Internal deterministic test hook. Not part of the public history API. */
|
|
25
|
+
afterInitialStat?: () => void | Promise<void>;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
interface FileIdentity {
|
|
29
|
+
size: bigint;
|
|
30
|
+
modifiedAtNs: bigint;
|
|
31
|
+
device: bigint;
|
|
32
|
+
inode: bigint;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export function asRecord(value: unknown): Record<string, unknown> | null {
|
|
36
|
+
return typeof value === "object" && value !== null && !Array.isArray(value)
|
|
37
|
+
? value as Record<string, unknown>
|
|
38
|
+
: null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function asString(value: unknown): string | null {
|
|
42
|
+
return typeof value === "string" && value.trim().length > 0 ? value : null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function isoDate(value: unknown, fallback: string | null = null): string | null {
|
|
46
|
+
if (typeof value !== "string") return fallback;
|
|
47
|
+
const parsed = new Date(value);
|
|
48
|
+
return Number.isNaN(parsed.getTime()) ? fallback : parsed.toISOString();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function cleanTitle(value: string | null): string | null {
|
|
52
|
+
const cleaned = value
|
|
53
|
+
?.replace(/<[^>]+>/g, " ")
|
|
54
|
+
.replace(/\[\[[^\]]+\]\]/g, " ")
|
|
55
|
+
.replace(/^[#>*_`\s-]+/g, "")
|
|
56
|
+
.replace(/\s+/g, " ")
|
|
57
|
+
.trim();
|
|
58
|
+
if (!cleaned) return null;
|
|
59
|
+
return cleaned.length > 120 ? `${cleaned.slice(0, 117).trimEnd()}...` : cleaned;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export async function isDirectory(filePath: string): Promise<boolean> {
|
|
63
|
+
try {
|
|
64
|
+
return (await stat(filePath)).isDirectory();
|
|
65
|
+
} catch {
|
|
66
|
+
return false;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export async function listJsonlFiles(
|
|
71
|
+
root: string,
|
|
72
|
+
options: { maxDepth: number; limit?: number; accept?: (name: string) => boolean },
|
|
73
|
+
): Promise<string[]> {
|
|
74
|
+
const files: string[] = [];
|
|
75
|
+
const limit = Math.max(1, options.limit ?? Number.MAX_SAFE_INTEGER);
|
|
76
|
+
|
|
77
|
+
async function visit(dir: string, depth: number): Promise<void> {
|
|
78
|
+
if (depth > options.maxDepth || files.length >= limit) return;
|
|
79
|
+
let entries;
|
|
80
|
+
try {
|
|
81
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
82
|
+
} catch {
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
entries.sort((a, b) => a.name.localeCompare(b.name));
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
if (files.length >= limit) break;
|
|
88
|
+
const child = path.join(dir, entry.name);
|
|
89
|
+
if (entry.isDirectory()) await visit(child, depth + 1);
|
|
90
|
+
else if (entry.isFile() && entry.name.endsWith(".jsonl") && (options.accept?.(entry.name) ?? true)) {
|
|
91
|
+
files.push(child);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
await visit(root, 0);
|
|
97
|
+
return files;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function mapLimited<T, U>(
|
|
101
|
+
values: readonly T[],
|
|
102
|
+
concurrency: number,
|
|
103
|
+
mapper: (value: T) => Promise<U>,
|
|
104
|
+
): Promise<U[]> {
|
|
105
|
+
const output = new Array<U>(values.length);
|
|
106
|
+
let cursor = 0;
|
|
107
|
+
async function worker(): Promise<void> {
|
|
108
|
+
while (cursor < values.length) {
|
|
109
|
+
const index = cursor++;
|
|
110
|
+
output[index] = await mapper(values[index]!);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
await Promise.all(Array.from(
|
|
114
|
+
{ length: Math.min(Math.max(1, concurrency), values.length) },
|
|
115
|
+
() => worker(),
|
|
116
|
+
));
|
|
117
|
+
return output;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
async function fileIdentity(handle: FileHandle): Promise<FileIdentity> {
|
|
121
|
+
const info = await handle.stat({ bigint: true });
|
|
122
|
+
if (!info.isFile()) throw new LocalHistoryError("invalid_session", "Local history source is not a file");
|
|
123
|
+
return {
|
|
124
|
+
size: info.size,
|
|
125
|
+
modifiedAtNs: info.mtimeNs,
|
|
126
|
+
device: info.dev,
|
|
127
|
+
inode: info.ino,
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function sameFileIdentity(left: FileIdentity, right: FileIdentity): boolean {
|
|
132
|
+
return left.size === right.size
|
|
133
|
+
&& left.modifiedAtNs === right.modifiedAtNs
|
|
134
|
+
&& left.device === right.device
|
|
135
|
+
&& left.inode === right.inode;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function sourceChangedError(): LocalHistoryError {
|
|
139
|
+
return new LocalHistoryError(
|
|
140
|
+
"source_changed_during_read",
|
|
141
|
+
"Local history source changed during the read",
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function wrapFileError(error: unknown, action: "inspect" | "read" | "hash"): LocalHistoryError {
|
|
146
|
+
if (error instanceof LocalHistoryError) return error;
|
|
147
|
+
const code = (error as NodeJS.ErrnoException).code;
|
|
148
|
+
return new LocalHistoryError(
|
|
149
|
+
code === "ENOENT" ? "source_missing" : code === "EACCES" ? "permission_denied" : "io_error",
|
|
150
|
+
code === "ENOENT"
|
|
151
|
+
? "Local history source is missing"
|
|
152
|
+
: `Unable to ${action} local history source`,
|
|
153
|
+
{ cause: error },
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function recentEligible<T>(
|
|
158
|
+
filePaths: readonly string[],
|
|
159
|
+
options: { limit?: number; concurrency?: number },
|
|
160
|
+
inspect: (filePath: string) => Promise<T | null>,
|
|
161
|
+
): Promise<T[]> {
|
|
162
|
+
const limit = options.limit === undefined
|
|
163
|
+
? Number.MAX_SAFE_INTEGER
|
|
164
|
+
: Math.max(0, options.limit);
|
|
165
|
+
if (limit === 0 || filePaths.length === 0) return [];
|
|
166
|
+
|
|
167
|
+
const metadata = await mapLimited(filePaths, 32, async (filePath) => {
|
|
168
|
+
try {
|
|
169
|
+
const info = await stat(filePath, { bigint: true });
|
|
170
|
+
return info.isFile() ? { filePath, modifiedAtNs: info.mtimeNs } : null;
|
|
171
|
+
} catch {
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
});
|
|
175
|
+
const ordered = metadata
|
|
176
|
+
.filter((value): value is { filePath: string; modifiedAtNs: bigint } => value !== null)
|
|
177
|
+
.sort((a, b) => a.modifiedAtNs === b.modifiedAtNs
|
|
178
|
+
? a.filePath.localeCompare(b.filePath)
|
|
179
|
+
: a.modifiedAtNs > b.modifiedAtNs ? -1 : 1);
|
|
180
|
+
|
|
181
|
+
const output: T[] = [];
|
|
182
|
+
const concurrency = Math.max(1, options.concurrency ?? 16);
|
|
183
|
+
let cursor = 0;
|
|
184
|
+
while (cursor < ordered.length && output.length < limit) {
|
|
185
|
+
// Never inspect more files than the number of results still requested in
|
|
186
|
+
// one batch. Excluded files cause another bounded batch to be pulled.
|
|
187
|
+
const batchSize = Math.min(concurrency, limit - output.length, ordered.length - cursor);
|
|
188
|
+
const batch = ordered.slice(cursor, cursor + batchSize);
|
|
189
|
+
cursor += batch.length;
|
|
190
|
+
const inspected = await mapLimited(batch, concurrency, ({ filePath }) => inspect(filePath));
|
|
191
|
+
for (const value of inspected) {
|
|
192
|
+
if (value !== null) output.push(value);
|
|
193
|
+
if (output.length >= limit) break;
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return output;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export async function sourceFingerprint(
|
|
200
|
+
filePath: string,
|
|
201
|
+
includeSha256 = false,
|
|
202
|
+
internalOptions: StableReadOptions = {},
|
|
203
|
+
): Promise<LocalHistorySourceFingerprint> {
|
|
204
|
+
let handle: FileHandle | null = null;
|
|
205
|
+
try {
|
|
206
|
+
handle = await openFile(filePath, "r");
|
|
207
|
+
const before = await fileIdentity(handle);
|
|
208
|
+
const fingerprint: LocalHistorySourceFingerprint = {
|
|
209
|
+
size: Number(before.size),
|
|
210
|
+
modifiedAtNs: before.modifiedAtNs.toString(),
|
|
211
|
+
};
|
|
212
|
+
if (!includeSha256) return fingerprint;
|
|
213
|
+
|
|
214
|
+
await internalOptions.afterInitialStat?.();
|
|
215
|
+
const hash = createHash("sha256");
|
|
216
|
+
const stream = handle.createReadStream({ autoClose: false });
|
|
217
|
+
let after: FileIdentity;
|
|
218
|
+
try {
|
|
219
|
+
for await (const chunk of stream) hash.update(chunk as Buffer);
|
|
220
|
+
after = await fileIdentity(handle);
|
|
221
|
+
} finally {
|
|
222
|
+
stream.destroy();
|
|
223
|
+
}
|
|
224
|
+
if (!sameFileIdentity(before, after)) throw sourceChangedError();
|
|
225
|
+
fingerprint.sha256 = hash.digest("hex");
|
|
226
|
+
return fingerprint;
|
|
227
|
+
} catch (error) {
|
|
228
|
+
throw wrapFileError(error, includeSha256 ? "hash" : "inspect");
|
|
229
|
+
} finally {
|
|
230
|
+
await handle?.close();
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
export async function* readJsonlRecords(
|
|
235
|
+
filePath: string,
|
|
236
|
+
fromOffset = 0,
|
|
237
|
+
options: StableReadOptions = {},
|
|
238
|
+
): AsyncIterable<JsonlRecord> {
|
|
239
|
+
let handle: FileHandle | null = null;
|
|
240
|
+
let stream: ReturnType<FileHandle["createReadStream"]> | null = null;
|
|
241
|
+
let rl: readline.Interface | null = null;
|
|
242
|
+
let offset = fromOffset;
|
|
243
|
+
try {
|
|
244
|
+
handle = await openFile(filePath, "r");
|
|
245
|
+
const before = await fileIdentity(handle);
|
|
246
|
+
await options.afterInitialStat?.();
|
|
247
|
+
stream = handle.createReadStream({ start: fromOffset, encoding: "utf8", autoClose: false });
|
|
248
|
+
stream.on("error", () => {});
|
|
249
|
+
rl = readline.createInterface({ input: stream, crlfDelay: Infinity });
|
|
250
|
+
for await (const line of rl) {
|
|
251
|
+
const lineStartOffset = offset;
|
|
252
|
+
offset += Buffer.byteLength(line, "utf8") + 1;
|
|
253
|
+
if (!line.trim()) continue;
|
|
254
|
+
try {
|
|
255
|
+
const raw = asRecord(JSON.parse(line));
|
|
256
|
+
if (raw) yield { raw, text: line, lineStartOffset, nextOffset: offset };
|
|
257
|
+
} catch {
|
|
258
|
+
// A damaged historical record must not hide later valid records.
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (options.rejectChanges) {
|
|
262
|
+
const after = await fileIdentity(handle);
|
|
263
|
+
if (!sameFileIdentity(before, after)) throw sourceChangedError();
|
|
264
|
+
let pathInfo;
|
|
265
|
+
try {
|
|
266
|
+
pathInfo = await stat(filePath, { bigint: true });
|
|
267
|
+
} catch (error) {
|
|
268
|
+
throw wrapFileError(error, "inspect");
|
|
269
|
+
}
|
|
270
|
+
if (pathInfo.dev !== after.device || pathInfo.ino !== after.inode) throw sourceChangedError();
|
|
271
|
+
}
|
|
272
|
+
} catch (error) {
|
|
273
|
+
throw wrapFileError(error, "read");
|
|
274
|
+
} finally {
|
|
275
|
+
rl?.close();
|
|
276
|
+
stream?.destroy();
|
|
277
|
+
await handle?.close();
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
export function textFromContent(content: unknown, acceptedTypes: ReadonlySet<string>): string | null {
|
|
282
|
+
if (typeof content === "string") return content.trim() || null;
|
|
283
|
+
if (!Array.isArray(content)) return null;
|
|
284
|
+
const parts: string[] = [];
|
|
285
|
+
for (const value of content) {
|
|
286
|
+
const block = asRecord(value);
|
|
287
|
+
if (!block) continue;
|
|
288
|
+
const type = asString(block["type"]);
|
|
289
|
+
if (type && !acceptedTypes.has(type)) continue;
|
|
290
|
+
const text = asString(block["text"]) ?? asString(block["content"]);
|
|
291
|
+
if (text) parts.push(text);
|
|
292
|
+
}
|
|
293
|
+
return parts.join("\n\n").trim() || null;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export function meaningfulHumanText(text: string | null): string | null {
|
|
297
|
+
if (!text) return null;
|
|
298
|
+
const trimmed = text.trim();
|
|
299
|
+
if (!trimmed) return null;
|
|
300
|
+
if (/^<(?:environment_context|user_instructions|developer_instructions|system-reminder)[\s>]/i.test(trimmed)) {
|
|
301
|
+
return null;
|
|
302
|
+
}
|
|
303
|
+
if (/^<(?:local-command-caveat|command-name|command-message|command-args)[\s>]/i.test(trimmed)) {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
return trimmed;
|
|
307
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export {
|
|
2
|
+
LocalHistoryError,
|
|
3
|
+
} from "./types.js";
|
|
4
|
+
export type {
|
|
5
|
+
LocalHistoryArchiveState,
|
|
6
|
+
LocalHistoryDiscoverOptions,
|
|
7
|
+
LocalHistoryErrorCode,
|
|
8
|
+
LocalHistoryEvent,
|
|
9
|
+
LocalHistoryFingerprintOptions,
|
|
10
|
+
LocalHistoryOps,
|
|
11
|
+
LocalHistoryProbeOptions,
|
|
12
|
+
LocalHistoryProbeResult,
|
|
13
|
+
LocalHistoryReadOptions,
|
|
14
|
+
LocalHistorySession,
|
|
15
|
+
LocalHistorySourceFingerprint,
|
|
16
|
+
LocalHistoryUserEvent,
|
|
17
|
+
LocalHistoryYield,
|
|
18
|
+
} from "./types.js";
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import type { BaseStreamEventFields, StreamEvent } from "../types.js";
|
|
2
|
+
|
|
3
|
+
export interface LocalHistoryDiscoverOptions {
|
|
4
|
+
includeArchived?: boolean;
|
|
5
|
+
mainSessionsOnly?: boolean;
|
|
6
|
+
requireUserMessage?: boolean;
|
|
7
|
+
cwd?: string;
|
|
8
|
+
limit?: number;
|
|
9
|
+
env?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface LocalHistoryProbeOptions {
|
|
13
|
+
limit?: number;
|
|
14
|
+
env?: Record<string, string>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface LocalHistoryProbeResult {
|
|
18
|
+
providerType: string;
|
|
19
|
+
homeAvailable: boolean;
|
|
20
|
+
historyAvailable: boolean;
|
|
21
|
+
/** A file count only. It is not an eligible-session count. */
|
|
22
|
+
approximateCount?: number;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type LocalHistoryArchiveState = "active" | "archived" | "unknown";
|
|
26
|
+
|
|
27
|
+
export interface LocalHistorySourceFingerprint {
|
|
28
|
+
size: number;
|
|
29
|
+
/** String because nanosecond timestamps can exceed Number.MAX_SAFE_INTEGER. */
|
|
30
|
+
modifiedAtNs: string;
|
|
31
|
+
sha256?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface LocalHistorySession {
|
|
35
|
+
version: 1;
|
|
36
|
+
providerType: string;
|
|
37
|
+
externalSessionId: string;
|
|
38
|
+
/** Local-runtime detail. Network hosts must not serialize this to clients. */
|
|
39
|
+
transcriptPath: string;
|
|
40
|
+
cwd: string | null;
|
|
41
|
+
title: string | null;
|
|
42
|
+
startedAt: string | null;
|
|
43
|
+
updatedAt: string;
|
|
44
|
+
branch: string | null;
|
|
45
|
+
gitOriginUrl: string | null;
|
|
46
|
+
archiveState: LocalHistoryArchiveState;
|
|
47
|
+
hasUserMessage: boolean;
|
|
48
|
+
source: LocalHistorySourceFingerprint;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export type LocalHistoryUserEvent = {
|
|
52
|
+
type: "user";
|
|
53
|
+
text: string;
|
|
54
|
+
} & BaseStreamEventFields;
|
|
55
|
+
|
|
56
|
+
export type LocalHistoryEvent = StreamEvent | LocalHistoryUserEvent;
|
|
57
|
+
|
|
58
|
+
export interface LocalHistoryYield {
|
|
59
|
+
event: LocalHistoryEvent & { eventId: string };
|
|
60
|
+
lineStartOffset: number;
|
|
61
|
+
nextOffset: number;
|
|
62
|
+
/** Disambiguates several normalized events produced from one source record. */
|
|
63
|
+
partIndex: number;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
export interface LocalHistoryReadOptions {
|
|
67
|
+
fromOffset?: number;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export interface LocalHistoryFingerprintOptions {
|
|
71
|
+
sha256?: boolean;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export type LocalHistoryErrorCode =
|
|
75
|
+
| "home_missing"
|
|
76
|
+
| "permission_denied"
|
|
77
|
+
| "source_missing"
|
|
78
|
+
| "unsupported_format"
|
|
79
|
+
| "source_changed_during_read"
|
|
80
|
+
| "invalid_session"
|
|
81
|
+
| "io_error";
|
|
82
|
+
|
|
83
|
+
export class LocalHistoryError extends Error {
|
|
84
|
+
readonly code: LocalHistoryErrorCode;
|
|
85
|
+
|
|
86
|
+
constructor(code: LocalHistoryErrorCode, message: string, options?: ErrorOptions) {
|
|
87
|
+
super(message, options);
|
|
88
|
+
this.name = "LocalHistoryError";
|
|
89
|
+
this.code = code;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export interface LocalHistoryOps {
|
|
94
|
+
probe(options?: LocalHistoryProbeOptions): Promise<LocalHistoryProbeResult>;
|
|
95
|
+
discover(options?: LocalHistoryDiscoverOptions): AsyncIterable<LocalHistorySession>;
|
|
96
|
+
read(
|
|
97
|
+
session: LocalHistorySession,
|
|
98
|
+
options?: LocalHistoryReadOptions,
|
|
99
|
+
): AsyncIterable<LocalHistoryYield>;
|
|
100
|
+
fingerprint(
|
|
101
|
+
session: LocalHistorySession,
|
|
102
|
+
options?: LocalHistoryFingerprintOptions,
|
|
103
|
+
): Promise<LocalHistorySourceFingerprint>;
|
|
104
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -206,6 +206,24 @@ export type {
|
|
|
206
206
|
GoalSentinelContext,
|
|
207
207
|
} from "./types.js";
|
|
208
208
|
|
|
209
|
+
// Local persisted session discovery. Runtime errors are exported from the
|
|
210
|
+
// `@agentex/agent/history` subpath to keep the root barrel lazy.
|
|
211
|
+
export type {
|
|
212
|
+
LocalHistoryArchiveState,
|
|
213
|
+
LocalHistoryDiscoverOptions,
|
|
214
|
+
LocalHistoryErrorCode,
|
|
215
|
+
LocalHistoryEvent,
|
|
216
|
+
LocalHistoryFingerprintOptions,
|
|
217
|
+
LocalHistoryOps,
|
|
218
|
+
LocalHistoryProbeOptions,
|
|
219
|
+
LocalHistoryProbeResult,
|
|
220
|
+
LocalHistoryReadOptions,
|
|
221
|
+
LocalHistorySession,
|
|
222
|
+
LocalHistorySourceFingerprint,
|
|
223
|
+
LocalHistoryUserEvent,
|
|
224
|
+
LocalHistoryYield,
|
|
225
|
+
} from "./history/index.js";
|
|
226
|
+
|
|
209
227
|
// Goals
|
|
210
228
|
export {
|
|
211
229
|
GoalController,
|