stratagate-dsh 0.2.61 → 0.2.63
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 +31 -5
- package/LICENSE +21 -21
- package/README.zh-CN.md +43 -43
- package/cordis.patch.yml +3 -2
- package/dist/client.js +5 -5
- package/dist/index.d.ts +4 -2
- package/dist/index.js +74 -8378
- package/dist/index.js.map +1 -1
- package/dist/plugin.d.ts +30 -0
- package/dist/plugin.js +8934 -0
- package/dist/plugin.js.map +1 -0
- package/dist/repair-profile.d.ts +1 -0
- package/dist/repair-profile.js +102 -0
- package/dist/repair-profile.js.map +1 -0
- package/docs/ARCHITECTURE.md +29 -29
- package/docs/DSH.md +4 -1
- package/docs/DSH.zh-CN.md +4 -1
- package/docs/EXTERNAL_MEMORY_IMPORT.zh-CN.md +54 -54
- package/package.json +75 -26
- package/screenshots.json +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/repair-profile.ts
|
|
4
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
5
|
+
import { randomUUID } from "node:crypto";
|
|
6
|
+
import { dirname, join } from "node:path";
|
|
7
|
+
import { fileURLToPath } from "node:url";
|
|
8
|
+
var PROFILE_PACKAGES = [
|
|
9
|
+
"@deepseek-ai/cordis",
|
|
10
|
+
"@deepseek-ai/cosmokit",
|
|
11
|
+
"@deepseek-ai/dsh-agent",
|
|
12
|
+
"@deepseek-ai/dsh-agent-default-model",
|
|
13
|
+
"@deepseek-ai/dsh-brand",
|
|
14
|
+
"@deepseek-ai/dsh-client-ui-conversation",
|
|
15
|
+
"@deepseek-ai/dsh-code-runtime",
|
|
16
|
+
"@deepseek-ai/dsh-invariants",
|
|
17
|
+
"@deepseek-ai/dsh-llm",
|
|
18
|
+
"@deepseek-ai/dsh-scope",
|
|
19
|
+
"@deepseek-ai/dsh-session",
|
|
20
|
+
"@deepseek-ai/dsh-session-format",
|
|
21
|
+
"@deepseek-ai/dsh-session-format-catalog",
|
|
22
|
+
"@deepseek-ai/dsh-session-format-v0-to-v1",
|
|
23
|
+
"@deepseek-ai/dsh-session-projection",
|
|
24
|
+
"@deepseek-ai/dsh-settings",
|
|
25
|
+
"@deepseek-ai/dsh-system-prompt",
|
|
26
|
+
"@deepseek-ai/dsh-timeout",
|
|
27
|
+
"@deepseek-ai/dsh-tools",
|
|
28
|
+
"@deepseek-ai/dsh-typert-protocol",
|
|
29
|
+
"@deepseek-ai/dsh-user-approval",
|
|
30
|
+
"@deepseek-ai/dsh-util-crypto",
|
|
31
|
+
"@deepseek-ai/dsh-util-values",
|
|
32
|
+
"@deepseek-ai/schemastery"
|
|
33
|
+
];
|
|
34
|
+
function profileDirectory(start) {
|
|
35
|
+
let directory = start;
|
|
36
|
+
for (; ; ) {
|
|
37
|
+
const manifestPath = join(directory, "package.json");
|
|
38
|
+
if (existsSync(manifestPath)) {
|
|
39
|
+
try {
|
|
40
|
+
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
|
41
|
+
if (manifest.dsh?.profile && typeof manifest.dsh.profile === "object") return directory;
|
|
42
|
+
} catch {
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
const parent = dirname(directory);
|
|
46
|
+
if (parent === directory) {
|
|
47
|
+
throw new Error("stratagate-dsh-repair must run inside an installed DSH profile");
|
|
48
|
+
}
|
|
49
|
+
directory = parent;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
function main() {
|
|
53
|
+
const profile = profileDirectory(dirname(fileURLToPath(import.meta.url)));
|
|
54
|
+
const modules = join(profile, "node_modules");
|
|
55
|
+
const candidates = PROFILE_PACKAGES.map((name) => ({ name, source: join(modules, ...name.split("/")) })).filter(({ source }) => {
|
|
56
|
+
try {
|
|
57
|
+
lstatSync(source);
|
|
58
|
+
return true;
|
|
59
|
+
} catch (error) {
|
|
60
|
+
if (error.code === "ENOENT") return false;
|
|
61
|
+
throw error;
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
if (candidates.length === 0) {
|
|
65
|
+
process.stdout.write("StrataGate profile runtime is already isolated; no DSH peer residue found.\n");
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const backup = join(profile, ".stratagate-runtime-backups", `${Date.now()}-${process.pid}-${randomUUID()}`);
|
|
69
|
+
const moved = [];
|
|
70
|
+
try {
|
|
71
|
+
for (const candidate of candidates) {
|
|
72
|
+
const target = join(backup, "node_modules", ...candidate.name.split("/"));
|
|
73
|
+
mkdirSync(dirname(target), { recursive: true });
|
|
74
|
+
renameSync(candidate.source, target);
|
|
75
|
+
moved.push({ ...candidate, target });
|
|
76
|
+
}
|
|
77
|
+
const receipt = {
|
|
78
|
+
schema: "stratagate-dsh-profile-runtime-repair/v1",
|
|
79
|
+
profile,
|
|
80
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
81
|
+
packages: moved.map(({ name, source, target }) => ({ name, source, target })),
|
|
82
|
+
recovery: `While DSH is stopped, move the listed targets back to their source paths if this repair must be reversed.`
|
|
83
|
+
};
|
|
84
|
+
writeFileSync(join(backup, "receipt.json"), `${JSON.stringify(receipt, null, 2)}
|
|
85
|
+
`, { flag: "wx", mode: 384 });
|
|
86
|
+
} catch (error) {
|
|
87
|
+
for (const item of moved.reverse()) {
|
|
88
|
+
if (existsSync(item.target) && !existsSync(item.source)) renameSync(item.target, item.source);
|
|
89
|
+
}
|
|
90
|
+
throw error;
|
|
91
|
+
}
|
|
92
|
+
process.stdout.write(`Quarantined ${moved.length} stale DSH profile package(s) to ${backup}
|
|
93
|
+
`);
|
|
94
|
+
}
|
|
95
|
+
try {
|
|
96
|
+
main();
|
|
97
|
+
} catch (error) {
|
|
98
|
+
process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}
|
|
99
|
+
`);
|
|
100
|
+
process.exitCode = 1;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=repair-profile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/repair-profile.ts"],"sourcesContent":["#!/usr/bin/env node\nimport { existsSync, lstatSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs'\nimport { randomUUID } from 'node:crypto'\nimport { basename, dirname, join } from 'node:path'\nimport { fileURLToPath } from 'node:url'\n\nconst PROFILE_PACKAGES = [\n '@deepseek-ai/cordis',\n '@deepseek-ai/cosmokit',\n '@deepseek-ai/dsh-agent',\n '@deepseek-ai/dsh-agent-default-model',\n '@deepseek-ai/dsh-brand',\n '@deepseek-ai/dsh-client-ui-conversation',\n '@deepseek-ai/dsh-code-runtime',\n '@deepseek-ai/dsh-invariants',\n '@deepseek-ai/dsh-llm',\n '@deepseek-ai/dsh-scope',\n '@deepseek-ai/dsh-session',\n '@deepseek-ai/dsh-session-format',\n '@deepseek-ai/dsh-session-format-catalog',\n '@deepseek-ai/dsh-session-format-v0-to-v1',\n '@deepseek-ai/dsh-session-projection',\n '@deepseek-ai/dsh-settings',\n '@deepseek-ai/dsh-system-prompt',\n '@deepseek-ai/dsh-timeout',\n '@deepseek-ai/dsh-tools',\n '@deepseek-ai/dsh-typert-protocol',\n '@deepseek-ai/dsh-user-approval',\n '@deepseek-ai/dsh-util-crypto',\n '@deepseek-ai/dsh-util-values',\n '@deepseek-ai/schemastery',\n] as const\n\nfunction profileDirectory(start: string): string {\n let directory = start\n for (;;) {\n const manifestPath = join(directory, 'package.json')\n if (existsSync(manifestPath)) {\n try {\n const manifest = JSON.parse(readFileSync(manifestPath, 'utf8')) as { dsh?: { profile?: unknown } }\n if (manifest.dsh?.profile && typeof manifest.dsh.profile === 'object') return directory\n } catch {}\n }\n const parent = dirname(directory)\n if (parent === directory) {\n throw new Error('stratagate-dsh-repair must run inside an installed DSH profile')\n }\n directory = parent\n }\n}\n\nfunction main(): void {\n const profile = profileDirectory(dirname(fileURLToPath(import.meta.url)))\n const modules = join(profile, 'node_modules')\n const candidates = PROFILE_PACKAGES\n .map((name) => ({ name, source: join(modules, ...name.split('/')) }))\n .filter(({ source }) => {\n try {\n lstatSync(source)\n return true\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code === 'ENOENT') return false\n throw error\n }\n })\n if (candidates.length === 0) {\n process.stdout.write('StrataGate profile runtime is already isolated; no DSH peer residue found.\\n')\n return\n }\n\n const backup = join(profile, '.stratagate-runtime-backups', `${Date.now()}-${process.pid}-${randomUUID()}`)\n const moved: Array<{ name: string; source: string; target: string }> = []\n try {\n for (const candidate of candidates) {\n const target = join(backup, 'node_modules', ...candidate.name.split('/'))\n mkdirSync(dirname(target), { recursive: true })\n renameSync(candidate.source, target)\n moved.push({ ...candidate, target })\n }\n const receipt = {\n schema: 'stratagate-dsh-profile-runtime-repair/v1',\n profile,\n createdAt: new Date().toISOString(),\n packages: moved.map(({ name, source, target }) => ({ name, source, target })),\n recovery: `While DSH is stopped, move the listed targets back to their source paths if this repair must be reversed.`,\n }\n writeFileSync(join(backup, 'receipt.json'), `${JSON.stringify(receipt, null, 2)}\\n`, { flag: 'wx', mode: 0o600 })\n } catch (error) {\n for (const item of moved.reverse()) {\n if (existsSync(item.target) && !existsSync(item.source)) renameSync(item.target, item.source)\n }\n throw error\n }\n process.stdout.write(`Quarantined ${moved.length} stale DSH profile package(s) to ${backup}\\n`)\n}\n\ntry {\n main()\n} catch (error) {\n process.stderr.write(`${error instanceof Error ? error.stack ?? error.message : String(error)}\\n`)\n process.exitCode = 1\n}\n"],"mappings":"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","names":[]}
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -48,20 +48,20 @@ A completed user/assistant pair is one turn. The default block boundary is 12 co
|
|
|
48
48
|
|
|
49
49
|
Hosts may attach a `threadId` to each turn. Open tails, Block boundaries, neighboring extraction context, turn ranges, and decay pointers are then isolated by thread. Persisted Blocks remain available as provenance for long-term cards, while host integrations must inject only the active thread's short-term Block context.
|
|
50
50
|
|
|
51
|
-
When the boundary is reached:
|
|
52
|
-
|
|
53
|
-
1. One atomic sealing transaction moves the source messages into permanent L5 and writes deterministic L4 and L3.
|
|
54
|
-
2. The sealed Block is marked model-pending. It is provenance, but it is excluded from decay and cannot replace native conversation history.
|
|
55
|
-
3. A background summarizer produces and validates L0-L2 plus a conservative `shouldExtract` decision.
|
|
56
|
-
4. Event extraction completes with either validated Events or an explicit valid empty result.
|
|
57
|
-
5. Only then is the Block marked ready: its pointer starts at L5, it may replace native history, and it decays toward L0 as newer ready Blocks enter the same thread.
|
|
51
|
+
When the boundary is reached:
|
|
52
|
+
|
|
53
|
+
1. One atomic sealing transaction moves the source messages into permanent L5 and writes deterministic L4 and L3.
|
|
54
|
+
2. The sealed Block is marked model-pending. It is provenance, but it is excluded from decay and cannot replace native conversation history.
|
|
55
|
+
3. A background summarizer produces and validates L0-L2 plus a conservative `shouldExtract` decision.
|
|
56
|
+
4. Event extraction completes with either validated Events or an explicit valid empty result.
|
|
57
|
+
5. Only then is the Block marked ready: its pointer starts at L5, it may replace native history, and it decays toward L0 as newer ready Blocks enter the same thread.
|
|
58
58
|
|
|
59
59
|
The block weight is:
|
|
60
60
|
|
|
61
61
|
```text
|
|
62
62
|
w(age) = exp(-lambda_block * age)
|
|
63
63
|
|
|
64
|
-
age = latest ready Block position - pointer anchor Block position
|
|
64
|
+
age = latest ready Block position - pointer anchor Block position
|
|
65
65
|
lambda_block = 0.30 by default
|
|
66
66
|
```
|
|
67
67
|
|
|
@@ -78,13 +78,13 @@ L3 may remove only:
|
|
|
78
78
|
|
|
79
79
|
Short repeated natural-language messages are retained. L3 never performs semantic paraphrasing.
|
|
80
80
|
|
|
81
|
-
## Event extraction
|
|
82
|
-
|
|
83
|
-
After L0-L2 validates, a candidate Block is extracted independently; a later Block is not required. The extractor receives:
|
|
81
|
+
## Event extraction
|
|
82
|
+
|
|
83
|
+
After L0-L2 validates, a candidate Block is extracted independently; a later Block is not required. The extractor receives:
|
|
84
84
|
|
|
85
85
|
- target block `N`, including its L5 source and legal evidence IDs;
|
|
86
86
|
- previous block `N-1` L2 keypoints for context, if it exists;
|
|
87
|
-
- the nearest available later ready Block's L2 keypoints for context, if one exists;
|
|
87
|
+
- the nearest available later ready Block's L2 keypoints for context, if one exists;
|
|
88
88
|
- a compact timeline of existing event IDs, titles, and temporal fields.
|
|
89
89
|
|
|
90
90
|
The target is the only legal source of new facts and quotations. Neighbor blocks are context-only and must not contribute events or source references. Source message IDs are checked against the target block. The reference implementation falls back to all target messages when an extractor returns no valid source ID; stricter adapters may reject the card instead.
|
|
@@ -144,12 +144,12 @@ Applications that manage their own model loop may use `claimNextElementProjectio
|
|
|
144
144
|
|
|
145
145
|
## Hybrid retrieval
|
|
146
146
|
|
|
147
|
-
Event and fact-level element search use two inspectable ranking sources:
|
|
148
|
-
|
|
149
|
-
1. BM25 over field-weighted lexical tokens, including overlapping Han bigrams;
|
|
150
|
-
2. structured rankings from fields such as participant, event type, time range, element name, and element type.
|
|
151
|
-
|
|
152
|
-
Reciprocal-rank fusion combines the available rankings. A non-empty query with no lexical or structured match returns an empty result rather than all candidates. Element search returns the matched fact plus its element ID, validity interval, and event provenance; callers expand the full element card only when needed. The reference path does not use embeddings or vector similarity.
|
|
147
|
+
Event and fact-level element search use two inspectable ranking sources:
|
|
148
|
+
|
|
149
|
+
1. BM25 over field-weighted lexical tokens, including overlapping Han bigrams;
|
|
150
|
+
2. structured rankings from fields such as participant, event type, time range, element name, and element type.
|
|
151
|
+
|
|
152
|
+
Reciprocal-rank fusion combines the available rankings. A non-empty query with no lexical or structured match returns an empty result rather than all candidates. Element search returns the matched fact plus its element ID, validity interval, and event provenance; callers expand the full element card only when needed. The reference path does not use embeddings or vector similarity.
|
|
153
153
|
|
|
154
154
|
Integration tool responses intentionally expose compact search cards. They retain stable IDs, summaries,
|
|
155
155
|
timestamps, and evidence references while leaving narrative/quotes/source-message lists and full graph
|
|
@@ -220,19 +220,19 @@ If the retrieval budget ends without sufficient evidence, the caller should pass
|
|
|
220
220
|
|
|
221
221
|
Every namespace has a monotonically increasing revision. A write supplies the revision it loaded; SQLite commits the new revision and all related rows in one immediate transaction. A stale process receives `StorageConflictError` rather than overwriting newer state.
|
|
222
222
|
|
|
223
|
-
External model calls are never made inside a database transaction:
|
|
224
|
-
|
|
225
|
-
1. a completed raw turn is committed immediately;
|
|
226
|
-
2. every complete Block is sealed atomically with real L3-L5 before any model call;
|
|
227
|
-
3. summarization first claims a persisted job, runs outside the transaction, and commits validated L0-L2 or a failed job with bounded retry metadata;
|
|
228
|
-
4. extraction first commits a running job, calls the extractor, then atomically commits either the event cards, a valid empty result, or a failed job state;
|
|
229
|
-
5. element projection follows the same claim/call/complete boundary after its source events are durable;
|
|
230
|
-
6. failed model jobs retry at most three total attempts with exponential backoff; completed empty extraction is terminal and is not retried.
|
|
223
|
+
External model calls are never made inside a database transaction:
|
|
224
|
+
|
|
225
|
+
1. a completed raw turn is committed immediately;
|
|
226
|
+
2. every complete Block is sealed atomically with real L3-L5 before any model call;
|
|
227
|
+
3. summarization first claims a persisted job, runs outside the transaction, and commits validated L0-L2 or a failed job with bounded retry metadata;
|
|
228
|
+
4. extraction first commits a running job, calls the extractor, then atomically commits either the event cards, a valid empty result, or a failed job state;
|
|
229
|
+
5. element projection follows the same claim/call/complete boundary after its source events are durable;
|
|
230
|
+
6. failed model jobs retry at most three total attempts with exponential backoff; completed empty extraction is terminal and is not retried.
|
|
231
231
|
|
|
232
232
|
The adapter preserves these invariants:
|
|
233
233
|
|
|
234
|
-
- blocks and L5 messages are append-only, including when every derived task fails;
|
|
235
|
-
- model-pending Blocks are excluded from decay and native-history replacement;
|
|
234
|
+
- blocks and L5 messages are append-only, including when every derived task fails;
|
|
235
|
+
- model-pending Blocks are excluded from decay and native-history replacement;
|
|
236
236
|
- card provenance references an existing source block and message set;
|
|
237
237
|
- search hits do not increment adoption state;
|
|
238
238
|
- supersession retains the old event;
|
|
@@ -241,4 +241,4 @@ The adapter preserves these invariants:
|
|
|
241
241
|
- forget is reversible unless an application explicitly implements irreversible deletion;
|
|
242
242
|
- usage receipts are idempotent for one answer turn through a unique `receiptId`.
|
|
243
243
|
|
|
244
|
-
SQLite schema v10 includes durable external-memory import jobs and per-candidate progress, in addition to the normalized Block processing state, summary/extraction retry jobs, graph, element, provenance, receipt, decay-anchor, and lift-source data introduced earlier. Opening a schema-v1 through v9 database migrates it in one transaction and preserves existing namespaces, Blocks, Events, jobs, and receipts. Existing pre-v9 Blocks are treated as ready because their persisted L0-L5 layers were already accepted by the older engine. Schema-v5 turn anchors are converted to per-thread Block positions; schema-v6 lift timestamps retain an unknown legacy source. Pre-v5 Blocks retain no inferred thread ownership, so they remain archival provenance without being attached to a new session. SQLite uses WAL, foreign keys, and per-namespace optimistic concurrency. It does not provide encryption at rest. Search still uses the reference in-memory ranking after hydration, so enabling persistence does not silently change retrieval semantics. Database-native lexical/vector indexes and a Postgres implementation remain separate future work.
|
|
244
|
+
SQLite schema v10 includes durable external-memory import jobs and per-candidate progress, in addition to the normalized Block processing state, summary/extraction retry jobs, graph, element, provenance, receipt, decay-anchor, and lift-source data introduced earlier. Opening a schema-v1 through v9 database migrates it in one transaction and preserves existing namespaces, Blocks, Events, jobs, and receipts. Existing pre-v9 Blocks are treated as ready because their persisted L0-L5 layers were already accepted by the older engine. Schema-v5 turn anchors are converted to per-thread Block positions; schema-v6 lift timestamps retain an unknown legacy source. Pre-v5 Blocks retain no inferred thread ownership, so they remain archival provenance without being attached to a new session. SQLite uses WAL, foreign keys, and per-namespace optimistic concurrency. It does not provide encryption at rest. Search still uses the reference in-memory ranking after hydration, so enabling persistence does not silently change retrieval semantics. Database-native lexical/vector indexes and a Postgres implementation remain separate future work.
|
package/docs/DSH.md
CHANGED
|
@@ -147,6 +147,7 @@ config:
|
|
|
147
147
|
blockDecayLambda: 0.3
|
|
148
148
|
ingestSubagents: false
|
|
149
149
|
maxOutputTokens: 10000
|
|
150
|
+
structuredTaskTimeoutMs: 120000
|
|
150
151
|
structuredReasoningEffort: auto # auto | force-off
|
|
151
152
|
# Optional: use a dedicated model for memory processing.
|
|
152
153
|
# provider: deepseek
|
|
@@ -171,7 +172,9 @@ For diagnostics, the five most recent successful memory-model responses are reta
|
|
|
171
172
|
|
|
172
173
|
## Compatibility and permissions
|
|
173
174
|
|
|
174
|
-
Release gates exercise DSH `0.1.2-rc.1` on Node `24`,
|
|
175
|
+
Release gates exercise the complete DSH `0.1.2-rc.1` family and `@deepseek-ai/dsh@0.1.5-rc.1` on Node `24`, on both Linux and Windows. The latter's real dependency tree resolves its internal DSH packages to `0.1.5-rc.2`; that is intentional. The plugin declares these DSH packages as optional, exact-version peers so the host supplies one coherent runtime instead of npm installing a second copy. Other `0.1.x` versions are not implicitly supported.
|
|
176
|
+
|
|
177
|
+
DSH core packages are host-provided optional peers. After upgrading a profile that once installed those peers locally, run `dsh plugin --profile <name> exec stratagate-dsh-repair` before starting it. The command moves only the known DSH runtime packages into `.stratagate-runtime-backups` inside that profile and writes a recovery receipt; it does not delete them or touch session data. At startup, a bootstrap resolver also forces StrataGate's own imports through the installation-owned module fallback, then verifies the resolved DSH core family and stops with an actionable error for an unknown host family. On DSH `0.1.5`, v0 sessions containing the retired `stratagate/memory-citations` event are copied to a validated v1 generation before the host migrates them to v3. The original v0 log is never modified; a `stratagate-legacy-citations-v1.json` receipt records source/target hashes and recovery instructions.
|
|
175
178
|
|
|
176
179
|
The package declares local filesystem read/write and Harness tool registration. It does not request direct network, subprocess, shell, Python, or credential access. Model calls still flow through DSH's existing LLM service.
|
|
177
180
|
|
package/docs/DSH.zh-CN.md
CHANGED
|
@@ -144,6 +144,7 @@ config:
|
|
|
144
144
|
blockDecayLambda: 0.3
|
|
145
145
|
ingestSubagents: false
|
|
146
146
|
maxOutputTokens: 10000
|
|
147
|
+
structuredTaskTimeoutMs: 120000
|
|
147
148
|
structuredReasoningEffort: auto # auto | force-off
|
|
148
149
|
# 可选:为记忆处理指定专用模型。
|
|
149
150
|
# provider: deepseek
|
|
@@ -168,7 +169,9 @@ config:
|
|
|
168
169
|
|
|
169
170
|
## 兼容性与权限
|
|
170
171
|
|
|
171
|
-
发布门禁会在 Node `24
|
|
172
|
+
发布门禁会在 Node `24`、Linux 和 Windows 上测试完整的 DSH `0.1.2-rc.1` 依赖族,以及 `@deepseek-ai/dsh@0.1.5-rc.1`。后者的真实依赖树会把内部 DSH 包解析为 `0.1.5-rc.2`,这是预期行为。插件将这些 DSH 包声明为可选且精确版本的 peer,由宿主提供一套一致的运行时,避免 npm 在插件目录再安装第二套核心包;不会笼统承诺其他 `0.1.x` 版本。
|
|
173
|
+
|
|
174
|
+
DSH 核心包现在都是由宿主提供的可选 peer。若某个 profile 曾在本地安装这些 peer,升级后、启动前运行 `dsh plugin --profile <名称> exec stratagate-dsh-repair`。该命令只会把已知 DSH 运行时包移动到 profile 内的 `.stratagate-runtime-backups` 并写入恢复收据,不会删除包或触碰会话数据。启动时 bootstrap 还会把 StrataGate 自身的 DSH 导入定向到宿主维护的共享模块回退目录,再检查实际解析出的 DSH 核心族;未知宿主会立即给出明确错误并停止,而不是让正文区域空白。对于 DSH `0.1.5`,含已废弃 `stratagate/memory-citations` 事件的 v0 会话会先复制成经过校验的 v1 代际,再交给宿主迁移到 v3;原始 v0 日志不会被修改,并写入 `stratagate-legacy-citations-v1.json` 记录源/目标哈希和恢复说明。
|
|
172
175
|
|
|
173
176
|
该包申请本地文件系统读写权限和 Harness 工具注册权限,不申请直接网络访问、子进程、Shell、Python 或凭证访问权限。模型调用仍通过 DSH 现有的 LLM 服务进行。
|
|
174
177
|
|
|
@@ -1,54 +1,54 @@
|
|
|
1
|
-
# 外部 AI 记忆导入
|
|
2
|
-
|
|
3
|
-
StrataGate 提供 `importExternalMemory()` 编排外部记忆迁移:
|
|
4
|
-
|
|
5
|
-
```text
|
|
6
|
-
外部 AI JSON
|
|
7
|
-
↓ extractor(候选 Event)
|
|
8
|
-
每个候选 Event → searchEvents()(确定性 BM25,Top-K)
|
|
9
|
-
↓ decider(ADD / MERGE / SUPERSEDE / CONFLICT / IGNORE)
|
|
10
|
-
写入新的规范 Event,保留来源和 supersedes/conflicts 关系
|
|
11
|
-
↓
|
|
12
|
-
只为新 Event 创建元素/知识图谱投影任务
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## 最小接入
|
|
16
|
-
|
|
17
|
-
```ts
|
|
18
|
-
import {
|
|
19
|
-
EXTERNAL_MEMORY_EXPORT_PROMPT_ZH_CN,
|
|
20
|
-
StrataGate,
|
|
21
|
-
externalMemoryJsonExtractor,
|
|
22
|
-
} from '@diqier/stratagate';
|
|
23
|
-
|
|
24
|
-
// 把 EXTERNAL_MEMORY_EXPORT_PROMPT_ZH_CN 交给外部 AI,并把它返回的 JSON 放入 text。
|
|
25
|
-
const result = await memory.importExternalMemory({
|
|
26
|
-
text,
|
|
27
|
-
extractor: externalMemoryJsonExtractor,
|
|
28
|
-
topK: 5,
|
|
29
|
-
decider: async ({ candidate, matches }) => {
|
|
30
|
-
// 这里通常调用你的 LLM;它只能从 matches 中选择 existingEventIds。
|
|
31
|
-
// 返回的 MERGE/SUPERSEDE 会创建新 Event,不会覆盖旧 Event。
|
|
32
|
-
return {
|
|
33
|
-
action: matches.length === 0 ? 'ADD' : 'MERGE',
|
|
34
|
-
existingEventIds: matches.slice(0, 1).map(({ event }) => event.id),
|
|
35
|
-
mergedCandidate: candidate,
|
|
36
|
-
};
|
|
37
|
-
},
|
|
38
|
-
});
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
`decider` 的 `matches` 已经被限制为 `topK` 条;即使模型返回其它事件 ID,库也会丢弃这些越界引用。`IGNORE` 只留下审计记录,不会写入 Event。`CONFLICT` 会在新旧事件两侧建立对称的 `conflictsWithEventIds`。
|
|
42
|
-
|
|
43
|
-
## 给外部 AI 的提示词
|
|
44
|
-
|
|
45
|
-
直接使用导出的 `EXTERNAL_MEMORY_EXPORT_PROMPT_ZH_CN`。它要求外部 AI 只输出 `stratagate.external-memory.v2` JSON,并区分 `memoryKind`(instruction/preference/fact/event)与 `category`,同时特别约束时间:
|
|
46
|
-
|
|
47
|
-
- `mentionedAt`(被提及时间)与 `happenedStart/happenedEnd`(实际发生/计划时间)分开;
|
|
48
|
-
- 没有明确时间或可靠参照时,不填写日期,不把当前时间、导出时间或聊天顺序当作事件时间;
|
|
49
|
-
- 保留 `originalText`,用 `precision` 和 `basis` 标记粒度与依据;
|
|
50
|
-
- “上周”等相对时间只有在能依据已知消息时间唯一换算时才转换,否则保持 `unknown`。
|
|
51
|
-
|
|
52
|
-
如果外部 AI 仍然返回 Markdown 代码块,`parseExternalMemoryExport()` 会自动去除围栏;其它非 JSON 文本会被拒绝,避免把模型解释误写进记忆。
|
|
53
|
-
|
|
54
|
-
用于第二阶段裁决的系统提示词可使用 `EXTERNAL_MEMORY_DECIDER_PROMPT_ZH_CN`。它明确规定了五种写入动作的边界,并要求模型只能引用本次 Top-K 结果中的事件 ID。
|
|
1
|
+
# 外部 AI 记忆导入
|
|
2
|
+
|
|
3
|
+
StrataGate 提供 `importExternalMemory()` 编排外部记忆迁移:
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
外部 AI JSON
|
|
7
|
+
↓ extractor(候选 Event)
|
|
8
|
+
每个候选 Event → searchEvents()(确定性 BM25,Top-K)
|
|
9
|
+
↓ decider(ADD / MERGE / SUPERSEDE / CONFLICT / IGNORE)
|
|
10
|
+
写入新的规范 Event,保留来源和 supersedes/conflicts 关系
|
|
11
|
+
↓
|
|
12
|
+
只为新 Event 创建元素/知识图谱投影任务
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## 最小接入
|
|
16
|
+
|
|
17
|
+
```ts
|
|
18
|
+
import {
|
|
19
|
+
EXTERNAL_MEMORY_EXPORT_PROMPT_ZH_CN,
|
|
20
|
+
StrataGate,
|
|
21
|
+
externalMemoryJsonExtractor,
|
|
22
|
+
} from '@diqier/stratagate';
|
|
23
|
+
|
|
24
|
+
// 把 EXTERNAL_MEMORY_EXPORT_PROMPT_ZH_CN 交给外部 AI,并把它返回的 JSON 放入 text。
|
|
25
|
+
const result = await memory.importExternalMemory({
|
|
26
|
+
text,
|
|
27
|
+
extractor: externalMemoryJsonExtractor,
|
|
28
|
+
topK: 5,
|
|
29
|
+
decider: async ({ candidate, matches }) => {
|
|
30
|
+
// 这里通常调用你的 LLM;它只能从 matches 中选择 existingEventIds。
|
|
31
|
+
// 返回的 MERGE/SUPERSEDE 会创建新 Event,不会覆盖旧 Event。
|
|
32
|
+
return {
|
|
33
|
+
action: matches.length === 0 ? 'ADD' : 'MERGE',
|
|
34
|
+
existingEventIds: matches.slice(0, 1).map(({ event }) => event.id),
|
|
35
|
+
mergedCandidate: candidate,
|
|
36
|
+
};
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
`decider` 的 `matches` 已经被限制为 `topK` 条;即使模型返回其它事件 ID,库也会丢弃这些越界引用。`IGNORE` 只留下审计记录,不会写入 Event。`CONFLICT` 会在新旧事件两侧建立对称的 `conflictsWithEventIds`。
|
|
42
|
+
|
|
43
|
+
## 给外部 AI 的提示词
|
|
44
|
+
|
|
45
|
+
直接使用导出的 `EXTERNAL_MEMORY_EXPORT_PROMPT_ZH_CN`。它要求外部 AI 只输出 `stratagate.external-memory.v2` JSON,并区分 `memoryKind`(instruction/preference/fact/event)与 `category`,同时特别约束时间:
|
|
46
|
+
|
|
47
|
+
- `mentionedAt`(被提及时间)与 `happenedStart/happenedEnd`(实际发生/计划时间)分开;
|
|
48
|
+
- 没有明确时间或可靠参照时,不填写日期,不把当前时间、导出时间或聊天顺序当作事件时间;
|
|
49
|
+
- 保留 `originalText`,用 `precision` 和 `basis` 标记粒度与依据;
|
|
50
|
+
- “上周”等相对时间只有在能依据已知消息时间唯一换算时才转换,否则保持 `unknown`。
|
|
51
|
+
|
|
52
|
+
如果外部 AI 仍然返回 Markdown 代码块,`parseExternalMemoryExport()` 会自动去除围栏;其它非 JSON 文本会被拒绝,避免把模型解释误写进记忆。
|
|
53
|
+
|
|
54
|
+
用于第二阶段裁决的系统提示词可使用 `EXTERNAL_MEMORY_DECIDER_PROMPT_ZH_CN`。它明确规定了五种写入动作的边界,并要求模型只能引用本次 Top-K 结果中的事件 ID。
|
package/package.json
CHANGED
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "stratagate-dsh",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.63",
|
|
4
4
|
"description": "Recent conversations stay vivid. Older ones fade into summaries, not oblivion. StrataGate gives DeepSeek Harness six-layer, time-decaying memory, while lasting events and relationships settle into a knowledge graph. Bring your memories from other AIs with you—no need to start over.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
|
-
"types": "./dist/index.d.ts",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"bin": {
|
|
9
|
+
"stratagate-dsh-repair": "./dist/repair-profile.js"
|
|
10
|
+
},
|
|
8
11
|
"exports": {
|
|
9
12
|
".": {
|
|
10
13
|
"types": "./dist/index.d.ts",
|
|
@@ -55,7 +58,7 @@
|
|
|
55
58
|
"test:dsh": "vitest run --config vitest.config.ts",
|
|
56
59
|
"test:core": "npm run test --workspace @diqier/stratagate",
|
|
57
60
|
"test:workbuddy": "npm run test --workspace stratagate-workbuddy",
|
|
58
|
-
"verify:dsh": "npm run build:dsh && node scripts/verify-package.mjs",
|
|
61
|
+
"verify:dsh": "npm run build:dsh && node scripts/verify-package.mjs && npm pack --ignore-scripts --pack-destination . && node scripts/verify-dsh-install.mjs stratagate-dsh-0.2.63.tgz",
|
|
59
62
|
"verify:workbuddy": "npm run verify:package --workspace stratagate-workbuddy",
|
|
60
63
|
"test:watch": "vitest --config vitest.config.ts",
|
|
61
64
|
"prepare": "npm run build:dsh"
|
|
@@ -111,7 +114,8 @@
|
|
|
111
114
|
},
|
|
112
115
|
"compatibility": {
|
|
113
116
|
"dshVersions": [
|
|
114
|
-
"0.1.2-rc.1"
|
|
117
|
+
"0.1.2-rc.1",
|
|
118
|
+
"0.1.5-rc.1"
|
|
115
119
|
]
|
|
116
120
|
},
|
|
117
121
|
"permissions": [
|
|
@@ -144,26 +148,71 @@
|
|
|
144
148
|
"node": "^22.19.0 || >=24.0.0"
|
|
145
149
|
},
|
|
146
150
|
"peerDependencies": {
|
|
147
|
-
"@deepseek-ai/cordis": "
|
|
148
|
-
"@deepseek-ai/dsh-agent-default-model": "
|
|
149
|
-
"@deepseek-ai/dsh-client-ui-conversation": "
|
|
150
|
-
"@deepseek-ai/dsh-llm": "
|
|
151
|
-
"@deepseek-ai/dsh-session": "
|
|
152
|
-
"@deepseek-ai/dsh-
|
|
153
|
-
"@deepseek-ai/dsh-
|
|
154
|
-
"@deepseek-ai/dsh-
|
|
155
|
-
"@deepseek-ai/
|
|
151
|
+
"@deepseek-ai/cordis": "4.0.2",
|
|
152
|
+
"@deepseek-ai/dsh-agent-default-model": "0.1.2-rc.1 || 0.1.5-rc.2",
|
|
153
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.2-rc.1 || 0.1.5-rc.2",
|
|
154
|
+
"@deepseek-ai/dsh-llm": "0.1.2-rc.1 || 0.1.5-rc.2",
|
|
155
|
+
"@deepseek-ai/dsh-session": "0.1.2-rc.1 || 0.1.5-rc.2",
|
|
156
|
+
"@deepseek-ai/dsh-session-format": "0.1.5-rc.2",
|
|
157
|
+
"@deepseek-ai/dsh-session-format-catalog": "0.1.5-rc.2",
|
|
158
|
+
"@deepseek-ai/dsh-session-format-v0-to-v1": "0.1.5-rc.2",
|
|
159
|
+
"@deepseek-ai/dsh-settings": "0.1.2-rc.1 || 0.1.5-rc.2",
|
|
160
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.2-rc.1 || 0.1.5-rc.2",
|
|
161
|
+
"@deepseek-ai/dsh-tools": "0.1.2-rc.1 || 0.1.5-rc.2",
|
|
162
|
+
"@deepseek-ai/schemastery": "3.18.2"
|
|
163
|
+
},
|
|
164
|
+
"peerDependenciesMeta": {
|
|
165
|
+
"@deepseek-ai/cordis": {
|
|
166
|
+
"optional": true
|
|
167
|
+
},
|
|
168
|
+
"@deepseek-ai/dsh-agent-default-model": {
|
|
169
|
+
"optional": true
|
|
170
|
+
},
|
|
171
|
+
"@deepseek-ai/dsh-client-ui-conversation": {
|
|
172
|
+
"optional": true
|
|
173
|
+
},
|
|
174
|
+
"@deepseek-ai/dsh-llm": {
|
|
175
|
+
"optional": true
|
|
176
|
+
},
|
|
177
|
+
"@deepseek-ai/dsh-session": {
|
|
178
|
+
"optional": true
|
|
179
|
+
},
|
|
180
|
+
"@deepseek-ai/dsh-session-format": {
|
|
181
|
+
"optional": true
|
|
182
|
+
},
|
|
183
|
+
"@deepseek-ai/dsh-session-format-catalog": {
|
|
184
|
+
"optional": true
|
|
185
|
+
},
|
|
186
|
+
"@deepseek-ai/dsh-session-format-v0-to-v1": {
|
|
187
|
+
"optional": true
|
|
188
|
+
},
|
|
189
|
+
"@deepseek-ai/dsh-settings": {
|
|
190
|
+
"optional": true
|
|
191
|
+
},
|
|
192
|
+
"@deepseek-ai/dsh-system-prompt": {
|
|
193
|
+
"optional": true
|
|
194
|
+
},
|
|
195
|
+
"@deepseek-ai/dsh-tools": {
|
|
196
|
+
"optional": true
|
|
197
|
+
},
|
|
198
|
+
"@deepseek-ai/schemastery": {
|
|
199
|
+
"optional": true
|
|
200
|
+
}
|
|
156
201
|
},
|
|
157
202
|
"devDependencies": {
|
|
158
|
-
"@deepseek-ai/cordis": "
|
|
159
|
-
"@deepseek-ai/dsh
|
|
160
|
-
"@deepseek-ai/dsh-
|
|
161
|
-
"@deepseek-ai/dsh-
|
|
162
|
-
"@deepseek-ai/dsh-
|
|
163
|
-
"@deepseek-ai/dsh-
|
|
164
|
-
"@deepseek-ai/dsh-
|
|
165
|
-
"@deepseek-ai/dsh-
|
|
166
|
-
"@deepseek-ai/
|
|
203
|
+
"@deepseek-ai/cordis": "4.0.2",
|
|
204
|
+
"@deepseek-ai/dsh": "0.1.5-rc.1",
|
|
205
|
+
"@deepseek-ai/dsh-agent-default-model": "0.1.5-rc.2",
|
|
206
|
+
"@deepseek-ai/dsh-client-ui-conversation": "0.1.5-rc.2",
|
|
207
|
+
"@deepseek-ai/dsh-llm": "0.1.5-rc.2",
|
|
208
|
+
"@deepseek-ai/dsh-session": "0.1.5-rc.2",
|
|
209
|
+
"@deepseek-ai/dsh-session-format": "0.1.5-rc.2",
|
|
210
|
+
"@deepseek-ai/dsh-session-format-catalog": "0.1.5-rc.2",
|
|
211
|
+
"@deepseek-ai/dsh-session-format-v0-to-v1": "0.1.5-rc.2",
|
|
212
|
+
"@deepseek-ai/dsh-settings": "0.1.5-rc.2",
|
|
213
|
+
"@deepseek-ai/dsh-system-prompt": "0.1.5-rc.2",
|
|
214
|
+
"@deepseek-ai/dsh-tools": "0.1.5-rc.2",
|
|
215
|
+
"@deepseek-ai/schemastery": "3.18.2",
|
|
167
216
|
"@types/node": "^22.0.0",
|
|
168
217
|
"cytoscape": "^3.34.1",
|
|
169
218
|
"cytoscape-fcose": "^2.2.0",
|
|
@@ -172,10 +221,10 @@
|
|
|
172
221
|
"typescript": "^5.7.0",
|
|
173
222
|
"vitest": "^3.0.0"
|
|
174
223
|
},
|
|
175
|
-
"dependencies": {
|
|
176
|
-
"ngraph.graph": "^20.1.2",
|
|
177
|
-
"ngraph.leiden": "^0.3.0"
|
|
178
|
-
},
|
|
224
|
+
"dependencies": {
|
|
225
|
+
"ngraph.graph": "^20.1.2",
|
|
226
|
+
"ngraph.leiden": "^0.3.0"
|
|
227
|
+
},
|
|
179
228
|
"directories": {
|
|
180
229
|
"doc": "docs",
|
|
181
230
|
"test": "tests"
|
package/screenshots.json
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
[
|
|
2
|
-
"docs/assets/stratagate-knowledge-graph.png",
|
|
3
|
-
"docs/assets/stratagate-short-term-memory.png"
|
|
4
|
-
]
|
|
1
|
+
[
|
|
2
|
+
"docs/assets/stratagate-knowledge-graph.png",
|
|
3
|
+
"docs/assets/stratagate-short-term-memory.png"
|
|
4
|
+
]
|