@pasko70/pibo 3.2.4 → 3.2.5
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/dist/data/payload-store.js +20 -10
- package/dist/data/schema.js +56 -22
- package/dist/tools/mcp-bridge.js +1 -1
- package/npm-shrinkwrap.json +2 -2
- package/package.json +1 -1
|
@@ -34,11 +34,8 @@ export class PayloadStore {
|
|
|
34
34
|
const createdAt = input.createdAt ?? new Date().toISOString();
|
|
35
35
|
const bytes = payloadToBytes(input.value, contentType);
|
|
36
36
|
const sha256 = createHash("sha256").update(bytes).digest("hex");
|
|
37
|
-
const existing = this.
|
|
37
|
+
const existing = this.findByIdentity(sha256, contentType, input.retentionClass);
|
|
38
38
|
if (existing) {
|
|
39
|
-
if (existing.contentType !== contentType || existing.retentionClass !== input.retentionClass) {
|
|
40
|
-
throw new PiboPayloadMetadataConflictError(sha256, existing.contentType, contentType, existing.retentionClass, input.retentionClass);
|
|
41
|
-
}
|
|
42
39
|
this.db.prepare("UPDATE payloads SET ref_count = ref_count + 1 WHERE id = ?").run(existing.id);
|
|
43
40
|
return this.getPayload(existing.id) ?? existing;
|
|
44
41
|
}
|
|
@@ -46,7 +43,7 @@ export class PayloadStore {
|
|
|
46
43
|
const encoding = shouldCompress ? "gzip" : "identity";
|
|
47
44
|
const bytesToStore = shouldCompress ? gzipSync(bytes) : bytes;
|
|
48
45
|
const compressedByteSize = shouldCompress ? bytesToStore.byteLength : null;
|
|
49
|
-
const relativePath = buildRelativePayloadPath(sha256, contentType, encoding);
|
|
46
|
+
const relativePath = buildRelativePayloadPath(sha256, contentType, input.retentionClass, encoding);
|
|
50
47
|
const absolutePath = this.rootDir === ":memory:" ? relativePath : join(this.rootDir, relativePath);
|
|
51
48
|
writePayloadFile(absolutePath, bytesToStore);
|
|
52
49
|
const id = input.id ?? `payload_${randomUUID()}`;
|
|
@@ -127,7 +124,14 @@ export class PayloadStore {
|
|
|
127
124
|
return JSON.parse(Buffer.from(this.readPayloadBytesBounded(id, maxBytes)).toString("utf8"));
|
|
128
125
|
}
|
|
129
126
|
findBySha256(sha256) {
|
|
130
|
-
const row = this.db.prepare("SELECT * FROM payloads WHERE sha256 = ?").get(sha256);
|
|
127
|
+
const row = this.db.prepare("SELECT * FROM payloads WHERE sha256 = ? ORDER BY created_at ASC, id ASC LIMIT 1").get(sha256);
|
|
128
|
+
return row ? payloadFromRow(row) : undefined;
|
|
129
|
+
}
|
|
130
|
+
findByIdentity(sha256, contentType, retentionClass) {
|
|
131
|
+
const row = this.db.prepare(`
|
|
132
|
+
SELECT * FROM payloads
|
|
133
|
+
WHERE sha256 = ? AND content_type = ? AND retention_class = ?
|
|
134
|
+
`).get(sha256, contentType, retentionClass);
|
|
131
135
|
return row ? payloadFromRow(row) : undefined;
|
|
132
136
|
}
|
|
133
137
|
releaseReferences(id, count = 1) {
|
|
@@ -185,12 +189,12 @@ function defaultContentType(value) {
|
|
|
185
189
|
return "application/json";
|
|
186
190
|
}
|
|
187
191
|
function payloadToBytes(value, contentType) {
|
|
188
|
-
if (typeof value === "string")
|
|
189
|
-
return Buffer.from(value, "utf8");
|
|
190
192
|
if (value instanceof Uint8Array)
|
|
191
193
|
return value;
|
|
192
194
|
if (contentType.includes("json"))
|
|
193
195
|
return Buffer.from(JSON.stringify(value), "utf8");
|
|
196
|
+
if (typeof value === "string")
|
|
197
|
+
return Buffer.from(value, "utf8");
|
|
194
198
|
return Buffer.from(String(value), "utf8");
|
|
195
199
|
}
|
|
196
200
|
function previewTextFromValue(value) {
|
|
@@ -200,10 +204,16 @@ function previewTextFromValue(value) {
|
|
|
200
204
|
const normalized = text.replace(/\s+/g, " ").trim();
|
|
201
205
|
return normalized ? normalized.slice(0, 1024) : undefined;
|
|
202
206
|
}
|
|
203
|
-
function buildRelativePayloadPath(sha256, contentType, encoding) {
|
|
207
|
+
function buildRelativePayloadPath(sha256, contentType, retentionClass, encoding) {
|
|
204
208
|
const extension = extensionForContentType(contentType);
|
|
205
209
|
const suffix = encoding === "gzip" ? `${extension}.gz` : extension;
|
|
206
|
-
|
|
210
|
+
const metadataHash = createHash("sha256")
|
|
211
|
+
.update(contentType)
|
|
212
|
+
.update("\0")
|
|
213
|
+
.update(retentionClass)
|
|
214
|
+
.digest("hex")
|
|
215
|
+
.slice(0, 16);
|
|
216
|
+
return join("sha256", sha256.slice(0, 2), sha256.slice(2, 4), `${sha256}.${metadataHash}.${suffix}`);
|
|
207
217
|
}
|
|
208
218
|
function extensionForContentType(contentType) {
|
|
209
219
|
if (contentType.includes("json"))
|
package/dist/data/schema.js
CHANGED
|
@@ -1,6 +1,27 @@
|
|
|
1
|
-
export const PIBO_DATA_SCHEMA_VERSION =
|
|
1
|
+
export const PIBO_DATA_SCHEMA_VERSION = 8;
|
|
2
2
|
const NATIVE_HISTORY_FALLBACK_SCHEMA_VERSION = 5;
|
|
3
3
|
const retiredScopeColumn = ["owner", "scope"].join("_");
|
|
4
|
+
const payloadTableDefinition = `
|
|
5
|
+
id TEXT PRIMARY KEY,
|
|
6
|
+
sha256 TEXT NOT NULL,
|
|
7
|
+
storage_kind TEXT NOT NULL,
|
|
8
|
+
storage_path TEXT,
|
|
9
|
+
content_type TEXT NOT NULL,
|
|
10
|
+
encoding TEXT NOT NULL DEFAULT 'gzip',
|
|
11
|
+
byte_size INTEGER NOT NULL,
|
|
12
|
+
compressed_byte_size INTEGER,
|
|
13
|
+
preview_text TEXT,
|
|
14
|
+
retention_class TEXT NOT NULL,
|
|
15
|
+
ref_count INTEGER NOT NULL DEFAULT 0,
|
|
16
|
+
status TEXT NOT NULL DEFAULT 'committed',
|
|
17
|
+
created_at TEXT NOT NULL,
|
|
18
|
+
last_verified_at TEXT
|
|
19
|
+
`;
|
|
20
|
+
const payloadTableColumns = [
|
|
21
|
+
"id", "sha256", "storage_kind", "storage_path", "content_type", "encoding",
|
|
22
|
+
"byte_size", "compressed_byte_size", "preview_text", "retention_class", "ref_count",
|
|
23
|
+
"status", "created_at", "last_verified_at",
|
|
24
|
+
];
|
|
4
25
|
const retiredScopeTables = [
|
|
5
26
|
{
|
|
6
27
|
name: "sessions",
|
|
@@ -93,6 +114,26 @@ function rebuildRetiredScopeTables(db, tablesToRebuild) {
|
|
|
93
114
|
`);
|
|
94
115
|
}
|
|
95
116
|
}
|
|
117
|
+
function payloadTableNeedsMetadataIdentityRebuild(db) {
|
|
118
|
+
const table = db.prepare("SELECT 1 FROM sqlite_schema WHERE type = 'table' AND name = 'payloads'").get();
|
|
119
|
+
if (!table)
|
|
120
|
+
return false;
|
|
121
|
+
const indexes = db.prepare("SELECT name FROM pragma_index_list('payloads') WHERE \"unique\" = 1").all();
|
|
122
|
+
return indexes.some((index) => {
|
|
123
|
+
const columns = db.prepare("SELECT name FROM pragma_index_info(?) ORDER BY seqno").all(index.name);
|
|
124
|
+
return columns.length === 1 && columns[0]?.name === "sha256";
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
function rebuildPayloadTableForMetadataIdentity(db) {
|
|
128
|
+
const columns = payloadTableColumns.join(", ");
|
|
129
|
+
db.exec(`
|
|
130
|
+
DROP TABLE IF EXISTS __pibo_schema_v8_payloads;
|
|
131
|
+
CREATE TABLE __pibo_schema_v8_payloads (${payloadTableDefinition});
|
|
132
|
+
INSERT INTO __pibo_schema_v8_payloads (${columns}) SELECT ${columns} FROM payloads;
|
|
133
|
+
DROP TABLE payloads;
|
|
134
|
+
ALTER TABLE __pibo_schema_v8_payloads RENAME TO payloads;
|
|
135
|
+
`);
|
|
136
|
+
}
|
|
96
137
|
export function assertSupportedPiboDataSchemaVersion(db) {
|
|
97
138
|
const version = Number(db.prepare("PRAGMA user_version").get()?.user_version ?? 0);
|
|
98
139
|
if (version > PIBO_DATA_SCHEMA_VERSION) {
|
|
@@ -101,6 +142,7 @@ export function assertSupportedPiboDataSchemaVersion(db) {
|
|
|
101
142
|
return version;
|
|
102
143
|
}
|
|
103
144
|
export const PIBO_DATA_SCHEMA_MIGRATION_STEPS = [
|
|
145
|
+
"payload-metadata-identity",
|
|
104
146
|
"schema",
|
|
105
147
|
"runtime-bindings",
|
|
106
148
|
"render-high-water",
|
|
@@ -114,20 +156,22 @@ export const PIBO_DATA_SCHEMA_MIGRATION_STEPS = [
|
|
|
114
156
|
export function applyPiboDataSchema(db, hooks = {}) {
|
|
115
157
|
const previousVersion = assertSupportedPiboDataSchemaVersion(db);
|
|
116
158
|
const tablesToRebuild = retiredScopeTablesToRebuild(db);
|
|
159
|
+
const rebuildPayloadTable = payloadTableNeedsMetadataIdentityRebuild(db);
|
|
160
|
+
const requiresTableRebuild = tablesToRebuild.length > 0 || rebuildPayloadTable;
|
|
117
161
|
const ownsTransaction = !db.isTransaction;
|
|
118
|
-
if (!ownsTransaction &&
|
|
162
|
+
if (!ownsTransaction && requiresTableRebuild) {
|
|
119
163
|
throw new Error("Pibo data schema migration requires an independent transaction");
|
|
120
164
|
}
|
|
121
165
|
const foreignKeysEnabled = ownsTransaction
|
|
122
|
-
&&
|
|
166
|
+
&& requiresTableRebuild
|
|
123
167
|
&& Number(db.prepare("PRAGMA foreign_keys").get().foreign_keys ?? 0) === 1;
|
|
124
168
|
if (foreignKeysEnabled)
|
|
125
169
|
db.exec("PRAGMA foreign_keys = OFF");
|
|
126
170
|
try {
|
|
127
171
|
if (ownsTransaction)
|
|
128
172
|
db.exec("BEGIN IMMEDIATE");
|
|
129
|
-
applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRebuild);
|
|
130
|
-
if (
|
|
173
|
+
applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRebuild, rebuildPayloadTable);
|
|
174
|
+
if (requiresTableRebuild) {
|
|
131
175
|
const violations = db.prepare("PRAGMA foreign_key_check").all();
|
|
132
176
|
if (violations.length > 0) {
|
|
133
177
|
throw new Error(`Pibo data schema migration would retain ${violations.length} foreign-key violation(s)`);
|
|
@@ -146,11 +190,14 @@ export function applyPiboDataSchema(db, hooks = {}) {
|
|
|
146
190
|
db.exec("PRAGMA foreign_keys = ON");
|
|
147
191
|
}
|
|
148
192
|
}
|
|
149
|
-
function applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRebuild) {
|
|
193
|
+
function applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRebuild, rebuildPayloadTable) {
|
|
150
194
|
const existingSessionCount = db.prepare("SELECT COUNT(*) AS count FROM sqlite_schema WHERE type = 'table' AND name = 'sessions'").get();
|
|
151
195
|
const hadSessionsBeforeMigration = existingSessionCount.count > 0
|
|
152
196
|
&& Number(db.prepare("SELECT COUNT(*) AS count FROM sessions").get().count) > 0;
|
|
153
197
|
rebuildRetiredScopeTables(db, tablesToRebuild);
|
|
198
|
+
if (rebuildPayloadTable)
|
|
199
|
+
rebuildPayloadTableForMetadataIdentity(db);
|
|
200
|
+
hooks.afterStep?.("payload-metadata-identity");
|
|
154
201
|
db.exec(`
|
|
155
202
|
CREATE TABLE IF NOT EXISTS sessions (
|
|
156
203
|
id TEXT PRIMARY KEY,
|
|
@@ -300,22 +347,9 @@ function applyPiboDataSchemaInTransaction(db, hooks, previousVersion, tablesToRe
|
|
|
300
347
|
);
|
|
301
348
|
|
|
302
349
|
|
|
303
|
-
CREATE TABLE IF NOT EXISTS payloads (
|
|
304
|
-
|
|
305
|
-
sha256
|
|
306
|
-
storage_kind TEXT NOT NULL,
|
|
307
|
-
storage_path TEXT,
|
|
308
|
-
content_type TEXT NOT NULL,
|
|
309
|
-
encoding TEXT NOT NULL DEFAULT 'gzip',
|
|
310
|
-
byte_size INTEGER NOT NULL,
|
|
311
|
-
compressed_byte_size INTEGER,
|
|
312
|
-
preview_text TEXT,
|
|
313
|
-
retention_class TEXT NOT NULL,
|
|
314
|
-
ref_count INTEGER NOT NULL DEFAULT 0,
|
|
315
|
-
status TEXT NOT NULL DEFAULT 'committed',
|
|
316
|
-
created_at TEXT NOT NULL,
|
|
317
|
-
last_verified_at TEXT
|
|
318
|
-
);
|
|
350
|
+
CREATE TABLE IF NOT EXISTS payloads (${payloadTableDefinition});
|
|
351
|
+
CREATE UNIQUE INDEX IF NOT EXISTS idx_payloads_identity
|
|
352
|
+
ON payloads(sha256, content_type, retention_class);
|
|
319
353
|
|
|
320
354
|
CREATE TABLE IF NOT EXISTS event_log (
|
|
321
355
|
stream_id INTEGER PRIMARY KEY,
|
package/dist/tools/mcp-bridge.js
CHANGED
|
@@ -180,7 +180,7 @@ async function piboResultToMcp(result, options) {
|
|
|
180
180
|
tool: options.tool,
|
|
181
181
|
toolCallId: options.toolCallId,
|
|
182
182
|
contentType: "application/json",
|
|
183
|
-
value:
|
|
183
|
+
value: structuredContent,
|
|
184
184
|
});
|
|
185
185
|
payloadRefs.push(stored.ref);
|
|
186
186
|
structuredContent = undefined;
|
package/npm-shrinkwrap.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pasko70/pibo",
|
|
3
|
-
"version": "3.2.
|
|
3
|
+
"version": "3.2.5",
|
|
4
4
|
"lockfileVersion": 3,
|
|
5
5
|
"requires": true,
|
|
6
6
|
"packages": {
|
|
7
7
|
"": {
|
|
8
8
|
"name": "@pasko70/pibo",
|
|
9
|
-
"version": "3.2.
|
|
9
|
+
"version": "3.2.5",
|
|
10
10
|
"workspaces": [
|
|
11
11
|
"packages/workflows"
|
|
12
12
|
],
|