@sammorrowdrums/mcpi-agent-core 0.85.0-bootstrap.0
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/LICENSE +21 -0
- package/README.md +513 -0
- package/dist/agent-loop.d.ts +24 -0
- package/dist/agent-loop.d.ts.map +1 -0
- package/dist/agent-loop.js +553 -0
- package/dist/agent-loop.js.map +1 -0
- package/dist/agent.d.ts +122 -0
- package/dist/agent.d.ts.map +1 -0
- package/dist/agent.js +422 -0
- package/dist/agent.js.map +1 -0
- package/dist/harness/agent-harness.d.ts +461 -0
- package/dist/harness/agent-harness.d.ts.map +1 -0
- package/dist/harness/agent-harness.js +252 -0
- package/dist/harness/agent-harness.js.map +1 -0
- package/dist/harness/compaction/branch-summarization.d.ts +62 -0
- package/dist/harness/compaction/branch-summarization.d.ts.map +1 -0
- package/dist/harness/compaction/branch-summarization.js +169 -0
- package/dist/harness/compaction/branch-summarization.js.map +1 -0
- package/dist/harness/compaction/compaction.d.ts +104 -0
- package/dist/harness/compaction/compaction.d.ts.map +1 -0
- package/dist/harness/compaction/compaction.js +570 -0
- package/dist/harness/compaction/compaction.js.map +1 -0
- package/dist/harness/compaction/utils.d.ts +25 -0
- package/dist/harness/compaction/utils.d.ts.map +1 -0
- package/dist/harness/compaction/utils.js +120 -0
- package/dist/harness/compaction/utils.js.map +1 -0
- package/dist/harness/env/nodejs.d.ts +46 -0
- package/dist/harness/env/nodejs.d.ts.map +1 -0
- package/dist/harness/env/nodejs.js +639 -0
- package/dist/harness/env/nodejs.js.map +1 -0
- package/dist/harness/events.d.ts +43 -0
- package/dist/harness/events.d.ts.map +1 -0
- package/dist/harness/events.js +65 -0
- package/dist/harness/events.js.map +1 -0
- package/dist/harness/messages.d.ts +51 -0
- package/dist/harness/messages.d.ts.map +1 -0
- package/dist/harness/messages.js +102 -0
- package/dist/harness/messages.js.map +1 -0
- package/dist/harness/prompt-templates.d.ts +48 -0
- package/dist/harness/prompt-templates.d.ts.map +1 -0
- package/dist/harness/prompt-templates.js +225 -0
- package/dist/harness/prompt-templates.js.map +1 -0
- package/dist/harness/reducer.d.ts +100 -0
- package/dist/harness/reducer.d.ts.map +1 -0
- package/dist/harness/reducer.js +415 -0
- package/dist/harness/reducer.js.map +1 -0
- package/dist/harness/result.d.ts +40 -0
- package/dist/harness/result.d.ts.map +1 -0
- package/dist/harness/result.js +41 -0
- package/dist/harness/result.js.map +1 -0
- package/dist/harness/session/context.d.ts +22 -0
- package/dist/harness/session/context.d.ts.map +1 -0
- package/dist/harness/session/context.js +67 -0
- package/dist/harness/session/context.js.map +1 -0
- package/dist/harness/session/index.d.ts +7 -0
- package/dist/harness/session/index.d.ts.map +1 -0
- package/dist/harness/session/index.js +6 -0
- package/dist/harness/session/index.js.map +1 -0
- package/dist/harness/session/jsonl/codec.d.ts +10 -0
- package/dist/harness/session/jsonl/codec.d.ts.map +1 -0
- package/dist/harness/session/jsonl/codec.js +229 -0
- package/dist/harness/session/jsonl/codec.js.map +1 -0
- package/dist/harness/session/jsonl/errors.d.ts +9 -0
- package/dist/harness/session/jsonl/errors.d.ts.map +1 -0
- package/dist/harness/session/jsonl/errors.js +19 -0
- package/dist/harness/session/jsonl/errors.js.map +1 -0
- package/dist/harness/session/jsonl/repo.d.ts +27 -0
- package/dist/harness/session/jsonl/repo.d.ts.map +1 -0
- package/dist/harness/session/jsonl/repo.js +175 -0
- package/dist/harness/session/jsonl/repo.js.map +1 -0
- package/dist/harness/session/jsonl/storage.d.ts +43 -0
- package/dist/harness/session/jsonl/storage.d.ts.map +1 -0
- package/dist/harness/session/jsonl/storage.js +217 -0
- package/dist/harness/session/jsonl/storage.js.map +1 -0
- package/dist/harness/session/jsonl/types.d.ts +38 -0
- package/dist/harness/session/jsonl/types.d.ts.map +1 -0
- package/dist/harness/session/jsonl/types.js +2 -0
- package/dist/harness/session/jsonl/types.js.map +1 -0
- package/dist/harness/session/jsonl.d.ts +3 -0
- package/dist/harness/session/jsonl.d.ts.map +1 -0
- package/dist/harness/session/jsonl.js +2 -0
- package/dist/harness/session/jsonl.js.map +1 -0
- package/dist/harness/session/memory.d.ts +44 -0
- package/dist/harness/session/memory.d.ts.map +1 -0
- package/dist/harness/session/memory.js +141 -0
- package/dist/harness/session/memory.js.map +1 -0
- package/dist/harness/session/session.d.ts +50 -0
- package/dist/harness/session/session.d.ts.map +1 -0
- package/dist/harness/session/session.js +227 -0
- package/dist/harness/session/session.js.map +1 -0
- package/dist/harness/session/state.d.ts +65 -0
- package/dist/harness/session/state.d.ts.map +1 -0
- package/dist/harness/session/state.js +319 -0
- package/dist/harness/session/state.js.map +1 -0
- package/dist/harness/session/testing/conformance.d.ts +4 -0
- package/dist/harness/session/testing/conformance.d.ts.map +1 -0
- package/dist/harness/session/testing/conformance.js +762 -0
- package/dist/harness/session/testing/conformance.js.map +1 -0
- package/dist/harness/session/testing/index.d.ts +3 -0
- package/dist/harness/session/testing/index.d.ts.map +1 -0
- package/dist/harness/session/testing/index.js +2 -0
- package/dist/harness/session/testing/index.js.map +1 -0
- package/dist/harness/session/testing/types.d.ts +14 -0
- package/dist/harness/session/testing/types.d.ts.map +1 -0
- package/dist/harness/session/testing/types.js +2 -0
- package/dist/harness/session/testing/types.js.map +1 -0
- package/dist/harness/session/types.d.ts +346 -0
- package/dist/harness/session/types.d.ts.map +1 -0
- package/dist/harness/session/types.js +10 -0
- package/dist/harness/session/types.js.map +1 -0
- package/dist/harness/skills.d.ts +44 -0
- package/dist/harness/skills.d.ts.map +1 -0
- package/dist/harness/skills.js +313 -0
- package/dist/harness/skills.js.map +1 -0
- package/dist/harness/system-prompt.d.ts +3 -0
- package/dist/harness/system-prompt.d.ts.map +1 -0
- package/dist/harness/system-prompt.js +30 -0
- package/dist/harness/system-prompt.js.map +1 -0
- package/dist/harness/telemetry.d.ts +1221 -0
- package/dist/harness/telemetry.d.ts.map +1 -0
- package/dist/harness/telemetry.js +508 -0
- package/dist/harness/telemetry.js.map +1 -0
- package/dist/harness/tools/bash.d.ts +27 -0
- package/dist/harness/tools/bash.d.ts.map +1 -0
- package/dist/harness/tools/bash.js +132 -0
- package/dist/harness/tools/bash.js.map +1 -0
- package/dist/harness/tools/edit-diff.d.ts +89 -0
- package/dist/harness/tools/edit-diff.d.ts.map +1 -0
- package/dist/harness/tools/edit-diff.js +386 -0
- package/dist/harness/tools/edit-diff.js.map +1 -0
- package/dist/harness/tools/edit.d.ts +19 -0
- package/dist/harness/tools/edit.d.ts.map +1 -0
- package/dist/harness/tools/edit.js +95 -0
- package/dist/harness/tools/edit.js.map +1 -0
- package/dist/harness/tools/file-mutation-queue.d.ts +4 -0
- package/dist/harness/tools/file-mutation-queue.d.ts.map +1 -0
- package/dist/harness/tools/file-mutation-queue.js +46 -0
- package/dist/harness/tools/file-mutation-queue.js.map +1 -0
- package/dist/harness/tools/image.d.ts +3 -0
- package/dist/harness/tools/image.d.ts.map +1 -0
- package/dist/harness/tools/image.js +106 -0
- package/dist/harness/tools/image.js.map +1 -0
- package/dist/harness/tools/index.d.ts +6 -0
- package/dist/harness/tools/index.d.ts.map +1 -0
- package/dist/harness/tools/index.js +5 -0
- package/dist/harness/tools/index.js.map +1 -0
- package/dist/harness/tools/path-utils.d.ts +4 -0
- package/dist/harness/tools/path-utils.d.ts.map +1 -0
- package/dist/harness/tools/path-utils.js +26 -0
- package/dist/harness/tools/path-utils.js.map +1 -0
- package/dist/harness/tools/read.d.ts +34 -0
- package/dist/harness/tools/read.d.ts.map +1 -0
- package/dist/harness/tools/read.js +110 -0
- package/dist/harness/tools/read.js.map +1 -0
- package/dist/harness/tools/tool-context.d.ts +6 -0
- package/dist/harness/tools/tool-context.d.ts.map +1 -0
- package/dist/harness/tools/tool-context.js +2 -0
- package/dist/harness/tools/tool-context.js.map +1 -0
- package/dist/harness/tools/write.d.ts +11 -0
- package/dist/harness/tools/write.d.ts.map +1 -0
- package/dist/harness/tools/write.js +31 -0
- package/dist/harness/tools/write.js.map +1 -0
- package/dist/harness/types.d.ts +228 -0
- package/dist/harness/types.d.ts.map +1 -0
- package/dist/harness/types.js +75 -0
- package/dist/harness/types.js.map +1 -0
- package/dist/harness/utils/shell-output.d.ts +22 -0
- package/dist/harness/utils/shell-output.d.ts.map +1 -0
- package/dist/harness/utils/shell-output.js +181 -0
- package/dist/harness/utils/shell-output.js.map +1 -0
- package/dist/harness/utils/truncate.d.ts +70 -0
- package/dist/harness/utils/truncate.d.ts.map +1 -0
- package/dist/harness/utils/truncate.js +296 -0
- package/dist/harness/utils/truncate.js.map +1 -0
- package/dist/index.d.ts +25 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +29 -0
- package/dist/index.js.map +1 -0
- package/dist/node.d.ts +3 -0
- package/dist/node.d.ts.map +1 -0
- package/dist/node.js +3 -0
- package/dist/node.js.map +1 -0
- package/dist/proxy.d.ts +70 -0
- package/dist/proxy.d.ts.map +1 -0
- package/dist/proxy.js +280 -0
- package/dist/proxy.js.map +1 -0
- package/dist/search/index.d.ts +21 -0
- package/dist/search/index.d.ts.map +1 -0
- package/dist/search/index.js +2 -0
- package/dist/search/index.js.map +1 -0
- package/dist/search/scanning.d.ts +29 -0
- package/dist/search/scanning.d.ts.map +1 -0
- package/dist/search/scanning.js +103 -0
- package/dist/search/scanning.js.map +1 -0
- package/dist/stream-fn.d.ts +10 -0
- package/dist/stream-fn.d.ts.map +1 -0
- package/dist/stream-fn.js +17 -0
- package/dist/stream-fn.js.map +1 -0
- package/dist/types.d.ts +413 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { Session } from "./session.ts";
|
|
2
|
+
import { type BranchBounds, type Entry, type EntryQuery, type ForkOptions, type LanePointer, type LaneRecord, type LogItem, type LogOptions, type NewRecord, type OperationStartedRecord, type ProvisionedEntry, type RecordQuery, type SessionCreateOptions, type SessionMetadata, type SessionRepo, type SessionStats, type SessionStorage } from "./types.ts";
|
|
3
|
+
export declare class InMemorySessionStorage implements SessionStorage {
|
|
4
|
+
private readonly metadata;
|
|
5
|
+
private readonly state;
|
|
6
|
+
constructor(metadata: SessionMetadata);
|
|
7
|
+
fork(metadata: SessionMetadata, options: ForkOptions & SessionCreateOptions): InMemorySessionStorage;
|
|
8
|
+
getMetadata(): Promise<SessionMetadata>;
|
|
9
|
+
getLanes(): Promise<LanePointer[]>;
|
|
10
|
+
createLane(lane: string, at: string | null): Promise<void>;
|
|
11
|
+
moveLane(lane: string, to: string | null): Promise<void>;
|
|
12
|
+
appendEntry<TEntry extends Entry>(newEntry: ProvisionedEntry<TEntry>, lane: string): Promise<TEntry>;
|
|
13
|
+
appendRecord<TRecord extends LaneRecord>(newRecord: NewRecord<TRecord>): Promise<TRecord>;
|
|
14
|
+
getEntry(id: string): Promise<Entry | undefined>;
|
|
15
|
+
findEntries(query?: EntryQuery): Promise<Entry[]>;
|
|
16
|
+
findEntriesOnBranch(query: EntryQuery & BranchBounds & {
|
|
17
|
+
start: string;
|
|
18
|
+
}): Promise<Entry[]>;
|
|
19
|
+
findRecords<K extends LaneRecord["type"]>(query: RecordQuery & {
|
|
20
|
+
type: K;
|
|
21
|
+
}): Promise<Extract<LaneRecord, {
|
|
22
|
+
type: K;
|
|
23
|
+
}>[]>;
|
|
24
|
+
findRecords(query?: RecordQuery): Promise<LaneRecord[]>;
|
|
25
|
+
findOpenOperations(lane: string, options?: {
|
|
26
|
+
limit?: number;
|
|
27
|
+
}): Promise<OperationStartedRecord[]>;
|
|
28
|
+
getLog(options?: LogOptions): Promise<LogItem[]>;
|
|
29
|
+
getName(): Promise<string | undefined>;
|
|
30
|
+
setName(name: string | undefined): Promise<void>;
|
|
31
|
+
getLabel(id: string): Promise<string | undefined>;
|
|
32
|
+
setLabel(id: string, label: string | undefined): Promise<void>;
|
|
33
|
+
getStats(): Promise<SessionStats>;
|
|
34
|
+
}
|
|
35
|
+
export declare class InMemorySessionRepo implements SessionRepo {
|
|
36
|
+
private readonly sessions;
|
|
37
|
+
create(options?: SessionCreateOptions): Promise<Session>;
|
|
38
|
+
open(metadata: SessionMetadata): Promise<Session>;
|
|
39
|
+
list(): Promise<SessionMetadata[]>;
|
|
40
|
+
delete(metadata: SessionMetadata): Promise<void>;
|
|
41
|
+
fork(source: SessionMetadata, options?: ForkOptions & SessionCreateOptions): Promise<Session>;
|
|
42
|
+
private requireStorage;
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=memory.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.d.ts","sourceRoot":"","sources":["../../../src/harness/session/memory.ts"],"names":[],"mappings":"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","sourcesContent":["import { uuidv7 } from \"@sammorrowdrums/mcpi-ai\";\nimport { Session } from \"./session.ts\";\nimport { SessionState } from \"./state.ts\";\nimport {\n\ttype BranchBounds,\n\ttype Entry,\n\ttype EntryQuery,\n\ttype ForkOptions,\n\ttype LanePointer,\n\ttype LaneRecord,\n\ttype LogItem,\n\ttype LogOptions,\n\ttype NewRecord,\n\ttype OperationStartedRecord,\n\ttype ProvisionedEntry,\n\ttype RecordQuery,\n\ttype SessionCreateOptions,\n\tSessionError,\n\ttype SessionMetadata,\n\ttype SessionRepo,\n\ttype SessionStats,\n\ttype SessionStorage,\n} from \"./types.ts\";\n\nexport class InMemorySessionStorage implements SessionStorage {\n\tprivate readonly metadata: SessionMetadata;\n\tprivate readonly state = new SessionState();\n\n\tconstructor(metadata: SessionMetadata) {\n\t\tthis.metadata = structuredClone(metadata);\n\t}\n\n\tfork(metadata: SessionMetadata, options: ForkOptions & SessionCreateOptions): InMemorySessionStorage {\n\t\tconst storage = new InMemorySessionStorage(metadata);\n\t\tfor (const mutation of this.state.createForkMutations(options)) storage.state.applyMutation(mutation);\n\t\treturn storage;\n\t}\n\n\tasync getMetadata(): Promise<SessionMetadata> {\n\t\treturn structuredClone(this.metadata);\n\t}\n\n\tasync getLanes(): Promise<LanePointer[]> {\n\t\treturn this.state.getLanes();\n\t}\n\n\tasync createLane(lane: string, at: string | null): Promise<void> {\n\t\tthis.state.validateNewLane(lane);\n\t\tthis.state.validateTarget(at);\n\t\tthis.state.applyMutation({ kind: \"lane\", seq: this.state.nextSequence, lane, leafId: at });\n\t}\n\n\tasync moveLane(lane: string, to: string | null): Promise<void> {\n\t\tthis.state.requireLane(lane);\n\t\tthis.state.validateTarget(to);\n\t\tthis.state.applyMutation({ kind: \"lane\", seq: this.state.nextSequence, lane, leafId: to });\n\t}\n\n\tasync appendEntry<TEntry extends Entry>(newEntry: ProvisionedEntry<TEntry>, lane: string): Promise<TEntry> {\n\t\tconst parentId = this.state.requireLane(lane);\n\t\tthis.state.validateUnusedId(newEntry.id);\n\t\tconst entry = {\n\t\t\t...structuredClone(newEntry),\n\t\t\tparentId,\n\t\t\tseq: this.state.nextSequence,\n\t\t\ttimestamp: Date.now(),\n\t\t} as unknown as TEntry;\n\t\tthis.state.applyMutation({ kind: \"entry\", lane, entry });\n\t\treturn structuredClone(entry);\n\t}\n\n\tasync appendRecord<TRecord extends LaneRecord>(newRecord: NewRecord<TRecord>): Promise<TRecord> {\n\t\tthis.state.requireLane(newRecord.lane);\n\t\tthis.state.validateUnusedId(newRecord.id);\n\t\tconst currentOpenOperationId = this.state.findOpenOperations(newRecord.lane, { limit: 1 })[0]?.id;\n\t\tif (newRecord.type === \"operation_started\" && currentOpenOperationId !== undefined) {\n\t\t\tthrow new SessionError(\n\t\t\t\t\"storage\",\n\t\t\t\t`Lane ${newRecord.lane} already has an open operation ${currentOpenOperationId}`,\n\t\t\t);\n\t\t}\n\t\tconst record = {\n\t\t\t...structuredClone(newRecord),\n\t\t\tseq: this.state.nextSequence,\n\t\t\ttimestamp: Date.now(),\n\t\t} as unknown as TRecord;\n\t\tthis.state.applyMutation({ kind: \"record\", record });\n\t\treturn structuredClone(record);\n\t}\n\n\tasync getEntry(id: string): Promise<Entry | undefined> {\n\t\tconst entry = this.state.getEntry(id);\n\t\treturn entry === undefined ? undefined : structuredClone(entry);\n\t}\n\n\tasync findEntries(query: EntryQuery = {}): Promise<Entry[]> {\n\t\treturn structuredClone(this.state.findEntries(query));\n\t}\n\n\tasync findEntriesOnBranch(query: EntryQuery & BranchBounds & { start: string }): Promise<Entry[]> {\n\t\treturn structuredClone(this.state.findEntriesOnBranch(query));\n\t}\n\n\tasync findRecords<K extends LaneRecord[\"type\"]>(\n\t\tquery: RecordQuery & { type: K },\n\t): Promise<Extract<LaneRecord, { type: K }>[]>;\n\tasync findRecords(query?: RecordQuery): Promise<LaneRecord[]>;\n\tasync findRecords(query: RecordQuery = {}): Promise<LaneRecord[]> {\n\t\treturn structuredClone(this.state.findRecords(query));\n\t}\n\n\tasync findOpenOperations(lane: string, options?: { limit?: number }): Promise<OperationStartedRecord[]> {\n\t\treturn structuredClone(this.state.findOpenOperations(lane, options));\n\t}\n\n\tasync getLog(options: LogOptions = {}): Promise<LogItem[]> {\n\t\treturn structuredClone(this.state.getLog(options));\n\t}\n\n\tasync getName(): Promise<string | undefined> {\n\t\treturn this.state.getName();\n\t}\n\n\tasync setName(name: string | undefined): Promise<void> {\n\t\tthis.state.applyMutation({ kind: \"fact\", seq: this.state.nextSequence, fact: \"name\", name });\n\t}\n\n\tasync getLabel(id: string): Promise<string | undefined> {\n\t\treturn this.state.getLabel(id);\n\t}\n\n\tasync setLabel(id: string, label: string | undefined): Promise<void> {\n\t\tthis.state.validateTarget(id);\n\t\tthis.state.applyMutation({\n\t\t\tkind: \"fact\",\n\t\t\tseq: this.state.nextSequence,\n\t\t\tfact: \"label\",\n\t\t\ttargetId: id,\n\t\t\tlabel,\n\t\t});\n\t}\n\n\tasync getStats(): Promise<SessionStats> {\n\t\treturn structuredClone(this.state.getStats());\n\t}\n}\n\nexport class InMemorySessionRepo implements SessionRepo {\n\tprivate readonly sessions = new Map<string, InMemorySessionStorage>();\n\n\tasync create(options: SessionCreateOptions = {}): Promise<Session> {\n\t\tconst id = options.id ?? uuidv7();\n\t\tif (this.sessions.has(id)) throw new SessionError(\"already_exists\", `Session already exists: ${id}`);\n\t\tconst storage = new InMemorySessionStorage({\n\t\t\tid,\n\t\t\tcreatedAt: Date.now(),\n\t\t\tparentSessionId: options.parentSessionId,\n\t\t});\n\t\tthis.sessions.set(id, storage);\n\t\treturn new Session(storage);\n\t}\n\n\tasync open(metadata: SessionMetadata): Promise<Session> {\n\t\treturn new Session(this.requireStorage(metadata.id));\n\t}\n\n\tasync list(): Promise<SessionMetadata[]> {\n\t\treturn Promise.all([...this.sessions.values()].map((storage) => storage.getMetadata()));\n\t}\n\n\tasync delete(metadata: SessionMetadata): Promise<void> {\n\t\tthis.sessions.delete(metadata.id);\n\t}\n\n\tasync fork(source: SessionMetadata, options: ForkOptions & SessionCreateOptions = {}): Promise<Session> {\n\t\tconst sourceStorage = this.requireStorage(source.id);\n\t\tconst id = options.id ?? uuidv7();\n\t\tif (this.sessions.has(id)) throw new SessionError(\"already_exists\", `Session already exists: ${id}`);\n\t\tconst storage = sourceStorage.fork(\n\t\t\t{ id, createdAt: Date.now(), parentSessionId: options.parentSessionId ?? source.id },\n\t\t\toptions,\n\t\t);\n\t\tthis.sessions.set(id, storage);\n\t\treturn new Session(storage);\n\t}\n\n\tprivate requireStorage(id: string): InMemorySessionStorage {\n\t\tconst storage = this.sessions.get(id);\n\t\tif (!storage) throw new SessionError(\"not_found\", `Session not found: ${id}`);\n\t\treturn storage;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import { uuidv7 } from "@sammorrowdrums/mcpi-ai";
|
|
2
|
+
import { Session } from "./session.js";
|
|
3
|
+
import { SessionState } from "./state.js";
|
|
4
|
+
import { SessionError, } from "./types.js";
|
|
5
|
+
export class InMemorySessionStorage {
|
|
6
|
+
metadata;
|
|
7
|
+
state = new SessionState();
|
|
8
|
+
constructor(metadata) {
|
|
9
|
+
this.metadata = structuredClone(metadata);
|
|
10
|
+
}
|
|
11
|
+
fork(metadata, options) {
|
|
12
|
+
const storage = new InMemorySessionStorage(metadata);
|
|
13
|
+
for (const mutation of this.state.createForkMutations(options))
|
|
14
|
+
storage.state.applyMutation(mutation);
|
|
15
|
+
return storage;
|
|
16
|
+
}
|
|
17
|
+
async getMetadata() {
|
|
18
|
+
return structuredClone(this.metadata);
|
|
19
|
+
}
|
|
20
|
+
async getLanes() {
|
|
21
|
+
return this.state.getLanes();
|
|
22
|
+
}
|
|
23
|
+
async createLane(lane, at) {
|
|
24
|
+
this.state.validateNewLane(lane);
|
|
25
|
+
this.state.validateTarget(at);
|
|
26
|
+
this.state.applyMutation({ kind: "lane", seq: this.state.nextSequence, lane, leafId: at });
|
|
27
|
+
}
|
|
28
|
+
async moveLane(lane, to) {
|
|
29
|
+
this.state.requireLane(lane);
|
|
30
|
+
this.state.validateTarget(to);
|
|
31
|
+
this.state.applyMutation({ kind: "lane", seq: this.state.nextSequence, lane, leafId: to });
|
|
32
|
+
}
|
|
33
|
+
async appendEntry(newEntry, lane) {
|
|
34
|
+
const parentId = this.state.requireLane(lane);
|
|
35
|
+
this.state.validateUnusedId(newEntry.id);
|
|
36
|
+
const entry = {
|
|
37
|
+
...structuredClone(newEntry),
|
|
38
|
+
parentId,
|
|
39
|
+
seq: this.state.nextSequence,
|
|
40
|
+
timestamp: Date.now(),
|
|
41
|
+
};
|
|
42
|
+
this.state.applyMutation({ kind: "entry", lane, entry });
|
|
43
|
+
return structuredClone(entry);
|
|
44
|
+
}
|
|
45
|
+
async appendRecord(newRecord) {
|
|
46
|
+
this.state.requireLane(newRecord.lane);
|
|
47
|
+
this.state.validateUnusedId(newRecord.id);
|
|
48
|
+
const currentOpenOperationId = this.state.findOpenOperations(newRecord.lane, { limit: 1 })[0]?.id;
|
|
49
|
+
if (newRecord.type === "operation_started" && currentOpenOperationId !== undefined) {
|
|
50
|
+
throw new SessionError("storage", `Lane ${newRecord.lane} already has an open operation ${currentOpenOperationId}`);
|
|
51
|
+
}
|
|
52
|
+
const record = {
|
|
53
|
+
...structuredClone(newRecord),
|
|
54
|
+
seq: this.state.nextSequence,
|
|
55
|
+
timestamp: Date.now(),
|
|
56
|
+
};
|
|
57
|
+
this.state.applyMutation({ kind: "record", record });
|
|
58
|
+
return structuredClone(record);
|
|
59
|
+
}
|
|
60
|
+
async getEntry(id) {
|
|
61
|
+
const entry = this.state.getEntry(id);
|
|
62
|
+
return entry === undefined ? undefined : structuredClone(entry);
|
|
63
|
+
}
|
|
64
|
+
async findEntries(query = {}) {
|
|
65
|
+
return structuredClone(this.state.findEntries(query));
|
|
66
|
+
}
|
|
67
|
+
async findEntriesOnBranch(query) {
|
|
68
|
+
return structuredClone(this.state.findEntriesOnBranch(query));
|
|
69
|
+
}
|
|
70
|
+
async findRecords(query = {}) {
|
|
71
|
+
return structuredClone(this.state.findRecords(query));
|
|
72
|
+
}
|
|
73
|
+
async findOpenOperations(lane, options) {
|
|
74
|
+
return structuredClone(this.state.findOpenOperations(lane, options));
|
|
75
|
+
}
|
|
76
|
+
async getLog(options = {}) {
|
|
77
|
+
return structuredClone(this.state.getLog(options));
|
|
78
|
+
}
|
|
79
|
+
async getName() {
|
|
80
|
+
return this.state.getName();
|
|
81
|
+
}
|
|
82
|
+
async setName(name) {
|
|
83
|
+
this.state.applyMutation({ kind: "fact", seq: this.state.nextSequence, fact: "name", name });
|
|
84
|
+
}
|
|
85
|
+
async getLabel(id) {
|
|
86
|
+
return this.state.getLabel(id);
|
|
87
|
+
}
|
|
88
|
+
async setLabel(id, label) {
|
|
89
|
+
this.state.validateTarget(id);
|
|
90
|
+
this.state.applyMutation({
|
|
91
|
+
kind: "fact",
|
|
92
|
+
seq: this.state.nextSequence,
|
|
93
|
+
fact: "label",
|
|
94
|
+
targetId: id,
|
|
95
|
+
label,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
async getStats() {
|
|
99
|
+
return structuredClone(this.state.getStats());
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export class InMemorySessionRepo {
|
|
103
|
+
sessions = new Map();
|
|
104
|
+
async create(options = {}) {
|
|
105
|
+
const id = options.id ?? uuidv7();
|
|
106
|
+
if (this.sessions.has(id))
|
|
107
|
+
throw new SessionError("already_exists", `Session already exists: ${id}`);
|
|
108
|
+
const storage = new InMemorySessionStorage({
|
|
109
|
+
id,
|
|
110
|
+
createdAt: Date.now(),
|
|
111
|
+
parentSessionId: options.parentSessionId,
|
|
112
|
+
});
|
|
113
|
+
this.sessions.set(id, storage);
|
|
114
|
+
return new Session(storage);
|
|
115
|
+
}
|
|
116
|
+
async open(metadata) {
|
|
117
|
+
return new Session(this.requireStorage(metadata.id));
|
|
118
|
+
}
|
|
119
|
+
async list() {
|
|
120
|
+
return Promise.all([...this.sessions.values()].map((storage) => storage.getMetadata()));
|
|
121
|
+
}
|
|
122
|
+
async delete(metadata) {
|
|
123
|
+
this.sessions.delete(metadata.id);
|
|
124
|
+
}
|
|
125
|
+
async fork(source, options = {}) {
|
|
126
|
+
const sourceStorage = this.requireStorage(source.id);
|
|
127
|
+
const id = options.id ?? uuidv7();
|
|
128
|
+
if (this.sessions.has(id))
|
|
129
|
+
throw new SessionError("already_exists", `Session already exists: ${id}`);
|
|
130
|
+
const storage = sourceStorage.fork({ id, createdAt: Date.now(), parentSessionId: options.parentSessionId ?? source.id }, options);
|
|
131
|
+
this.sessions.set(id, storage);
|
|
132
|
+
return new Session(storage);
|
|
133
|
+
}
|
|
134
|
+
requireStorage(id) {
|
|
135
|
+
const storage = this.sessions.get(id);
|
|
136
|
+
if (!storage)
|
|
137
|
+
throw new SessionError("not_found", `Session not found: ${id}`);
|
|
138
|
+
return storage;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
//# sourceMappingURL=memory.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"memory.js","sourceRoot":"","sources":["../../../src/harness/session/memory.ts"],"names":[],"mappings":"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","sourcesContent":["import { uuidv7 } from \"@sammorrowdrums/mcpi-ai\";\nimport { Session } from \"./session.ts\";\nimport { SessionState } from \"./state.ts\";\nimport {\n\ttype BranchBounds,\n\ttype Entry,\n\ttype EntryQuery,\n\ttype ForkOptions,\n\ttype LanePointer,\n\ttype LaneRecord,\n\ttype LogItem,\n\ttype LogOptions,\n\ttype NewRecord,\n\ttype OperationStartedRecord,\n\ttype ProvisionedEntry,\n\ttype RecordQuery,\n\ttype SessionCreateOptions,\n\tSessionError,\n\ttype SessionMetadata,\n\ttype SessionRepo,\n\ttype SessionStats,\n\ttype SessionStorage,\n} from \"./types.ts\";\n\nexport class InMemorySessionStorage implements SessionStorage {\n\tprivate readonly metadata: SessionMetadata;\n\tprivate readonly state = new SessionState();\n\n\tconstructor(metadata: SessionMetadata) {\n\t\tthis.metadata = structuredClone(metadata);\n\t}\n\n\tfork(metadata: SessionMetadata, options: ForkOptions & SessionCreateOptions): InMemorySessionStorage {\n\t\tconst storage = new InMemorySessionStorage(metadata);\n\t\tfor (const mutation of this.state.createForkMutations(options)) storage.state.applyMutation(mutation);\n\t\treturn storage;\n\t}\n\n\tasync getMetadata(): Promise<SessionMetadata> {\n\t\treturn structuredClone(this.metadata);\n\t}\n\n\tasync getLanes(): Promise<LanePointer[]> {\n\t\treturn this.state.getLanes();\n\t}\n\n\tasync createLane(lane: string, at: string | null): Promise<void> {\n\t\tthis.state.validateNewLane(lane);\n\t\tthis.state.validateTarget(at);\n\t\tthis.state.applyMutation({ kind: \"lane\", seq: this.state.nextSequence, lane, leafId: at });\n\t}\n\n\tasync moveLane(lane: string, to: string | null): Promise<void> {\n\t\tthis.state.requireLane(lane);\n\t\tthis.state.validateTarget(to);\n\t\tthis.state.applyMutation({ kind: \"lane\", seq: this.state.nextSequence, lane, leafId: to });\n\t}\n\n\tasync appendEntry<TEntry extends Entry>(newEntry: ProvisionedEntry<TEntry>, lane: string): Promise<TEntry> {\n\t\tconst parentId = this.state.requireLane(lane);\n\t\tthis.state.validateUnusedId(newEntry.id);\n\t\tconst entry = {\n\t\t\t...structuredClone(newEntry),\n\t\t\tparentId,\n\t\t\tseq: this.state.nextSequence,\n\t\t\ttimestamp: Date.now(),\n\t\t} as unknown as TEntry;\n\t\tthis.state.applyMutation({ kind: \"entry\", lane, entry });\n\t\treturn structuredClone(entry);\n\t}\n\n\tasync appendRecord<TRecord extends LaneRecord>(newRecord: NewRecord<TRecord>): Promise<TRecord> {\n\t\tthis.state.requireLane(newRecord.lane);\n\t\tthis.state.validateUnusedId(newRecord.id);\n\t\tconst currentOpenOperationId = this.state.findOpenOperations(newRecord.lane, { limit: 1 })[0]?.id;\n\t\tif (newRecord.type === \"operation_started\" && currentOpenOperationId !== undefined) {\n\t\t\tthrow new SessionError(\n\t\t\t\t\"storage\",\n\t\t\t\t`Lane ${newRecord.lane} already has an open operation ${currentOpenOperationId}`,\n\t\t\t);\n\t\t}\n\t\tconst record = {\n\t\t\t...structuredClone(newRecord),\n\t\t\tseq: this.state.nextSequence,\n\t\t\ttimestamp: Date.now(),\n\t\t} as unknown as TRecord;\n\t\tthis.state.applyMutation({ kind: \"record\", record });\n\t\treturn structuredClone(record);\n\t}\n\n\tasync getEntry(id: string): Promise<Entry | undefined> {\n\t\tconst entry = this.state.getEntry(id);\n\t\treturn entry === undefined ? undefined : structuredClone(entry);\n\t}\n\n\tasync findEntries(query: EntryQuery = {}): Promise<Entry[]> {\n\t\treturn structuredClone(this.state.findEntries(query));\n\t}\n\n\tasync findEntriesOnBranch(query: EntryQuery & BranchBounds & { start: string }): Promise<Entry[]> {\n\t\treturn structuredClone(this.state.findEntriesOnBranch(query));\n\t}\n\n\tasync findRecords<K extends LaneRecord[\"type\"]>(\n\t\tquery: RecordQuery & { type: K },\n\t): Promise<Extract<LaneRecord, { type: K }>[]>;\n\tasync findRecords(query?: RecordQuery): Promise<LaneRecord[]>;\n\tasync findRecords(query: RecordQuery = {}): Promise<LaneRecord[]> {\n\t\treturn structuredClone(this.state.findRecords(query));\n\t}\n\n\tasync findOpenOperations(lane: string, options?: { limit?: number }): Promise<OperationStartedRecord[]> {\n\t\treturn structuredClone(this.state.findOpenOperations(lane, options));\n\t}\n\n\tasync getLog(options: LogOptions = {}): Promise<LogItem[]> {\n\t\treturn structuredClone(this.state.getLog(options));\n\t}\n\n\tasync getName(): Promise<string | undefined> {\n\t\treturn this.state.getName();\n\t}\n\n\tasync setName(name: string | undefined): Promise<void> {\n\t\tthis.state.applyMutation({ kind: \"fact\", seq: this.state.nextSequence, fact: \"name\", name });\n\t}\n\n\tasync getLabel(id: string): Promise<string | undefined> {\n\t\treturn this.state.getLabel(id);\n\t}\n\n\tasync setLabel(id: string, label: string | undefined): Promise<void> {\n\t\tthis.state.validateTarget(id);\n\t\tthis.state.applyMutation({\n\t\t\tkind: \"fact\",\n\t\t\tseq: this.state.nextSequence,\n\t\t\tfact: \"label\",\n\t\t\ttargetId: id,\n\t\t\tlabel,\n\t\t});\n\t}\n\n\tasync getStats(): Promise<SessionStats> {\n\t\treturn structuredClone(this.state.getStats());\n\t}\n}\n\nexport class InMemorySessionRepo implements SessionRepo {\n\tprivate readonly sessions = new Map<string, InMemorySessionStorage>();\n\n\tasync create(options: SessionCreateOptions = {}): Promise<Session> {\n\t\tconst id = options.id ?? uuidv7();\n\t\tif (this.sessions.has(id)) throw new SessionError(\"already_exists\", `Session already exists: ${id}`);\n\t\tconst storage = new InMemorySessionStorage({\n\t\t\tid,\n\t\t\tcreatedAt: Date.now(),\n\t\t\tparentSessionId: options.parentSessionId,\n\t\t});\n\t\tthis.sessions.set(id, storage);\n\t\treturn new Session(storage);\n\t}\n\n\tasync open(metadata: SessionMetadata): Promise<Session> {\n\t\treturn new Session(this.requireStorage(metadata.id));\n\t}\n\n\tasync list(): Promise<SessionMetadata[]> {\n\t\treturn Promise.all([...this.sessions.values()].map((storage) => storage.getMetadata()));\n\t}\n\n\tasync delete(metadata: SessionMetadata): Promise<void> {\n\t\tthis.sessions.delete(metadata.id);\n\t}\n\n\tasync fork(source: SessionMetadata, options: ForkOptions & SessionCreateOptions = {}): Promise<Session> {\n\t\tconst sourceStorage = this.requireStorage(source.id);\n\t\tconst id = options.id ?? uuidv7();\n\t\tif (this.sessions.has(id)) throw new SessionError(\"already_exists\", `Session already exists: ${id}`);\n\t\tconst storage = sourceStorage.fork(\n\t\t\t{ id, createdAt: Date.now(), parentSessionId: options.parentSessionId ?? source.id },\n\t\t\toptions,\n\t\t);\n\t\tthis.sessions.set(id, storage);\n\t\treturn new Session(storage);\n\t}\n\n\tprivate requireStorage(id: string): InMemorySessionStorage {\n\t\tconst storage = this.sessions.get(id);\n\t\tif (!storage) throw new SessionError(\"not_found\", `Session not found: ${id}`);\n\t\treturn storage;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import type { AgentMessage } from "../../types.ts";
|
|
2
|
+
import type { BranchBounds, Entry, EntryQuery, IdGenerator, LanePointer, LaneRecord, LogItem, LogOptions, NewRecord, OperationStartedRecord, ProvisionedEntry, RecordBase, RecordQuery, SessionMetadata, SessionStats, SessionStorage, SessionTree } from "./types.ts";
|
|
3
|
+
export declare function assertJsonSerializable(value: unknown): void;
|
|
4
|
+
export declare class Session<TMetadata extends SessionMetadata = SessionMetadata> implements SessionTree {
|
|
5
|
+
private readonly storage;
|
|
6
|
+
readonly idGenerator: IdGenerator;
|
|
7
|
+
constructor(storage: SessionStorage<TMetadata>, options?: {
|
|
8
|
+
idGenerator?: IdGenerator;
|
|
9
|
+
});
|
|
10
|
+
getMetadata(): Promise<TMetadata>;
|
|
11
|
+
view(lane: string): SessionTree;
|
|
12
|
+
getLeafId(): Promise<string | null>;
|
|
13
|
+
getEntry(id: string): Promise<Entry | undefined>;
|
|
14
|
+
getStats(): Promise<SessionStats>;
|
|
15
|
+
getName(): Promise<string | undefined>;
|
|
16
|
+
setName(name: string | undefined): Promise<void>;
|
|
17
|
+
getLabel(targetId: string): Promise<string | undefined>;
|
|
18
|
+
setLabel(targetId: string, label: string | undefined): Promise<void>;
|
|
19
|
+
findEntries(query?: EntryQuery): Promise<Entry[]>;
|
|
20
|
+
findEntry(query?: EntryQuery): Promise<Entry | undefined>;
|
|
21
|
+
findEntriesOnBranch(query?: EntryQuery & BranchBounds): Promise<Entry[]>;
|
|
22
|
+
findEntryOnBranch(query?: EntryQuery & BranchBounds): Promise<Entry | undefined>;
|
|
23
|
+
appendMessage(message: AgentMessage): Promise<string>;
|
|
24
|
+
appendCustomEntry(customType: string, data?: unknown): Promise<string>;
|
|
25
|
+
getLanes(): Promise<LanePointer[]>;
|
|
26
|
+
createLane(lane: string, at: string | null): Promise<void>;
|
|
27
|
+
moveLane(lane: string, to: string | null): Promise<void>;
|
|
28
|
+
appendEntry<TEntry extends Entry>(entry: ProvisionedEntry<TEntry>, lane: string): Promise<TEntry>;
|
|
29
|
+
appendRecord<TNewRecord extends NewRecord>(record: TNewRecord): Promise<TNewRecord & Pick<RecordBase, "seq" | "timestamp">>;
|
|
30
|
+
findRecords<K extends LaneRecord["type"]>(query: RecordQuery & {
|
|
31
|
+
type: K;
|
|
32
|
+
}): Promise<Extract<LaneRecord, {
|
|
33
|
+
type: K;
|
|
34
|
+
}>[]>;
|
|
35
|
+
findRecords(query?: RecordQuery): Promise<LaneRecord[]>;
|
|
36
|
+
findOpenOperations(lane: string, options?: {
|
|
37
|
+
limit?: number;
|
|
38
|
+
}): Promise<OperationStartedRecord[]>;
|
|
39
|
+
getLog(options?: LogOptions): Promise<LogItem[]>;
|
|
40
|
+
private getLeafIdForLane;
|
|
41
|
+
private queryEntries;
|
|
42
|
+
private queryBranchEntries;
|
|
43
|
+
private queryRecords;
|
|
44
|
+
private queryLog;
|
|
45
|
+
private appendMessageToLane;
|
|
46
|
+
private appendCustomEntryToLane;
|
|
47
|
+
private commitEntry;
|
|
48
|
+
private commitRecord;
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=session.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../../src/harness/session/session.ts"],"names":[],"mappings":"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","sourcesContent":["import { uuidv7 } from \"@sammorrowdrums/mcpi-ai\";\nimport type { AgentMessage } from \"../../types.ts\";\nimport type {\n\tBranchBounds,\n\tEntry,\n\tEntryQuery,\n\tIdGenerator,\n\tLanePointer,\n\tLaneRecord,\n\tLogItem,\n\tLogOptions,\n\tNewRecord,\n\tOperationStartedRecord,\n\tProvisionedEntry,\n\tRecordBase,\n\tRecordQuery,\n\tSessionMetadata,\n\tSessionStats,\n\tSessionStorage,\n\tSessionTree,\n} from \"./types.ts\";\nimport { SessionError } from \"./types.ts\";\n\ntype JsonValidationFrame = { value: unknown } | { exit: object };\n\nfunction invalidPayload(reason: string): never {\n\tthrow new SessionError(\"invalid_payload\", `Durable payload ${reason}`);\n}\n\nfunction assertValidLimit(limit: number | undefined): void {\n\tif (limit !== undefined && (!Number.isInteger(limit) || limit <= 0)) {\n\t\tthrow new SessionError(\"invalid_query\", \"limit must be a positive integer\");\n\t}\n}\n\nfunction assertValidCursor(afterSeq: number | undefined): void {\n\tif (afterSeq !== undefined && (!Number.isInteger(afterSeq) || afterSeq < 0)) {\n\t\tthrow new SessionError(\"invalid_query\", \"cursor sequence must be a non-negative integer\");\n\t}\n}\n\nexport function assertJsonSerializable(value: unknown): void {\n\tconst active = new WeakSet<object>();\n\tconst stack: JsonValidationFrame[] = [{ value }];\n\twhile (stack.length > 0) {\n\t\tconst frame = stack.pop()!;\n\t\tif (\"exit\" in frame) {\n\t\t\tactive.delete(frame.exit);\n\t\t\tcontinue;\n\t\t}\n\t\tconst candidate = frame.value;\n\t\tif (candidate === null || typeof candidate === \"string\" || typeof candidate === \"boolean\") {\n\t\t\tcontinue;\n\t\t}\n\t\tif (typeof candidate === \"number\") {\n\t\t\tif (!Number.isFinite(candidate)) invalidPayload(\"contains a non-finite number\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (typeof candidate !== \"object\") invalidPayload(`contains ${typeof candidate}`);\n\t\tif (active.has(candidate)) invalidPayload(\"contains a cycle\");\n\t\tactive.add(candidate);\n\t\tstack.push({ exit: candidate });\n\n\t\tif (Array.isArray(candidate)) {\n\t\t\tif (Object.getPrototypeOf(candidate) !== Array.prototype) {\n\t\t\t\tinvalidPayload(\"contains a non-standard array\");\n\t\t\t}\n\t\t\tif (\n\t\t\t\tObject.getOwnPropertySymbols(candidate).length > 0 ||\n\t\t\t\tObject.getOwnPropertyNames(candidate).length !== candidate.length + 1\n\t\t\t) {\n\t\t\t\tinvalidPayload(\"contains an array with unsupported properties\");\n\t\t\t}\n\t\t\tfor (let index = candidate.length - 1; index >= 0; index--) {\n\t\t\t\tif (!Object.hasOwn(candidate, index)) invalidPayload(\"contains a sparse array\");\n\t\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(candidate, index)!;\n\t\t\t\tif (!(\"value\" in descriptor)) invalidPayload(\"contains an array accessor\");\n\t\t\t\tstack.push({ value: descriptor.value });\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst prototype = Object.getPrototypeOf(candidate);\n\t\tif (prototype !== Object.prototype && prototype !== null) {\n\t\t\tinvalidPayload(\"contains a non-plain object\");\n\t\t}\n\t\tif (Object.getOwnPropertySymbols(candidate).length > 0) {\n\t\t\tinvalidPayload(\"contains a symbol-keyed property\");\n\t\t}\n\t\tconst keys = Object.keys(candidate);\n\t\tif (Object.getOwnPropertyNames(candidate).length !== keys.length) {\n\t\t\tinvalidPayload(\"contains a non-enumerable property\");\n\t\t}\n\t\tfor (let index = keys.length - 1; index >= 0; index--) {\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(candidate, keys[index]!)!;\n\t\t\tif (!(\"value\" in descriptor)) invalidPayload(\"contains an accessor\");\n\t\t\tstack.push({ value: descriptor.value });\n\t\t}\n\t}\n}\n\nexport class Session<TMetadata extends SessionMetadata = SessionMetadata> implements SessionTree {\n\tprivate readonly storage: SessionStorage<TMetadata>;\n\treadonly idGenerator: IdGenerator;\n\n\tconstructor(storage: SessionStorage<TMetadata>, options: { idGenerator?: IdGenerator } = {}) {\n\t\tthis.storage = storage;\n\t\tthis.idGenerator = options.idGenerator ?? { next: () => uuidv7() };\n\t}\n\n\tasync getMetadata(): Promise<TMetadata> {\n\t\treturn this.storage.getMetadata();\n\t}\n\n\tview(lane: string): SessionTree {\n\t\tif (lane === \"main\") return this;\n\t\treturn {\n\t\t\tgetLeafId: () => this.getLeafIdForLane(lane),\n\t\t\tgetEntry: (id) => this.getEntry(id),\n\t\t\tgetStats: () => this.getStats(),\n\t\t\tgetName: () => this.getName(),\n\t\t\tsetName: (name) => this.setName(name),\n\t\t\tgetLabel: (targetId) => this.getLabel(targetId),\n\t\t\tsetLabel: (targetId, label) => this.setLabel(targetId, label),\n\t\t\tfindEntries: (query) => this.queryEntries(query),\n\t\t\tfindEntry: async (query = {}) => (await this.queryEntries(query, 1))[0],\n\t\t\tfindEntriesOnBranch: (query) => this.queryBranchEntries(lane, query),\n\t\t\tfindEntryOnBranch: async (query = {}) => (await this.queryBranchEntries(lane, query, 1))[0],\n\t\t\tappendMessage: (message) => this.appendMessageToLane(lane, message),\n\t\t\tappendCustomEntry: (customType, data) => this.appendCustomEntryToLane(lane, customType, data),\n\t\t};\n\t}\n\n\tasync getLeafId(): Promise<string | null> {\n\t\treturn this.getLeafIdForLane(\"main\");\n\t}\n\n\tasync getEntry(id: string): Promise<Entry | undefined> {\n\t\treturn this.storage.getEntry(id);\n\t}\n\n\tasync getStats(): Promise<SessionStats> {\n\t\treturn this.storage.getStats();\n\t}\n\n\tasync getName(): Promise<string | undefined> {\n\t\treturn this.storage.getName();\n\t}\n\n\tasync setName(name: string | undefined): Promise<void> {\n\t\tawait this.storage.setName(name);\n\t}\n\n\tasync getLabel(targetId: string): Promise<string | undefined> {\n\t\treturn this.storage.getLabel(targetId);\n\t}\n\n\tasync setLabel(targetId: string, label: string | undefined): Promise<void> {\n\t\tawait this.storage.setLabel(targetId, label);\n\t}\n\n\tasync findEntries(query?: EntryQuery): Promise<Entry[]> {\n\t\treturn this.queryEntries(query);\n\t}\n\n\tasync findEntry(query: EntryQuery = {}): Promise<Entry | undefined> {\n\t\treturn (await this.queryEntries(query, 1))[0];\n\t}\n\n\tasync findEntriesOnBranch(query?: EntryQuery & BranchBounds): Promise<Entry[]> {\n\t\treturn this.queryBranchEntries(\"main\", query);\n\t}\n\n\tasync findEntryOnBranch(query: EntryQuery & BranchBounds = {}): Promise<Entry | undefined> {\n\t\treturn (await this.queryBranchEntries(\"main\", query, 1))[0];\n\t}\n\n\tasync appendMessage(message: AgentMessage): Promise<string> {\n\t\treturn this.appendMessageToLane(\"main\", message);\n\t}\n\n\tasync appendCustomEntry(customType: string, data?: unknown): Promise<string> {\n\t\treturn this.appendCustomEntryToLane(\"main\", customType, data);\n\t}\n\n\tasync getLanes(): Promise<LanePointer[]> {\n\t\treturn this.storage.getLanes();\n\t}\n\n\tasync createLane(lane: string, at: string | null): Promise<void> {\n\t\tawait this.storage.createLane(lane, at);\n\t}\n\n\tasync moveLane(lane: string, to: string | null): Promise<void> {\n\t\tawait this.storage.moveLane(lane, to);\n\t}\n\n\tasync appendEntry<TEntry extends Entry>(entry: ProvisionedEntry<TEntry>, lane: string): Promise<TEntry> {\n\t\treturn this.commitEntry(entry, lane);\n\t}\n\n\tasync appendRecord<TNewRecord extends NewRecord>(\n\t\trecord: TNewRecord,\n\t): Promise<TNewRecord & Pick<RecordBase, \"seq\" | \"timestamp\">>;\n\tasync appendRecord(record: NewRecord): Promise<LaneRecord> {\n\t\treturn this.commitRecord(record);\n\t}\n\n\tasync findRecords<K extends LaneRecord[\"type\"]>(\n\t\tquery: RecordQuery & { type: K },\n\t): Promise<Extract<LaneRecord, { type: K }>[]>;\n\tasync findRecords(query?: RecordQuery): Promise<LaneRecord[]>;\n\tasync findRecords(query?: RecordQuery): Promise<LaneRecord[]> {\n\t\treturn this.queryRecords(query);\n\t}\n\n\tasync findOpenOperations(lane: string, options?: { limit?: number }): Promise<OperationStartedRecord[]> {\n\t\tassertValidLimit(options?.limit);\n\t\treturn this.storage.findOpenOperations(lane, options);\n\t}\n\n\tasync getLog(options?: LogOptions): Promise<LogItem[]> {\n\t\treturn this.queryLog(options);\n\t}\n\n\t/** Returns the lane's current leaf, or null when empty. Throws when the lane does not exist. */\n\tprivate async getLeafIdForLane(lane: string): Promise<string | null> {\n\t\tconst pointer = (await this.getLanes()).find((candidate) => candidate.lane === lane);\n\t\tif (!pointer) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\t\treturn pointer.leafId;\n\t}\n\n\tprivate async queryEntries(query: EntryQuery = {}, resultLimit = query.limit): Promise<Entry[]> {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.cursor?.afterSeq);\n\t\treturn this.storage.findEntries(resultLimit === query.limit ? query : { ...query, limit: resultLimit });\n\t}\n\n\t/**\n\t * Queries from `query.start` toward the root, defaulting to the lane's current leaf.\n\t * `resultLimit` lets single-entry queries cap results without changing the caller's query.\n\t */\n\tprivate async queryBranchEntries(\n\t\tdefaultLane: string,\n\t\tquery: EntryQuery & BranchBounds = {},\n\t\tresultLimit = query.limit,\n\t): Promise<Entry[]> {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.cursor?.afterSeq);\n\t\tconst start = query.start ?? (await this.getLeafIdForLane(defaultLane));\n\t\tif (start === null) return [];\n\t\tconst storageQuery = resultLimit === query.limit ? query : { ...query, limit: resultLimit };\n\t\treturn this.storage.findEntriesOnBranch({ ...storageQuery, start });\n\t}\n\n\tprivate async queryRecords(query: RecordQuery = {}): Promise<LaneRecord[]> {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.afterSeq);\n\t\tif (query.operationKind !== undefined && query.type !== \"operation_started\") {\n\t\t\tthrow new SessionError(\"invalid_query\", 'operationKind requires type \"operation_started\"');\n\t\t}\n\t\treturn this.storage.findRecords(query);\n\t}\n\n\tprivate async queryLog(options: LogOptions = {}): Promise<LogItem[]> {\n\t\tassertValidLimit(options.limit);\n\t\tassertValidCursor(options.afterSeq);\n\t\treturn this.storage.getLog(options);\n\t}\n\n\tprivate async appendMessageToLane(lane: string, message: AgentMessage): Promise<string> {\n\t\tconst entry = await this.commitEntry({ type: \"message\", id: this.idGenerator.next(), message }, lane);\n\t\treturn entry.id;\n\t}\n\n\tprivate async appendCustomEntryToLane(lane: string, customType: string, data?: unknown): Promise<string> {\n\t\tconst entry = await this.commitEntry(\n\t\t\tdata === undefined\n\t\t\t\t? { type: \"custom\", id: this.idGenerator.next(), customType }\n\t\t\t\t: { type: \"custom\", id: this.idGenerator.next(), customType, data },\n\t\t\tlane,\n\t\t);\n\t\treturn entry.id;\n\t}\n\n\tprivate async commitEntry<TEntry extends Entry>(entry: ProvisionedEntry<TEntry>, lane: string): Promise<TEntry> {\n\t\tassertJsonSerializable(entry);\n\t\treturn this.storage.appendEntry(entry, lane);\n\t}\n\n\tprivate async commitRecord<TNewRecord extends NewRecord>(\n\t\trecord: TNewRecord,\n\t): Promise<TNewRecord & Pick<RecordBase, \"seq\" | \"timestamp\">> {\n\t\tassertJsonSerializable(record);\n\t\treturn this.storage.appendRecord<LaneRecord>(record) as unknown as Promise<\n\t\t\tTNewRecord & Pick<RecordBase, \"seq\" | \"timestamp\">\n\t\t>;\n\t}\n}\n"]}
|
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
import { uuidv7 } from "@sammorrowdrums/mcpi-ai";
|
|
2
|
+
import { SessionError } from "./types.js";
|
|
3
|
+
function invalidPayload(reason) {
|
|
4
|
+
throw new SessionError("invalid_payload", `Durable payload ${reason}`);
|
|
5
|
+
}
|
|
6
|
+
function assertValidLimit(limit) {
|
|
7
|
+
if (limit !== undefined && (!Number.isInteger(limit) || limit <= 0)) {
|
|
8
|
+
throw new SessionError("invalid_query", "limit must be a positive integer");
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
function assertValidCursor(afterSeq) {
|
|
12
|
+
if (afterSeq !== undefined && (!Number.isInteger(afterSeq) || afterSeq < 0)) {
|
|
13
|
+
throw new SessionError("invalid_query", "cursor sequence must be a non-negative integer");
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function assertJsonSerializable(value) {
|
|
17
|
+
const active = new WeakSet();
|
|
18
|
+
const stack = [{ value }];
|
|
19
|
+
while (stack.length > 0) {
|
|
20
|
+
const frame = stack.pop();
|
|
21
|
+
if ("exit" in frame) {
|
|
22
|
+
active.delete(frame.exit);
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
const candidate = frame.value;
|
|
26
|
+
if (candidate === null || typeof candidate === "string" || typeof candidate === "boolean") {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (typeof candidate === "number") {
|
|
30
|
+
if (!Number.isFinite(candidate))
|
|
31
|
+
invalidPayload("contains a non-finite number");
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (typeof candidate !== "object")
|
|
35
|
+
invalidPayload(`contains ${typeof candidate}`);
|
|
36
|
+
if (active.has(candidate))
|
|
37
|
+
invalidPayload("contains a cycle");
|
|
38
|
+
active.add(candidate);
|
|
39
|
+
stack.push({ exit: candidate });
|
|
40
|
+
if (Array.isArray(candidate)) {
|
|
41
|
+
if (Object.getPrototypeOf(candidate) !== Array.prototype) {
|
|
42
|
+
invalidPayload("contains a non-standard array");
|
|
43
|
+
}
|
|
44
|
+
if (Object.getOwnPropertySymbols(candidate).length > 0 ||
|
|
45
|
+
Object.getOwnPropertyNames(candidate).length !== candidate.length + 1) {
|
|
46
|
+
invalidPayload("contains an array with unsupported properties");
|
|
47
|
+
}
|
|
48
|
+
for (let index = candidate.length - 1; index >= 0; index--) {
|
|
49
|
+
if (!Object.hasOwn(candidate, index))
|
|
50
|
+
invalidPayload("contains a sparse array");
|
|
51
|
+
const descriptor = Object.getOwnPropertyDescriptor(candidate, index);
|
|
52
|
+
if (!("value" in descriptor))
|
|
53
|
+
invalidPayload("contains an array accessor");
|
|
54
|
+
stack.push({ value: descriptor.value });
|
|
55
|
+
}
|
|
56
|
+
continue;
|
|
57
|
+
}
|
|
58
|
+
const prototype = Object.getPrototypeOf(candidate);
|
|
59
|
+
if (prototype !== Object.prototype && prototype !== null) {
|
|
60
|
+
invalidPayload("contains a non-plain object");
|
|
61
|
+
}
|
|
62
|
+
if (Object.getOwnPropertySymbols(candidate).length > 0) {
|
|
63
|
+
invalidPayload("contains a symbol-keyed property");
|
|
64
|
+
}
|
|
65
|
+
const keys = Object.keys(candidate);
|
|
66
|
+
if (Object.getOwnPropertyNames(candidate).length !== keys.length) {
|
|
67
|
+
invalidPayload("contains a non-enumerable property");
|
|
68
|
+
}
|
|
69
|
+
for (let index = keys.length - 1; index >= 0; index--) {
|
|
70
|
+
const descriptor = Object.getOwnPropertyDescriptor(candidate, keys[index]);
|
|
71
|
+
if (!("value" in descriptor))
|
|
72
|
+
invalidPayload("contains an accessor");
|
|
73
|
+
stack.push({ value: descriptor.value });
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
export class Session {
|
|
78
|
+
storage;
|
|
79
|
+
idGenerator;
|
|
80
|
+
constructor(storage, options = {}) {
|
|
81
|
+
this.storage = storage;
|
|
82
|
+
this.idGenerator = options.idGenerator ?? { next: () => uuidv7() };
|
|
83
|
+
}
|
|
84
|
+
async getMetadata() {
|
|
85
|
+
return this.storage.getMetadata();
|
|
86
|
+
}
|
|
87
|
+
view(lane) {
|
|
88
|
+
if (lane === "main")
|
|
89
|
+
return this;
|
|
90
|
+
return {
|
|
91
|
+
getLeafId: () => this.getLeafIdForLane(lane),
|
|
92
|
+
getEntry: (id) => this.getEntry(id),
|
|
93
|
+
getStats: () => this.getStats(),
|
|
94
|
+
getName: () => this.getName(),
|
|
95
|
+
setName: (name) => this.setName(name),
|
|
96
|
+
getLabel: (targetId) => this.getLabel(targetId),
|
|
97
|
+
setLabel: (targetId, label) => this.setLabel(targetId, label),
|
|
98
|
+
findEntries: (query) => this.queryEntries(query),
|
|
99
|
+
findEntry: async (query = {}) => (await this.queryEntries(query, 1))[0],
|
|
100
|
+
findEntriesOnBranch: (query) => this.queryBranchEntries(lane, query),
|
|
101
|
+
findEntryOnBranch: async (query = {}) => (await this.queryBranchEntries(lane, query, 1))[0],
|
|
102
|
+
appendMessage: (message) => this.appendMessageToLane(lane, message),
|
|
103
|
+
appendCustomEntry: (customType, data) => this.appendCustomEntryToLane(lane, customType, data),
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
async getLeafId() {
|
|
107
|
+
return this.getLeafIdForLane("main");
|
|
108
|
+
}
|
|
109
|
+
async getEntry(id) {
|
|
110
|
+
return this.storage.getEntry(id);
|
|
111
|
+
}
|
|
112
|
+
async getStats() {
|
|
113
|
+
return this.storage.getStats();
|
|
114
|
+
}
|
|
115
|
+
async getName() {
|
|
116
|
+
return this.storage.getName();
|
|
117
|
+
}
|
|
118
|
+
async setName(name) {
|
|
119
|
+
await this.storage.setName(name);
|
|
120
|
+
}
|
|
121
|
+
async getLabel(targetId) {
|
|
122
|
+
return this.storage.getLabel(targetId);
|
|
123
|
+
}
|
|
124
|
+
async setLabel(targetId, label) {
|
|
125
|
+
await this.storage.setLabel(targetId, label);
|
|
126
|
+
}
|
|
127
|
+
async findEntries(query) {
|
|
128
|
+
return this.queryEntries(query);
|
|
129
|
+
}
|
|
130
|
+
async findEntry(query = {}) {
|
|
131
|
+
return (await this.queryEntries(query, 1))[0];
|
|
132
|
+
}
|
|
133
|
+
async findEntriesOnBranch(query) {
|
|
134
|
+
return this.queryBranchEntries("main", query);
|
|
135
|
+
}
|
|
136
|
+
async findEntryOnBranch(query = {}) {
|
|
137
|
+
return (await this.queryBranchEntries("main", query, 1))[0];
|
|
138
|
+
}
|
|
139
|
+
async appendMessage(message) {
|
|
140
|
+
return this.appendMessageToLane("main", message);
|
|
141
|
+
}
|
|
142
|
+
async appendCustomEntry(customType, data) {
|
|
143
|
+
return this.appendCustomEntryToLane("main", customType, data);
|
|
144
|
+
}
|
|
145
|
+
async getLanes() {
|
|
146
|
+
return this.storage.getLanes();
|
|
147
|
+
}
|
|
148
|
+
async createLane(lane, at) {
|
|
149
|
+
await this.storage.createLane(lane, at);
|
|
150
|
+
}
|
|
151
|
+
async moveLane(lane, to) {
|
|
152
|
+
await this.storage.moveLane(lane, to);
|
|
153
|
+
}
|
|
154
|
+
async appendEntry(entry, lane) {
|
|
155
|
+
return this.commitEntry(entry, lane);
|
|
156
|
+
}
|
|
157
|
+
async appendRecord(record) {
|
|
158
|
+
return this.commitRecord(record);
|
|
159
|
+
}
|
|
160
|
+
async findRecords(query) {
|
|
161
|
+
return this.queryRecords(query);
|
|
162
|
+
}
|
|
163
|
+
async findOpenOperations(lane, options) {
|
|
164
|
+
assertValidLimit(options?.limit);
|
|
165
|
+
return this.storage.findOpenOperations(lane, options);
|
|
166
|
+
}
|
|
167
|
+
async getLog(options) {
|
|
168
|
+
return this.queryLog(options);
|
|
169
|
+
}
|
|
170
|
+
/** Returns the lane's current leaf, or null when empty. Throws when the lane does not exist. */
|
|
171
|
+
async getLeafIdForLane(lane) {
|
|
172
|
+
const pointer = (await this.getLanes()).find((candidate) => candidate.lane === lane);
|
|
173
|
+
if (!pointer)
|
|
174
|
+
throw new SessionError("invalid_lane", `Lane not found: ${lane}`);
|
|
175
|
+
return pointer.leafId;
|
|
176
|
+
}
|
|
177
|
+
async queryEntries(query = {}, resultLimit = query.limit) {
|
|
178
|
+
assertValidLimit(query.limit);
|
|
179
|
+
assertValidCursor(query.cursor?.afterSeq);
|
|
180
|
+
return this.storage.findEntries(resultLimit === query.limit ? query : { ...query, limit: resultLimit });
|
|
181
|
+
}
|
|
182
|
+
/**
|
|
183
|
+
* Queries from `query.start` toward the root, defaulting to the lane's current leaf.
|
|
184
|
+
* `resultLimit` lets single-entry queries cap results without changing the caller's query.
|
|
185
|
+
*/
|
|
186
|
+
async queryBranchEntries(defaultLane, query = {}, resultLimit = query.limit) {
|
|
187
|
+
assertValidLimit(query.limit);
|
|
188
|
+
assertValidCursor(query.cursor?.afterSeq);
|
|
189
|
+
const start = query.start ?? (await this.getLeafIdForLane(defaultLane));
|
|
190
|
+
if (start === null)
|
|
191
|
+
return [];
|
|
192
|
+
const storageQuery = resultLimit === query.limit ? query : { ...query, limit: resultLimit };
|
|
193
|
+
return this.storage.findEntriesOnBranch({ ...storageQuery, start });
|
|
194
|
+
}
|
|
195
|
+
async queryRecords(query = {}) {
|
|
196
|
+
assertValidLimit(query.limit);
|
|
197
|
+
assertValidCursor(query.afterSeq);
|
|
198
|
+
if (query.operationKind !== undefined && query.type !== "operation_started") {
|
|
199
|
+
throw new SessionError("invalid_query", 'operationKind requires type "operation_started"');
|
|
200
|
+
}
|
|
201
|
+
return this.storage.findRecords(query);
|
|
202
|
+
}
|
|
203
|
+
async queryLog(options = {}) {
|
|
204
|
+
assertValidLimit(options.limit);
|
|
205
|
+
assertValidCursor(options.afterSeq);
|
|
206
|
+
return this.storage.getLog(options);
|
|
207
|
+
}
|
|
208
|
+
async appendMessageToLane(lane, message) {
|
|
209
|
+
const entry = await this.commitEntry({ type: "message", id: this.idGenerator.next(), message }, lane);
|
|
210
|
+
return entry.id;
|
|
211
|
+
}
|
|
212
|
+
async appendCustomEntryToLane(lane, customType, data) {
|
|
213
|
+
const entry = await this.commitEntry(data === undefined
|
|
214
|
+
? { type: "custom", id: this.idGenerator.next(), customType }
|
|
215
|
+
: { type: "custom", id: this.idGenerator.next(), customType, data }, lane);
|
|
216
|
+
return entry.id;
|
|
217
|
+
}
|
|
218
|
+
async commitEntry(entry, lane) {
|
|
219
|
+
assertJsonSerializable(entry);
|
|
220
|
+
return this.storage.appendEntry(entry, lane);
|
|
221
|
+
}
|
|
222
|
+
async commitRecord(record) {
|
|
223
|
+
assertJsonSerializable(record);
|
|
224
|
+
return this.storage.appendRecord(record);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../../src/harness/session/session.ts"],"names":[],"mappings":"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","sourcesContent":["import { uuidv7 } from \"@sammorrowdrums/mcpi-ai\";\nimport type { AgentMessage } from \"../../types.ts\";\nimport type {\n\tBranchBounds,\n\tEntry,\n\tEntryQuery,\n\tIdGenerator,\n\tLanePointer,\n\tLaneRecord,\n\tLogItem,\n\tLogOptions,\n\tNewRecord,\n\tOperationStartedRecord,\n\tProvisionedEntry,\n\tRecordBase,\n\tRecordQuery,\n\tSessionMetadata,\n\tSessionStats,\n\tSessionStorage,\n\tSessionTree,\n} from \"./types.ts\";\nimport { SessionError } from \"./types.ts\";\n\ntype JsonValidationFrame = { value: unknown } | { exit: object };\n\nfunction invalidPayload(reason: string): never {\n\tthrow new SessionError(\"invalid_payload\", `Durable payload ${reason}`);\n}\n\nfunction assertValidLimit(limit: number | undefined): void {\n\tif (limit !== undefined && (!Number.isInteger(limit) || limit <= 0)) {\n\t\tthrow new SessionError(\"invalid_query\", \"limit must be a positive integer\");\n\t}\n}\n\nfunction assertValidCursor(afterSeq: number | undefined): void {\n\tif (afterSeq !== undefined && (!Number.isInteger(afterSeq) || afterSeq < 0)) {\n\t\tthrow new SessionError(\"invalid_query\", \"cursor sequence must be a non-negative integer\");\n\t}\n}\n\nexport function assertJsonSerializable(value: unknown): void {\n\tconst active = new WeakSet<object>();\n\tconst stack: JsonValidationFrame[] = [{ value }];\n\twhile (stack.length > 0) {\n\t\tconst frame = stack.pop()!;\n\t\tif (\"exit\" in frame) {\n\t\t\tactive.delete(frame.exit);\n\t\t\tcontinue;\n\t\t}\n\t\tconst candidate = frame.value;\n\t\tif (candidate === null || typeof candidate === \"string\" || typeof candidate === \"boolean\") {\n\t\t\tcontinue;\n\t\t}\n\t\tif (typeof candidate === \"number\") {\n\t\t\tif (!Number.isFinite(candidate)) invalidPayload(\"contains a non-finite number\");\n\t\t\tcontinue;\n\t\t}\n\t\tif (typeof candidate !== \"object\") invalidPayload(`contains ${typeof candidate}`);\n\t\tif (active.has(candidate)) invalidPayload(\"contains a cycle\");\n\t\tactive.add(candidate);\n\t\tstack.push({ exit: candidate });\n\n\t\tif (Array.isArray(candidate)) {\n\t\t\tif (Object.getPrototypeOf(candidate) !== Array.prototype) {\n\t\t\t\tinvalidPayload(\"contains a non-standard array\");\n\t\t\t}\n\t\t\tif (\n\t\t\t\tObject.getOwnPropertySymbols(candidate).length > 0 ||\n\t\t\t\tObject.getOwnPropertyNames(candidate).length !== candidate.length + 1\n\t\t\t) {\n\t\t\t\tinvalidPayload(\"contains an array with unsupported properties\");\n\t\t\t}\n\t\t\tfor (let index = candidate.length - 1; index >= 0; index--) {\n\t\t\t\tif (!Object.hasOwn(candidate, index)) invalidPayload(\"contains a sparse array\");\n\t\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(candidate, index)!;\n\t\t\t\tif (!(\"value\" in descriptor)) invalidPayload(\"contains an array accessor\");\n\t\t\t\tstack.push({ value: descriptor.value });\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\n\t\tconst prototype = Object.getPrototypeOf(candidate);\n\t\tif (prototype !== Object.prototype && prototype !== null) {\n\t\t\tinvalidPayload(\"contains a non-plain object\");\n\t\t}\n\t\tif (Object.getOwnPropertySymbols(candidate).length > 0) {\n\t\t\tinvalidPayload(\"contains a symbol-keyed property\");\n\t\t}\n\t\tconst keys = Object.keys(candidate);\n\t\tif (Object.getOwnPropertyNames(candidate).length !== keys.length) {\n\t\t\tinvalidPayload(\"contains a non-enumerable property\");\n\t\t}\n\t\tfor (let index = keys.length - 1; index >= 0; index--) {\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(candidate, keys[index]!)!;\n\t\t\tif (!(\"value\" in descriptor)) invalidPayload(\"contains an accessor\");\n\t\t\tstack.push({ value: descriptor.value });\n\t\t}\n\t}\n}\n\nexport class Session<TMetadata extends SessionMetadata = SessionMetadata> implements SessionTree {\n\tprivate readonly storage: SessionStorage<TMetadata>;\n\treadonly idGenerator: IdGenerator;\n\n\tconstructor(storage: SessionStorage<TMetadata>, options: { idGenerator?: IdGenerator } = {}) {\n\t\tthis.storage = storage;\n\t\tthis.idGenerator = options.idGenerator ?? { next: () => uuidv7() };\n\t}\n\n\tasync getMetadata(): Promise<TMetadata> {\n\t\treturn this.storage.getMetadata();\n\t}\n\n\tview(lane: string): SessionTree {\n\t\tif (lane === \"main\") return this;\n\t\treturn {\n\t\t\tgetLeafId: () => this.getLeafIdForLane(lane),\n\t\t\tgetEntry: (id) => this.getEntry(id),\n\t\t\tgetStats: () => this.getStats(),\n\t\t\tgetName: () => this.getName(),\n\t\t\tsetName: (name) => this.setName(name),\n\t\t\tgetLabel: (targetId) => this.getLabel(targetId),\n\t\t\tsetLabel: (targetId, label) => this.setLabel(targetId, label),\n\t\t\tfindEntries: (query) => this.queryEntries(query),\n\t\t\tfindEntry: async (query = {}) => (await this.queryEntries(query, 1))[0],\n\t\t\tfindEntriesOnBranch: (query) => this.queryBranchEntries(lane, query),\n\t\t\tfindEntryOnBranch: async (query = {}) => (await this.queryBranchEntries(lane, query, 1))[0],\n\t\t\tappendMessage: (message) => this.appendMessageToLane(lane, message),\n\t\t\tappendCustomEntry: (customType, data) => this.appendCustomEntryToLane(lane, customType, data),\n\t\t};\n\t}\n\n\tasync getLeafId(): Promise<string | null> {\n\t\treturn this.getLeafIdForLane(\"main\");\n\t}\n\n\tasync getEntry(id: string): Promise<Entry | undefined> {\n\t\treturn this.storage.getEntry(id);\n\t}\n\n\tasync getStats(): Promise<SessionStats> {\n\t\treturn this.storage.getStats();\n\t}\n\n\tasync getName(): Promise<string | undefined> {\n\t\treturn this.storage.getName();\n\t}\n\n\tasync setName(name: string | undefined): Promise<void> {\n\t\tawait this.storage.setName(name);\n\t}\n\n\tasync getLabel(targetId: string): Promise<string | undefined> {\n\t\treturn this.storage.getLabel(targetId);\n\t}\n\n\tasync setLabel(targetId: string, label: string | undefined): Promise<void> {\n\t\tawait this.storage.setLabel(targetId, label);\n\t}\n\n\tasync findEntries(query?: EntryQuery): Promise<Entry[]> {\n\t\treturn this.queryEntries(query);\n\t}\n\n\tasync findEntry(query: EntryQuery = {}): Promise<Entry | undefined> {\n\t\treturn (await this.queryEntries(query, 1))[0];\n\t}\n\n\tasync findEntriesOnBranch(query?: EntryQuery & BranchBounds): Promise<Entry[]> {\n\t\treturn this.queryBranchEntries(\"main\", query);\n\t}\n\n\tasync findEntryOnBranch(query: EntryQuery & BranchBounds = {}): Promise<Entry | undefined> {\n\t\treturn (await this.queryBranchEntries(\"main\", query, 1))[0];\n\t}\n\n\tasync appendMessage(message: AgentMessage): Promise<string> {\n\t\treturn this.appendMessageToLane(\"main\", message);\n\t}\n\n\tasync appendCustomEntry(customType: string, data?: unknown): Promise<string> {\n\t\treturn this.appendCustomEntryToLane(\"main\", customType, data);\n\t}\n\n\tasync getLanes(): Promise<LanePointer[]> {\n\t\treturn this.storage.getLanes();\n\t}\n\n\tasync createLane(lane: string, at: string | null): Promise<void> {\n\t\tawait this.storage.createLane(lane, at);\n\t}\n\n\tasync moveLane(lane: string, to: string | null): Promise<void> {\n\t\tawait this.storage.moveLane(lane, to);\n\t}\n\n\tasync appendEntry<TEntry extends Entry>(entry: ProvisionedEntry<TEntry>, lane: string): Promise<TEntry> {\n\t\treturn this.commitEntry(entry, lane);\n\t}\n\n\tasync appendRecord<TNewRecord extends NewRecord>(\n\t\trecord: TNewRecord,\n\t): Promise<TNewRecord & Pick<RecordBase, \"seq\" | \"timestamp\">>;\n\tasync appendRecord(record: NewRecord): Promise<LaneRecord> {\n\t\treturn this.commitRecord(record);\n\t}\n\n\tasync findRecords<K extends LaneRecord[\"type\"]>(\n\t\tquery: RecordQuery & { type: K },\n\t): Promise<Extract<LaneRecord, { type: K }>[]>;\n\tasync findRecords(query?: RecordQuery): Promise<LaneRecord[]>;\n\tasync findRecords(query?: RecordQuery): Promise<LaneRecord[]> {\n\t\treturn this.queryRecords(query);\n\t}\n\n\tasync findOpenOperations(lane: string, options?: { limit?: number }): Promise<OperationStartedRecord[]> {\n\t\tassertValidLimit(options?.limit);\n\t\treturn this.storage.findOpenOperations(lane, options);\n\t}\n\n\tasync getLog(options?: LogOptions): Promise<LogItem[]> {\n\t\treturn this.queryLog(options);\n\t}\n\n\t/** Returns the lane's current leaf, or null when empty. Throws when the lane does not exist. */\n\tprivate async getLeafIdForLane(lane: string): Promise<string | null> {\n\t\tconst pointer = (await this.getLanes()).find((candidate) => candidate.lane === lane);\n\t\tif (!pointer) throw new SessionError(\"invalid_lane\", `Lane not found: ${lane}`);\n\t\treturn pointer.leafId;\n\t}\n\n\tprivate async queryEntries(query: EntryQuery = {}, resultLimit = query.limit): Promise<Entry[]> {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.cursor?.afterSeq);\n\t\treturn this.storage.findEntries(resultLimit === query.limit ? query : { ...query, limit: resultLimit });\n\t}\n\n\t/**\n\t * Queries from `query.start` toward the root, defaulting to the lane's current leaf.\n\t * `resultLimit` lets single-entry queries cap results without changing the caller's query.\n\t */\n\tprivate async queryBranchEntries(\n\t\tdefaultLane: string,\n\t\tquery: EntryQuery & BranchBounds = {},\n\t\tresultLimit = query.limit,\n\t): Promise<Entry[]> {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.cursor?.afterSeq);\n\t\tconst start = query.start ?? (await this.getLeafIdForLane(defaultLane));\n\t\tif (start === null) return [];\n\t\tconst storageQuery = resultLimit === query.limit ? query : { ...query, limit: resultLimit };\n\t\treturn this.storage.findEntriesOnBranch({ ...storageQuery, start });\n\t}\n\n\tprivate async queryRecords(query: RecordQuery = {}): Promise<LaneRecord[]> {\n\t\tassertValidLimit(query.limit);\n\t\tassertValidCursor(query.afterSeq);\n\t\tif (query.operationKind !== undefined && query.type !== \"operation_started\") {\n\t\t\tthrow new SessionError(\"invalid_query\", 'operationKind requires type \"operation_started\"');\n\t\t}\n\t\treturn this.storage.findRecords(query);\n\t}\n\n\tprivate async queryLog(options: LogOptions = {}): Promise<LogItem[]> {\n\t\tassertValidLimit(options.limit);\n\t\tassertValidCursor(options.afterSeq);\n\t\treturn this.storage.getLog(options);\n\t}\n\n\tprivate async appendMessageToLane(lane: string, message: AgentMessage): Promise<string> {\n\t\tconst entry = await this.commitEntry({ type: \"message\", id: this.idGenerator.next(), message }, lane);\n\t\treturn entry.id;\n\t}\n\n\tprivate async appendCustomEntryToLane(lane: string, customType: string, data?: unknown): Promise<string> {\n\t\tconst entry = await this.commitEntry(\n\t\t\tdata === undefined\n\t\t\t\t? { type: \"custom\", id: this.idGenerator.next(), customType }\n\t\t\t\t: { type: \"custom\", id: this.idGenerator.next(), customType, data },\n\t\t\tlane,\n\t\t);\n\t\treturn entry.id;\n\t}\n\n\tprivate async commitEntry<TEntry extends Entry>(entry: ProvisionedEntry<TEntry>, lane: string): Promise<TEntry> {\n\t\tassertJsonSerializable(entry);\n\t\treturn this.storage.appendEntry(entry, lane);\n\t}\n\n\tprivate async commitRecord<TNewRecord extends NewRecord>(\n\t\trecord: TNewRecord,\n\t): Promise<TNewRecord & Pick<RecordBase, \"seq\" | \"timestamp\">> {\n\t\tassertJsonSerializable(record);\n\t\treturn this.storage.appendRecord<LaneRecord>(record) as unknown as Promise<\n\t\t\tTNewRecord & Pick<RecordBase, \"seq\" | \"timestamp\">\n\t\t>;\n\t}\n}\n"]}
|