stratagate-dsh 0.2.15 → 0.2.16
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 +5 -0
- package/README.md +3 -3
- package/dist/index.js +118 -40
- package/dist/index.js.map +1 -1
- package/{README.zh-CN.md → docs/README.zh-CN.md} +3 -3
- package/package.json +12 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.2.16 - 2026-08-21
|
|
4
|
+
|
|
5
|
+
- Isolate open tails, Block sealing, decay, and automatic Block context by DSH session while keeping Events and Elements project-scoped for cross-session recall.
|
|
6
|
+
- Migrate SQLite storage to schema v5 with optional thread ownership on raw messages and Blocks; pre-v5 Blocks remain unowned archival provenance instead of being injected into new sessions.
|
|
7
|
+
|
|
3
8
|
## 0.2.15 - 2026-08-21
|
|
4
9
|
|
|
5
10
|
- Disable reasoning for internal structured memory workers because the current DSH adapters do not map `tool_choice` to the provider request.
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# StrataGate for DeepSeek Harness
|
|
2
2
|
|
|
3
|
-
[English](README.md) · [简体中文](README.zh-CN.md)
|
|
3
|
+
[English](README.md) · [简体中文](docs/README.zh-CN.md)
|
|
4
4
|
|
|
5
5
|
Automatic, local-first cross-session memory for DeepSeek Harness. StrataGate remembers user preferences, project decisions, completed conversations, and tool results, then checks recalled evidence and can expand it back to the original messages before the agent answers. No separate memory server is required.
|
|
6
6
|
|
|
@@ -70,9 +70,9 @@ Removing the plugin does not delete that database.
|
|
|
70
70
|
- Subagent turns are not ingested by default; subagents in the same project can still read project memory.
|
|
71
71
|
- Each DSH turn has a durable ingestion receipt, so replay or retry cannot store it twice.
|
|
72
72
|
- StrataGate performs the existing Block summarization, Event extraction, Element projection, search, Evidence Gate, and use-only reinforcement.
|
|
73
|
-
- Before every main-model call, the plugin injects the
|
|
73
|
+
- Before every main-model call, the plugin injects only the current session's open tail and sealed Blocks, plus up to four project-scoped activated Events and four active Element facts. Blocks remain persisted as source evidence, but they are never automatically carried into another session.
|
|
74
74
|
|
|
75
|
-
Activated memory uses the current human message plus the latest two open-tail turns as its query. Existing BM25 search remains the lexical relevance gate; pinned and safety memory are the only exceptions. Existing memory weights provide a second ranking, and RRF fuses the relevance and weight rankings. The activated section has a fixed budget of about 900 tokens, so it does not grow with the database.
|
|
75
|
+
Activated memory uses the current human message plus the latest two open-tail turns from the current session as its query. Existing BM25 search remains the lexical relevance gate; pinned and safety memory are the only exceptions. Existing memory weights provide a second ranking, and RRF fuses the relevance and weight rankings. The activated section has a fixed budget of about 900 tokens, so it does not grow with the database.
|
|
76
76
|
|
|
77
77
|
Automatic context contains only compact Event and fact fields and is explicitly marked as historical background rather than instructions. Building it never calls `recordMemoryUse`, increments `mentionCount`, or changes `lastAdoptedTurn`. The existing `memory_*` tools remain available for deeper, evidence-gated retrieval and are the only path to adoption reinforcement.
|
|
78
78
|
|
package/dist/index.js
CHANGED
|
@@ -358,7 +358,7 @@ function rrfRank(rankings) {
|
|
|
358
358
|
}
|
|
359
359
|
|
|
360
360
|
// ../../src/storage.ts
|
|
361
|
-
var STRATAGATE_STORAGE_SCHEMA_VERSION =
|
|
361
|
+
var STRATAGATE_STORAGE_SCHEMA_VERSION = 5;
|
|
362
362
|
var StorageConflictError = class extends Error {
|
|
363
363
|
constructor(namespace, expectedRevision, actualRevision) {
|
|
364
364
|
super(`Storage revision conflict for ${namespace}: expected ${expectedRevision}, found ${actualRevision ?? "missing"}`);
|
|
@@ -399,6 +399,11 @@ function normalizeSnapshot(value) {
|
|
|
399
399
|
...structuredClone(value),
|
|
400
400
|
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION
|
|
401
401
|
};
|
|
402
|
+
} else if (schemaVersion === 4) {
|
|
403
|
+
snapshot = {
|
|
404
|
+
...structuredClone(value),
|
|
405
|
+
schemaVersion: STRATAGATE_STORAGE_SCHEMA_VERSION
|
|
406
|
+
};
|
|
402
407
|
} else if (schemaVersion === STRATAGATE_STORAGE_SCHEMA_VERSION) {
|
|
403
408
|
snapshot = structuredClone(value);
|
|
404
409
|
} else {
|
|
@@ -594,6 +599,7 @@ CREATE TABLE IF NOT EXISTS memory_spaces (
|
|
|
594
599
|
CREATE TABLE IF NOT EXISTS blocks (
|
|
595
600
|
namespace TEXT NOT NULL,
|
|
596
601
|
id TEXT NOT NULL,
|
|
602
|
+
thread_id TEXT,
|
|
597
603
|
sequence INTEGER NOT NULL,
|
|
598
604
|
start_turn INTEGER NOT NULL,
|
|
599
605
|
end_turn INTEGER NOT NULL,
|
|
@@ -618,6 +624,7 @@ CREATE TABLE IF NOT EXISTS messages (
|
|
|
618
624
|
namespace TEXT NOT NULL,
|
|
619
625
|
id TEXT NOT NULL,
|
|
620
626
|
block_id TEXT,
|
|
627
|
+
thread_id TEXT,
|
|
621
628
|
position INTEGER NOT NULL,
|
|
622
629
|
role TEXT NOT NULL,
|
|
623
630
|
content TEXT NOT NULL,
|
|
@@ -781,6 +788,10 @@ CREATE TABLE IF NOT EXISTS ingestion_receipts (
|
|
|
781
788
|
FOREIGN KEY (namespace) REFERENCES memory_spaces(namespace) ON DELETE CASCADE
|
|
782
789
|
) STRICT;
|
|
783
790
|
`;
|
|
791
|
+
var THREAD_INDEXES = `
|
|
792
|
+
CREATE INDEX IF NOT EXISTS messages_thread_idx ON messages(namespace, thread_id, position);
|
|
793
|
+
CREATE INDEX IF NOT EXISTS blocks_thread_idx ON blocks(namespace, thread_id, sequence);
|
|
794
|
+
`;
|
|
784
795
|
function parseJson(value, label) {
|
|
785
796
|
try {
|
|
786
797
|
return JSON.parse(value);
|
|
@@ -828,7 +839,7 @@ var SqliteStorage = class {
|
|
|
828
839
|
throw new Error(`Unsupported stored StrataGate schema: ${space.schema_version}`);
|
|
829
840
|
}
|
|
830
841
|
const messageRows = this.database.prepare(`
|
|
831
|
-
SELECT id, block_id, position, role, content, created_at, tool_calls_json
|
|
842
|
+
SELECT id, block_id, thread_id, position, role, content, created_at, tool_calls_json
|
|
832
843
|
FROM messages WHERE namespace = ? ORDER BY block_id, position
|
|
833
844
|
`).all(key);
|
|
834
845
|
const openTail = [];
|
|
@@ -839,6 +850,7 @@ var SqliteStorage = class {
|
|
|
839
850
|
role: row.role,
|
|
840
851
|
content: row.content,
|
|
841
852
|
createdAt: row.created_at,
|
|
853
|
+
...row.thread_id ? { threadId: row.thread_id } : {},
|
|
842
854
|
...row.tool_calls_json ? { toolCalls: parseJson(row.tool_calls_json, "messages.tool_calls_json") } : {}
|
|
843
855
|
};
|
|
844
856
|
if (row.block_id === null) openTail.push(message);
|
|
@@ -853,6 +865,7 @@ var SqliteStorage = class {
|
|
|
853
865
|
`).all(key);
|
|
854
866
|
const blocks = blockRows.map((row) => ({
|
|
855
867
|
id: row.id,
|
|
868
|
+
...row.thread_id ? { threadId: row.thread_id } : {},
|
|
856
869
|
sequence: row.sequence,
|
|
857
870
|
startTurn: row.start_turn,
|
|
858
871
|
endTurn: row.end_turn,
|
|
@@ -1099,11 +1112,12 @@ var SqliteStorage = class {
|
|
|
1099
1112
|
}
|
|
1100
1113
|
const insertBlock = this.database.prepare(`
|
|
1101
1114
|
INSERT INTO blocks (
|
|
1102
|
-
namespace, id, sequence, start_turn, end_turn, created_at, should_extract,
|
|
1115
|
+
namespace, id, thread_id, sequence, start_turn, end_turn, created_at, should_extract,
|
|
1103
1116
|
l0_title, l0_tags_json, l1_summary, l2_keypoints_json, l3_condensed, l4_readable,
|
|
1104
1117
|
pointer_current_level, pointer_anchor_level, pointer_anchor_turn, last_lifted_at
|
|
1105
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1118
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1106
1119
|
ON CONFLICT (namespace, id) DO UPDATE SET
|
|
1120
|
+
thread_id = excluded.thread_id,
|
|
1107
1121
|
sequence = excluded.sequence,
|
|
1108
1122
|
start_turn = excluded.start_turn,
|
|
1109
1123
|
end_turn = excluded.end_turn,
|
|
@@ -1124,6 +1138,7 @@ var SqliteStorage = class {
|
|
|
1124
1138
|
insertBlock.run(
|
|
1125
1139
|
namespace,
|
|
1126
1140
|
block.id,
|
|
1141
|
+
block.threadId ?? null,
|
|
1127
1142
|
block.sequence,
|
|
1128
1143
|
block.startTurn,
|
|
1129
1144
|
block.endTurn,
|
|
@@ -1143,10 +1158,11 @@ var SqliteStorage = class {
|
|
|
1143
1158
|
}
|
|
1144
1159
|
const insertMessage = this.database.prepare(`
|
|
1145
1160
|
INSERT INTO messages (
|
|
1146
|
-
namespace, id, block_id, position, role, content, created_at, tool_calls_json
|
|
1147
|
-
) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1161
|
+
namespace, id, block_id, thread_id, position, role, content, created_at, tool_calls_json
|
|
1162
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1148
1163
|
ON CONFLICT (namespace, id) DO UPDATE SET
|
|
1149
1164
|
block_id = excluded.block_id,
|
|
1165
|
+
thread_id = excluded.thread_id,
|
|
1150
1166
|
position = excluded.position,
|
|
1151
1167
|
role = excluded.role,
|
|
1152
1168
|
content = excluded.content,
|
|
@@ -1159,6 +1175,7 @@ var SqliteStorage = class {
|
|
|
1159
1175
|
namespace,
|
|
1160
1176
|
message.id,
|
|
1161
1177
|
blockId,
|
|
1178
|
+
message.threadId ?? null,
|
|
1162
1179
|
position,
|
|
1163
1180
|
message.role,
|
|
1164
1181
|
message.content,
|
|
@@ -1402,9 +1419,10 @@ var SqliteStorage = class {
|
|
|
1402
1419
|
if (version === 0) {
|
|
1403
1420
|
this.immediateTransaction(() => {
|
|
1404
1421
|
this.database.exec(SCHEMA);
|
|
1422
|
+
this.database.exec(THREAD_INDEXES);
|
|
1405
1423
|
this.database.exec(`PRAGMA user_version = ${STRATAGATE_STORAGE_SCHEMA_VERSION}`);
|
|
1406
1424
|
});
|
|
1407
|
-
} else if (version === 1 || version === 2 || version === 3) {
|
|
1425
|
+
} else if (version === 1 || version === 2 || version === 3 || version === 4) {
|
|
1408
1426
|
this.immediateTransaction(() => {
|
|
1409
1427
|
if (version === 1) {
|
|
1410
1428
|
const receiptColumns2 = this.database.prepare("PRAGMA table_info('usage_receipts')").all();
|
|
@@ -1417,11 +1435,21 @@ var SqliteStorage = class {
|
|
|
1417
1435
|
this.database.exec("ALTER TABLE usage_receipts ADD COLUMN audit_json TEXT NOT NULL DEFAULT '{}'");
|
|
1418
1436
|
}
|
|
1419
1437
|
this.database.exec(SCHEMA);
|
|
1438
|
+
const blockColumns = this.database.prepare("PRAGMA table_info('blocks')").all();
|
|
1439
|
+
if (!blockColumns.some(({ name: name2 }) => name2 === "thread_id")) {
|
|
1440
|
+
this.database.exec("ALTER TABLE blocks ADD COLUMN thread_id TEXT");
|
|
1441
|
+
}
|
|
1442
|
+
const messageColumns = this.database.prepare("PRAGMA table_info('messages')").all();
|
|
1443
|
+
if (!messageColumns.some(({ name: name2 }) => name2 === "thread_id")) {
|
|
1444
|
+
this.database.exec("ALTER TABLE messages ADD COLUMN thread_id TEXT");
|
|
1445
|
+
}
|
|
1446
|
+
this.database.exec(THREAD_INDEXES);
|
|
1420
1447
|
this.database.prepare("UPDATE memory_spaces SET schema_version = ? WHERE schema_version < ?").run(STRATAGATE_STORAGE_SCHEMA_VERSION, STRATAGATE_STORAGE_SCHEMA_VERSION);
|
|
1421
1448
|
this.database.exec(`PRAGMA user_version = ${STRATAGATE_STORAGE_SCHEMA_VERSION}`);
|
|
1422
1449
|
});
|
|
1423
1450
|
} else if (version === STRATAGATE_STORAGE_SCHEMA_VERSION) {
|
|
1424
1451
|
this.database.exec(SCHEMA);
|
|
1452
|
+
this.database.exec(THREAD_INDEXES);
|
|
1425
1453
|
}
|
|
1426
1454
|
this.assertSchemaVersion();
|
|
1427
1455
|
}
|
|
@@ -1634,8 +1662,9 @@ var StrataGate = class _StrataGate {
|
|
|
1634
1662
|
listElements() {
|
|
1635
1663
|
return this.elements;
|
|
1636
1664
|
}
|
|
1637
|
-
listOpenTail() {
|
|
1638
|
-
return this.openTail;
|
|
1665
|
+
listOpenTail(threadId) {
|
|
1666
|
+
if (threadId === void 0) return this.openTail;
|
|
1667
|
+
return this.openTail.filter((message) => message.threadId === threadId);
|
|
1639
1668
|
}
|
|
1640
1669
|
listExtractionJobs() {
|
|
1641
1670
|
return [...this.extractionJobs.values()];
|
|
@@ -1685,12 +1714,17 @@ var StrataGate = class _StrataGate {
|
|
|
1685
1714
|
if (input.receiptId !== void 0 && !receiptId) {
|
|
1686
1715
|
throw new TypeError("Turn receiptId must not be empty");
|
|
1687
1716
|
}
|
|
1717
|
+
const threadId = input.threadId?.trim();
|
|
1718
|
+
if (input.threadId !== void 0 && !threadId) {
|
|
1719
|
+
throw new TypeError("Turn threadId must not be empty");
|
|
1720
|
+
}
|
|
1688
1721
|
const createdAt = toUtc8Iso(input.createdAt ?? this.now());
|
|
1689
1722
|
const userMessage = {
|
|
1690
1723
|
id: this.idFactory("msg"),
|
|
1691
1724
|
role: "user",
|
|
1692
1725
|
content: input.user,
|
|
1693
1726
|
createdAt,
|
|
1727
|
+
...threadId ? { threadId } : {},
|
|
1694
1728
|
...input.userToolCalls ? { toolCalls: input.userToolCalls } : {}
|
|
1695
1729
|
};
|
|
1696
1730
|
const assistantMessage = {
|
|
@@ -1698,6 +1732,7 @@ var StrataGate = class _StrataGate {
|
|
|
1698
1732
|
role: "assistant",
|
|
1699
1733
|
content: input.assistant,
|
|
1700
1734
|
createdAt,
|
|
1735
|
+
...threadId ? { threadId } : {},
|
|
1701
1736
|
...input.assistantToolCalls ? { toolCalls: input.assistantToolCalls } : {}
|
|
1702
1737
|
};
|
|
1703
1738
|
const appended = await this.commitMutation(() => {
|
|
@@ -1711,11 +1746,11 @@ var StrataGate = class _StrataGate {
|
|
|
1711
1746
|
if (options.deferProcessing === true) {
|
|
1712
1747
|
return { sealedBlock: null, extractedEvents: [], projectedElements: [] };
|
|
1713
1748
|
}
|
|
1714
|
-
if (this.
|
|
1749
|
+
if (this.threadOpenTail(threadId).filter((message) => message.role === "user").length < this.blockTurnSize) {
|
|
1715
1750
|
const projectedElements2 = await this.projectEligibleElements() ?? [];
|
|
1716
1751
|
return { sealedBlock: null, extractedEvents: [], projectedElements: projectedElements2 };
|
|
1717
1752
|
}
|
|
1718
|
-
const sealedBlock = await this.sealOpenTail();
|
|
1753
|
+
const sealedBlock = await this.sealOpenTail(threadId);
|
|
1719
1754
|
const extractedEvents = await this.extractEligibleBlock() ?? [];
|
|
1720
1755
|
const projectedElements = await this.projectEligibleElements() ?? [];
|
|
1721
1756
|
return { sealedBlock, extractedEvents, projectedElements };
|
|
@@ -1724,8 +1759,10 @@ var StrataGate = class _StrataGate {
|
|
|
1724
1759
|
const sealedBlocks = [];
|
|
1725
1760
|
const extractedEvents = [];
|
|
1726
1761
|
const projectedElements = [];
|
|
1727
|
-
while (
|
|
1728
|
-
|
|
1762
|
+
while (true) {
|
|
1763
|
+
const sealable = this.nextSealableThread();
|
|
1764
|
+
if (sealable === null) break;
|
|
1765
|
+
sealedBlocks.push(await this.sealOpenTail(sealable.threadId));
|
|
1729
1766
|
extractedEvents.push(...await this.extractEligibleBlock() ?? []);
|
|
1730
1767
|
projectedElements.push(...await this.projectEligibleElements() ?? []);
|
|
1731
1768
|
}
|
|
@@ -1736,7 +1773,7 @@ var StrataGate = class _StrataGate {
|
|
|
1736
1773
|
projectedElements.push(...await this.projectEligibleElements() ?? []);
|
|
1737
1774
|
}
|
|
1738
1775
|
if (options.retrySkipped === true) {
|
|
1739
|
-
const skippedBlockIds = this.blocks.filter((block
|
|
1776
|
+
const skippedBlockIds = this.blocks.filter((block) => this.nextBlockInThread(block) !== null && block.shouldExtract && this.extractionJobs.get(block.id)?.status === "skipped").map((block) => block.id);
|
|
1740
1777
|
for (const blockId of skippedBlockIds) {
|
|
1741
1778
|
const extracted = await this.extractEligibleBlock({ blockId, includeSkipped: true });
|
|
1742
1779
|
if (extracted === null) continue;
|
|
@@ -1955,12 +1992,15 @@ var StrataGate = class _StrataGate {
|
|
|
1955
1992
|
}
|
|
1956
1993
|
return hits;
|
|
1957
1994
|
}
|
|
1958
|
-
getBlockContext() {
|
|
1959
|
-
|
|
1960
|
-
|
|
1995
|
+
getBlockContext(threadId) {
|
|
1996
|
+
const blocks = threadId === void 0 ? this.blocks : this.blocks.filter((block) => block.threadId === threadId);
|
|
1997
|
+
return blocks.map((block) => {
|
|
1998
|
+
const currentTurn = block.threadId === void 0 ? this.currentTurn : this.threadTurn(block.threadId);
|
|
1999
|
+
const level = getDecayedBlockLevel(block.pointerAnchorLevel, block.pointerAnchorTurn, currentTurn);
|
|
1961
2000
|
block.pointerCurrentLevel = level;
|
|
1962
2001
|
return {
|
|
1963
2002
|
id: block.id,
|
|
2003
|
+
...block.threadId ? { threadId: block.threadId } : {},
|
|
1964
2004
|
turnRange: [block.startTurn, block.endTurn],
|
|
1965
2005
|
level,
|
|
1966
2006
|
label: blockLevelLabel(level),
|
|
@@ -1972,14 +2012,16 @@ var StrataGate = class _StrataGate {
|
|
|
1972
2012
|
return this.commitMutation(() => {
|
|
1973
2013
|
const block = this.blocks.find((candidate) => candidate.id === id);
|
|
1974
2014
|
if (!block) throw new Error(`Unknown block: ${id}`);
|
|
1975
|
-
const
|
|
2015
|
+
const currentTurn = block.threadId === void 0 ? this.currentTurn : this.threadTurn(block.threadId);
|
|
2016
|
+
const current = getDecayedBlockLevel(block.pointerAnchorLevel, block.pointerAnchorTurn, currentTurn);
|
|
1976
2017
|
const level = normalizeBlockLevel(target, current);
|
|
1977
2018
|
block.pointerCurrentLevel = level;
|
|
1978
2019
|
block.pointerAnchorLevel = level;
|
|
1979
|
-
block.pointerAnchorTurn =
|
|
2020
|
+
block.pointerAnchorTurn = currentTurn;
|
|
1980
2021
|
block.lastLiftedAt = toUtc8Iso(this.now());
|
|
1981
2022
|
return {
|
|
1982
2023
|
id: block.id,
|
|
2024
|
+
...block.threadId ? { threadId: block.threadId } : {},
|
|
1983
2025
|
turnRange: [block.startTurn, block.endTurn],
|
|
1984
2026
|
level,
|
|
1985
2027
|
label: blockLevelLabel(level),
|
|
@@ -2129,36 +2171,67 @@ var StrataGate = class _StrataGate {
|
|
|
2129
2171
|
this.elementProjectionJobs.set(job.id, job);
|
|
2130
2172
|
return job;
|
|
2131
2173
|
}
|
|
2132
|
-
|
|
2174
|
+
threadOpenTail(threadId) {
|
|
2175
|
+
return this.openTail.filter((message) => message.threadId === threadId);
|
|
2176
|
+
}
|
|
2177
|
+
threadBlocks(threadId) {
|
|
2178
|
+
return this.blocks.filter((block) => block.threadId === threadId);
|
|
2179
|
+
}
|
|
2180
|
+
threadTurn(threadId) {
|
|
2181
|
+
const sealedTurns = this.threadBlocks(threadId).reduce((total, block) => total + block.l5Raw.filter((message) => message.role === "user").length, 0);
|
|
2182
|
+
return sealedTurns + this.threadOpenTail(threadId).filter((message) => message.role === "user").length;
|
|
2183
|
+
}
|
|
2184
|
+
nextSealableThread() {
|
|
2185
|
+
const counts = [];
|
|
2186
|
+
for (const message of this.openTail) {
|
|
2187
|
+
if (message.role !== "user") continue;
|
|
2188
|
+
let entry = counts.find((candidate) => candidate.threadId === message.threadId);
|
|
2189
|
+
if (!entry) {
|
|
2190
|
+
entry = { threadId: message.threadId, users: 0 };
|
|
2191
|
+
counts.push(entry);
|
|
2192
|
+
}
|
|
2193
|
+
entry.users += 1;
|
|
2194
|
+
if (entry.users >= this.blockTurnSize) return { threadId: entry.threadId };
|
|
2195
|
+
}
|
|
2196
|
+
return null;
|
|
2197
|
+
}
|
|
2198
|
+
nextBlockInThread(block) {
|
|
2199
|
+
const index = this.blocks.indexOf(block);
|
|
2200
|
+
return this.blocks.slice(index + 1).find((candidate) => candidate.threadId === block.threadId) ?? null;
|
|
2201
|
+
}
|
|
2202
|
+
pendingBlockMessages(threadId) {
|
|
2203
|
+
const messages = this.threadOpenTail(threadId);
|
|
2133
2204
|
let users = 0;
|
|
2134
|
-
let end =
|
|
2135
|
-
for (const [index, message] of
|
|
2205
|
+
let end = messages.length;
|
|
2206
|
+
for (const [index, message] of messages.entries()) {
|
|
2136
2207
|
if (message.role !== "user") continue;
|
|
2137
2208
|
users += 1;
|
|
2138
2209
|
if (users !== this.blockTurnSize) continue;
|
|
2139
|
-
const nextUserOffset =
|
|
2140
|
-
end = nextUserOffset === -1 ?
|
|
2210
|
+
const nextUserOffset = messages.slice(index + 1).findIndex((candidate) => candidate.role === "user");
|
|
2211
|
+
end = nextUserOffset === -1 ? messages.length : index + 1 + nextUserOffset;
|
|
2141
2212
|
break;
|
|
2142
2213
|
}
|
|
2143
|
-
return
|
|
2214
|
+
return messages.slice(0, end);
|
|
2144
2215
|
}
|
|
2145
|
-
async sealOpenTail() {
|
|
2146
|
-
const raw = this.pendingBlockMessages();
|
|
2216
|
+
async sealOpenTail(threadId) {
|
|
2217
|
+
const raw = this.pendingBlockMessages(threadId);
|
|
2147
2218
|
if (raw.filter((message) => message.role === "user").length < this.blockTurnSize) {
|
|
2148
2219
|
throw new Error("Open tail does not contain enough turns to seal a block");
|
|
2149
2220
|
}
|
|
2150
2221
|
const generated = this.summarizer ? await this.summarizer(raw) : defaultSummary(raw);
|
|
2151
2222
|
const deterministic = deterministicBlockLayers(raw);
|
|
2152
2223
|
const sequence = this.blocks.length + 1;
|
|
2153
|
-
const
|
|
2224
|
+
const previous = this.threadBlocks(threadId).at(-1);
|
|
2225
|
+
const startTurn = previous ? previous.endTurn + 1 : 1;
|
|
2154
2226
|
const endTurn = startTurn + this.blockTurnSize - 1;
|
|
2155
2227
|
return this.commitMutation(() => {
|
|
2156
|
-
const currentRaw = this.pendingBlockMessages();
|
|
2228
|
+
const currentRaw = this.pendingBlockMessages(threadId);
|
|
2157
2229
|
if (!sameIds(currentRaw.map((message) => message.id), raw.map((message) => message.id))) {
|
|
2158
2230
|
throw new Error("Open tail changed while the block summary was being prepared");
|
|
2159
2231
|
}
|
|
2160
2232
|
const block = {
|
|
2161
2233
|
id: this.idFactory("blk"),
|
|
2234
|
+
...threadId ? { threadId } : {},
|
|
2162
2235
|
sequence,
|
|
2163
2236
|
startTurn,
|
|
2164
2237
|
endTurn,
|
|
@@ -2174,23 +2247,26 @@ var StrataGate = class _StrataGate {
|
|
|
2174
2247
|
pointerAnchorTurn: endTurn,
|
|
2175
2248
|
lastLiftedAt: null
|
|
2176
2249
|
};
|
|
2177
|
-
|
|
2250
|
+
const sealedIds = new Set(raw.map((message) => message.id));
|
|
2251
|
+
const remaining = this.openTail.filter((message) => !sealedIds.has(message.id));
|
|
2252
|
+
this.openTail.splice(0, this.openTail.length, ...remaining);
|
|
2178
2253
|
this.blocks.push(block);
|
|
2179
2254
|
return block;
|
|
2180
2255
|
});
|
|
2181
2256
|
}
|
|
2182
2257
|
async extractEligibleBlock(options = {}) {
|
|
2183
2258
|
if (!this.extractor || this.blocks.length < 2) return null;
|
|
2184
|
-
const
|
|
2185
|
-
if (
|
|
2259
|
+
const target = this.blocks.find((block) => {
|
|
2260
|
+
if (this.nextBlockInThread(block) === null || !block.shouldExtract) return false;
|
|
2186
2261
|
if (options.blockId !== void 0 && block.id !== options.blockId) return false;
|
|
2187
2262
|
const status = this.extractionJobs.get(block.id)?.status;
|
|
2188
2263
|
return status === void 0 || status === "failed" || options.includeSkipped === true && status === "skipped";
|
|
2189
2264
|
});
|
|
2190
|
-
if (
|
|
2191
|
-
const
|
|
2192
|
-
const
|
|
2193
|
-
|
|
2265
|
+
if (!target) return null;
|
|
2266
|
+
const threadBlocks = this.threadBlocks(target.threadId);
|
|
2267
|
+
const targetIndex = threadBlocks.indexOf(target);
|
|
2268
|
+
const next = threadBlocks[targetIndex + 1];
|
|
2269
|
+
if (!next) return null;
|
|
2194
2270
|
const existing = this.extractionJobs.get(target.id);
|
|
2195
2271
|
await this.commitMutation(() => {
|
|
2196
2272
|
const currentStatus = this.extractionJobs.get(target.id)?.status;
|
|
@@ -2209,7 +2285,7 @@ var StrataGate = class _StrataGate {
|
|
|
2209
2285
|
let result;
|
|
2210
2286
|
try {
|
|
2211
2287
|
result = await this.extractor({
|
|
2212
|
-
previous:
|
|
2288
|
+
previous: threadBlocks[targetIndex - 1] ?? null,
|
|
2213
2289
|
target,
|
|
2214
2290
|
next,
|
|
2215
2291
|
timeline: this.events.map((event) => ({ id: event.id, title: event.title, temporal: event.temporal }))
|
|
@@ -2950,6 +3026,7 @@ var TurnFolder = class {
|
|
|
2950
3026
|
return {
|
|
2951
3027
|
user: pending.user.join("\n\n"),
|
|
2952
3028
|
assistant: pending.assistant.join("\n\n") || reasonLabel(event.data.reason),
|
|
3029
|
+
threadId: sessionId,
|
|
2953
3030
|
assistantToolCalls: [...pending.tools.values()],
|
|
2954
3031
|
createdAt: toUtc8Iso(event.time),
|
|
2955
3032
|
receiptId: `dsh:${sessionId}:turn:${event.data.turn}`
|
|
@@ -3045,7 +3122,7 @@ var StrataGateRuntime = class {
|
|
|
3045
3122
|
}
|
|
3046
3123
|
async blocks(session) {
|
|
3047
3124
|
await this.flush();
|
|
3048
|
-
const results = (await this.space(session)).getBlockContext();
|
|
3125
|
+
const results = (await this.space(session)).getBlockContext(String(session.id));
|
|
3049
3126
|
return this.batch(session, results.map((result) => ({
|
|
3050
3127
|
ref: `block:${result.id}:level:${result.level}`,
|
|
3051
3128
|
target: { eventIds: [], elementIds: [] }
|
|
@@ -3167,7 +3244,8 @@ var StrataGateRuntime = class {
|
|
|
3167
3244
|
async buildAutoContext(session) {
|
|
3168
3245
|
await this.flush();
|
|
3169
3246
|
const memory = await this.space(session);
|
|
3170
|
-
const
|
|
3247
|
+
const threadId = String(session.id);
|
|
3248
|
+
const openTail = memory.listOpenTail(threadId);
|
|
3171
3249
|
const activationQuery = [currentUserMessage(session), renderMessages(recentTurns(openTail, 2))].filter(Boolean).join("\n\n");
|
|
3172
3250
|
const [eventHits, elementHits] = activationQuery ? await Promise.all([
|
|
3173
3251
|
memory.searchEvents(activationQuery, { limit: 20 }),
|
|
@@ -3180,7 +3258,7 @@ var StrataGateRuntime = class {
|
|
|
3180
3258
|
openTail.length > 0 ? renderMessages(openTail) : "(open tail is empty)",
|
|
3181
3259
|
"",
|
|
3182
3260
|
"[Decayed memory blocks]",
|
|
3183
|
-
renderBlocks2(memory.getBlockContext()),
|
|
3261
|
+
renderBlocks2(memory.getBlockContext(threadId)),
|
|
3184
3262
|
"",
|
|
3185
3263
|
renderActivatedMemory(events, elements)
|
|
3186
3264
|
].join("\n");
|