stratagate-dsh 0.2.36 → 0.2.37
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +26 -20
- package/LICENSE +21 -21
- package/README.md +336 -133
- package/README.zh-CN.md +400 -0
- package/dist/client.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +476 -134
- package/dist/index.js.map +1 -1
- package/docs/ARCHITECTURE.md +244 -0
- package/docs/DSH.md +191 -0
- package/docs/{README.zh-CN.md → DSH.zh-CN.md} +30 -27
- package/docs/EVALUATION.md +179 -0
- package/docs/EXTERNAL_MEMORY_IMPORT.zh-CN.md +54 -0
- package/docs/assets/stratagate-avatar.png +0 -0
- package/docs/assets/stratagate-how-it-works.en.png +0 -0
- package/docs/assets/stratagate-how-it-works.zh-CN.png +0 -0
- package/docs/assets/stratagate-knowledge-graph.png +0 -0
- package/docs/assets/stratagate-memory-structure.png +0 -0
- package/docs/assets/stratagate-short-term-memory.png +0 -0
- package/package.json +176 -153
- package/screenshots.json +4 -0
package/dist/index.js
CHANGED
|
@@ -15,7 +15,8 @@ var Config = z.object({
|
|
|
15
15
|
ingestSubagents: z.boolean().default(false),
|
|
16
16
|
provider: z.string(),
|
|
17
17
|
model: z.string(),
|
|
18
|
-
maxOutputTokens: z.natural().min(256).default(
|
|
18
|
+
maxOutputTokens: z.natural().min(256).default(2048),
|
|
19
|
+
structuredTaskTimeoutMs: z.natural().min(1e3).default(45e3)
|
|
19
20
|
});
|
|
20
21
|
function resolveConfig(config) {
|
|
21
22
|
const database = config.database?.trim() ?? "";
|
|
@@ -36,7 +37,8 @@ function resolveConfig(config) {
|
|
|
36
37
|
blockDecayLambda: Math.max(0, config.blockDecayLambda ?? 0.3),
|
|
37
38
|
ingestSubagents: config.ingestSubagents ?? false,
|
|
38
39
|
...provider && model ? { provider, model } : {},
|
|
39
|
-
maxOutputTokens: Math.max(256, Math.floor(config.maxOutputTokens ??
|
|
40
|
+
maxOutputTokens: Math.max(256, Math.floor(config.maxOutputTokens ?? 2048)),
|
|
41
|
+
structuredTaskTimeoutMs: Math.max(1e3, Math.floor(config.structuredTaskTimeoutMs ?? 45e3))
|
|
40
42
|
};
|
|
41
43
|
}
|
|
42
44
|
|
|
@@ -45,7 +47,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
45
47
|
import { BlockAssembler, createUserMessage } from "@deepseek-ai/dsh-llm";
|
|
46
48
|
import { parameterSchemaSpecToJsonSchema, validateArgs } from "@deepseek-ai/dsh-tools";
|
|
47
49
|
|
|
48
|
-
//
|
|
50
|
+
// packages/core/src/blocks.ts
|
|
49
51
|
var DEFAULT_BLOCK_TURN_SIZE = 12;
|
|
50
52
|
var BLOCK_MAX_LEVEL = 5;
|
|
51
53
|
var BLOCK_DECAY_LAMBDA = 0.3;
|
|
@@ -252,7 +254,7 @@ function deterministicBlockLayers(messages) {
|
|
|
252
254
|
};
|
|
253
255
|
}
|
|
254
256
|
|
|
255
|
-
//
|
|
257
|
+
// packages/core/src/search.ts
|
|
256
258
|
var wordSegmenter = new Intl.Segmenter(void 0, { granularity: "word" });
|
|
257
259
|
function normalizeSearchText(value) {
|
|
258
260
|
return value.normalize("NFKC").toLocaleLowerCase().replace(/\s+/g, " ").trim();
|
|
@@ -329,7 +331,7 @@ function rrfRank(rankings) {
|
|
|
329
331
|
return [...fused.values()].sort((left, right) => right.score - left.score || left.bestRank - right.bestRank || left.item.id.localeCompare(right.item.id)).map(({ item, score }) => ({ item, score }));
|
|
330
332
|
}
|
|
331
333
|
|
|
332
|
-
//
|
|
334
|
+
// packages/core/src/graph.ts
|
|
333
335
|
var NODE_TYPES = /* @__PURE__ */ new Set(["person", "project", "organization", "tool", "place"]);
|
|
334
336
|
var STATUSES = /* @__PURE__ */ new Set(["active", "superseded", "disputed", "archived"]);
|
|
335
337
|
function text(value, limit = 240) {
|
|
@@ -467,7 +469,7 @@ function applyGraphProjection(options) {
|
|
|
467
469
|
return { nodeIds: [...touchedNodes], edgeIds: [...touchedEdges] };
|
|
468
470
|
}
|
|
469
471
|
|
|
470
|
-
//
|
|
472
|
+
// packages/core/src/events.ts
|
|
471
473
|
function normalizeStandardEventType(value) {
|
|
472
474
|
const normalized = normalizeSearchText(value ?? "").replace(/[\s_-]+/g, "");
|
|
473
475
|
const aliases = {
|
|
@@ -505,7 +507,7 @@ function normalizeStandardEventType(value) {
|
|
|
505
507
|
return aliases[normalized] ?? "other";
|
|
506
508
|
}
|
|
507
509
|
|
|
508
|
-
//
|
|
510
|
+
// packages/core/src/external-memory.ts
|
|
509
511
|
var EXTERNAL_MEMORY_EXPORT_SCHEMA = "stratagate.external-memory.v2";
|
|
510
512
|
var EXTERNAL_MEMORY_DECIDER_PROMPT_ZH_CN = `
|
|
511
513
|
\u4F60\u662F StrataGate \u7684\u5916\u90E8\u8BB0\u5FC6\u5408\u5E76\u88C1\u51B3\u5668\u3002\u8F93\u5165\u5305\u542B\u4E00\u4E2A\u5916\u90E8 candidate Event\uFF0C\u4EE5\u53CA\u672C\u5730\u68C0\u7D22\u5F97\u5230\u7684 Top-K existing Events\u3002\u53EA\u80FD\u4F9D\u636E candidate \u548C existing Events \u5224\u65AD\uFF0C\u4E0D\u5F97\u5F15\u5165\u5217\u8868\u4E4B\u5916\u7684\u672C\u5730\u4E8B\u4EF6\uFF0C\u4E5F\u4E0D\u5F97\u51ED\u5E38\u8BC6\u8865\u5168\u65F6\u95F4\u3002
|
|
@@ -1184,7 +1186,7 @@ function parseExternalMemoryExport(text3) {
|
|
|
1184
1186
|
}
|
|
1185
1187
|
var externalMemoryJsonExtractor = async ({ text: text3 }) => parseExternalMemoryExport(text3);
|
|
1186
1188
|
|
|
1187
|
-
//
|
|
1189
|
+
// packages/core/src/retrieval.ts
|
|
1188
1190
|
var RETRIEVAL_STRATEGIES = [
|
|
1189
1191
|
"answer",
|
|
1190
1192
|
"search_events",
|
|
@@ -1260,8 +1262,8 @@ function normalizeRetrievalAssessment(input, batchEvidenceRefs) {
|
|
|
1260
1262
|
};
|
|
1261
1263
|
}
|
|
1262
1264
|
|
|
1263
|
-
//
|
|
1264
|
-
var STRATAGATE_STORAGE_SCHEMA_VERSION =
|
|
1265
|
+
// packages/core/src/storage.ts
|
|
1266
|
+
var STRATAGATE_STORAGE_SCHEMA_VERSION = 9;
|
|
1265
1267
|
var KNOWLEDGE_GRAPH_PROJECTOR_VERSION = 1;
|
|
1266
1268
|
var StorageConflictError = class extends Error {
|
|
1267
1269
|
constructor(namespace, expectedRevision, actualRevision) {
|
|
@@ -1278,6 +1280,12 @@ var StorageConflictError = class extends Error {
|
|
|
1278
1280
|
function cloneSnapshot(snapshot) {
|
|
1279
1281
|
return structuredClone(snapshot);
|
|
1280
1282
|
}
|
|
1283
|
+
function readyLegacyBlocks(blocks) {
|
|
1284
|
+
return blocks.map((block) => ({ ...structuredClone(block), processingStatus: "ready" }));
|
|
1285
|
+
}
|
|
1286
|
+
function legacyExtractionJobs(jobs) {
|
|
1287
|
+
return jobs.map((job) => ({ ...structuredClone(job), nextRetryAt: null }));
|
|
1288
|
+
}
|
|
1281
1289
|
function emptyGraph() {
|
|
1282
1290
|
return { graphNodes: [], graphEdges: [], graphProjectionJobs: [] };
|
|
1283
1291
|
}
|
|
@@ -1285,7 +1293,7 @@ function migrateLegacyBlocks(blocks) {
|
|
|
1285
1293
|
return blocks.map((block) => {
|
|
1286
1294
|
const { pointerAnchorTurn, ...current } = block;
|
|
1287
1295
|
const position = blocks.filter((candidate) => candidate.threadId === block.threadId && candidate.endTurn <= pointerAnchorTurn).length;
|
|
1288
|
-
return { ...current, pointerAnchorBlockPosition: Math.max(1, position), lastLiftedBy: null };
|
|
1296
|
+
return { ...current, processingStatus: "ready", pointerAnchorBlockPosition: Math.max(1, position), lastLiftedBy: null };
|
|
1289
1297
|
});
|
|
1290
1298
|
}
|
|
1291
1299
|
function normalizeSnapshot(value) {
|
|
@@ -1299,6 +1307,8 @@ function normalizeSnapshot(value) {
|
|
|
1299
1307
|
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
|
|
1300
1308
|
blockDecayLambda: BLOCK_DECAY_LAMBDA,
|
|
1301
1309
|
blocks: migrateLegacyBlocks(legacy.blocks),
|
|
1310
|
+
summaryJobs: [],
|
|
1311
|
+
extractionJobs: legacyExtractionJobs(legacy.extractionJobs),
|
|
1302
1312
|
elements: [],
|
|
1303
1313
|
elementProjectionJobs: [],
|
|
1304
1314
|
usageReceipts: Array.isArray(legacy.usageReceipts) ? legacy.usageReceipts.map((receipt) => ({ ...receipt, elementIds: [] })) : [],
|
|
@@ -1312,6 +1322,8 @@ function normalizeSnapshot(value) {
|
|
|
1312
1322
|
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
|
|
1313
1323
|
blockDecayLambda: BLOCK_DECAY_LAMBDA,
|
|
1314
1324
|
blocks: migrateLegacyBlocks(legacy.blocks),
|
|
1325
|
+
summaryJobs: [],
|
|
1326
|
+
extractionJobs: legacyExtractionJobs(legacy.extractionJobs),
|
|
1315
1327
|
ingestionReceipts: [],
|
|
1316
1328
|
...emptyGraph()
|
|
1317
1329
|
};
|
|
@@ -1322,6 +1334,8 @@ function normalizeSnapshot(value) {
|
|
|
1322
1334
|
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
|
|
1323
1335
|
blockDecayLambda: BLOCK_DECAY_LAMBDA,
|
|
1324
1336
|
blocks: migrateLegacyBlocks(legacy.blocks),
|
|
1337
|
+
summaryJobs: [],
|
|
1338
|
+
extractionJobs: legacyExtractionJobs(legacy.extractionJobs),
|
|
1325
1339
|
...emptyGraph()
|
|
1326
1340
|
};
|
|
1327
1341
|
} else if (schemaVersion === 4) {
|
|
@@ -1331,6 +1345,8 @@ function normalizeSnapshot(value) {
|
|
|
1331
1345
|
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
|
|
1332
1346
|
blockDecayLambda: BLOCK_DECAY_LAMBDA,
|
|
1333
1347
|
blocks: migrateLegacyBlocks(legacy.blocks),
|
|
1348
|
+
summaryJobs: [],
|
|
1349
|
+
extractionJobs: legacyExtractionJobs(legacy.extractionJobs),
|
|
1334
1350
|
...emptyGraph()
|
|
1335
1351
|
};
|
|
1336
1352
|
} else if (schemaVersion === 5) {
|
|
@@ -1340,6 +1356,8 @@ function normalizeSnapshot(value) {
|
|
|
1340
1356
|
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
|
|
1341
1357
|
blockDecayLambda: BLOCK_DECAY_LAMBDA,
|
|
1342
1358
|
blocks: migrateLegacyBlocks(legacy.blocks),
|
|
1359
|
+
summaryJobs: [],
|
|
1360
|
+
extractionJobs: legacyExtractionJobs(legacy.extractionJobs),
|
|
1343
1361
|
...emptyGraph()
|
|
1344
1362
|
};
|
|
1345
1363
|
} else if (schemaVersion === 6) {
|
|
@@ -1347,12 +1365,30 @@ function normalizeSnapshot(value) {
|
|
|
1347
1365
|
snapshot = {
|
|
1348
1366
|
...structuredClone(legacy),
|
|
1349
1367
|
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
|
|
1350
|
-
blocks: legacy.blocks.map((block) => ({ ...structuredClone(block), lastLiftedBy: null })),
|
|
1368
|
+
blocks: readyLegacyBlocks(legacy.blocks.map((block) => ({ ...structuredClone(block), lastLiftedBy: null }))),
|
|
1369
|
+
summaryJobs: [],
|
|
1370
|
+
extractionJobs: legacyExtractionJobs(legacy.extractionJobs),
|
|
1351
1371
|
...emptyGraph()
|
|
1352
1372
|
};
|
|
1353
1373
|
} else if (schemaVersion === 7) {
|
|
1354
1374
|
const legacy = value;
|
|
1355
|
-
snapshot = {
|
|
1375
|
+
snapshot = {
|
|
1376
|
+
...structuredClone(legacy),
|
|
1377
|
+
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
|
|
1378
|
+
blocks: readyLegacyBlocks(legacy.blocks),
|
|
1379
|
+
summaryJobs: [],
|
|
1380
|
+
extractionJobs: legacyExtractionJobs(legacy.extractionJobs),
|
|
1381
|
+
...emptyGraph()
|
|
1382
|
+
};
|
|
1383
|
+
} else if (schemaVersion === 8) {
|
|
1384
|
+
const legacy = value;
|
|
1385
|
+
snapshot = {
|
|
1386
|
+
...structuredClone(legacy),
|
|
1387
|
+
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION,
|
|
1388
|
+
blocks: readyLegacyBlocks(legacy.blocks),
|
|
1389
|
+
summaryJobs: [],
|
|
1390
|
+
extractionJobs: legacyExtractionJobs(legacy.extractionJobs)
|
|
1391
|
+
};
|
|
1356
1392
|
} else if (schemaVersion === STRATAGATE_STORAGE_SCHEMA_VERSION) {
|
|
1357
1393
|
snapshot = structuredClone(value);
|
|
1358
1394
|
} else {
|
|
@@ -1367,7 +1403,7 @@ function normalizeSnapshot(value) {
|
|
|
1367
1403
|
if (!Number.isFinite(snapshot.blockDecayLambda) || snapshot.blockDecayLambda < 0) {
|
|
1368
1404
|
throw new TypeError("Invalid StrataGate snapshot: blockDecayLambda must be a non-negative finite number");
|
|
1369
1405
|
}
|
|
1370
|
-
for (const key of ["openTail", "blocks", "events", "graphNodes", "graphEdges", "graphProjectionJobs", "elements", "extractionJobs", "elementProjectionJobs", "usageReceipts", "ingestionReceipts"]) {
|
|
1406
|
+
for (const key of ["openTail", "blocks", "summaryJobs", "events", "graphNodes", "graphEdges", "graphProjectionJobs", "elements", "extractionJobs", "elementProjectionJobs", "usageReceipts", "ingestionReceipts"]) {
|
|
1371
1407
|
if (!Array.isArray(snapshot[key])) throw new TypeError(`Invalid StrataGate snapshot: ${key} must be an array`);
|
|
1372
1408
|
}
|
|
1373
1409
|
if (!Array.isArray(snapshot.successfulModelResponses)) snapshot.successfulModelResponses = [];
|
|
@@ -1375,6 +1411,12 @@ function normalizeSnapshot(value) {
|
|
|
1375
1411
|
event.temporal = { ...event.temporal, eventType: normalizeStandardEventType(event.temporal.eventType) };
|
|
1376
1412
|
}
|
|
1377
1413
|
for (const block of snapshot.blocks) {
|
|
1414
|
+
if (block.processingStatus !== "pending" && block.processingStatus !== "ready") {
|
|
1415
|
+
throw new TypeError("Invalid StrataGate snapshot: Block processingStatus must be pending or ready");
|
|
1416
|
+
}
|
|
1417
|
+
if (block.processingStatus === "ready" && (!block.l0Title || !block.l1Summary || !Array.isArray(block.l0Tags) || !Array.isArray(block.l2Keypoints) || typeof block.shouldExtract !== "boolean")) {
|
|
1418
|
+
throw new TypeError("Invalid StrataGate snapshot: ready Block must contain validated L0-L2 layers");
|
|
1419
|
+
}
|
|
1378
1420
|
if (!Number.isSafeInteger(block.pointerAnchorBlockPosition) || block.pointerAnchorBlockPosition < 1) {
|
|
1379
1421
|
throw new TypeError("Invalid StrataGate snapshot: pointerAnchorBlockPosition must be a positive integer");
|
|
1380
1422
|
}
|
|
@@ -1391,7 +1433,7 @@ function assertValidSnapshot(value) {
|
|
|
1391
1433
|
normalizeSnapshot(value);
|
|
1392
1434
|
}
|
|
1393
1435
|
|
|
1394
|
-
//
|
|
1436
|
+
// packages/core/src/weights.ts
|
|
1395
1437
|
var BASE_DECAY = 0.15;
|
|
1396
1438
|
var REHEARSAL_FACTOR = 1.5;
|
|
1397
1439
|
function criticalityFloor(criticality) {
|
|
@@ -1410,7 +1452,7 @@ function memoryWeightAt(memory, currentTurn) {
|
|
|
1410
1452
|
return memory.weight.pinned ? 1 : capped;
|
|
1411
1453
|
}
|
|
1412
1454
|
|
|
1413
|
-
//
|
|
1455
|
+
// packages/core/src/elements.ts
|
|
1414
1456
|
var ELEMENT_TYPES = /* @__PURE__ */ new Set(["person", "project", "organization", "tool", "place"]);
|
|
1415
1457
|
var FACT_MODES = /* @__PURE__ */ new Set(["state", "set", "relation"]);
|
|
1416
1458
|
function compactText(value, limit) {
|
|
@@ -1533,10 +1575,10 @@ function elementViewAt(element, at) {
|
|
|
1533
1575
|
return view;
|
|
1534
1576
|
}
|
|
1535
1577
|
|
|
1536
|
-
//
|
|
1578
|
+
// packages/core/src/sqlite.ts
|
|
1537
1579
|
import { DatabaseSync } from "node:sqlite";
|
|
1538
1580
|
|
|
1539
|
-
//
|
|
1581
|
+
// packages/core/src/time.ts
|
|
1540
1582
|
var UTC8_OFFSET_MS = 8 * 60 * 60 * 1e3;
|
|
1541
1583
|
function toUtc8Iso(value = /* @__PURE__ */ new Date()) {
|
|
1542
1584
|
const date = value instanceof Date ? new Date(value.getTime()) : new Date(value);
|
|
@@ -1547,7 +1589,7 @@ function nowUtc8() {
|
|
|
1547
1589
|
return toUtc8Iso(/* @__PURE__ */ new Date());
|
|
1548
1590
|
}
|
|
1549
1591
|
|
|
1550
|
-
//
|
|
1592
|
+
// packages/core/src/sqlite.ts
|
|
1551
1593
|
var SCHEMA = `
|
|
1552
1594
|
CREATE TABLE IF NOT EXISTS memory_spaces (
|
|
1553
1595
|
namespace TEXT PRIMARY KEY,
|
|
@@ -1580,6 +1622,7 @@ CREATE TABLE IF NOT EXISTS blocks (
|
|
|
1580
1622
|
pointer_anchor_block_position INTEGER NOT NULL,
|
|
1581
1623
|
last_lifted_at TEXT,
|
|
1582
1624
|
last_lifted_by TEXT CHECK (last_lifted_by IS NULL OR last_lifted_by IN ('user', 'agent')),
|
|
1625
|
+
processing_status TEXT NOT NULL CHECK (processing_status IN ('pending', 'ready')),
|
|
1583
1626
|
PRIMARY KEY (namespace, id),
|
|
1584
1627
|
UNIQUE (namespace, sequence),
|
|
1585
1628
|
FOREIGN KEY (namespace) REFERENCES memory_spaces(namespace) ON DELETE CASCADE
|
|
@@ -1704,6 +1747,19 @@ CREATE TABLE IF NOT EXISTS extraction_jobs (
|
|
|
1704
1747
|
status TEXT NOT NULL,
|
|
1705
1748
|
attempts INTEGER NOT NULL,
|
|
1706
1749
|
last_error TEXT,
|
|
1750
|
+
next_retry_at TEXT,
|
|
1751
|
+
updated_at TEXT NOT NULL,
|
|
1752
|
+
PRIMARY KEY (namespace, block_id),
|
|
1753
|
+
FOREIGN KEY (namespace, block_id) REFERENCES blocks(namespace, id) ON DELETE CASCADE
|
|
1754
|
+
) STRICT;
|
|
1755
|
+
|
|
1756
|
+
CREATE TABLE IF NOT EXISTS block_summary_jobs (
|
|
1757
|
+
namespace TEXT NOT NULL,
|
|
1758
|
+
block_id TEXT NOT NULL,
|
|
1759
|
+
status TEXT NOT NULL,
|
|
1760
|
+
attempts INTEGER NOT NULL,
|
|
1761
|
+
last_error TEXT,
|
|
1762
|
+
next_retry_at TEXT,
|
|
1707
1763
|
updated_at TEXT NOT NULL,
|
|
1708
1764
|
PRIMARY KEY (namespace, block_id),
|
|
1709
1765
|
FOREIGN KEY (namespace, block_id) REFERENCES blocks(namespace, id) ON DELETE CASCADE
|
|
@@ -1844,11 +1900,14 @@ var SqliteStorage = class {
|
|
|
1844
1900
|
startTurn: row.start_turn,
|
|
1845
1901
|
endTurn: row.end_turn,
|
|
1846
1902
|
createdAt: row.created_at,
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1903
|
+
processingStatus: row.processing_status,
|
|
1904
|
+
...row.l0_title ? {
|
|
1905
|
+
shouldExtract: Boolean(row.should_extract),
|
|
1906
|
+
l0Title: row.l0_title,
|
|
1907
|
+
l0Tags: parseJson(row.l0_tags_json, "blocks.l0_tags_json"),
|
|
1908
|
+
l1Summary: row.l1_summary,
|
|
1909
|
+
l2Keypoints: parseJson(row.l2_keypoints_json, "blocks.l2_keypoints_json")
|
|
1910
|
+
} : {},
|
|
1852
1911
|
l3Condensed: row.l3_condensed,
|
|
1853
1912
|
l4Readable: row.l4_readable,
|
|
1854
1913
|
l5Raw: messagesByBlock.get(row.id) ?? [],
|
|
@@ -1858,6 +1917,17 @@ var SqliteStorage = class {
|
|
|
1858
1917
|
lastLiftedAt: row.last_lifted_at,
|
|
1859
1918
|
lastLiftedBy: row.last_lifted_by
|
|
1860
1919
|
}));
|
|
1920
|
+
const summaryJobs = this.database.prepare(`
|
|
1921
|
+
SELECT block_id, status, attempts, last_error, next_retry_at, updated_at
|
|
1922
|
+
FROM block_summary_jobs WHERE namespace = ? ORDER BY block_id
|
|
1923
|
+
`).all(key).map((row) => ({
|
|
1924
|
+
blockId: row.block_id,
|
|
1925
|
+
status: row.status,
|
|
1926
|
+
attempts: row.attempts,
|
|
1927
|
+
lastError: row.last_error,
|
|
1928
|
+
nextRetryAt: row.next_retry_at,
|
|
1929
|
+
updatedAt: row.updated_at
|
|
1930
|
+
}));
|
|
1861
1931
|
const sourceRows = this.database.prepare(`
|
|
1862
1932
|
SELECT event_id, message_id, position FROM event_sources
|
|
1863
1933
|
WHERE namespace = ? ORDER BY event_id, position
|
|
@@ -1969,13 +2039,14 @@ var SqliteStorage = class {
|
|
|
1969
2039
|
};
|
|
1970
2040
|
});
|
|
1971
2041
|
const extractionJobs = this.database.prepare(`
|
|
1972
|
-
SELECT block_id, status, attempts, last_error, updated_at
|
|
2042
|
+
SELECT block_id, status, attempts, last_error, next_retry_at, updated_at
|
|
1973
2043
|
FROM extraction_jobs WHERE namespace = ? ORDER BY block_id
|
|
1974
2044
|
`).all(key).map((row) => ({
|
|
1975
2045
|
blockId: row.block_id,
|
|
1976
2046
|
status: row.status,
|
|
1977
2047
|
attempts: row.attempts,
|
|
1978
2048
|
lastError: row.last_error,
|
|
2049
|
+
nextRetryAt: row.next_retry_at,
|
|
1979
2050
|
updatedAt: row.updated_at
|
|
1980
2051
|
}));
|
|
1981
2052
|
const elementProjectionJobs = this.database.prepare(`
|
|
@@ -2034,6 +2105,7 @@ var SqliteStorage = class {
|
|
|
2034
2105
|
blockDecayLambda: space.block_decay_lambda,
|
|
2035
2106
|
openTail,
|
|
2036
2107
|
blocks,
|
|
2108
|
+
summaryJobs,
|
|
2037
2109
|
events,
|
|
2038
2110
|
graphNodes,
|
|
2039
2111
|
graphEdges,
|
|
@@ -2104,8 +2176,9 @@ var SqliteStorage = class {
|
|
|
2104
2176
|
INSERT INTO blocks (
|
|
2105
2177
|
namespace, id, thread_id, sequence, start_turn, end_turn, created_at, should_extract,
|
|
2106
2178
|
l0_title, l0_tags_json, l1_summary, l2_keypoints_json, l3_condensed, l4_readable,
|
|
2107
|
-
pointer_current_level, pointer_anchor_level, pointer_anchor_block_position, last_lifted_at, last_lifted_by
|
|
2108
|
-
|
|
2179
|
+
pointer_current_level, pointer_anchor_level, pointer_anchor_block_position, last_lifted_at, last_lifted_by,
|
|
2180
|
+
processing_status
|
|
2181
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
2109
2182
|
ON CONFLICT (namespace, id) DO UPDATE SET
|
|
2110
2183
|
thread_id = excluded.thread_id,
|
|
2111
2184
|
sequence = excluded.sequence,
|
|
@@ -2123,7 +2196,8 @@ var SqliteStorage = class {
|
|
|
2123
2196
|
pointer_anchor_level = excluded.pointer_anchor_level,
|
|
2124
2197
|
pointer_anchor_block_position = excluded.pointer_anchor_block_position,
|
|
2125
2198
|
last_lifted_at = excluded.last_lifted_at,
|
|
2126
|
-
last_lifted_by = excluded.last_lifted_by
|
|
2199
|
+
last_lifted_by = excluded.last_lifted_by,
|
|
2200
|
+
processing_status = excluded.processing_status
|
|
2127
2201
|
`);
|
|
2128
2202
|
for (const block of snapshot.blocks) {
|
|
2129
2203
|
insertBlock.run(
|
|
@@ -2134,18 +2208,19 @@ var SqliteStorage = class {
|
|
|
2134
2208
|
block.startTurn,
|
|
2135
2209
|
block.endTurn,
|
|
2136
2210
|
block.createdAt,
|
|
2137
|
-
Number(block.shouldExtract),
|
|
2138
|
-
block.l0Title,
|
|
2139
|
-
JSON.stringify(block.l0Tags),
|
|
2140
|
-
block.l1Summary,
|
|
2141
|
-
JSON.stringify(block.l2Keypoints),
|
|
2211
|
+
Number(block.shouldExtract ?? false),
|
|
2212
|
+
block.l0Title ?? "",
|
|
2213
|
+
JSON.stringify(block.l0Tags ?? []),
|
|
2214
|
+
block.l1Summary ?? "",
|
|
2215
|
+
JSON.stringify(block.l2Keypoints ?? []),
|
|
2142
2216
|
block.l3Condensed,
|
|
2143
2217
|
block.l4Readable,
|
|
2144
2218
|
block.pointerCurrentLevel,
|
|
2145
2219
|
block.pointerAnchorLevel,
|
|
2146
2220
|
block.pointerAnchorBlockPosition,
|
|
2147
2221
|
block.lastLiftedAt,
|
|
2148
|
-
block.lastLiftedBy
|
|
2222
|
+
block.lastLiftedBy,
|
|
2223
|
+
block.processingStatus
|
|
2149
2224
|
);
|
|
2150
2225
|
}
|
|
2151
2226
|
const insertMessage = this.database.prepare(`
|
|
@@ -2331,16 +2406,31 @@ var SqliteStorage = class {
|
|
|
2331
2406
|
}
|
|
2332
2407
|
const insertJob = this.database.prepare(`
|
|
2333
2408
|
INSERT INTO extraction_jobs (
|
|
2334
|
-
namespace, block_id, status, attempts, last_error, updated_at
|
|
2335
|
-
) VALUES (?, ?, ?, ?, ?, ?)
|
|
2409
|
+
namespace, block_id, status, attempts, last_error, next_retry_at, updated_at
|
|
2410
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
2336
2411
|
ON CONFLICT (namespace, block_id) DO UPDATE SET
|
|
2337
2412
|
status = excluded.status,
|
|
2338
2413
|
attempts = excluded.attempts,
|
|
2339
2414
|
last_error = excluded.last_error,
|
|
2415
|
+
next_retry_at = excluded.next_retry_at,
|
|
2340
2416
|
updated_at = excluded.updated_at
|
|
2341
2417
|
`);
|
|
2342
2418
|
for (const job of snapshot.extractionJobs) {
|
|
2343
|
-
insertJob.run(namespace, job.blockId, job.status, job.attempts, job.lastError, job.updatedAt);
|
|
2419
|
+
insertJob.run(namespace, job.blockId, job.status, job.attempts, job.lastError, job.nextRetryAt, job.updatedAt);
|
|
2420
|
+
}
|
|
2421
|
+
const insertSummaryJob = this.database.prepare(`
|
|
2422
|
+
INSERT INTO block_summary_jobs (
|
|
2423
|
+
namespace, block_id, status, attempts, last_error, next_retry_at, updated_at
|
|
2424
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
2425
|
+
ON CONFLICT (namespace, block_id) DO UPDATE SET
|
|
2426
|
+
status = excluded.status,
|
|
2427
|
+
attempts = excluded.attempts,
|
|
2428
|
+
last_error = excluded.last_error,
|
|
2429
|
+
next_retry_at = excluded.next_retry_at,
|
|
2430
|
+
updated_at = excluded.updated_at
|
|
2431
|
+
`);
|
|
2432
|
+
for (const job of snapshot.summaryJobs) {
|
|
2433
|
+
insertSummaryJob.run(namespace, job.blockId, job.status, job.attempts, job.lastError, job.nextRetryAt, job.updatedAt);
|
|
2344
2434
|
}
|
|
2345
2435
|
const insertElementProjectionJob = this.database.prepare(`
|
|
2346
2436
|
INSERT INTO element_projection_jobs (
|
|
@@ -2429,7 +2519,7 @@ var SqliteStorage = class {
|
|
|
2429
2519
|
this.database.exec(THREAD_INDEXES);
|
|
2430
2520
|
this.database.exec(`PRAGMA user_version = ${STRATAGATE_STORAGE_SCHEMA_VERSION}`);
|
|
2431
2521
|
});
|
|
2432
|
-
} else if (version
|
|
2522
|
+
} else if (version >= 1 && version < STRATAGATE_STORAGE_SCHEMA_VERSION) {
|
|
2433
2523
|
this.immediateTransaction(() => {
|
|
2434
2524
|
this.database.exec(SCHEMA);
|
|
2435
2525
|
if (version === 1) {
|
|
@@ -2465,6 +2555,13 @@ var SqliteStorage = class {
|
|
|
2465
2555
|
if (!blockColumns.some(({ name: name2 }) => name2 === "last_lifted_by")) {
|
|
2466
2556
|
this.database.exec("ALTER TABLE blocks ADD COLUMN last_lifted_by TEXT CHECK (last_lifted_by IS NULL OR last_lifted_by IN ('user', 'agent'))");
|
|
2467
2557
|
}
|
|
2558
|
+
if (!blockColumns.some(({ name: name2 }) => name2 === "processing_status")) {
|
|
2559
|
+
this.database.exec("ALTER TABLE blocks ADD COLUMN processing_status TEXT NOT NULL DEFAULT 'ready' CHECK (processing_status IN ('pending', 'ready'))");
|
|
2560
|
+
}
|
|
2561
|
+
const extractionColumns = this.database.prepare("PRAGMA table_info('extraction_jobs')").all();
|
|
2562
|
+
if (!extractionColumns.some(({ name: name2 }) => name2 === "next_retry_at")) {
|
|
2563
|
+
this.database.exec("ALTER TABLE extraction_jobs ADD COLUMN next_retry_at TEXT");
|
|
2564
|
+
}
|
|
2468
2565
|
const messageColumns = this.database.prepare("PRAGMA table_info('messages')").all();
|
|
2469
2566
|
if (!messageColumns.some(({ name: name2 }) => name2 === "thread_id")) {
|
|
2470
2567
|
this.database.exec("ALTER TABLE messages ADD COLUMN thread_id TEXT");
|
|
@@ -2512,7 +2609,7 @@ var SqliteStorage = class {
|
|
|
2512
2609
|
}
|
|
2513
2610
|
};
|
|
2514
2611
|
|
|
2515
|
-
//
|
|
2612
|
+
// packages/core/src/store.ts
|
|
2516
2613
|
function defaultIdFactory(prefix) {
|
|
2517
2614
|
return `${prefix}_${crypto.randomUUID()}`;
|
|
2518
2615
|
}
|
|
@@ -2522,21 +2619,13 @@ function defaultElementIdFactory(prefix) {
|
|
|
2522
2619
|
function defaultGraphIdFactory(prefix) {
|
|
2523
2620
|
return `${prefix}_${crypto.randomUUID()}`;
|
|
2524
2621
|
}
|
|
2525
|
-
function defaultSummary(messages) {
|
|
2526
|
-
const natural = messages.filter((message) => message.role === "user" || message.role === "assistant");
|
|
2527
|
-
const firstUser = natural.find((message) => message.role === "user");
|
|
2528
|
-
return {
|
|
2529
|
-
l0Title: (firstUser?.content ?? "Conversation block").replace(/\s+/g, " ").trim().slice(0, 80),
|
|
2530
|
-
l0Tags: [],
|
|
2531
|
-
l1Summary: natural.slice(0, 4).map((message) => message.content.replace(/\s+/g, " ").trim()).join(" ").slice(0, 500),
|
|
2532
|
-
l2Keypoints: natural.slice(0, 8).map((message) => message.content.replace(/\s+/g, " ").trim().slice(0, 160)),
|
|
2533
|
-
shouldExtract: false
|
|
2534
|
-
};
|
|
2535
|
-
}
|
|
2536
2622
|
function renderBlock(block, level) {
|
|
2623
|
+
if (block.processingStatus !== "ready" || !block.l0Title || !block.l0Tags || !block.l1Summary || !block.l2Keypoints) {
|
|
2624
|
+
throw new Error(`Block ${block.id} is not ready for rendering`);
|
|
2625
|
+
}
|
|
2537
2626
|
if (level === 0) return `${block.l0Title}
|
|
2538
2627
|
Tags: ${block.l0Tags.join(", ") || "none"}`;
|
|
2539
|
-
if (level === 1) return block.l1Summary
|
|
2628
|
+
if (level === 1) return block.l1Summary;
|
|
2540
2629
|
if (level === 2) return block.l2Keypoints.map((point) => `- ${point}`).join("\n") || block.l1Summary;
|
|
2541
2630
|
if (level === 3) return block.l3Condensed;
|
|
2542
2631
|
if (level === 4) return block.l4Readable;
|
|
@@ -2553,6 +2642,8 @@ function errorMessage(error) {
|
|
|
2553
2642
|
return error instanceof Error ? error.message : String(error);
|
|
2554
2643
|
}
|
|
2555
2644
|
var STRATAGATE_CONSTRUCTOR_TOKEN = /* @__PURE__ */ Symbol("StrataGate constructor");
|
|
2645
|
+
var DERIVATION_MAX_ATTEMPTS = 3;
|
|
2646
|
+
var DERIVATION_BACKOFF_MS = 1e3;
|
|
2556
2647
|
var StrataGate = class _StrataGate {
|
|
2557
2648
|
blockTurnSizeValue;
|
|
2558
2649
|
blockDecayLambdaValue;
|
|
@@ -2572,6 +2663,7 @@ var StrataGate = class _StrataGate {
|
|
|
2572
2663
|
graphNodes = [];
|
|
2573
2664
|
graphEdges = [];
|
|
2574
2665
|
extractionJobs = /* @__PURE__ */ new Map();
|
|
2666
|
+
summaryJobs = /* @__PURE__ */ new Map();
|
|
2575
2667
|
elementProjectionJobs = /* @__PURE__ */ new Map();
|
|
2576
2668
|
graphProjectionJobs = /* @__PURE__ */ new Map();
|
|
2577
2669
|
usageReceipts = /* @__PURE__ */ new Map();
|
|
@@ -2680,6 +2772,21 @@ var StrataGate = class _StrataGate {
|
|
|
2680
2772
|
if (loaded && loadedSnapshot) {
|
|
2681
2773
|
memory.restoreSnapshot(loadedSnapshot);
|
|
2682
2774
|
memory.revision = loadedRevision;
|
|
2775
|
+
const interruptedSummaries = [...memory.summaryJobs.values()].filter((job) => job.status === "running");
|
|
2776
|
+
if (interruptedSummaries.length > 0) {
|
|
2777
|
+
await memory.commitMutation(() => {
|
|
2778
|
+
const now = toUtc8Iso(memory.now());
|
|
2779
|
+
for (const job of interruptedSummaries) {
|
|
2780
|
+
memory.summaryJobs.set(job.blockId, {
|
|
2781
|
+
...job,
|
|
2782
|
+
status: "failed",
|
|
2783
|
+
lastError: "Block summarization was interrupted before completion.",
|
|
2784
|
+
nextRetryAt: now,
|
|
2785
|
+
updatedAt: now
|
|
2786
|
+
});
|
|
2787
|
+
}
|
|
2788
|
+
});
|
|
2789
|
+
}
|
|
2683
2790
|
const interrupted = [...memory.extractionJobs.values()].filter((job) => job.status === "running");
|
|
2684
2791
|
if (interrupted.length > 0) {
|
|
2685
2792
|
await memory.commitMutation(() => {
|
|
@@ -2689,6 +2796,7 @@ var StrataGate = class _StrataGate {
|
|
|
2689
2796
|
...job,
|
|
2690
2797
|
status: "failed",
|
|
2691
2798
|
lastError: "Extraction was interrupted before completion.",
|
|
2799
|
+
nextRetryAt: now,
|
|
2692
2800
|
updatedAt: now
|
|
2693
2801
|
});
|
|
2694
2802
|
}
|
|
@@ -2780,6 +2888,9 @@ var StrataGate = class _StrataGate {
|
|
|
2780
2888
|
listExtractionJobs() {
|
|
2781
2889
|
return [...this.extractionJobs.values()];
|
|
2782
2890
|
}
|
|
2891
|
+
listSummaryJobs() {
|
|
2892
|
+
return [...this.summaryJobs.values()];
|
|
2893
|
+
}
|
|
2783
2894
|
listElementProjectionJobs() {
|
|
2784
2895
|
return [...this.elementProjectionJobs.values()];
|
|
2785
2896
|
}
|
|
@@ -2812,6 +2923,7 @@ var StrataGate = class _StrataGate {
|
|
|
2812
2923
|
blockDecayLambda: this.blockDecayLambda,
|
|
2813
2924
|
openTail: this.openTail,
|
|
2814
2925
|
blocks: this.blocks,
|
|
2926
|
+
summaryJobs: [...this.summaryJobs.values()],
|
|
2815
2927
|
events: this.events,
|
|
2816
2928
|
graphNodes: this.graphNodes,
|
|
2817
2929
|
graphEdges: this.graphEdges,
|
|
@@ -2860,57 +2972,55 @@ var StrataGate = class _StrataGate {
|
|
|
2860
2972
|
if (receiptId) this.ingestionReceipts.set(receiptId, { id: receiptId, createdAt });
|
|
2861
2973
|
return true;
|
|
2862
2974
|
});
|
|
2863
|
-
if (!appended) return { sealedBlock: null, extractedEvents: [], projectedElements: [] };
|
|
2975
|
+
if (!appended) return { sealedBlock: null, readyBlocks: [], extractedEvents: [], projectedElements: [] };
|
|
2864
2976
|
if (options.deferProcessing === true) {
|
|
2865
|
-
return { sealedBlock: null, extractedEvents: [], projectedElements: [] };
|
|
2977
|
+
return { sealedBlock: null, readyBlocks: [], extractedEvents: [], projectedElements: [] };
|
|
2866
2978
|
}
|
|
2867
2979
|
if (this.threadOpenTail(threadId).filter((message) => message.role === "user").length < this.blockTurnSize) {
|
|
2868
2980
|
const projectedElements2 = await this.projectEligibleElements() ?? [];
|
|
2869
2981
|
await this.projectEligibleGraph();
|
|
2870
|
-
return { sealedBlock: null, extractedEvents: [], projectedElements: projectedElements2 };
|
|
2982
|
+
return { sealedBlock: null, readyBlocks: [], extractedEvents: [], projectedElements: projectedElements2 };
|
|
2871
2983
|
}
|
|
2872
2984
|
const sealedBlock = await this.sealOpenTail(threadId);
|
|
2873
|
-
|
|
2985
|
+
if (options.deferDerivation === true) {
|
|
2986
|
+
return { sealedBlock, readyBlocks: [], extractedEvents: [], projectedElements: [] };
|
|
2987
|
+
}
|
|
2988
|
+
const beforeReady = sealedBlock.processingStatus === "ready";
|
|
2989
|
+
const extractedEvents = await this.processBlock(sealedBlock, { retryFailed: false });
|
|
2990
|
+
const readyBlocks = !beforeReady && sealedBlock.processingStatus === "ready" ? [sealedBlock] : [];
|
|
2874
2991
|
const projectedElements = await this.projectEligibleElements() ?? [];
|
|
2875
2992
|
await this.projectEligibleGraph();
|
|
2876
|
-
return { sealedBlock, extractedEvents, projectedElements };
|
|
2993
|
+
return { sealedBlock, readyBlocks, extractedEvents, projectedElements };
|
|
2877
2994
|
}
|
|
2878
2995
|
async resumePendingWork(options = {}) {
|
|
2879
2996
|
const sealedBlocks = [];
|
|
2997
|
+
const readyBlocks = [];
|
|
2880
2998
|
const extractedEvents = [];
|
|
2881
2999
|
const projectedElements = [];
|
|
2882
3000
|
while (true) {
|
|
2883
3001
|
const sealable = this.nextSealableThread();
|
|
2884
3002
|
if (sealable === null) break;
|
|
2885
3003
|
sealedBlocks.push(await this.sealOpenTail(sealable.threadId));
|
|
2886
|
-
extractedEvents.push(...await this.extractEligibleBlock() ?? []);
|
|
2887
|
-
projectedElements.push(...await this.projectEligibleElements() ?? []);
|
|
2888
|
-
await this.projectEligibleGraph();
|
|
2889
3004
|
}
|
|
2890
|
-
|
|
2891
|
-
|
|
2892
|
-
|
|
3005
|
+
if (options.deferDerivation === true) {
|
|
3006
|
+
return { sealedBlocks, readyBlocks, extractedEvents, projectedElements };
|
|
3007
|
+
}
|
|
3008
|
+
for (const block of this.blocks) {
|
|
3009
|
+
if (options.threadId !== void 0 && block.threadId !== options.threadId) continue;
|
|
3010
|
+
if (block.processingStatus === "ready") continue;
|
|
3011
|
+
const extracted = await this.processBlock(block, { retryFailed: options.retryFailed === true });
|
|
2893
3012
|
extractedEvents.push(...extracted);
|
|
3013
|
+
if (this.blocks.find((candidate) => candidate.id === block.id)?.processingStatus === "ready") readyBlocks.push(block);
|
|
2894
3014
|
projectedElements.push(...await this.projectEligibleElements() ?? []);
|
|
2895
3015
|
await this.projectEligibleGraph();
|
|
2896
3016
|
}
|
|
2897
|
-
if (options.retrySkipped === true) {
|
|
2898
|
-
const skippedBlockIds = this.blocks.filter((block) => this.nextBlockInThread(block) !== null && block.shouldExtract && this.extractionJobs.get(block.id)?.status === "skipped").map((block) => block.id);
|
|
2899
|
-
for (const blockId of skippedBlockIds) {
|
|
2900
|
-
const extracted = await this.extractEligibleBlock({ blockId, includeSkipped: true });
|
|
2901
|
-
if (extracted === null) continue;
|
|
2902
|
-
extractedEvents.push(...extracted);
|
|
2903
|
-
projectedElements.push(...await this.projectEligibleElements() ?? []);
|
|
2904
|
-
await this.projectEligibleGraph();
|
|
2905
|
-
}
|
|
2906
|
-
}
|
|
2907
3017
|
while (true) {
|
|
2908
3018
|
const projected = await this.projectEligibleElements();
|
|
2909
3019
|
if (projected === null) break;
|
|
2910
3020
|
projectedElements.push(...projected);
|
|
2911
3021
|
}
|
|
2912
3022
|
await this.projectEligibleGraph();
|
|
2913
|
-
return { sealedBlocks, extractedEvents, projectedElements };
|
|
3023
|
+
return { sealedBlocks, readyBlocks, extractedEvents, projectedElements };
|
|
2914
3024
|
}
|
|
2915
3025
|
async addEvent(input) {
|
|
2916
3026
|
return this.commitMutation(() => {
|
|
@@ -3272,9 +3382,9 @@ var StrataGate = class _StrataGate {
|
|
|
3272
3382
|
* StrataGate namespace (never another namespace).
|
|
3273
3383
|
*/
|
|
3274
3384
|
getBlockContext(threadId) {
|
|
3275
|
-
const blocks = threadId === void 0 ? this.blocks : this.blocks.filter((block) => block.threadId === threadId);
|
|
3385
|
+
const blocks = threadId === void 0 ? this.blocks.filter((block) => block.processingStatus === "ready") : this.blocks.filter((block) => block.threadId === threadId && block.processingStatus === "ready");
|
|
3276
3386
|
return blocks.map((block) => {
|
|
3277
|
-
const threadBlocks = this.threadBlocks(block.threadId);
|
|
3387
|
+
const threadBlocks = this.threadBlocks(block.threadId).filter((candidate) => candidate.processingStatus === "ready");
|
|
3278
3388
|
const latestBlockPosition = threadBlocks.length;
|
|
3279
3389
|
const blockPosition = threadBlocks.indexOf(block) + 1;
|
|
3280
3390
|
const age = Math.max(0, latestBlockPosition - blockPosition);
|
|
@@ -3300,8 +3410,10 @@ var StrataGate = class _StrataGate {
|
|
|
3300
3410
|
return this.commitMutation(() => {
|
|
3301
3411
|
const block = this.blocks.find((candidate) => candidate.id === id);
|
|
3302
3412
|
if (!block) throw new Error(`Unknown block: ${id}`);
|
|
3303
|
-
|
|
3304
|
-
const
|
|
3413
|
+
if (block.processingStatus !== "ready") throw new Error(`Block ${id} is not ready for decay or expansion`);
|
|
3414
|
+
const readyBlocks = this.threadBlocks(block.threadId).filter((candidate) => candidate.processingStatus === "ready");
|
|
3415
|
+
const latestBlockPosition = readyBlocks.length;
|
|
3416
|
+
const blockPosition = readyBlocks.indexOf(block) + 1;
|
|
3305
3417
|
const current = getDecayedBlockLevel(
|
|
3306
3418
|
block.pointerAnchorLevel,
|
|
3307
3419
|
block.pointerAnchorBlockPosition,
|
|
@@ -3420,6 +3532,7 @@ var StrataGate = class _StrataGate {
|
|
|
3420
3532
|
l1Summary: text3.replace(/\s+/gu, " ").trim().slice(0, 500),
|
|
3421
3533
|
l2Keypoints: text3.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean).slice(0, 8),
|
|
3422
3534
|
shouldExtract: false,
|
|
3535
|
+
processingStatus: "ready",
|
|
3423
3536
|
...deterministicBlockLayers([message]),
|
|
3424
3537
|
pointerCurrentLevel: 5,
|
|
3425
3538
|
pointerAnchorLevel: 5,
|
|
@@ -3636,7 +3749,6 @@ var StrataGate = class _StrataGate {
|
|
|
3636
3749
|
if (raw.filter((message) => message.role === "user").length < this.blockTurnSize) {
|
|
3637
3750
|
throw new Error("Open tail does not contain enough turns to seal a block");
|
|
3638
3751
|
}
|
|
3639
|
-
const generated = this.summarizer ? await this.summarizer(raw) : defaultSummary(raw);
|
|
3640
3752
|
const deterministic = deterministicBlockLayers(raw);
|
|
3641
3753
|
const sequence = this.blocks.length + 1;
|
|
3642
3754
|
const threadBlocks = this.threadBlocks(threadId);
|
|
@@ -3656,11 +3768,7 @@ var StrataGate = class _StrataGate {
|
|
|
3656
3768
|
startTurn,
|
|
3657
3769
|
endTurn,
|
|
3658
3770
|
createdAt: raw.at(-1)?.createdAt ?? toUtc8Iso(this.now()),
|
|
3659
|
-
|
|
3660
|
-
l0Tags: generated.l0Tags,
|
|
3661
|
-
l1Summary: generated.l1Summary,
|
|
3662
|
-
l2Keypoints: generated.l2Keypoints,
|
|
3663
|
-
shouldExtract: generated.shouldExtract,
|
|
3771
|
+
processingStatus: "pending",
|
|
3664
3772
|
...deterministic,
|
|
3665
3773
|
pointerCurrentLevel: 5,
|
|
3666
3774
|
pointerAnchorLevel: 5,
|
|
@@ -3672,27 +3780,132 @@ var StrataGate = class _StrataGate {
|
|
|
3672
3780
|
const remaining = this.openTail.filter((message) => !sealedIds.has(message.id));
|
|
3673
3781
|
this.openTail.splice(0, this.openTail.length, ...remaining);
|
|
3674
3782
|
this.blocks.push(block);
|
|
3783
|
+
const updatedAt = toUtc8Iso(this.now());
|
|
3784
|
+
this.summaryJobs.set(block.id, {
|
|
3785
|
+
blockId: block.id,
|
|
3786
|
+
status: "pending",
|
|
3787
|
+
attempts: 0,
|
|
3788
|
+
lastError: null,
|
|
3789
|
+
nextRetryAt: null,
|
|
3790
|
+
updatedAt
|
|
3791
|
+
});
|
|
3675
3792
|
return block;
|
|
3676
3793
|
});
|
|
3677
3794
|
}
|
|
3795
|
+
async processBlock(block, options) {
|
|
3796
|
+
if (block.processingStatus === "ready") return [];
|
|
3797
|
+
const summary = this.summaryJobs.get(block.id);
|
|
3798
|
+
if (!summary || summary.status !== "succeeded") {
|
|
3799
|
+
if (!this.summarizer || !this.jobCanRun(summary, options.retryFailed)) return [];
|
|
3800
|
+
const claimed = await this.commitMutation(() => {
|
|
3801
|
+
const current = this.summaryJobs.get(block.id);
|
|
3802
|
+
if (!current || !this.jobCanRun(current, options.retryFailed)) return false;
|
|
3803
|
+
this.summaryJobs.set(block.id, {
|
|
3804
|
+
...current,
|
|
3805
|
+
status: "running",
|
|
3806
|
+
attempts: current.attempts + 1,
|
|
3807
|
+
lastError: null,
|
|
3808
|
+
nextRetryAt: null,
|
|
3809
|
+
updatedAt: toUtc8Iso(this.now())
|
|
3810
|
+
});
|
|
3811
|
+
return true;
|
|
3812
|
+
});
|
|
3813
|
+
if (!claimed) return [];
|
|
3814
|
+
try {
|
|
3815
|
+
const generated = await this.summarizer(block.l5Raw);
|
|
3816
|
+
if (!generated.l0Title.trim() || !generated.l1Summary.trim() || !Array.isArray(generated.l0Tags) || !Array.isArray(generated.l2Keypoints) || typeof generated.shouldExtract !== "boolean") {
|
|
3817
|
+
throw new Error("Block summarizer returned invalid L0-L2 layers");
|
|
3818
|
+
}
|
|
3819
|
+
await this.commitMutation(() => {
|
|
3820
|
+
block.l0Title = generated.l0Title;
|
|
3821
|
+
block.l0Tags = [...generated.l0Tags];
|
|
3822
|
+
block.l1Summary = generated.l1Summary;
|
|
3823
|
+
block.l2Keypoints = [...generated.l2Keypoints];
|
|
3824
|
+
block.shouldExtract = generated.shouldExtract;
|
|
3825
|
+
const current = this.summaryJobs.get(block.id);
|
|
3826
|
+
if (!current) throw new Error(`Missing summary job for block: ${block.id}`);
|
|
3827
|
+
this.summaryJobs.set(block.id, {
|
|
3828
|
+
...current,
|
|
3829
|
+
status: "succeeded",
|
|
3830
|
+
lastError: null,
|
|
3831
|
+
nextRetryAt: null,
|
|
3832
|
+
updatedAt: toUtc8Iso(this.now())
|
|
3833
|
+
});
|
|
3834
|
+
});
|
|
3835
|
+
} catch (error) {
|
|
3836
|
+
await this.failSummary(block.id, error);
|
|
3837
|
+
return [];
|
|
3838
|
+
}
|
|
3839
|
+
}
|
|
3840
|
+
if (block.shouldExtract === false) {
|
|
3841
|
+
await this.commitMutation(() => {
|
|
3842
|
+
const now = toUtc8Iso(this.now());
|
|
3843
|
+
this.extractionJobs.set(block.id, {
|
|
3844
|
+
blockId: block.id,
|
|
3845
|
+
status: "skipped",
|
|
3846
|
+
attempts: this.extractionJobs.get(block.id)?.attempts ?? 0,
|
|
3847
|
+
lastError: null,
|
|
3848
|
+
nextRetryAt: null,
|
|
3849
|
+
updatedAt: now
|
|
3850
|
+
});
|
|
3851
|
+
this.markBlockReady(block);
|
|
3852
|
+
});
|
|
3853
|
+
return [];
|
|
3854
|
+
}
|
|
3855
|
+
try {
|
|
3856
|
+
const extracted = await this.extractEligibleBlock({ blockId: block.id, retryFailed: options.retryFailed });
|
|
3857
|
+
return extracted ?? [];
|
|
3858
|
+
} catch {
|
|
3859
|
+
return [];
|
|
3860
|
+
}
|
|
3861
|
+
}
|
|
3862
|
+
jobCanRun(job, force) {
|
|
3863
|
+
if (!job || job.attempts >= DERIVATION_MAX_ATTEMPTS || job.status === "running" || job.status === "succeeded") return false;
|
|
3864
|
+
return force || job.nextRetryAt === null || Date.parse(job.nextRetryAt) <= this.now().getTime();
|
|
3865
|
+
}
|
|
3866
|
+
async failSummary(blockId, error) {
|
|
3867
|
+
await this.commitMutation(() => {
|
|
3868
|
+
const job = this.summaryJobs.get(blockId);
|
|
3869
|
+
if (!job) return;
|
|
3870
|
+
this.summaryJobs.set(blockId, {
|
|
3871
|
+
...job,
|
|
3872
|
+
status: "failed",
|
|
3873
|
+
lastError: errorMessage(error),
|
|
3874
|
+
nextRetryAt: this.retryAt(job.attempts),
|
|
3875
|
+
updatedAt: toUtc8Iso(this.now())
|
|
3876
|
+
});
|
|
3877
|
+
});
|
|
3878
|
+
}
|
|
3879
|
+
retryAt(attempts) {
|
|
3880
|
+
if (attempts >= DERIVATION_MAX_ATTEMPTS) return null;
|
|
3881
|
+
return toUtc8Iso(new Date(this.now().getTime() + DERIVATION_BACKOFF_MS * 2 ** Math.max(0, attempts - 1)));
|
|
3882
|
+
}
|
|
3883
|
+
markBlockReady(block) {
|
|
3884
|
+
if (!block.l0Title || !block.l0Tags || !block.l1Summary || !block.l2Keypoints || typeof block.shouldExtract !== "boolean") {
|
|
3885
|
+
throw new Error(`Block ${block.id} cannot become ready without validated L0-L2 layers`);
|
|
3886
|
+
}
|
|
3887
|
+
const ready = this.threadBlocks(block.threadId).filter((candidate) => candidate.processingStatus === "ready");
|
|
3888
|
+
block.processingStatus = "ready";
|
|
3889
|
+
block.pointerCurrentLevel = 5;
|
|
3890
|
+
block.pointerAnchorLevel = 5;
|
|
3891
|
+
block.pointerAnchorBlockPosition = ready.length + 1;
|
|
3892
|
+
}
|
|
3678
3893
|
async extractEligibleBlock(options = {}) {
|
|
3679
|
-
if (!this.extractor
|
|
3894
|
+
if (!this.extractor) return null;
|
|
3680
3895
|
const target = this.blocks.find((block) => {
|
|
3681
|
-
if (
|
|
3896
|
+
if (block.processingStatus === "ready" || block.shouldExtract !== true) return false;
|
|
3682
3897
|
if (options.blockId !== void 0 && block.id !== options.blockId) return false;
|
|
3683
|
-
const
|
|
3684
|
-
return
|
|
3898
|
+
const job = this.extractionJobs.get(block.id);
|
|
3899
|
+
return job === void 0 || this.jobCanRun(job, options.retryFailed === true);
|
|
3685
3900
|
});
|
|
3686
3901
|
if (!target) return null;
|
|
3687
3902
|
const threadBlocks = this.threadBlocks(target.threadId);
|
|
3688
3903
|
const targetIndex = threadBlocks.indexOf(target);
|
|
3689
|
-
const next = threadBlocks
|
|
3690
|
-
if (!next) return null;
|
|
3904
|
+
const next = threadBlocks.slice(targetIndex + 1).find((block) => block.l2Keypoints !== void 0) ?? null;
|
|
3691
3905
|
const existing = this.extractionJobs.get(target.id);
|
|
3692
3906
|
await this.commitMutation(() => {
|
|
3693
3907
|
const currentStatus = this.extractionJobs.get(target.id)?.status;
|
|
3694
|
-
|
|
3695
|
-
if (currentStatus !== void 0 && currentStatus !== "failed" && !canRetrySkipped) {
|
|
3908
|
+
if (currentStatus !== void 0 && currentStatus !== "failed") {
|
|
3696
3909
|
throw new Error(`Extraction block ${target.id} is already ${currentStatus}`);
|
|
3697
3910
|
}
|
|
3698
3911
|
this.extractionJobs.set(target.id, {
|
|
@@ -3700,13 +3913,14 @@ var StrataGate = class _StrataGate {
|
|
|
3700
3913
|
status: "running",
|
|
3701
3914
|
attempts: (existing?.attempts ?? 0) + 1,
|
|
3702
3915
|
lastError: null,
|
|
3916
|
+
nextRetryAt: null,
|
|
3703
3917
|
updatedAt: toUtc8Iso(this.now())
|
|
3704
3918
|
});
|
|
3705
3919
|
});
|
|
3706
3920
|
let result;
|
|
3707
3921
|
try {
|
|
3708
3922
|
result = await this.extractor({
|
|
3709
|
-
previous: threadBlocks
|
|
3923
|
+
previous: threadBlocks.slice(0, targetIndex).reverse().find((block) => block.l2Keypoints !== void 0) ?? null,
|
|
3710
3924
|
target,
|
|
3711
3925
|
next,
|
|
3712
3926
|
timeline: this.events.map((event) => ({ id: event.id, title: event.title, temporal: event.temporal }))
|
|
@@ -3719,6 +3933,7 @@ var StrataGate = class _StrataGate {
|
|
|
3719
3933
|
...job,
|
|
3720
3934
|
status: "failed",
|
|
3721
3935
|
lastError: errorMessage(error),
|
|
3936
|
+
nextRetryAt: this.retryAt(job.attempts),
|
|
3722
3937
|
updatedAt: toUtc8Iso(this.now())
|
|
3723
3938
|
});
|
|
3724
3939
|
});
|
|
@@ -3733,6 +3948,7 @@ var StrataGate = class _StrataGate {
|
|
|
3733
3948
|
...job,
|
|
3734
3949
|
status: "failed",
|
|
3735
3950
|
lastError: reason,
|
|
3951
|
+
nextRetryAt: this.retryAt(job.attempts),
|
|
3736
3952
|
updatedAt: toUtc8Iso(this.now())
|
|
3737
3953
|
});
|
|
3738
3954
|
});
|
|
@@ -3751,8 +3967,10 @@ var StrataGate = class _StrataGate {
|
|
|
3751
3967
|
...job,
|
|
3752
3968
|
status: result.shouldExtract ? "succeeded" : "skipped",
|
|
3753
3969
|
lastError: null,
|
|
3970
|
+
nextRetryAt: null,
|
|
3754
3971
|
updatedAt: toUtc8Iso(this.now())
|
|
3755
3972
|
});
|
|
3973
|
+
this.markBlockReady(target);
|
|
3756
3974
|
return extracted;
|
|
3757
3975
|
});
|
|
3758
3976
|
}
|
|
@@ -3821,6 +4039,8 @@ var StrataGate = class _StrataGate {
|
|
|
3821
4039
|
this.currentTurn = copy.currentTurn;
|
|
3822
4040
|
this.openTail.splice(0, this.openTail.length, ...copy.openTail);
|
|
3823
4041
|
this.blocks.splice(0, this.blocks.length, ...copy.blocks);
|
|
4042
|
+
this.summaryJobs.clear();
|
|
4043
|
+
for (const job of copy.summaryJobs) this.summaryJobs.set(job.blockId, job);
|
|
3824
4044
|
this.events.splice(0, this.events.length, ...copy.events);
|
|
3825
4045
|
this.graphNodes.splice(0, this.graphNodes.length, ...copy.graphNodes);
|
|
3826
4046
|
this.graphEdges.splice(0, this.graphEdges.length, ...copy.graphEdges);
|
|
@@ -3867,6 +4087,9 @@ var StrataGate = class _StrataGate {
|
|
|
3867
4087
|
for (const job of this.extractionJobs.values()) {
|
|
3868
4088
|
if (!blockIds.has(job.blockId)) throw new Error(`Unknown extraction job block in snapshot: ${job.blockId}`);
|
|
3869
4089
|
}
|
|
4090
|
+
for (const job of this.summaryJobs.values()) {
|
|
4091
|
+
if (!blockIds.has(job.blockId)) throw new Error(`Unknown summary job block in snapshot: ${job.blockId}`);
|
|
4092
|
+
}
|
|
3870
4093
|
const elementIds = /* @__PURE__ */ new Set();
|
|
3871
4094
|
for (const element of this.elements) {
|
|
3872
4095
|
if (elementIds.has(element.id)) throw new Error(`Duplicate element ID in snapshot: ${element.id}`);
|
|
@@ -4095,7 +4318,7 @@ function extractorPayload(context) {
|
|
|
4095
4318
|
}
|
|
4096
4319
|
var JSON_RESPONSE_ATTEMPTS = 2;
|
|
4097
4320
|
var JSON_RETRY_INSTRUCTION = "Your previous response did not make one valid call to the requested tool. Do not spend output on analysis or reasoning. Immediately call that tool exactly once with complete arguments. Do not return an answer as text or markdown.";
|
|
4098
|
-
var
|
|
4321
|
+
var DEFAULT_STRUCTURED_TIMEOUT_MS = 45e3;
|
|
4099
4322
|
var STRUCTURED_FIELDS = {
|
|
4100
4323
|
summarizer: ["l0Title", "l0Tags", "l1Summary", "l2Keypoints", "shouldExtract"],
|
|
4101
4324
|
extractor: ["shouldExtract", "reason", "events"],
|
|
@@ -4246,11 +4469,15 @@ var DshModelBridge = class {
|
|
|
4246
4469
|
constructor(ctx, config) {
|
|
4247
4470
|
this.ctx = ctx;
|
|
4248
4471
|
this.config = config;
|
|
4472
|
+
this.ctx.on?.("llm/adapters-updated", () => {
|
|
4473
|
+
this.offCapabilities.clear();
|
|
4474
|
+
});
|
|
4249
4475
|
}
|
|
4250
4476
|
ctx;
|
|
4251
4477
|
config;
|
|
4252
4478
|
sessions = new AsyncLocalStorage();
|
|
4253
4479
|
successfulResponses = [];
|
|
4480
|
+
offCapabilities = /* @__PURE__ */ new Map();
|
|
4254
4481
|
run(session, operation) {
|
|
4255
4482
|
return this.sessions.run(session, operation);
|
|
4256
4483
|
}
|
|
@@ -4265,7 +4492,7 @@ var DshModelBridge = class {
|
|
|
4265
4492
|
{ messages }
|
|
4266
4493
|
));
|
|
4267
4494
|
return {
|
|
4268
|
-
l0Title: text2(raw.l0Title
|
|
4495
|
+
l0Title: text2(raw.l0Title).slice(0, 120),
|
|
4269
4496
|
l0Tags: strings2(raw.l0Tags).slice(0, 12),
|
|
4270
4497
|
l1Summary: text2(raw.l1Summary).slice(0, 2e3),
|
|
4271
4498
|
l2Keypoints: strings2(raw.l2Keypoints).slice(0, 20),
|
|
@@ -4397,10 +4624,11 @@ var DshModelBridge = class {
|
|
|
4397
4624
|
async callStructured(kind, system, payload) {
|
|
4398
4625
|
const session = this.sessions.getStore();
|
|
4399
4626
|
if (!session) throw new Error("StrataGate model callback ran without a DSH session");
|
|
4400
|
-
const
|
|
4627
|
+
const baseRoute = this.resolveRoute(session);
|
|
4628
|
+
const routeKey = `${baseRoute.provider}\0${baseRoute.model}`;
|
|
4629
|
+
let useOff = await this.shouldUseOff(baseRoute);
|
|
4401
4630
|
let lastError;
|
|
4402
4631
|
let lastResponse = "";
|
|
4403
|
-
let retryMaxTokens = this.config.maxOutputTokens;
|
|
4404
4632
|
for (let attempt = 1; attempt <= JSON_RESPONSE_ATTEMPTS; attempt += 1) {
|
|
4405
4633
|
const message = createUserMessage({
|
|
4406
4634
|
content: [{ type: "text", text: JSON.stringify(payload) }],
|
|
@@ -4408,7 +4636,8 @@ var DshModelBridge = class {
|
|
|
4408
4636
|
});
|
|
4409
4637
|
const assembler = new BlockAssembler();
|
|
4410
4638
|
const request = {
|
|
4411
|
-
...
|
|
4639
|
+
...baseRoute,
|
|
4640
|
+
...useOff ? { reasoningEffort: "off" } : {},
|
|
4412
4641
|
messages: [message],
|
|
4413
4642
|
system: attempt === 1 ? system : `${system}
|
|
4414
4643
|
|
|
@@ -4422,15 +4651,35 @@ ${JSON_RETRY_INSTRUCTION}`,
|
|
|
4422
4651
|
type: "function",
|
|
4423
4652
|
function: { name: STRUCTURED_TOOLS[kind].name }
|
|
4424
4653
|
},
|
|
4425
|
-
maxTokens:
|
|
4654
|
+
maxTokens: this.config.maxOutputTokens,
|
|
4426
4655
|
sessionId: session.id,
|
|
4427
4656
|
purpose: "compaction"
|
|
4428
4657
|
};
|
|
4429
|
-
|
|
4658
|
+
try {
|
|
4659
|
+
await this.consumeStructuredStream(request, assembler);
|
|
4660
|
+
} catch (error) {
|
|
4661
|
+
if (useOff && isOffRejection(error)) {
|
|
4662
|
+
this.offCapabilities.set(routeKey, "unsupported");
|
|
4663
|
+
useOff = false;
|
|
4664
|
+
this.ctx.logger.warn(`stratagate-memory ${baseRoute.provider}/${baseRoute.model} rejected reasoningEffort=off; retrying once without it`);
|
|
4665
|
+
attempt -= 1;
|
|
4666
|
+
continue;
|
|
4667
|
+
}
|
|
4668
|
+
throw error;
|
|
4669
|
+
}
|
|
4430
4670
|
const finish = assembler.finish;
|
|
4431
4671
|
if (finish.kind === "error" || finish.kind === "aborted") {
|
|
4432
|
-
|
|
4672
|
+
const failure = new Error(`StrataGate model call failed [${finish.failure.code}]: ${finish.failure.message}`);
|
|
4673
|
+
if (useOff && isOffRejection(finish.failure)) {
|
|
4674
|
+
this.offCapabilities.set(routeKey, "unsupported");
|
|
4675
|
+
useOff = false;
|
|
4676
|
+
this.ctx.logger.warn(`stratagate-memory ${baseRoute.provider}/${baseRoute.model} rejected reasoningEffort=off; retrying once without it`);
|
|
4677
|
+
attempt -= 1;
|
|
4678
|
+
continue;
|
|
4679
|
+
}
|
|
4680
|
+
throw failure;
|
|
4433
4681
|
}
|
|
4682
|
+
if (useOff) this.offCapabilities.set(routeKey, "supported");
|
|
4434
4683
|
const blocks = assembler.blocks();
|
|
4435
4684
|
const calls = blocks.filter((block) => block.type === "tool-call");
|
|
4436
4685
|
const responseForError = `${renderBlocksForDiagnostics(blocks, finish.kind)}
|
|
@@ -4466,6 +4715,15 @@ ${JSON_RETRY_INSTRUCTION}`,
|
|
|
4466
4715
|
{ response: responseForError }
|
|
4467
4716
|
);
|
|
4468
4717
|
}
|
|
4718
|
+
if (kind === "summarizer") {
|
|
4719
|
+
const summary = object(parsed);
|
|
4720
|
+
if (!text2(summary.l0Title) || !text2(summary.l1Summary)) {
|
|
4721
|
+
throw new ModelJsonResponseError(
|
|
4722
|
+
`StrataGate ${expectedTool} arguments were invalid: l0Title and l1Summary must not be empty`,
|
|
4723
|
+
{ response: responseForError }
|
|
4724
|
+
);
|
|
4725
|
+
}
|
|
4726
|
+
}
|
|
4469
4727
|
this.successfulResponses.push({
|
|
4470
4728
|
id: `model_response_${crypto.randomUUID()}`,
|
|
4471
4729
|
kind,
|
|
@@ -4480,7 +4738,6 @@ ${JSON_RETRY_INSTRUCTION}`,
|
|
|
4480
4738
|
`StrataGate ${STRUCTURED_TOOLS[kind].name} call was truncated before valid arguments`,
|
|
4481
4739
|
{ cause: error, response: responseForError }
|
|
4482
4740
|
) : error;
|
|
4483
|
-
if (finish.kind === "max-tokens") retryMaxTokens = Math.max(this.config.maxOutputTokens, RETRY_MAX_TOKENS);
|
|
4484
4741
|
if (attempt < JSON_RESPONSE_ATTEMPTS) {
|
|
4485
4742
|
this.ctx.logger.warn(`stratagate-memory model returned an invalid structured tool call; retrying (${attempt}/${JSON_RESPONSE_ATTEMPTS})`);
|
|
4486
4743
|
}
|
|
@@ -4491,25 +4748,77 @@ ${JSON_RETRY_INSTRUCTION}`,
|
|
|
4491
4748
|
{ cause: lastError, response: lastResponse }
|
|
4492
4749
|
);
|
|
4493
4750
|
}
|
|
4494
|
-
|
|
4495
|
-
const
|
|
4496
|
-
const
|
|
4497
|
-
|
|
4498
|
-
|
|
4499
|
-
|
|
4751
|
+
async shouldUseOff(route) {
|
|
4752
|
+
const key = `${route.provider}\0${route.model}`;
|
|
4753
|
+
const cached = this.offCapabilities.get(key);
|
|
4754
|
+
if (cached) return cached === "supported";
|
|
4755
|
+
if (typeof this.ctx.llm.resolveModelInfo !== "function") return true;
|
|
4756
|
+
const controller = new AbortController();
|
|
4757
|
+
const lookupTimeoutMs = Math.min(5e3, this.config.structuredTaskTimeoutMs ?? DEFAULT_STRUCTURED_TIMEOUT_MS);
|
|
4758
|
+
let timer;
|
|
4759
|
+
try {
|
|
4760
|
+
const info = await Promise.race([
|
|
4761
|
+
this.ctx.llm.resolveModelInfo(route.provider, route.model, controller.signal),
|
|
4762
|
+
new Promise((_resolve, reject) => {
|
|
4763
|
+
timer = setTimeout(() => {
|
|
4764
|
+
controller.abort();
|
|
4765
|
+
reject(new Error(`Model capability lookup timed out after ${lookupTimeoutMs}ms`));
|
|
4766
|
+
}, lookupTimeoutMs);
|
|
4767
|
+
timer.unref?.();
|
|
4768
|
+
})
|
|
4769
|
+
]);
|
|
4770
|
+
if (!info.reasoning) return true;
|
|
4771
|
+
const supported = info.reasoning.efforts.some(({ id }) => String(id) === "off");
|
|
4772
|
+
this.offCapabilities.set(key, supported ? "supported" : "unsupported");
|
|
4773
|
+
return supported;
|
|
4774
|
+
} catch {
|
|
4775
|
+
return true;
|
|
4776
|
+
} finally {
|
|
4777
|
+
if (timer) clearTimeout(timer);
|
|
4778
|
+
}
|
|
4779
|
+
}
|
|
4780
|
+
async consumeStructuredStream(request, assembler) {
|
|
4781
|
+
const timeoutMs = this.config.structuredTaskTimeoutMs ?? DEFAULT_STRUCTURED_TIMEOUT_MS;
|
|
4782
|
+
const controller = new AbortController();
|
|
4783
|
+
const timedRequest = { ...request, signal: controller.signal };
|
|
4784
|
+
let timer;
|
|
4785
|
+
const timeout = new Promise((_resolve, reject) => {
|
|
4786
|
+
timer = setTimeout(() => {
|
|
4787
|
+
controller.abort(new Error(`StrataGate structured model task timed out after ${timeoutMs}ms`));
|
|
4788
|
+
reject(new Error(`StrataGate structured model task timed out after ${timeoutMs}ms`));
|
|
4789
|
+
}, timeoutMs);
|
|
4790
|
+
timer.unref?.();
|
|
4500
4791
|
});
|
|
4792
|
+
try {
|
|
4793
|
+
await Promise.race([
|
|
4794
|
+
(async () => {
|
|
4795
|
+
for await (const chunk of this.ctx.llm.stream(timedRequest)) assembler.push(chunk);
|
|
4796
|
+
})(),
|
|
4797
|
+
timeout
|
|
4798
|
+
]);
|
|
4799
|
+
} finally {
|
|
4800
|
+
if (timer) clearTimeout(timer);
|
|
4801
|
+
}
|
|
4802
|
+
}
|
|
4803
|
+
resolveRoute(session) {
|
|
4804
|
+
const request = session.requestHeader()?.config;
|
|
4501
4805
|
if (this.config.provider && this.config.model) {
|
|
4502
|
-
return
|
|
4806
|
+
return { provider: this.config.provider, model: this.config.model };
|
|
4503
4807
|
}
|
|
4504
|
-
if (request) return
|
|
4808
|
+
if (request) return { provider: request.provider, model: request.model };
|
|
4505
4809
|
const fallback = this.ctx.agentDefaultModel.currentSelection();
|
|
4506
|
-
return {
|
|
4507
|
-
provider: fallback.provider,
|
|
4508
|
-
model: fallback.model,
|
|
4509
|
-
...structured ? { reasoningEffort: "off" } : fallback.reasoningEffort !== void 0 ? { reasoningEffort: fallback.reasoningEffort } : {}
|
|
4510
|
-
};
|
|
4810
|
+
return { provider: fallback.provider, model: fallback.model };
|
|
4511
4811
|
}
|
|
4512
4812
|
};
|
|
4813
|
+
function isOffRejection(error) {
|
|
4814
|
+
let detail;
|
|
4815
|
+
try {
|
|
4816
|
+
detail = typeof error === "string" ? error : error && typeof error === "object" ? JSON.stringify(error, Object.getOwnPropertyNames(error)) : String(error);
|
|
4817
|
+
} catch {
|
|
4818
|
+
detail = error instanceof Error ? error.message : String(error);
|
|
4819
|
+
}
|
|
4820
|
+
return /(?:reasoning[_ -]?effort|reasoning).{0,100}\boff\b|\boff\b.{0,100}(?:reasoning[_ -]?effort|reasoning)/iu.test(detail) && /(?:unsupported|not supported|invalid|not allowed|unknown|unrecognized|reject|must be|expected)/iu.test(detail);
|
|
4821
|
+
}
|
|
4513
4822
|
|
|
4514
4823
|
// src/runtime.ts
|
|
4515
4824
|
import { createHash } from "node:crypto";
|
|
@@ -4736,6 +5045,8 @@ var StrataGateRuntime = class {
|
|
|
4736
5045
|
latestBatchIds = /* @__PURE__ */ new Map();
|
|
4737
5046
|
workspaceNames = /* @__PURE__ */ new Map();
|
|
4738
5047
|
migrationTimers = /* @__PURE__ */ new Map();
|
|
5048
|
+
derivationTimers = /* @__PURE__ */ new Map();
|
|
5049
|
+
derivationRuns = /* @__PURE__ */ new Map();
|
|
4739
5050
|
ingestTail = Promise.resolve();
|
|
4740
5051
|
settingsTail = Promise.resolve();
|
|
4741
5052
|
batchSequence = 0;
|
|
@@ -4752,15 +5063,8 @@ var StrataGateRuntime = class {
|
|
|
4752
5063
|
}).then(async () => {
|
|
4753
5064
|
const memory = await this.space(session);
|
|
4754
5065
|
try {
|
|
4755
|
-
const result = await
|
|
4756
|
-
if (result.sealedBlock)
|
|
4757
|
-
const contexts = memory.getBlockContext(String(session.id));
|
|
4758
|
-
const sealedContext = contexts.find(({ id }) => id === result.sealedBlock.id);
|
|
4759
|
-
if (!sealedContext) throw new Error(`Missing context for sealed StrataGate block ${result.sealedBlock.id}`);
|
|
4760
|
-
this.replaceSealedSurface(session, result.sealedBlock, sealedContext, turn.dshTurn);
|
|
4761
|
-
this.syncDecayedBlockSurface(session, contexts);
|
|
4762
|
-
await this.flushNativeSession(session);
|
|
4763
|
-
}
|
|
5066
|
+
const result = await memory.appendTurn(turn, { deferDerivation: true });
|
|
5067
|
+
if (result.sealedBlock) this.scheduleBlockDerivation(session, memory);
|
|
4764
5068
|
} finally {
|
|
4765
5069
|
await this.persistSuccessfulResponses(memory);
|
|
4766
5070
|
}
|
|
@@ -5074,6 +5378,8 @@ var StrataGateRuntime = class {
|
|
|
5074
5378
|
this.closed = true;
|
|
5075
5379
|
for (const timer of this.migrationTimers.values()) clearTimeout(timer);
|
|
5076
5380
|
this.migrationTimers.clear();
|
|
5381
|
+
for (const timer of this.derivationTimers.values()) clearTimeout(timer);
|
|
5382
|
+
this.derivationTimers.clear();
|
|
5077
5383
|
let flushError;
|
|
5078
5384
|
try {
|
|
5079
5385
|
await this.flush();
|
|
@@ -5081,6 +5387,7 @@ var StrataGateRuntime = class {
|
|
|
5081
5387
|
flushError = error;
|
|
5082
5388
|
}
|
|
5083
5389
|
const settled = await Promise.allSettled(this.spaces.values());
|
|
5390
|
+
await Promise.allSettled(this.derivationRuns.values());
|
|
5084
5391
|
await Promise.all(settled.flatMap((result) => result.status === "fulfilled" ? [result.value.close()] : []));
|
|
5085
5392
|
if (flushError !== void 0) throw flushError;
|
|
5086
5393
|
}
|
|
@@ -5319,23 +5626,14 @@ var StrataGateRuntime = class {
|
|
|
5319
5626
|
}).then(async (memory) => {
|
|
5320
5627
|
try {
|
|
5321
5628
|
try {
|
|
5322
|
-
|
|
5629
|
+
await memory.resumePendingWork({ deferDerivation: true, threadId: String(session.id) });
|
|
5323
5630
|
const contexts = memory.getBlockContext(String(session.id));
|
|
5324
|
-
for (const block of resumed.sealedBlocks) {
|
|
5325
|
-
if (block.threadId !== String(session.id)) continue;
|
|
5326
|
-
const endTurn = dshTurnAtBlockEnd(session, block);
|
|
5327
|
-
const context = contexts.find(({ id }) => id === block.id);
|
|
5328
|
-
if (!context) throw new Error(`Missing context for recovered StrataGate block ${block.id}`);
|
|
5329
|
-
this.replaceSealedSurface(session, block, context, endTurn);
|
|
5330
|
-
}
|
|
5331
5631
|
this.syncDecayedBlockSurface(session, contexts);
|
|
5332
|
-
if (resumed.sealedBlocks.some((block) => block.threadId === String(session.id))) {
|
|
5333
|
-
await this.flushNativeSession(session);
|
|
5334
|
-
}
|
|
5335
5632
|
} finally {
|
|
5336
5633
|
await this.persistSuccessfulResponses(memory);
|
|
5337
5634
|
}
|
|
5338
5635
|
this.scheduleGraphMigration(session, memory);
|
|
5636
|
+
this.scheduleBlockDerivation(session, memory);
|
|
5339
5637
|
return memory;
|
|
5340
5638
|
} catch (error) {
|
|
5341
5639
|
await memory.close().catch(() => {
|
|
@@ -5369,6 +5667,43 @@ var StrataGateRuntime = class {
|
|
|
5369
5667
|
target: { eventIds: [.../* @__PURE__ */ new Set([...node.sourceEventIds, ...edges.flatMap(({ sourceEventIds }) => sourceEventIds)])], elementIds: [] }
|
|
5370
5668
|
}], { node, edges });
|
|
5371
5669
|
}
|
|
5670
|
+
scheduleBlockDerivation(session, memory) {
|
|
5671
|
+
const threadId = String(session.id);
|
|
5672
|
+
const key = `${this.namespaceFor(session)}\0${threadId}`;
|
|
5673
|
+
if (this.closed || this.derivationTimers.has(key) || this.derivationRuns.has(key)) return;
|
|
5674
|
+
const pendingBlockIds = new Set(memory.listBlocks().filter((block) => block.threadId === threadId && block.processingStatus === "pending").map((block) => block.id));
|
|
5675
|
+
if (pendingBlockIds.size === 0) return;
|
|
5676
|
+
const retryTimes = [
|
|
5677
|
+
...memory.listSummaryJobs(),
|
|
5678
|
+
...memory.listExtractionJobs()
|
|
5679
|
+
].filter((job) => pendingBlockIds.has(job.blockId) && (job.status === "pending" || job.status === "failed" && job.nextRetryAt !== null)).map((job) => job.nextRetryAt ? Date.parse(job.nextRetryAt) : Date.now()).filter(Number.isFinite);
|
|
5680
|
+
if (retryTimes.length === 0) return;
|
|
5681
|
+
const delay = Math.max(0, Math.min(...retryTimes) - Date.now());
|
|
5682
|
+
const timer = setTimeout(() => {
|
|
5683
|
+
this.derivationTimers.delete(key);
|
|
5684
|
+
if (this.closed) return;
|
|
5685
|
+
const run = this.models.run(session, () => memory.resumePendingWork({ threadId })).then(async (resumed) => {
|
|
5686
|
+
await this.persistSuccessfulResponses(memory);
|
|
5687
|
+
const contexts = memory.getBlockContext(threadId);
|
|
5688
|
+
for (const block of resumed.readyBlocks) {
|
|
5689
|
+
if (block.threadId !== threadId) continue;
|
|
5690
|
+
const context = contexts.find(({ id }) => id === block.id);
|
|
5691
|
+
if (!context) throw new Error(`Missing context for ready StrataGate block ${block.id}`);
|
|
5692
|
+
this.replaceSealedSurface(session, block, context, dshTurnAtBlockEnd(session, block));
|
|
5693
|
+
}
|
|
5694
|
+
const changed = this.syncDecayedBlockSurface(session, contexts);
|
|
5695
|
+
if (resumed.readyBlocks.length > 0 || changed) await this.flushNativeSession(session);
|
|
5696
|
+
}).catch((error) => {
|
|
5697
|
+
this.onIngestError(error);
|
|
5698
|
+
}).finally(() => {
|
|
5699
|
+
this.derivationRuns.delete(key);
|
|
5700
|
+
this.scheduleBlockDerivation(session, memory);
|
|
5701
|
+
});
|
|
5702
|
+
this.derivationRuns.set(key, run);
|
|
5703
|
+
}, delay);
|
|
5704
|
+
timer.unref?.();
|
|
5705
|
+
this.derivationTimers.set(key, timer);
|
|
5706
|
+
}
|
|
5372
5707
|
scheduleGraphMigration(session, memory) {
|
|
5373
5708
|
const namespace = this.namespaceFor(session);
|
|
5374
5709
|
if (this.closed || this.migrationTimers.has(namespace)) return;
|
|
@@ -6064,6 +6399,13 @@ function sourceMessages(snapshot, ids) {
|
|
|
6064
6399
|
return output;
|
|
6065
6400
|
}
|
|
6066
6401
|
function blockLayers(block) {
|
|
6402
|
+
if (block.processingStatus !== "ready" || !block.l0Title || !block.l0Tags || !block.l1Summary || !block.l2Keypoints) {
|
|
6403
|
+
return [
|
|
6404
|
+
{ level: 3, content: block.l3Condensed },
|
|
6405
|
+
{ level: 4, content: block.l4Readable },
|
|
6406
|
+
{ level: 5, content: block.l5Raw.map((message) => `${message.role}: ${message.content}`).join("\n\n") }
|
|
6407
|
+
];
|
|
6408
|
+
}
|
|
6067
6409
|
return [
|
|
6068
6410
|
{ level: 0, content: `${block.l0Title}
|
|
6069
6411
|
\u6807\u7B7E\uFF1A${block.l0Tags.join("\u3001") || "\u65E0"}` },
|