@handy_wote/pi-storage-sqlite-node 0.81.1

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.
Files changed (52) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/README.md +5 -0
  3. package/dist/index.d.ts +6 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +81 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/sqlite/index.d.ts +5 -0
  8. package/dist/sqlite/index.d.ts.map +1 -0
  9. package/dist/sqlite/index.js +5 -0
  10. package/dist/sqlite/index.js.map +1 -0
  11. package/dist/sqlite/migrations/001_initial.sql +59 -0
  12. package/dist/sqlite/migrations.d.ts +9 -0
  13. package/dist/sqlite/migrations.d.ts.map +1 -0
  14. package/dist/sqlite/migrations.js +40 -0
  15. package/dist/sqlite/migrations.js.map +1 -0
  16. package/dist/sqlite/repo.d.ts +26 -0
  17. package/dist/sqlite/repo.d.ts.map +1 -0
  18. package/dist/sqlite/repo.js +158 -0
  19. package/dist/sqlite/repo.js.map +1 -0
  20. package/dist/sqlite/storage/branch-entries.d.ts +8 -0
  21. package/dist/sqlite/storage/branch-entries.d.ts.map +1 -0
  22. package/dist/sqlite/storage/branch-entries.js +43 -0
  23. package/dist/sqlite/storage/branch-entries.js.map +1 -0
  24. package/dist/sqlite/storage/index.d.ts +38 -0
  25. package/dist/sqlite/storage/index.d.ts.map +1 -0
  26. package/dist/sqlite/storage/index.js +347 -0
  27. package/dist/sqlite/storage/index.js.map +1 -0
  28. package/dist/sqlite/storage/session-entries.d.ts +17 -0
  29. package/dist/sqlite/storage/session-entries.d.ts.map +1 -0
  30. package/dist/sqlite/storage/session-entries.js +173 -0
  31. package/dist/sqlite/storage/session-entries.js.map +1 -0
  32. package/dist/sqlite/storage/session-materialized.d.ts +43 -0
  33. package/dist/sqlite/storage/session-materialized.d.ts.map +1 -0
  34. package/dist/sqlite/storage/session-materialized.js +282 -0
  35. package/dist/sqlite/storage/session-materialized.js.map +1 -0
  36. package/dist/sqlite/storage/session-sequences.d.ts +4 -0
  37. package/dist/sqlite/storage/session-sequences.d.ts.map +1 -0
  38. package/dist/sqlite/storage/session-sequences.js +14 -0
  39. package/dist/sqlite/storage/session-sequences.js.map +1 -0
  40. package/dist/sqlite/storage/sessions.d.ts +11 -0
  41. package/dist/sqlite/storage/sessions.d.ts.map +1 -0
  42. package/dist/sqlite/storage/sessions.js +27 -0
  43. package/dist/sqlite/storage/sessions.js.map +1 -0
  44. package/dist/sqlite/storage/shared.d.ts +10 -0
  45. package/dist/sqlite/storage/shared.d.ts.map +1 -0
  46. package/dist/sqlite/storage/shared.js +25 -0
  47. package/dist/sqlite/storage/shared.js.map +1 -0
  48. package/dist/sqlite/types.d.ts +46 -0
  49. package/dist/sqlite/types.d.ts.map +1 -0
  50. package/dist/sqlite/types.js +2 -0
  51. package/dist/sqlite/types.js.map +1 -0
  52. package/package.json +38 -0
@@ -0,0 +1,173 @@
1
+ import { invalidEntry, isRecord } from "./shared.js";
2
+ function parsePayload(row) {
3
+ try {
4
+ return JSON.parse(row.payload);
5
+ }
6
+ catch (error) {
7
+ throw invalidEntry(`entry ${row.id} payload is not valid JSON`, error instanceof Error ? error : undefined);
8
+ }
9
+ }
10
+ function isTextImageContentArray(value) {
11
+ return (Array.isArray(value) &&
12
+ value.every((item) => isRecord(item) && typeof item.type === "string" && (item.type !== "text" || typeof item.text === "string")));
13
+ }
14
+ export function validateSessionTreeEntry(entry) {
15
+ if (typeof entry.id !== "string" || !entry.id)
16
+ throw invalidEntry("entry is missing id");
17
+ if (entry.parentId !== null && typeof entry.parentId !== "string") {
18
+ throw invalidEntry(`entry ${entry.id} has invalid parentId`);
19
+ }
20
+ if (typeof entry.timestamp !== "string" || !entry.timestamp) {
21
+ throw invalidEntry(`entry ${entry.id} is missing timestamp`);
22
+ }
23
+ switch (entry.type) {
24
+ case "message":
25
+ if (!isRecord(entry.message) || typeof entry.message.role !== "string") {
26
+ throw invalidEntry(`entry ${entry.id} is missing message payload`);
27
+ }
28
+ break;
29
+ case "thinking_level_change":
30
+ if (typeof entry.thinkingLevel !== "string")
31
+ throw invalidEntry(`entry ${entry.id} is missing thinkingLevel`);
32
+ break;
33
+ case "model_change":
34
+ if (typeof entry.provider !== "string" || typeof entry.modelId !== "string") {
35
+ throw invalidEntry(`entry ${entry.id} has invalid model_change payload`);
36
+ }
37
+ break;
38
+ case "active_tools_change":
39
+ if (!Array.isArray(entry.activeToolNames) ||
40
+ entry.activeToolNames.some((value) => typeof value !== "string")) {
41
+ throw invalidEntry(`entry ${entry.id} has invalid active_tools_change payload`);
42
+ }
43
+ break;
44
+ case "compaction":
45
+ if (typeof entry.summary !== "string" ||
46
+ typeof entry.firstKeptEntryId !== "string" ||
47
+ typeof entry.tokensBefore !== "number" ||
48
+ (entry.retainedTail !== undefined && !Array.isArray(entry.retainedTail))) {
49
+ throw invalidEntry(`entry ${entry.id} has invalid compaction payload`);
50
+ }
51
+ break;
52
+ case "branch_summary":
53
+ if (typeof entry.fromId !== "string" || typeof entry.summary !== "string") {
54
+ throw invalidEntry(`entry ${entry.id} has invalid branch_summary payload`);
55
+ }
56
+ break;
57
+ case "custom":
58
+ if (typeof entry.customType !== "string")
59
+ throw invalidEntry(`entry ${entry.id} has invalid custom payload`);
60
+ break;
61
+ case "custom_message":
62
+ if (typeof entry.customType !== "string" ||
63
+ typeof entry.display !== "boolean" ||
64
+ !(typeof entry.content === "string" || isTextImageContentArray(entry.content))) {
65
+ throw invalidEntry(`entry ${entry.id} has invalid custom_message payload`);
66
+ }
67
+ break;
68
+ case "label":
69
+ if (typeof entry.targetId !== "string" || (entry.label !== undefined && typeof entry.label !== "string")) {
70
+ throw invalidEntry(`entry ${entry.id} has invalid label payload`);
71
+ }
72
+ break;
73
+ case "session_info":
74
+ if (entry.name !== undefined && typeof entry.name !== "string") {
75
+ throw invalidEntry(`entry ${entry.id} has invalid session_info payload`);
76
+ }
77
+ break;
78
+ case "leaf":
79
+ if (entry.targetId !== null && typeof entry.targetId !== "string") {
80
+ throw invalidEntry(`entry ${entry.id} has invalid leaf payload`);
81
+ }
82
+ break;
83
+ default: {
84
+ const exhaustive = entry;
85
+ throw invalidEntry(`unknown entry type ${exhaustive.type ?? "unknown"}`);
86
+ }
87
+ }
88
+ }
89
+ function entryToPayload(entry) {
90
+ const { type: _type, id: _id, parentId: _parentId, timestamp: _timestamp, ...payload } = entry;
91
+ return payload;
92
+ }
93
+ export function encodeEntry(entry) {
94
+ validateSessionTreeEntry(entry);
95
+ return { payload: JSON.stringify(entryToPayload(entry)) };
96
+ }
97
+ export function decodeEntry(row) {
98
+ const payload = parsePayload(row);
99
+ if (!isRecord(payload))
100
+ throw invalidEntry(`entry ${row.id} payload is not an object`);
101
+ const base = {
102
+ id: row.id,
103
+ parentId: row.parent_id,
104
+ timestamp: row.timestamp,
105
+ };
106
+ switch (row.type) {
107
+ case "message": {
108
+ if (!("message" in payload))
109
+ throw invalidEntry(`entry ${row.id} is missing message payload`);
110
+ const messagePayload = payload;
111
+ return { ...base, type: "message", ...messagePayload };
112
+ }
113
+ case "thinking_level_change":
114
+ if (typeof payload.thinkingLevel !== "string")
115
+ throw invalidEntry(`entry ${row.id} is missing thinkingLevel`);
116
+ return { ...base, type: "thinking_level_change", ...payload };
117
+ case "model_change":
118
+ if (typeof payload.provider !== "string" || typeof payload.modelId !== "string") {
119
+ throw invalidEntry(`entry ${row.id} has invalid model_change payload`);
120
+ }
121
+ return { ...base, type: "model_change", ...payload };
122
+ case "active_tools_change":
123
+ if (!Array.isArray(payload.activeToolNames) ||
124
+ payload.activeToolNames.some((value) => typeof value !== "string")) {
125
+ throw invalidEntry(`entry ${row.id} has invalid active_tools_change payload`);
126
+ }
127
+ return { ...base, type: "active_tools_change", ...payload };
128
+ case "compaction":
129
+ if (typeof payload.summary !== "string" ||
130
+ typeof payload.firstKeptEntryId !== "string" ||
131
+ typeof payload.tokensBefore !== "number" ||
132
+ (payload.retainedTail !== undefined && !Array.isArray(payload.retainedTail))) {
133
+ throw invalidEntry(`entry ${row.id} has invalid compaction payload`);
134
+ }
135
+ return { ...base, type: "compaction", ...payload };
136
+ case "branch_summary":
137
+ if (typeof payload.fromId !== "string" || typeof payload.summary !== "string") {
138
+ throw invalidEntry(`entry ${row.id} has invalid branch_summary payload`);
139
+ }
140
+ return { ...base, type: "branch_summary", ...payload };
141
+ case "custom":
142
+ if (typeof payload.customType !== "string")
143
+ throw invalidEntry(`entry ${row.id} has invalid custom payload`);
144
+ return { ...base, type: "custom", ...payload };
145
+ case "custom_message":
146
+ if (typeof payload.customType !== "string" ||
147
+ typeof payload.display !== "boolean" ||
148
+ !("content" in payload)) {
149
+ throw invalidEntry(`entry ${row.id} has invalid custom_message payload`);
150
+ }
151
+ return { ...base, type: "custom_message", ...payload };
152
+ case "label":
153
+ if (typeof payload.targetId !== "string")
154
+ throw invalidEntry(`entry ${row.id} has invalid label payload`);
155
+ if (payload.label !== undefined && typeof payload.label !== "string") {
156
+ throw invalidEntry(`entry ${row.id} has invalid label payload`);
157
+ }
158
+ return { ...base, type: "label", ...payload };
159
+ case "session_info":
160
+ if (payload.name !== undefined && typeof payload.name !== "string") {
161
+ throw invalidEntry(`entry ${row.id} has invalid session_info payload`);
162
+ }
163
+ return { ...base, type: "session_info", ...payload };
164
+ case "leaf":
165
+ if (payload.targetId !== null && typeof payload.targetId !== "string") {
166
+ throw invalidEntry(`entry ${row.id} has invalid leaf payload`);
167
+ }
168
+ return { ...base, type: "leaf", ...payload };
169
+ default:
170
+ throw invalidEntry(`unknown entry type ${row.type}`);
171
+ }
172
+ }
173
+ //# sourceMappingURL=session-entries.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-entries.js","sourceRoot":"","sources":["../../../src/sqlite/storage/session-entries.ts"],"names":[],"mappings":"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","sourcesContent":["import type { SessionTreeEntry, SessionTreeEntryBase } from \"@handy_wote/pi-agent-core\";\nimport { invalidEntry, isRecord } from \"./shared.ts\";\n\nexport interface SessionEntryRow {\n\tsession_id: string;\n\tid: string;\n\tentry_seq: number;\n\tparent_id: string | null;\n\ttype: SessionTreeEntry[\"type\"];\n\ttimestamp: string;\n\tpayload: string;\n}\n\nexport type EncodedEntry = {\n\tpayload: string;\n};\n\ntype EntryPayload<TEntry extends SessionTreeEntry> = Omit<TEntry, keyof SessionTreeEntryBase | \"type\">;\n\ntype MessagePayload = EntryPayload<Extract<SessionTreeEntry, { type: \"message\" }>>;\ntype ThinkingLevelChangePayload = EntryPayload<Extract<SessionTreeEntry, { type: \"thinking_level_change\" }>>;\ntype ModelChangePayload = EntryPayload<Extract<SessionTreeEntry, { type: \"model_change\" }>>;\ntype ActiveToolsChangePayload = EntryPayload<Extract<SessionTreeEntry, { type: \"active_tools_change\" }>>;\ntype CompactionPayload = EntryPayload<Extract<SessionTreeEntry, { type: \"compaction\" }>>;\ntype BranchSummaryPayload = EntryPayload<Extract<SessionTreeEntry, { type: \"branch_summary\" }>>;\ntype CustomPayload = EntryPayload<Extract<SessionTreeEntry, { type: \"custom\" }>>;\ntype CustomMessagePayload = EntryPayload<Extract<SessionTreeEntry, { type: \"custom_message\" }>>;\ntype LabelPayload = EntryPayload<Extract<SessionTreeEntry, { type: \"label\" }>>;\ntype SessionInfoPayload = EntryPayload<Extract<SessionTreeEntry, { type: \"session_info\" }>>;\ntype LeafPayload = EntryPayload<Extract<SessionTreeEntry, { type: \"leaf\" }>>;\n\nfunction parsePayload(row: SessionEntryRow): unknown {\n\ttry {\n\t\treturn JSON.parse(row.payload);\n\t} catch (error) {\n\t\tthrow invalidEntry(`entry ${row.id} payload is not valid JSON`, error instanceof Error ? error : undefined);\n\t}\n}\n\nfunction isTextImageContentArray(value: unknown): boolean {\n\treturn (\n\t\tArray.isArray(value) &&\n\t\tvalue.every(\n\t\t\t(item) =>\n\t\t\t\tisRecord(item) && typeof item.type === \"string\" && (item.type !== \"text\" || typeof item.text === \"string\"),\n\t\t)\n\t);\n}\n\nexport function validateSessionTreeEntry(entry: SessionTreeEntry): void {\n\tif (typeof entry.id !== \"string\" || !entry.id) throw invalidEntry(\"entry is missing id\");\n\tif (entry.parentId !== null && typeof entry.parentId !== \"string\") {\n\t\tthrow invalidEntry(`entry ${entry.id} has invalid parentId`);\n\t}\n\tif (typeof entry.timestamp !== \"string\" || !entry.timestamp) {\n\t\tthrow invalidEntry(`entry ${entry.id} is missing timestamp`);\n\t}\n\n\tswitch (entry.type) {\n\t\tcase \"message\":\n\t\t\tif (!isRecord(entry.message) || typeof entry.message.role !== \"string\") {\n\t\t\t\tthrow invalidEntry(`entry ${entry.id} is missing message payload`);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"thinking_level_change\":\n\t\t\tif (typeof entry.thinkingLevel !== \"string\") throw invalidEntry(`entry ${entry.id} is missing thinkingLevel`);\n\t\t\tbreak;\n\t\tcase \"model_change\":\n\t\t\tif (typeof entry.provider !== \"string\" || typeof entry.modelId !== \"string\") {\n\t\t\t\tthrow invalidEntry(`entry ${entry.id} has invalid model_change payload`);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"active_tools_change\":\n\t\t\tif (\n\t\t\t\t!Array.isArray(entry.activeToolNames) ||\n\t\t\t\tentry.activeToolNames.some((value) => typeof value !== \"string\")\n\t\t\t) {\n\t\t\t\tthrow invalidEntry(`entry ${entry.id} has invalid active_tools_change payload`);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"compaction\":\n\t\t\tif (\n\t\t\t\ttypeof entry.summary !== \"string\" ||\n\t\t\t\ttypeof entry.firstKeptEntryId !== \"string\" ||\n\t\t\t\ttypeof entry.tokensBefore !== \"number\" ||\n\t\t\t\t(entry.retainedTail !== undefined && !Array.isArray(entry.retainedTail))\n\t\t\t) {\n\t\t\t\tthrow invalidEntry(`entry ${entry.id} has invalid compaction payload`);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"branch_summary\":\n\t\t\tif (typeof entry.fromId !== \"string\" || typeof entry.summary !== \"string\") {\n\t\t\t\tthrow invalidEntry(`entry ${entry.id} has invalid branch_summary payload`);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"custom\":\n\t\t\tif (typeof entry.customType !== \"string\") throw invalidEntry(`entry ${entry.id} has invalid custom payload`);\n\t\t\tbreak;\n\t\tcase \"custom_message\":\n\t\t\tif (\n\t\t\t\ttypeof entry.customType !== \"string\" ||\n\t\t\t\ttypeof entry.display !== \"boolean\" ||\n\t\t\t\t!(typeof entry.content === \"string\" || isTextImageContentArray(entry.content))\n\t\t\t) {\n\t\t\t\tthrow invalidEntry(`entry ${entry.id} has invalid custom_message payload`);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"label\":\n\t\t\tif (typeof entry.targetId !== \"string\" || (entry.label !== undefined && typeof entry.label !== \"string\")) {\n\t\t\t\tthrow invalidEntry(`entry ${entry.id} has invalid label payload`);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"session_info\":\n\t\t\tif (entry.name !== undefined && typeof entry.name !== \"string\") {\n\t\t\t\tthrow invalidEntry(`entry ${entry.id} has invalid session_info payload`);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"leaf\":\n\t\t\tif (entry.targetId !== null && typeof entry.targetId !== \"string\") {\n\t\t\t\tthrow invalidEntry(`entry ${entry.id} has invalid leaf payload`);\n\t\t\t}\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tconst exhaustive: never = entry;\n\t\t\tthrow invalidEntry(`unknown entry type ${(exhaustive as { type?: string }).type ?? \"unknown\"}`);\n\t\t}\n\t}\n}\n\nfunction entryToPayload<TEntry extends SessionTreeEntry>(entry: TEntry): EntryPayload<TEntry> {\n\tconst { type: _type, id: _id, parentId: _parentId, timestamp: _timestamp, ...payload } = entry;\n\treturn payload as EntryPayload<TEntry>;\n}\n\nexport function encodeEntry(entry: SessionTreeEntry): EncodedEntry {\n\tvalidateSessionTreeEntry(entry);\n\treturn { payload: JSON.stringify(entryToPayload(entry)) };\n}\n\nexport function decodeEntry(row: SessionEntryRow): SessionTreeEntry {\n\tconst payload = parsePayload(row);\n\tif (!isRecord(payload)) throw invalidEntry(`entry ${row.id} payload is not an object`);\n\tconst base = {\n\t\tid: row.id,\n\t\tparentId: row.parent_id,\n\t\ttimestamp: row.timestamp,\n\t};\n\n\tswitch (row.type) {\n\t\tcase \"message\": {\n\t\t\tif (!(\"message\" in payload)) throw invalidEntry(`entry ${row.id} is missing message payload`);\n\t\t\tconst messagePayload = payload as MessagePayload;\n\t\t\treturn { ...base, type: \"message\", ...messagePayload };\n\t\t}\n\t\tcase \"thinking_level_change\":\n\t\t\tif (typeof payload.thinkingLevel !== \"string\") throw invalidEntry(`entry ${row.id} is missing thinkingLevel`);\n\t\t\treturn { ...base, type: \"thinking_level_change\", ...(payload as ThinkingLevelChangePayload) };\n\t\tcase \"model_change\":\n\t\t\tif (typeof payload.provider !== \"string\" || typeof payload.modelId !== \"string\") {\n\t\t\t\tthrow invalidEntry(`entry ${row.id} has invalid model_change payload`);\n\t\t\t}\n\t\t\treturn { ...base, type: \"model_change\", ...(payload as ModelChangePayload) };\n\t\tcase \"active_tools_change\":\n\t\t\tif (\n\t\t\t\t!Array.isArray(payload.activeToolNames) ||\n\t\t\t\tpayload.activeToolNames.some((value) => typeof value !== \"string\")\n\t\t\t) {\n\t\t\t\tthrow invalidEntry(`entry ${row.id} has invalid active_tools_change payload`);\n\t\t\t}\n\t\t\treturn { ...base, type: \"active_tools_change\", ...(payload as ActiveToolsChangePayload) };\n\t\tcase \"compaction\":\n\t\t\tif (\n\t\t\t\ttypeof payload.summary !== \"string\" ||\n\t\t\t\ttypeof payload.firstKeptEntryId !== \"string\" ||\n\t\t\t\ttypeof payload.tokensBefore !== \"number\" ||\n\t\t\t\t(payload.retainedTail !== undefined && !Array.isArray(payload.retainedTail))\n\t\t\t) {\n\t\t\t\tthrow invalidEntry(`entry ${row.id} has invalid compaction payload`);\n\t\t\t}\n\t\t\treturn { ...base, type: \"compaction\", ...(payload as CompactionPayload) };\n\t\tcase \"branch_summary\":\n\t\t\tif (typeof payload.fromId !== \"string\" || typeof payload.summary !== \"string\") {\n\t\t\t\tthrow invalidEntry(`entry ${row.id} has invalid branch_summary payload`);\n\t\t\t}\n\t\t\treturn { ...base, type: \"branch_summary\", ...(payload as BranchSummaryPayload) };\n\t\tcase \"custom\":\n\t\t\tif (typeof payload.customType !== \"string\") throw invalidEntry(`entry ${row.id} has invalid custom payload`);\n\t\t\treturn { ...base, type: \"custom\", ...(payload as CustomPayload) };\n\t\tcase \"custom_message\":\n\t\t\tif (\n\t\t\t\ttypeof payload.customType !== \"string\" ||\n\t\t\t\ttypeof payload.display !== \"boolean\" ||\n\t\t\t\t!(\"content\" in payload)\n\t\t\t) {\n\t\t\t\tthrow invalidEntry(`entry ${row.id} has invalid custom_message payload`);\n\t\t\t}\n\t\t\treturn { ...base, type: \"custom_message\", ...(payload as CustomMessagePayload) };\n\t\tcase \"label\":\n\t\t\tif (typeof payload.targetId !== \"string\") throw invalidEntry(`entry ${row.id} has invalid label payload`);\n\t\t\tif (payload.label !== undefined && typeof payload.label !== \"string\") {\n\t\t\t\tthrow invalidEntry(`entry ${row.id} has invalid label payload`);\n\t\t\t}\n\t\t\treturn { ...base, type: \"label\", ...(payload as LabelPayload) };\n\t\tcase \"session_info\":\n\t\t\tif (payload.name !== undefined && typeof payload.name !== \"string\") {\n\t\t\t\tthrow invalidEntry(`entry ${row.id} has invalid session_info payload`);\n\t\t\t}\n\t\t\treturn { ...base, type: \"session_info\", ...(payload as SessionInfoPayload) };\n\t\tcase \"leaf\":\n\t\t\tif (payload.targetId !== null && typeof payload.targetId !== \"string\") {\n\t\t\t\tthrow invalidEntry(`entry ${row.id} has invalid leaf payload`);\n\t\t\t}\n\t\t\treturn { ...base, type: \"leaf\", ...(payload as LeafPayload) };\n\t\tdefault:\n\t\t\tthrow invalidEntry(`unknown entry type ${row.type}`);\n\t}\n}\n"]}
@@ -0,0 +1,43 @@
1
+ import type { SessionStats, SessionTreeEntry, ThinkingLevel } from "@handy_wote/pi-agent-core";
2
+ export interface SessionMaterializedRow {
3
+ session_id: string;
4
+ payload: string;
5
+ }
6
+ export interface EntryMaterializedRow {
7
+ session_id: string;
8
+ entry_seq: number;
9
+ type: string;
10
+ payload: string;
11
+ }
12
+ export interface ModelThinkingConfig {
13
+ provider: string;
14
+ modelId: string;
15
+ thinkingLevel: ThinkingLevel;
16
+ }
17
+ export interface SessionMaterializedState {
18
+ name: string | undefined;
19
+ messageCount: number;
20
+ cachedTokens: number;
21
+ uncachedTokens: number;
22
+ totalTokens: number;
23
+ costTotal: number;
24
+ labelsById: Map<string, string>;
25
+ modelThinkingConfigs: ModelThinkingConfig[];
26
+ currentModel: {
27
+ provider: string;
28
+ modelId: string;
29
+ } | null;
30
+ currentThinkingLevel: ThinkingLevel | null;
31
+ }
32
+ export declare function isThinkingLevel(value: unknown): value is ThinkingLevel;
33
+ export declare function createEmptyMaterializedState(): SessionMaterializedState;
34
+ export declare function applyEntryToMaterializedState(state: SessionMaterializedState, entry: SessionTreeEntry): void;
35
+ export declare function serializeSummary(state: SessionMaterializedState): string;
36
+ export declare function materializedStateFromRows(summaryRow: SessionMaterializedRow, entryRows: EntryMaterializedRow[]): SessionMaterializedState;
37
+ export declare function sessionStatsFromMaterializedState(state: SessionMaterializedState): SessionStats;
38
+ export declare function materializedStateValues(sessionId: string, state: SessionMaterializedState): [sessionId: string, payload: string];
39
+ export declare function entryMaterializedValues(entry: SessionTreeEntry): Array<{
40
+ type: EntryMaterializedRow["type"];
41
+ payload: string;
42
+ }>;
43
+ //# sourceMappingURL=session-materialized.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-materialized.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/session-materialized.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,gBAAgB,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAG/F,MAAM,WAAW,sBAAsB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oBAAoB;IACpC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mBAAmB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB,aAAa,EAAE,aAAa,CAAC;CAC7B;AAED,MAAM,WAAW,wBAAwB;IACxC,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,MAAM,CAAC;IACvB,WAAW,EAAE,MAAM,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,oBAAoB,EAAE,mBAAmB,EAAE,CAAC;IAC5C,YAAY,EAAE;QAAE,QAAQ,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,GAAG,IAAI,CAAC;IAC3D,oBAAoB,EAAE,aAAa,GAAG,IAAI,CAAC;CAC3C;AAyCD,wBAAgB,eAAe,CAAC,KAAK,EAAE,OAAO,GAAG,KAAK,IAAI,aAAa,CAStE;AAsCD,wBAAgB,4BAA4B,IAAI,wBAAwB,CAavE;AAED,wBAAgB,6BAA6B,CAAC,KAAK,EAAE,wBAAwB,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI,CAwE5G;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,wBAAwB,GAAG,MAAM,CAYxE;AA2DD,wBAAgB,yBAAyB,CACxC,UAAU,EAAE,sBAAsB,EAClC,SAAS,EAAE,oBAAoB,EAAE,GAC/B,wBAAwB,CAoC1B;AAED,wBAAgB,iCAAiC,CAAC,KAAK,EAAE,wBAAwB,GAAG,YAAY,CAQ/F;AAED,wBAAgB,uBAAuB,CACtC,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,wBAAwB,GAC7B,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,CAAC,CAEtC;AAED,wBAAgB,uBAAuB,CACtC,KAAK,EAAE,gBAAgB,GACrB,KAAK,CAAC;IAAE,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IAAC,OAAO,EAAE,MAAM,CAAA;CAAE,CAAC,CA2BhE","sourcesContent":["import type { SessionStats, SessionTreeEntry, ThinkingLevel } from \"@handy_wote/pi-agent-core\";\nimport { invalidSession, isRecord } from \"./shared.ts\";\n\nexport interface SessionMaterializedRow {\n\tsession_id: string;\n\tpayload: string;\n}\n\nexport interface EntryMaterializedRow {\n\tsession_id: string;\n\tentry_seq: number;\n\ttype: string;\n\tpayload: string;\n}\n\nexport interface ModelThinkingConfig {\n\tprovider: string;\n\tmodelId: string;\n\tthinkingLevel: ThinkingLevel;\n}\n\nexport interface SessionMaterializedState {\n\tname: string | undefined;\n\tmessageCount: number;\n\tcachedTokens: number;\n\tuncachedTokens: number;\n\ttotalTokens: number;\n\tcostTotal: number;\n\tlabelsById: Map<string, string>;\n\tmodelThinkingConfigs: ModelThinkingConfig[];\n\tcurrentModel: { provider: string; modelId: string } | null;\n\tcurrentThinkingLevel: ThinkingLevel | null;\n}\n\ninterface SessionMaterializedSummary {\n\tname?: string;\n\tmessageCount: number;\n\tcachedTokens: number;\n\tuncachedTokens: number;\n\ttotalTokens: number;\n\tcostTotal: number;\n\tcurrentModel?: { provider: string; modelId: string } | null;\n\tcurrentThinkingLevel?: ThinkingLevel | null;\n}\n\nfunction compareModelThinkingConfig(left: ModelThinkingConfig, right: ModelThinkingConfig): number {\n\treturn (\n\t\tleft.provider.localeCompare(right.provider) ||\n\t\tleft.modelId.localeCompare(right.modelId) ||\n\t\tleft.thinkingLevel.localeCompare(right.thinkingLevel)\n\t);\n}\n\nfunction normalizeModelThinkingConfigs(configs: readonly ModelThinkingConfig[]): ModelThinkingConfig[] {\n\tconst unique = new Map<string, ModelThinkingConfig>();\n\tfor (const config of configs) {\n\t\tunique.set(`${config.provider}\\u0000${config.modelId}\\u0000${config.thinkingLevel}`, config);\n\t}\n\treturn [...unique.values()].sort(compareModelThinkingConfig);\n}\n\nfunction addModelThinkingConfig(\n\tstate: SessionMaterializedState,\n\tprovider: string,\n\tmodelId: string,\n\tthinkingLevel: ThinkingLevel,\n): void {\n\tstate.modelThinkingConfigs = normalizeModelThinkingConfigs([\n\t\t...state.modelThinkingConfigs,\n\t\t{ provider, modelId, thinkingLevel },\n\t]);\n}\n\nexport function isThinkingLevel(value: unknown): value is ThinkingLevel {\n\treturn (\n\t\tvalue === \"off\" ||\n\t\tvalue === \"minimal\" ||\n\t\tvalue === \"low\" ||\n\t\tvalue === \"medium\" ||\n\t\tvalue === \"high\" ||\n\t\tvalue === \"xhigh\"\n\t);\n}\n\nfunction getAssistantUsage(message: unknown):\n\t| {\n\t\t\tprovider: string;\n\t\t\tmodelId: string;\n\t\t\tinput: number;\n\t\t\toutput: number;\n\t\t\tcacheRead: number;\n\t\t\tcacheWrite: number;\n\t\t\tcostTotal: number;\n\t }\n\t| undefined {\n\tif (!isRecord(message) || message.role !== \"assistant\") return undefined;\n\tif (typeof message.provider !== \"string\" || typeof message.model !== \"string\") return undefined;\n\tif (!isRecord(message.usage) || !isRecord(message.usage.cost)) return undefined;\n\tconst { input, output, cacheRead, cacheWrite } = message.usage;\n\tconst costTotal = message.usage.cost.total;\n\tif (\n\t\ttypeof input !== \"number\" ||\n\t\ttypeof output !== \"number\" ||\n\t\ttypeof cacheRead !== \"number\" ||\n\t\ttypeof cacheWrite !== \"number\" ||\n\t\ttypeof costTotal !== \"number\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tprovider: message.provider,\n\t\tmodelId: message.model,\n\t\tinput,\n\t\toutput,\n\t\tcacheRead,\n\t\tcacheWrite,\n\t\tcostTotal,\n\t};\n}\n\nexport function createEmptyMaterializedState(): SessionMaterializedState {\n\treturn {\n\t\tname: undefined,\n\t\tmessageCount: 0,\n\t\tcachedTokens: 0,\n\t\tuncachedTokens: 0,\n\t\ttotalTokens: 0,\n\t\tcostTotal: 0,\n\t\tlabelsById: new Map<string, string>(),\n\t\tmodelThinkingConfigs: [],\n\t\tcurrentModel: null,\n\t\tcurrentThinkingLevel: null,\n\t};\n}\n\nexport function applyEntryToMaterializedState(state: SessionMaterializedState, entry: SessionTreeEntry): void {\n\tswitch (entry.type) {\n\t\tcase \"session_info\":\n\t\t\tstate.name = entry.name?.trim() || undefined;\n\t\t\tbreak;\n\t\tcase \"label\": {\n\t\t\tconst label = entry.label?.trim();\n\t\t\tif (label) {\n\t\t\t\tstate.labelsById.set(entry.targetId, label);\n\t\t\t} else {\n\t\t\t\tstate.labelsById.delete(entry.targetId);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase \"model_change\":\n\t\t\tstate.currentModel = { provider: entry.provider, modelId: entry.modelId };\n\t\t\tif (state.currentThinkingLevel) {\n\t\t\t\taddModelThinkingConfig(state, entry.provider, entry.modelId, state.currentThinkingLevel);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"thinking_level_change\":\n\t\t\tif (!isThinkingLevel(entry.thinkingLevel)) break;\n\t\t\tstate.currentThinkingLevel = entry.thinkingLevel;\n\t\t\tif (state.currentModel) {\n\t\t\t\taddModelThinkingConfig(state, state.currentModel.provider, state.currentModel.modelId, entry.thinkingLevel);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"message\": {\n\t\t\tstate.messageCount += 1;\n\t\t\tconst usage = getAssistantUsage(entry.message);\n\t\t\tif (!usage) break;\n\t\t\tstate.cachedTokens += usage.cacheRead;\n\t\t\tstate.uncachedTokens += usage.input + usage.cacheWrite;\n\t\t\tstate.totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;\n\t\t\tstate.costTotal += usage.costTotal;\n\t\t\tstate.currentModel = { provider: usage.provider, modelId: usage.modelId };\n\t\t\tif (state.currentThinkingLevel) {\n\t\t\t\taddModelThinkingConfig(state, usage.provider, usage.modelId, state.currentThinkingLevel);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase \"compaction\":\n\t\tcase \"branch_summary\": {\n\t\t\tconst usage = entry.usage;\n\t\t\tif (\n\t\t\t\t!isRecord(usage) ||\n\t\t\t\t!isRecord(usage.cost) ||\n\t\t\t\ttypeof usage.input !== \"number\" ||\n\t\t\t\ttypeof usage.output !== \"number\" ||\n\t\t\t\ttypeof usage.cacheRead !== \"number\" ||\n\t\t\t\ttypeof usage.cacheWrite !== \"number\" ||\n\t\t\t\ttypeof usage.cost.total !== \"number\"\n\t\t\t) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tstate.cachedTokens += usage.cacheRead;\n\t\t\tstate.uncachedTokens += usage.input + usage.cacheWrite;\n\t\t\tstate.totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;\n\t\t\tstate.costTotal += usage.cost.total;\n\t\t\tbreak;\n\t\t}\n\t\tcase \"active_tools_change\":\n\t\tcase \"custom\":\n\t\tcase \"custom_message\":\n\t\tcase \"leaf\":\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tconst exhaustive: never = entry;\n\t\t\tvoid exhaustive;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nexport function serializeSummary(state: SessionMaterializedState): string {\n\tconst summary: SessionMaterializedSummary = {\n\t\tname: state.name,\n\t\tmessageCount: state.messageCount,\n\t\tcachedTokens: state.cachedTokens,\n\t\tuncachedTokens: state.uncachedTokens,\n\t\ttotalTokens: state.totalTokens,\n\t\tcostTotal: state.costTotal,\n\t\tcurrentModel: state.currentModel,\n\t\tcurrentThinkingLevel: state.currentThinkingLevel,\n\t};\n\treturn JSON.stringify(summary);\n}\n\nfunction parseSummary(json: string): SessionMaterializedSummary {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(json);\n\t} catch (error) {\n\t\tthrow invalidSession(\n\t\t\t`materialized session summary is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (!isRecord(parsed) || Array.isArray(parsed)) {\n\t\tthrow invalidSession(\"materialized session summary is not an object\");\n\t}\n\tconst currentModel = parsed.currentModel;\n\tconst currentThinkingLevel = parsed.currentThinkingLevel;\n\tif (\n\t\t(parsed.name !== undefined && typeof parsed.name !== \"string\") ||\n\t\ttypeof parsed.messageCount !== \"number\" ||\n\t\ttypeof parsed.cachedTokens !== \"number\" ||\n\t\ttypeof parsed.uncachedTokens !== \"number\" ||\n\t\ttypeof parsed.totalTokens !== \"number\" ||\n\t\ttypeof parsed.costTotal !== \"number\" ||\n\t\t(currentModel !== undefined &&\n\t\t\tcurrentModel !== null &&\n\t\t\t(!isRecord(currentModel) ||\n\t\t\t\ttypeof currentModel.provider !== \"string\" ||\n\t\t\t\ttypeof currentModel.modelId !== \"string\")) ||\n\t\t(currentThinkingLevel !== undefined && currentThinkingLevel !== null && !isThinkingLevel(currentThinkingLevel))\n\t) {\n\t\tthrow invalidSession(\"materialized session summary has invalid fields\");\n\t}\n\treturn {\n\t\tname: parsed.name?.trim() || undefined,\n\t\tmessageCount: parsed.messageCount,\n\t\tcachedTokens: parsed.cachedTokens,\n\t\tuncachedTokens: parsed.uncachedTokens,\n\t\ttotalTokens: parsed.totalTokens,\n\t\tcostTotal: parsed.costTotal,\n\t\tcurrentModel:\n\t\t\tcurrentModel && isRecord(currentModel)\n\t\t\t\t? { provider: currentModel.provider as string, modelId: currentModel.modelId as string }\n\t\t\t\t: (currentModel ?? undefined),\n\t\tcurrentThinkingLevel: (currentThinkingLevel as ThinkingLevel | null | undefined) ?? undefined,\n\t};\n}\n\nfunction parseEntryMaterializedPayload(row: EntryMaterializedRow): unknown {\n\ttry {\n\t\treturn JSON.parse(row.payload);\n\t} catch (error) {\n\t\tthrow invalidSession(\n\t\t\t`materialized entry row ${row.entry_seq} is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nexport function materializedStateFromRows(\n\tsummaryRow: SessionMaterializedRow,\n\tentryRows: EntryMaterializedRow[],\n): SessionMaterializedState {\n\tconst summary = parseSummary(summaryRow.payload);\n\tconst state: SessionMaterializedState = {\n\t\tname: summary.name,\n\t\tmessageCount: summary.messageCount,\n\t\tcachedTokens: summary.cachedTokens,\n\t\tuncachedTokens: summary.uncachedTokens,\n\t\ttotalTokens: summary.totalTokens,\n\t\tcostTotal: summary.costTotal,\n\t\tlabelsById: new Map<string, string>(),\n\t\tmodelThinkingConfigs: [],\n\t\tcurrentModel: summary.currentModel ?? null,\n\t\tcurrentThinkingLevel: summary.currentThinkingLevel ?? null,\n\t};\n\tfor (const row of entryRows) {\n\t\tconst payload = parseEntryMaterializedPayload(row);\n\t\tif (!isRecord(payload)) throw invalidSession(`materialized entry row ${row.entry_seq} is not an object`);\n\t\tif (row.type === \"label\") {\n\t\t\tif (typeof payload.targetId !== \"string\") {\n\t\t\t\tthrow invalidSession(`materialized label row ${row.entry_seq} is missing targetId`);\n\t\t\t}\n\t\t\tif (payload.label !== null && payload.label !== undefined && typeof payload.label !== \"string\") {\n\t\t\t\tthrow invalidSession(`materialized label row ${row.entry_seq} has invalid label`);\n\t\t\t}\n\t\t\tconst label = typeof payload.label === \"string\" ? payload.label.trim() : \"\";\n\t\t\tif (label) {\n\t\t\t\tstate.labelsById.set(payload.targetId, label);\n\t\t\t} else {\n\t\t\t\tstate.labelsById.delete(payload.targetId);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (row.type !== \"label\") {\n\t\t}\n\t}\n\treturn state;\n}\n\nexport function sessionStatsFromMaterializedState(state: SessionMaterializedState): SessionStats {\n\treturn {\n\t\tmessageCount: state.messageCount,\n\t\tcachedTokens: state.cachedTokens,\n\t\tuncachedTokens: state.uncachedTokens,\n\t\ttotalTokens: state.totalTokens,\n\t\tcostTotal: state.costTotal,\n\t};\n}\n\nexport function materializedStateValues(\n\tsessionId: string,\n\tstate: SessionMaterializedState,\n): [sessionId: string, payload: string] {\n\treturn [sessionId, serializeSummary(state)];\n}\n\nexport function entryMaterializedValues(\n\tentry: SessionTreeEntry,\n): Array<{ type: EntryMaterializedRow[\"type\"]; payload: string }> {\n\tswitch (entry.type) {\n\t\tcase \"label\":\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"label\",\n\t\t\t\t\tpayload: JSON.stringify({ targetId: entry.targetId, label: entry.label ?? null }),\n\t\t\t\t},\n\t\t\t];\n\t\tcase \"model_change\":\n\t\tcase \"thinking_level_change\":\n\t\tcase \"message\":\n\t\t\treturn [];\n\t\tcase \"active_tools_change\":\n\t\tcase \"branch_summary\":\n\t\tcase \"compaction\":\n\t\tcase \"custom\":\n\t\tcase \"custom_message\":\n\t\tcase \"leaf\":\n\t\tcase \"session_info\":\n\t\t\treturn [];\n\t\tdefault: {\n\t\t\tconst exhaustive: never = entry;\n\t\t\tvoid exhaustive;\n\t\t\treturn [];\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,282 @@
1
+ import { invalidSession, isRecord } from "./shared.js";
2
+ function compareModelThinkingConfig(left, right) {
3
+ return (left.provider.localeCompare(right.provider) ||
4
+ left.modelId.localeCompare(right.modelId) ||
5
+ left.thinkingLevel.localeCompare(right.thinkingLevel));
6
+ }
7
+ function normalizeModelThinkingConfigs(configs) {
8
+ const unique = new Map();
9
+ for (const config of configs) {
10
+ unique.set(`${config.provider}\u0000${config.modelId}\u0000${config.thinkingLevel}`, config);
11
+ }
12
+ return [...unique.values()].sort(compareModelThinkingConfig);
13
+ }
14
+ function addModelThinkingConfig(state, provider, modelId, thinkingLevel) {
15
+ state.modelThinkingConfigs = normalizeModelThinkingConfigs([
16
+ ...state.modelThinkingConfigs,
17
+ { provider, modelId, thinkingLevel },
18
+ ]);
19
+ }
20
+ export function isThinkingLevel(value) {
21
+ return (value === "off" ||
22
+ value === "minimal" ||
23
+ value === "low" ||
24
+ value === "medium" ||
25
+ value === "high" ||
26
+ value === "xhigh");
27
+ }
28
+ function getAssistantUsage(message) {
29
+ if (!isRecord(message) || message.role !== "assistant")
30
+ return undefined;
31
+ if (typeof message.provider !== "string" || typeof message.model !== "string")
32
+ return undefined;
33
+ if (!isRecord(message.usage) || !isRecord(message.usage.cost))
34
+ return undefined;
35
+ const { input, output, cacheRead, cacheWrite } = message.usage;
36
+ const costTotal = message.usage.cost.total;
37
+ if (typeof input !== "number" ||
38
+ typeof output !== "number" ||
39
+ typeof cacheRead !== "number" ||
40
+ typeof cacheWrite !== "number" ||
41
+ typeof costTotal !== "number") {
42
+ return undefined;
43
+ }
44
+ return {
45
+ provider: message.provider,
46
+ modelId: message.model,
47
+ input,
48
+ output,
49
+ cacheRead,
50
+ cacheWrite,
51
+ costTotal,
52
+ };
53
+ }
54
+ export function createEmptyMaterializedState() {
55
+ return {
56
+ name: undefined,
57
+ messageCount: 0,
58
+ cachedTokens: 0,
59
+ uncachedTokens: 0,
60
+ totalTokens: 0,
61
+ costTotal: 0,
62
+ labelsById: new Map(),
63
+ modelThinkingConfigs: [],
64
+ currentModel: null,
65
+ currentThinkingLevel: null,
66
+ };
67
+ }
68
+ export function applyEntryToMaterializedState(state, entry) {
69
+ switch (entry.type) {
70
+ case "session_info":
71
+ state.name = entry.name?.trim() || undefined;
72
+ break;
73
+ case "label": {
74
+ const label = entry.label?.trim();
75
+ if (label) {
76
+ state.labelsById.set(entry.targetId, label);
77
+ }
78
+ else {
79
+ state.labelsById.delete(entry.targetId);
80
+ }
81
+ break;
82
+ }
83
+ case "model_change":
84
+ state.currentModel = { provider: entry.provider, modelId: entry.modelId };
85
+ if (state.currentThinkingLevel) {
86
+ addModelThinkingConfig(state, entry.provider, entry.modelId, state.currentThinkingLevel);
87
+ }
88
+ break;
89
+ case "thinking_level_change":
90
+ if (!isThinkingLevel(entry.thinkingLevel))
91
+ break;
92
+ state.currentThinkingLevel = entry.thinkingLevel;
93
+ if (state.currentModel) {
94
+ addModelThinkingConfig(state, state.currentModel.provider, state.currentModel.modelId, entry.thinkingLevel);
95
+ }
96
+ break;
97
+ case "message": {
98
+ state.messageCount += 1;
99
+ const usage = getAssistantUsage(entry.message);
100
+ if (!usage)
101
+ break;
102
+ state.cachedTokens += usage.cacheRead;
103
+ state.uncachedTokens += usage.input + usage.cacheWrite;
104
+ state.totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
105
+ state.costTotal += usage.costTotal;
106
+ state.currentModel = { provider: usage.provider, modelId: usage.modelId };
107
+ if (state.currentThinkingLevel) {
108
+ addModelThinkingConfig(state, usage.provider, usage.modelId, state.currentThinkingLevel);
109
+ }
110
+ break;
111
+ }
112
+ case "compaction":
113
+ case "branch_summary": {
114
+ const usage = entry.usage;
115
+ if (!isRecord(usage) ||
116
+ !isRecord(usage.cost) ||
117
+ typeof usage.input !== "number" ||
118
+ typeof usage.output !== "number" ||
119
+ typeof usage.cacheRead !== "number" ||
120
+ typeof usage.cacheWrite !== "number" ||
121
+ typeof usage.cost.total !== "number") {
122
+ break;
123
+ }
124
+ state.cachedTokens += usage.cacheRead;
125
+ state.uncachedTokens += usage.input + usage.cacheWrite;
126
+ state.totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;
127
+ state.costTotal += usage.cost.total;
128
+ break;
129
+ }
130
+ case "active_tools_change":
131
+ case "custom":
132
+ case "custom_message":
133
+ case "leaf":
134
+ break;
135
+ default: {
136
+ const exhaustive = entry;
137
+ void exhaustive;
138
+ break;
139
+ }
140
+ }
141
+ }
142
+ export function serializeSummary(state) {
143
+ const summary = {
144
+ name: state.name,
145
+ messageCount: state.messageCount,
146
+ cachedTokens: state.cachedTokens,
147
+ uncachedTokens: state.uncachedTokens,
148
+ totalTokens: state.totalTokens,
149
+ costTotal: state.costTotal,
150
+ currentModel: state.currentModel,
151
+ currentThinkingLevel: state.currentThinkingLevel,
152
+ };
153
+ return JSON.stringify(summary);
154
+ }
155
+ function parseSummary(json) {
156
+ let parsed;
157
+ try {
158
+ parsed = JSON.parse(json);
159
+ }
160
+ catch (error) {
161
+ throw invalidSession(`materialized session summary is not valid JSON`, error instanceof Error ? error : undefined);
162
+ }
163
+ if (!isRecord(parsed) || Array.isArray(parsed)) {
164
+ throw invalidSession("materialized session summary is not an object");
165
+ }
166
+ const currentModel = parsed.currentModel;
167
+ const currentThinkingLevel = parsed.currentThinkingLevel;
168
+ if ((parsed.name !== undefined && typeof parsed.name !== "string") ||
169
+ typeof parsed.messageCount !== "number" ||
170
+ typeof parsed.cachedTokens !== "number" ||
171
+ typeof parsed.uncachedTokens !== "number" ||
172
+ typeof parsed.totalTokens !== "number" ||
173
+ typeof parsed.costTotal !== "number" ||
174
+ (currentModel !== undefined &&
175
+ currentModel !== null &&
176
+ (!isRecord(currentModel) ||
177
+ typeof currentModel.provider !== "string" ||
178
+ typeof currentModel.modelId !== "string")) ||
179
+ (currentThinkingLevel !== undefined && currentThinkingLevel !== null && !isThinkingLevel(currentThinkingLevel))) {
180
+ throw invalidSession("materialized session summary has invalid fields");
181
+ }
182
+ return {
183
+ name: parsed.name?.trim() || undefined,
184
+ messageCount: parsed.messageCount,
185
+ cachedTokens: parsed.cachedTokens,
186
+ uncachedTokens: parsed.uncachedTokens,
187
+ totalTokens: parsed.totalTokens,
188
+ costTotal: parsed.costTotal,
189
+ currentModel: currentModel && isRecord(currentModel)
190
+ ? { provider: currentModel.provider, modelId: currentModel.modelId }
191
+ : (currentModel ?? undefined),
192
+ currentThinkingLevel: currentThinkingLevel ?? undefined,
193
+ };
194
+ }
195
+ function parseEntryMaterializedPayload(row) {
196
+ try {
197
+ return JSON.parse(row.payload);
198
+ }
199
+ catch (error) {
200
+ throw invalidSession(`materialized entry row ${row.entry_seq} is not valid JSON`, error instanceof Error ? error : undefined);
201
+ }
202
+ }
203
+ export function materializedStateFromRows(summaryRow, entryRows) {
204
+ const summary = parseSummary(summaryRow.payload);
205
+ const state = {
206
+ name: summary.name,
207
+ messageCount: summary.messageCount,
208
+ cachedTokens: summary.cachedTokens,
209
+ uncachedTokens: summary.uncachedTokens,
210
+ totalTokens: summary.totalTokens,
211
+ costTotal: summary.costTotal,
212
+ labelsById: new Map(),
213
+ modelThinkingConfigs: [],
214
+ currentModel: summary.currentModel ?? null,
215
+ currentThinkingLevel: summary.currentThinkingLevel ?? null,
216
+ };
217
+ for (const row of entryRows) {
218
+ const payload = parseEntryMaterializedPayload(row);
219
+ if (!isRecord(payload))
220
+ throw invalidSession(`materialized entry row ${row.entry_seq} is not an object`);
221
+ if (row.type === "label") {
222
+ if (typeof payload.targetId !== "string") {
223
+ throw invalidSession(`materialized label row ${row.entry_seq} is missing targetId`);
224
+ }
225
+ if (payload.label !== null && payload.label !== undefined && typeof payload.label !== "string") {
226
+ throw invalidSession(`materialized label row ${row.entry_seq} has invalid label`);
227
+ }
228
+ const label = typeof payload.label === "string" ? payload.label.trim() : "";
229
+ if (label) {
230
+ state.labelsById.set(payload.targetId, label);
231
+ }
232
+ else {
233
+ state.labelsById.delete(payload.targetId);
234
+ }
235
+ continue;
236
+ }
237
+ if (row.type !== "label") {
238
+ }
239
+ }
240
+ return state;
241
+ }
242
+ export function sessionStatsFromMaterializedState(state) {
243
+ return {
244
+ messageCount: state.messageCount,
245
+ cachedTokens: state.cachedTokens,
246
+ uncachedTokens: state.uncachedTokens,
247
+ totalTokens: state.totalTokens,
248
+ costTotal: state.costTotal,
249
+ };
250
+ }
251
+ export function materializedStateValues(sessionId, state) {
252
+ return [sessionId, serializeSummary(state)];
253
+ }
254
+ export function entryMaterializedValues(entry) {
255
+ switch (entry.type) {
256
+ case "label":
257
+ return [
258
+ {
259
+ type: "label",
260
+ payload: JSON.stringify({ targetId: entry.targetId, label: entry.label ?? null }),
261
+ },
262
+ ];
263
+ case "model_change":
264
+ case "thinking_level_change":
265
+ case "message":
266
+ return [];
267
+ case "active_tools_change":
268
+ case "branch_summary":
269
+ case "compaction":
270
+ case "custom":
271
+ case "custom_message":
272
+ case "leaf":
273
+ case "session_info":
274
+ return [];
275
+ default: {
276
+ const exhaustive = entry;
277
+ void exhaustive;
278
+ return [];
279
+ }
280
+ }
281
+ }
282
+ //# sourceMappingURL=session-materialized.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-materialized.js","sourceRoot":"","sources":["../../../src/sqlite/storage/session-materialized.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AA4CvD,SAAS,0BAA0B,CAAC,IAAyB,EAAE,KAA0B,EAAU;IAClG,OAAO,CACN,IAAI,CAAC,QAAQ,CAAC,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC;QAC3C,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,CAAC;QACzC,IAAI,CAAC,aAAa,CAAC,aAAa,CAAC,KAAK,CAAC,aAAa,CAAC,CACrD,CAAC;AAAA,CACF;AAED,SAAS,6BAA6B,CAAC,OAAuC,EAAyB;IACtG,MAAM,MAAM,GAAG,IAAI,GAAG,EAA+B,CAAC;IACtD,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,MAAM,CAAC,GAAG,CAAC,GAAG,MAAM,CAAC,QAAQ,SAAS,MAAM,CAAC,OAAO,SAAS,MAAM,CAAC,aAAa,EAAE,EAAE,MAAM,CAAC,CAAC;IAC9F,CAAC;IACD,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,0BAA0B,CAAC,CAAC;AAAA,CAC7D;AAED,SAAS,sBAAsB,CAC9B,KAA+B,EAC/B,QAAgB,EAChB,OAAe,EACf,aAA4B,EACrB;IACP,KAAK,CAAC,oBAAoB,GAAG,6BAA6B,CAAC;QAC1D,GAAG,KAAK,CAAC,oBAAoB;QAC7B,EAAE,QAAQ,EAAE,OAAO,EAAE,aAAa,EAAE;KACpC,CAAC,CAAC;AAAA,CACH;AAED,MAAM,UAAU,eAAe,CAAC,KAAc,EAA0B;IACvE,OAAO,CACN,KAAK,KAAK,KAAK;QACf,KAAK,KAAK,SAAS;QACnB,KAAK,KAAK,KAAK;QACf,KAAK,KAAK,QAAQ;QAClB,KAAK,KAAK,MAAM;QAChB,KAAK,KAAK,OAAO,CACjB,CAAC;AAAA,CACF;AAED,SAAS,iBAAiB,CAAC,OAAgB,EAU9B;IACZ,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW;QAAE,OAAO,SAAS,CAAC;IACzE,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAC;IAChG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC;QAAE,OAAO,SAAS,CAAC;IAChF,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,OAAO,CAAC,KAAK,CAAC;IAC/D,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;IAC3C,IACC,OAAO,KAAK,KAAK,QAAQ;QACzB,OAAO,MAAM,KAAK,QAAQ;QAC1B,OAAO,SAAS,KAAK,QAAQ;QAC7B,OAAO,UAAU,KAAK,QAAQ;QAC9B,OAAO,SAAS,KAAK,QAAQ,EAC5B,CAAC;QACF,OAAO,SAAS,CAAC;IAClB,CAAC;IACD,OAAO;QACN,QAAQ,EAAE,OAAO,CAAC,QAAQ;QAC1B,OAAO,EAAE,OAAO,CAAC,KAAK;QACtB,KAAK;QACL,MAAM;QACN,SAAS;QACT,UAAU;QACV,SAAS;KACT,CAAC;AAAA,CACF;AAED,MAAM,UAAU,4BAA4B,GAA6B;IACxE,OAAO;QACN,IAAI,EAAE,SAAS;QACf,YAAY,EAAE,CAAC;QACf,YAAY,EAAE,CAAC;QACf,cAAc,EAAE,CAAC;QACjB,WAAW,EAAE,CAAC;QACd,SAAS,EAAE,CAAC;QACZ,UAAU,EAAE,IAAI,GAAG,EAAkB;QACrC,oBAAoB,EAAE,EAAE;QACxB,YAAY,EAAE,IAAI;QAClB,oBAAoB,EAAE,IAAI;KAC1B,CAAC;AAAA,CACF;AAED,MAAM,UAAU,6BAA6B,CAAC,KAA+B,EAAE,KAAuB,EAAQ;IAC7G,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,cAAc;YAClB,KAAK,CAAC,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,SAAS,CAAC;YAC7C,MAAM;QACP,KAAK,OAAO,EAAE,CAAC;YACd,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,EAAE,IAAI,EAAE,CAAC;YAClC,IAAI,KAAK,EAAE,CAAC;gBACX,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC7C,CAAC;iBAAM,CAAC;gBACP,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,CAAC,QAAQ,CAAC,CAAC;YACzC,CAAC;YACD,MAAM;QACP,CAAC;QACD,KAAK,cAAc;YAClB,KAAK,CAAC,YAAY,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YAC1E,IAAI,KAAK,CAAC,oBAAoB,EAAE,CAAC;gBAChC,sBAAsB,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC;YAC1F,CAAC;YACD,MAAM;QACP,KAAK,uBAAuB;YAC3B,IAAI,CAAC,eAAe,CAAC,KAAK,CAAC,aAAa,CAAC;gBAAE,MAAM;YACjD,KAAK,CAAC,oBAAoB,GAAG,KAAK,CAAC,aAAa,CAAC;YACjD,IAAI,KAAK,CAAC,YAAY,EAAE,CAAC;gBACxB,sBAAsB,CAAC,KAAK,EAAE,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,KAAK,CAAC,YAAY,CAAC,OAAO,EAAE,KAAK,CAAC,aAAa,CAAC,CAAC;YAC7G,CAAC;YACD,MAAM;QACP,KAAK,SAAS,EAAE,CAAC;YAChB,KAAK,CAAC,YAAY,IAAI,CAAC,CAAC;YACxB,MAAM,KAAK,GAAG,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YAC/C,IAAI,CAAC,KAAK;gBAAE,MAAM;YAClB,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,SAAS,CAAC;YACtC,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC;YACvD,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC;YACrF,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS,CAAC;YACnC,KAAK,CAAC,YAAY,GAAG,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,OAAO,EAAE,CAAC;YAC1E,IAAI,KAAK,CAAC,oBAAoB,EAAE,CAAC;gBAChC,sBAAsB,CAAC,KAAK,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,oBAAoB,CAAC,CAAC;YAC1F,CAAC;YACD,MAAM;QACP,CAAC;QACD,KAAK,YAAY,CAAC;QAClB,KAAK,gBAAgB,EAAE,CAAC;YACvB,MAAM,KAAK,GAAG,KAAK,CAAC,KAAK,CAAC;YAC1B,IACC,CAAC,QAAQ,CAAC,KAAK,CAAC;gBAChB,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,CAAC;gBACrB,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ;gBAC/B,OAAO,KAAK,CAAC,MAAM,KAAK,QAAQ;gBAChC,OAAO,KAAK,CAAC,SAAS,KAAK,QAAQ;gBACnC,OAAO,KAAK,CAAC,UAAU,KAAK,QAAQ;gBACpC,OAAO,KAAK,CAAC,IAAI,CAAC,KAAK,KAAK,QAAQ,EACnC,CAAC;gBACF,MAAM;YACP,CAAC;YACD,KAAK,CAAC,YAAY,IAAI,KAAK,CAAC,SAAS,CAAC;YACtC,KAAK,CAAC,cAAc,IAAI,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU,CAAC;YACvD,KAAK,CAAC,WAAW,IAAI,KAAK,CAAC,KAAK,GAAG,KAAK,CAAC,MAAM,GAAG,KAAK,CAAC,SAAS,GAAG,KAAK,CAAC,UAAU,CAAC;YACrF,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC;YACpC,MAAM;QACP,CAAC;QACD,KAAK,qBAAqB,CAAC;QAC3B,KAAK,QAAQ,CAAC;QACd,KAAK,gBAAgB,CAAC;QACtB,KAAK,MAAM;YACV,MAAM;QACP,SAAS,CAAC;YACT,MAAM,UAAU,GAAU,KAAK,CAAC;YAChC,KAAK,UAAU,CAAC;YAChB,MAAM;QACP,CAAC;IACF,CAAC;AAAA,CACD;AAED,MAAM,UAAU,gBAAgB,CAAC,KAA+B,EAAU;IACzE,MAAM,OAAO,GAA+B;QAC3C,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,oBAAoB,EAAE,KAAK,CAAC,oBAAoB;KAChD,CAAC;IACF,OAAO,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;AAAA,CAC/B;AAED,SAAS,YAAY,CAAC,IAAY,EAA8B;IAC/D,IAAI,MAAe,CAAC;IACpB,IAAI,CAAC;QACJ,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;IAC3B,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,cAAc,CACnB,gDAAgD,EAChD,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC1C,CAAC;IACH,CAAC;IACD,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,MAAM,CAAC,EAAE,CAAC;QAChD,MAAM,cAAc,CAAC,+CAA+C,CAAC,CAAC;IACvE,CAAC;IACD,MAAM,YAAY,GAAG,MAAM,CAAC,YAAY,CAAC;IACzC,MAAM,oBAAoB,GAAG,MAAM,CAAC,oBAAoB,CAAC;IACzD,IACC,CAAC,MAAM,CAAC,IAAI,KAAK,SAAS,IAAI,OAAO,MAAM,CAAC,IAAI,KAAK,QAAQ,CAAC;QAC9D,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ;QACvC,OAAO,MAAM,CAAC,YAAY,KAAK,QAAQ;QACvC,OAAO,MAAM,CAAC,cAAc,KAAK,QAAQ;QACzC,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ;QACtC,OAAO,MAAM,CAAC,SAAS,KAAK,QAAQ;QACpC,CAAC,YAAY,KAAK,SAAS;YAC1B,YAAY,KAAK,IAAI;YACrB,CAAC,CAAC,QAAQ,CAAC,YAAY,CAAC;gBACvB,OAAO,YAAY,CAAC,QAAQ,KAAK,QAAQ;gBACzC,OAAO,YAAY,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC;QAC5C,CAAC,oBAAoB,KAAK,SAAS,IAAI,oBAAoB,KAAK,IAAI,IAAI,CAAC,eAAe,CAAC,oBAAoB,CAAC,CAAC,EAC9G,CAAC;QACF,MAAM,cAAc,CAAC,iDAAiD,CAAC,CAAC;IACzE,CAAC;IACD,OAAO;QACN,IAAI,EAAE,MAAM,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,SAAS;QACtC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,YAAY,EAAE,MAAM,CAAC,YAAY;QACjC,cAAc,EAAE,MAAM,CAAC,cAAc;QACrC,WAAW,EAAE,MAAM,CAAC,WAAW;QAC/B,SAAS,EAAE,MAAM,CAAC,SAAS;QAC3B,YAAY,EACX,YAAY,IAAI,QAAQ,CAAC,YAAY,CAAC;YACrC,CAAC,CAAC,EAAE,QAAQ,EAAE,YAAY,CAAC,QAAkB,EAAE,OAAO,EAAE,YAAY,CAAC,OAAiB,EAAE;YACxF,CAAC,CAAC,CAAC,YAAY,IAAI,SAAS,CAAC;QAC/B,oBAAoB,EAAG,oBAAyD,IAAI,SAAS;KAC7F,CAAC;AAAA,CACF;AAED,SAAS,6BAA6B,CAAC,GAAyB,EAAW;IAC1E,IAAI,CAAC;QACJ,OAAO,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAChB,MAAM,cAAc,CACnB,0BAA0B,GAAG,CAAC,SAAS,oBAAoB,EAC3D,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAC1C,CAAC;IACH,CAAC;AAAA,CACD;AAED,MAAM,UAAU,yBAAyB,CACxC,UAAkC,EAClC,SAAiC,EACN;IAC3B,MAAM,OAAO,GAAG,YAAY,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IACjD,MAAM,KAAK,GAA6B;QACvC,IAAI,EAAE,OAAO,CAAC,IAAI;QAClB,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,YAAY,EAAE,OAAO,CAAC,YAAY;QAClC,cAAc,EAAE,OAAO,CAAC,cAAc;QACtC,WAAW,EAAE,OAAO,CAAC,WAAW;QAChC,SAAS,EAAE,OAAO,CAAC,SAAS;QAC5B,UAAU,EAAE,IAAI,GAAG,EAAkB;QACrC,oBAAoB,EAAE,EAAE;QACxB,YAAY,EAAE,OAAO,CAAC,YAAY,IAAI,IAAI;QAC1C,oBAAoB,EAAE,OAAO,CAAC,oBAAoB,IAAI,IAAI;KAC1D,CAAC;IACF,KAAK,MAAM,GAAG,IAAI,SAAS,EAAE,CAAC;QAC7B,MAAM,OAAO,GAAG,6BAA6B,CAAC,GAAG,CAAC,CAAC;QACnD,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,MAAM,cAAc,CAAC,0BAA0B,GAAG,CAAC,SAAS,mBAAmB,CAAC,CAAC;QACzG,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC1B,IAAI,OAAO,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAC1C,MAAM,cAAc,CAAC,0BAA0B,GAAG,CAAC,SAAS,sBAAsB,CAAC,CAAC;YACrF,CAAC;YACD,IAAI,OAAO,CAAC,KAAK,KAAK,IAAI,IAAI,OAAO,CAAC,KAAK,KAAK,SAAS,IAAI,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;gBAChG,MAAM,cAAc,CAAC,0BAA0B,GAAG,CAAC,SAAS,oBAAoB,CAAC,CAAC;YACnF,CAAC;YACD,MAAM,KAAK,GAAG,OAAO,OAAO,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;YAC5E,IAAI,KAAK,EAAE,CAAC;gBACX,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;YAC/C,CAAC;iBAAM,CAAC;gBACP,KAAK,CAAC,UAAU,CAAC,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC;YAC3C,CAAC;YACD,SAAS;QACV,CAAC;QACD,IAAI,GAAG,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;QAC3B,CAAC;IACF,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED,MAAM,UAAU,iCAAiC,CAAC,KAA+B,EAAgB;IAChG,OAAO;QACN,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,YAAY,EAAE,KAAK,CAAC,YAAY;QAChC,cAAc,EAAE,KAAK,CAAC,cAAc;QACpC,WAAW,EAAE,KAAK,CAAC,WAAW;QAC9B,SAAS,EAAE,KAAK,CAAC,SAAS;KAC1B,CAAC;AAAA,CACF;AAED,MAAM,UAAU,uBAAuB,CACtC,SAAiB,EACjB,KAA+B,EACQ;IACvC,OAAO,CAAC,SAAS,EAAE,gBAAgB,CAAC,KAAK,CAAC,CAAC,CAAC;AAAA,CAC5C;AAED,MAAM,UAAU,uBAAuB,CACtC,KAAuB,EAC0C;IACjE,QAAQ,KAAK,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,OAAO;YACX,OAAO;gBACN;oBACC,IAAI,EAAE,OAAO;oBACb,OAAO,EAAE,IAAI,CAAC,SAAS,CAAC,EAAE,QAAQ,EAAE,KAAK,CAAC,QAAQ,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,IAAI,IAAI,EAAE,CAAC;iBACjF;aACD,CAAC;QACH,KAAK,cAAc,CAAC;QACpB,KAAK,uBAAuB,CAAC;QAC7B,KAAK,SAAS;YACb,OAAO,EAAE,CAAC;QACX,KAAK,qBAAqB,CAAC;QAC3B,KAAK,gBAAgB,CAAC;QACtB,KAAK,YAAY,CAAC;QAClB,KAAK,QAAQ,CAAC;QACd,KAAK,gBAAgB,CAAC;QACtB,KAAK,MAAM,CAAC;QACZ,KAAK,cAAc;YAClB,OAAO,EAAE,CAAC;QACX,SAAS,CAAC;YACT,MAAM,UAAU,GAAU,KAAK,CAAC;YAChC,KAAK,UAAU,CAAC;YAChB,OAAO,EAAE,CAAC;QACX,CAAC;IACF,CAAC;AAAA,CACD","sourcesContent":["import type { SessionStats, SessionTreeEntry, ThinkingLevel } from \"@handy_wote/pi-agent-core\";\nimport { invalidSession, isRecord } from \"./shared.ts\";\n\nexport interface SessionMaterializedRow {\n\tsession_id: string;\n\tpayload: string;\n}\n\nexport interface EntryMaterializedRow {\n\tsession_id: string;\n\tentry_seq: number;\n\ttype: string;\n\tpayload: string;\n}\n\nexport interface ModelThinkingConfig {\n\tprovider: string;\n\tmodelId: string;\n\tthinkingLevel: ThinkingLevel;\n}\n\nexport interface SessionMaterializedState {\n\tname: string | undefined;\n\tmessageCount: number;\n\tcachedTokens: number;\n\tuncachedTokens: number;\n\ttotalTokens: number;\n\tcostTotal: number;\n\tlabelsById: Map<string, string>;\n\tmodelThinkingConfigs: ModelThinkingConfig[];\n\tcurrentModel: { provider: string; modelId: string } | null;\n\tcurrentThinkingLevel: ThinkingLevel | null;\n}\n\ninterface SessionMaterializedSummary {\n\tname?: string;\n\tmessageCount: number;\n\tcachedTokens: number;\n\tuncachedTokens: number;\n\ttotalTokens: number;\n\tcostTotal: number;\n\tcurrentModel?: { provider: string; modelId: string } | null;\n\tcurrentThinkingLevel?: ThinkingLevel | null;\n}\n\nfunction compareModelThinkingConfig(left: ModelThinkingConfig, right: ModelThinkingConfig): number {\n\treturn (\n\t\tleft.provider.localeCompare(right.provider) ||\n\t\tleft.modelId.localeCompare(right.modelId) ||\n\t\tleft.thinkingLevel.localeCompare(right.thinkingLevel)\n\t);\n}\n\nfunction normalizeModelThinkingConfigs(configs: readonly ModelThinkingConfig[]): ModelThinkingConfig[] {\n\tconst unique = new Map<string, ModelThinkingConfig>();\n\tfor (const config of configs) {\n\t\tunique.set(`${config.provider}\\u0000${config.modelId}\\u0000${config.thinkingLevel}`, config);\n\t}\n\treturn [...unique.values()].sort(compareModelThinkingConfig);\n}\n\nfunction addModelThinkingConfig(\n\tstate: SessionMaterializedState,\n\tprovider: string,\n\tmodelId: string,\n\tthinkingLevel: ThinkingLevel,\n): void {\n\tstate.modelThinkingConfigs = normalizeModelThinkingConfigs([\n\t\t...state.modelThinkingConfigs,\n\t\t{ provider, modelId, thinkingLevel },\n\t]);\n}\n\nexport function isThinkingLevel(value: unknown): value is ThinkingLevel {\n\treturn (\n\t\tvalue === \"off\" ||\n\t\tvalue === \"minimal\" ||\n\t\tvalue === \"low\" ||\n\t\tvalue === \"medium\" ||\n\t\tvalue === \"high\" ||\n\t\tvalue === \"xhigh\"\n\t);\n}\n\nfunction getAssistantUsage(message: unknown):\n\t| {\n\t\t\tprovider: string;\n\t\t\tmodelId: string;\n\t\t\tinput: number;\n\t\t\toutput: number;\n\t\t\tcacheRead: number;\n\t\t\tcacheWrite: number;\n\t\t\tcostTotal: number;\n\t }\n\t| undefined {\n\tif (!isRecord(message) || message.role !== \"assistant\") return undefined;\n\tif (typeof message.provider !== \"string\" || typeof message.model !== \"string\") return undefined;\n\tif (!isRecord(message.usage) || !isRecord(message.usage.cost)) return undefined;\n\tconst { input, output, cacheRead, cacheWrite } = message.usage;\n\tconst costTotal = message.usage.cost.total;\n\tif (\n\t\ttypeof input !== \"number\" ||\n\t\ttypeof output !== \"number\" ||\n\t\ttypeof cacheRead !== \"number\" ||\n\t\ttypeof cacheWrite !== \"number\" ||\n\t\ttypeof costTotal !== \"number\"\n\t) {\n\t\treturn undefined;\n\t}\n\treturn {\n\t\tprovider: message.provider,\n\t\tmodelId: message.model,\n\t\tinput,\n\t\toutput,\n\t\tcacheRead,\n\t\tcacheWrite,\n\t\tcostTotal,\n\t};\n}\n\nexport function createEmptyMaterializedState(): SessionMaterializedState {\n\treturn {\n\t\tname: undefined,\n\t\tmessageCount: 0,\n\t\tcachedTokens: 0,\n\t\tuncachedTokens: 0,\n\t\ttotalTokens: 0,\n\t\tcostTotal: 0,\n\t\tlabelsById: new Map<string, string>(),\n\t\tmodelThinkingConfigs: [],\n\t\tcurrentModel: null,\n\t\tcurrentThinkingLevel: null,\n\t};\n}\n\nexport function applyEntryToMaterializedState(state: SessionMaterializedState, entry: SessionTreeEntry): void {\n\tswitch (entry.type) {\n\t\tcase \"session_info\":\n\t\t\tstate.name = entry.name?.trim() || undefined;\n\t\t\tbreak;\n\t\tcase \"label\": {\n\t\t\tconst label = entry.label?.trim();\n\t\t\tif (label) {\n\t\t\t\tstate.labelsById.set(entry.targetId, label);\n\t\t\t} else {\n\t\t\t\tstate.labelsById.delete(entry.targetId);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase \"model_change\":\n\t\t\tstate.currentModel = { provider: entry.provider, modelId: entry.modelId };\n\t\t\tif (state.currentThinkingLevel) {\n\t\t\t\taddModelThinkingConfig(state, entry.provider, entry.modelId, state.currentThinkingLevel);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"thinking_level_change\":\n\t\t\tif (!isThinkingLevel(entry.thinkingLevel)) break;\n\t\t\tstate.currentThinkingLevel = entry.thinkingLevel;\n\t\t\tif (state.currentModel) {\n\t\t\t\taddModelThinkingConfig(state, state.currentModel.provider, state.currentModel.modelId, entry.thinkingLevel);\n\t\t\t}\n\t\t\tbreak;\n\t\tcase \"message\": {\n\t\t\tstate.messageCount += 1;\n\t\t\tconst usage = getAssistantUsage(entry.message);\n\t\t\tif (!usage) break;\n\t\t\tstate.cachedTokens += usage.cacheRead;\n\t\t\tstate.uncachedTokens += usage.input + usage.cacheWrite;\n\t\t\tstate.totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;\n\t\t\tstate.costTotal += usage.costTotal;\n\t\t\tstate.currentModel = { provider: usage.provider, modelId: usage.modelId };\n\t\t\tif (state.currentThinkingLevel) {\n\t\t\t\taddModelThinkingConfig(state, usage.provider, usage.modelId, state.currentThinkingLevel);\n\t\t\t}\n\t\t\tbreak;\n\t\t}\n\t\tcase \"compaction\":\n\t\tcase \"branch_summary\": {\n\t\t\tconst usage = entry.usage;\n\t\t\tif (\n\t\t\t\t!isRecord(usage) ||\n\t\t\t\t!isRecord(usage.cost) ||\n\t\t\t\ttypeof usage.input !== \"number\" ||\n\t\t\t\ttypeof usage.output !== \"number\" ||\n\t\t\t\ttypeof usage.cacheRead !== \"number\" ||\n\t\t\t\ttypeof usage.cacheWrite !== \"number\" ||\n\t\t\t\ttypeof usage.cost.total !== \"number\"\n\t\t\t) {\n\t\t\t\tbreak;\n\t\t\t}\n\t\t\tstate.cachedTokens += usage.cacheRead;\n\t\t\tstate.uncachedTokens += usage.input + usage.cacheWrite;\n\t\t\tstate.totalTokens += usage.input + usage.output + usage.cacheRead + usage.cacheWrite;\n\t\t\tstate.costTotal += usage.cost.total;\n\t\t\tbreak;\n\t\t}\n\t\tcase \"active_tools_change\":\n\t\tcase \"custom\":\n\t\tcase \"custom_message\":\n\t\tcase \"leaf\":\n\t\t\tbreak;\n\t\tdefault: {\n\t\t\tconst exhaustive: never = entry;\n\t\t\tvoid exhaustive;\n\t\t\tbreak;\n\t\t}\n\t}\n}\n\nexport function serializeSummary(state: SessionMaterializedState): string {\n\tconst summary: SessionMaterializedSummary = {\n\t\tname: state.name,\n\t\tmessageCount: state.messageCount,\n\t\tcachedTokens: state.cachedTokens,\n\t\tuncachedTokens: state.uncachedTokens,\n\t\ttotalTokens: state.totalTokens,\n\t\tcostTotal: state.costTotal,\n\t\tcurrentModel: state.currentModel,\n\t\tcurrentThinkingLevel: state.currentThinkingLevel,\n\t};\n\treturn JSON.stringify(summary);\n}\n\nfunction parseSummary(json: string): SessionMaterializedSummary {\n\tlet parsed: unknown;\n\ttry {\n\t\tparsed = JSON.parse(json);\n\t} catch (error) {\n\t\tthrow invalidSession(\n\t\t\t`materialized session summary is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n\tif (!isRecord(parsed) || Array.isArray(parsed)) {\n\t\tthrow invalidSession(\"materialized session summary is not an object\");\n\t}\n\tconst currentModel = parsed.currentModel;\n\tconst currentThinkingLevel = parsed.currentThinkingLevel;\n\tif (\n\t\t(parsed.name !== undefined && typeof parsed.name !== \"string\") ||\n\t\ttypeof parsed.messageCount !== \"number\" ||\n\t\ttypeof parsed.cachedTokens !== \"number\" ||\n\t\ttypeof parsed.uncachedTokens !== \"number\" ||\n\t\ttypeof parsed.totalTokens !== \"number\" ||\n\t\ttypeof parsed.costTotal !== \"number\" ||\n\t\t(currentModel !== undefined &&\n\t\t\tcurrentModel !== null &&\n\t\t\t(!isRecord(currentModel) ||\n\t\t\t\ttypeof currentModel.provider !== \"string\" ||\n\t\t\t\ttypeof currentModel.modelId !== \"string\")) ||\n\t\t(currentThinkingLevel !== undefined && currentThinkingLevel !== null && !isThinkingLevel(currentThinkingLevel))\n\t) {\n\t\tthrow invalidSession(\"materialized session summary has invalid fields\");\n\t}\n\treturn {\n\t\tname: parsed.name?.trim() || undefined,\n\t\tmessageCount: parsed.messageCount,\n\t\tcachedTokens: parsed.cachedTokens,\n\t\tuncachedTokens: parsed.uncachedTokens,\n\t\ttotalTokens: parsed.totalTokens,\n\t\tcostTotal: parsed.costTotal,\n\t\tcurrentModel:\n\t\t\tcurrentModel && isRecord(currentModel)\n\t\t\t\t? { provider: currentModel.provider as string, modelId: currentModel.modelId as string }\n\t\t\t\t: (currentModel ?? undefined),\n\t\tcurrentThinkingLevel: (currentThinkingLevel as ThinkingLevel | null | undefined) ?? undefined,\n\t};\n}\n\nfunction parseEntryMaterializedPayload(row: EntryMaterializedRow): unknown {\n\ttry {\n\t\treturn JSON.parse(row.payload);\n\t} catch (error) {\n\t\tthrow invalidSession(\n\t\t\t`materialized entry row ${row.entry_seq} is not valid JSON`,\n\t\t\terror instanceof Error ? error : undefined,\n\t\t);\n\t}\n}\n\nexport function materializedStateFromRows(\n\tsummaryRow: SessionMaterializedRow,\n\tentryRows: EntryMaterializedRow[],\n): SessionMaterializedState {\n\tconst summary = parseSummary(summaryRow.payload);\n\tconst state: SessionMaterializedState = {\n\t\tname: summary.name,\n\t\tmessageCount: summary.messageCount,\n\t\tcachedTokens: summary.cachedTokens,\n\t\tuncachedTokens: summary.uncachedTokens,\n\t\ttotalTokens: summary.totalTokens,\n\t\tcostTotal: summary.costTotal,\n\t\tlabelsById: new Map<string, string>(),\n\t\tmodelThinkingConfigs: [],\n\t\tcurrentModel: summary.currentModel ?? null,\n\t\tcurrentThinkingLevel: summary.currentThinkingLevel ?? null,\n\t};\n\tfor (const row of entryRows) {\n\t\tconst payload = parseEntryMaterializedPayload(row);\n\t\tif (!isRecord(payload)) throw invalidSession(`materialized entry row ${row.entry_seq} is not an object`);\n\t\tif (row.type === \"label\") {\n\t\t\tif (typeof payload.targetId !== \"string\") {\n\t\t\t\tthrow invalidSession(`materialized label row ${row.entry_seq} is missing targetId`);\n\t\t\t}\n\t\t\tif (payload.label !== null && payload.label !== undefined && typeof payload.label !== \"string\") {\n\t\t\t\tthrow invalidSession(`materialized label row ${row.entry_seq} has invalid label`);\n\t\t\t}\n\t\t\tconst label = typeof payload.label === \"string\" ? payload.label.trim() : \"\";\n\t\t\tif (label) {\n\t\t\t\tstate.labelsById.set(payload.targetId, label);\n\t\t\t} else {\n\t\t\t\tstate.labelsById.delete(payload.targetId);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tif (row.type !== \"label\") {\n\t\t}\n\t}\n\treturn state;\n}\n\nexport function sessionStatsFromMaterializedState(state: SessionMaterializedState): SessionStats {\n\treturn {\n\t\tmessageCount: state.messageCount,\n\t\tcachedTokens: state.cachedTokens,\n\t\tuncachedTokens: state.uncachedTokens,\n\t\ttotalTokens: state.totalTokens,\n\t\tcostTotal: state.costTotal,\n\t};\n}\n\nexport function materializedStateValues(\n\tsessionId: string,\n\tstate: SessionMaterializedState,\n): [sessionId: string, payload: string] {\n\treturn [sessionId, serializeSummary(state)];\n}\n\nexport function entryMaterializedValues(\n\tentry: SessionTreeEntry,\n): Array<{ type: EntryMaterializedRow[\"type\"]; payload: string }> {\n\tswitch (entry.type) {\n\t\tcase \"label\":\n\t\t\treturn [\n\t\t\t\t{\n\t\t\t\t\ttype: \"label\",\n\t\t\t\t\tpayload: JSON.stringify({ targetId: entry.targetId, label: entry.label ?? null }),\n\t\t\t\t},\n\t\t\t];\n\t\tcase \"model_change\":\n\t\tcase \"thinking_level_change\":\n\t\tcase \"message\":\n\t\t\treturn [];\n\t\tcase \"active_tools_change\":\n\t\tcase \"branch_summary\":\n\t\tcase \"compaction\":\n\t\tcase \"custom\":\n\t\tcase \"custom_message\":\n\t\tcase \"leaf\":\n\t\tcase \"session_info\":\n\t\t\treturn [];\n\t\tdefault: {\n\t\t\tconst exhaustive: never = entry;\n\t\t\tvoid exhaustive;\n\t\t\treturn [];\n\t\t}\n\t}\n}\n"]}
@@ -0,0 +1,4 @@
1
+ import type { SqliteDatabase } from "../types.ts";
2
+ export declare function getNextSequence(db: SqliteDatabase, sessionId: string): Promise<number>;
3
+ export declare function advanceSequence(db: SqliteDatabase, sessionId: string, nextSeq: number): Promise<void>;
4
+ //# sourceMappingURL=session-sequences.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-sequences.d.ts","sourceRoot":"","sources":["../../../src/sqlite/storage/session-sequences.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQ5F;AAED,wBAAsB,eAAe,CAAC,EAAE,EAAE,cAAc,EAAE,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3G","sourcesContent":["import type { SqliteDatabase } from \"../types.ts\";\nimport { invalidSession } from \"./shared.ts\";\n\nexport async function getNextSequence(db: SqliteDatabase, sessionId: string): Promise<number> {\n\tconst sequenceRow = await db\n\t\t.prepare(\"SELECT next_seq FROM session_sequences WHERE session_id = ?\")\n\t\t.get<{ next_seq: number }>(sessionId);\n\tif (!sequenceRow) {\n\t\tthrow invalidSession(`missing sequence row for session ${sessionId}`);\n\t}\n\treturn sequenceRow.next_seq;\n}\n\nexport async function advanceSequence(db: SqliteDatabase, sessionId: string, nextSeq: number): Promise<void> {\n\tawait db.prepare(\"UPDATE session_sequences SET next_seq = ? WHERE session_id = ?\").run(nextSeq + 1, sessionId);\n}\n"]}
@@ -0,0 +1,14 @@
1
+ import { invalidSession } from "./shared.js";
2
+ export async function getNextSequence(db, sessionId) {
3
+ const sequenceRow = await db
4
+ .prepare("SELECT next_seq FROM session_sequences WHERE session_id = ?")
5
+ .get(sessionId);
6
+ if (!sequenceRow) {
7
+ throw invalidSession(`missing sequence row for session ${sessionId}`);
8
+ }
9
+ return sequenceRow.next_seq;
10
+ }
11
+ export async function advanceSequence(db, sessionId, nextSeq) {
12
+ await db.prepare("UPDATE session_sequences SET next_seq = ? WHERE session_id = ?").run(nextSeq + 1, sessionId);
13
+ }
14
+ //# sourceMappingURL=session-sequences.js.map