dsh-tdai-memory 0.3.4 → 0.3.6
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/LICENSE +21 -21
- package/README.md +13 -4
- package/README.zh.md +13 -4
- package/THIRD-PARTY-NOTICES.md +20 -20
- package/cordis.patch.yml +8 -8
- package/index.js +13 -13
- package/package.json +9 -6
- package/recall-inject.js +86 -86
- package/vendor/tdai/adapters/index.js +15 -15
- package/vendor/tdai/adapters/openclaw/host-adapter.js +83 -83
- package/vendor/tdai/adapters/openclaw/index.js +5 -5
- package/vendor/tdai/adapters/openclaw/llm-runner.js +69 -69
- package/vendor/tdai/adapters/standalone/host-adapter.js +58 -58
- package/vendor/tdai/adapters/standalone/index.js +5 -5
- package/vendor/tdai/core/conversation/l0-recorder.js +420 -420
- package/vendor/tdai/core/hooks/auto-capture.js +236 -236
- package/vendor/tdai/core/hooks/auto-recall.js +640 -640
- package/vendor/tdai/core/index.js +8 -8
- package/vendor/tdai/core/persona/persona-generator.js +184 -184
- package/vendor/tdai/core/persona/persona-trigger.js +94 -94
- package/vendor/tdai/core/profile/profile-sync.js +200 -200
- package/vendor/tdai/core/prompts/l1-dedup.js +72 -72
- package/vendor/tdai/core/prompts/l1-extraction.js +34 -34
- package/vendor/tdai/core/prompts/persona-generation.js +35 -35
- package/vendor/tdai/core/prompts/scene-extraction.js +45 -45
- package/vendor/tdai/core/record/l1-dedup.js +318 -318
- package/vendor/tdai/core/record/l1-extractor.js +379 -379
- package/vendor/tdai/core/record/l1-reader.js +178 -178
- package/vendor/tdai/core/record/l1-writer.js +141 -141
- package/vendor/tdai/core/report/reporter.js +86 -86
- package/vendor/tdai/core/scene/filename-normalizer.js +161 -161
- package/vendor/tdai/core/scene/scene-extractor.js +410 -410
- package/vendor/tdai/core/scene/scene-format.js +53 -53
- package/vendor/tdai/core/scene/scene-index.js +82 -82
- package/vendor/tdai/core/scene/scene-navigation.js +57 -57
- package/vendor/tdai/core/seed/input.js +404 -404
- package/vendor/tdai/core/seed/types.js +9 -9
- package/vendor/tdai/core/store/bm25-client.js +117 -117
- package/vendor/tdai/core/store/bm25-local.js +68 -68
- package/vendor/tdai/core/store/embedding.js +632 -632
- package/vendor/tdai/core/store/factory.js +95 -95
- package/vendor/tdai/core/store/search-utils.js +54 -54
- package/vendor/tdai/core/store/sqlite.js +1632 -1632
- package/vendor/tdai/core/store/tcvdb-client.js +221 -221
- package/vendor/tdai/core/store/tcvdb.js +1047 -1047
- package/vendor/tdai/core/store/types.js +17 -17
- package/vendor/tdai/core/tools/conversation-search.js +199 -199
- package/vendor/tdai/core/tools/memory-search.js +207 -207
- package/vendor/tdai/core/types.js +12 -12
- package/vendor/tdai/utils/backup.js +189 -189
- package/vendor/tdai/utils/checkpoint.js +360 -360
- package/vendor/tdai/utils/clean-context-runner.js +472 -472
- package/vendor/tdai/utils/ensure-hook-policy.js +213 -213
- package/vendor/tdai/utils/env.js +13 -13
- package/vendor/tdai/utils/managed-timer.js +127 -127
- package/vendor/tdai/utils/manifest.js +91 -91
- package/vendor/tdai/utils/memory-cleaner.js +300 -300
- package/vendor/tdai/utils/no-think-fetch.js +109 -109
- package/vendor/tdai/utils/openclaw-state-dir.js +26 -26
- package/vendor/tdai/utils/pipeline-factory.js +517 -517
- package/vendor/tdai/utils/pipeline-manager.js +892 -892
- package/vendor/tdai/utils/sanitize.js +360 -360
- package/vendor/tdai/utils/serial-queue.js +103 -103
- package/vendor/tdai/utils/session-filter.js +88 -88
- package/vendor/tdai/utils/text-utils.js +29 -29
- package/vendor/tdai/utils/time.js +252 -252
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 Scorp1o117
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
-
SOFTWARE.
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Scorp1o117
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -11,11 +11,20 @@ Part of the [DeepSeek Harness Enhancement Suite](https://github.com/Scorp1o117/d
|
|
|
11
11
|
A port of **TencentDB Agent Memory** (Tencent Cloud's open-source four-layer
|
|
12
12
|
memory system, originally an OpenClaw plugin) into DeepSeek Harness.
|
|
13
13
|
|
|
14
|
-
## Compatibility (v0.3.
|
|
14
|
+
## Compatibility (v0.3.6)
|
|
15
|
+
|
|
16
|
+
Verified with DSH `0.1.7-rc.2` (Web) and `0.2.0-rc.2` (Desktop runtime) in isolated profiles. The Desktop app uses its own `desktop` profile. Other DSH prereleases remain unverified.
|
|
17
|
+
|
|
18
|
+
## Desktop install
|
|
19
|
+
|
|
20
|
+
Use the Desktop-installed `dsh` command (Application → Manage dsh Command), or the app’s Plugins page. Then install into the Desktop profile:
|
|
21
|
+
|
|
22
|
+
```powershell
|
|
23
|
+
dsh plugin --profile desktop add dsh-tdai-memory@0.3.6
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
Restart the Desktop app to load the client bundle. Desktop keeps its profile under `$DSH_HOME/profiles/desktop`.
|
|
15
27
|
|
|
16
|
-
Targets DSH `0.1.7-rc.1` (`next`); npm `latest` is `0.1.5-rc.3`.
|
|
17
|
-
Settings now live in the Profile patch, and the browser uses `configForms`.
|
|
18
|
-
Older hosts require an older plugin release; alpha builds remain `unknown`.
|
|
19
28
|
|
|
20
29
|
## Features
|
|
21
30
|
|
package/README.zh.md
CHANGED
|
@@ -8,11 +8,20 @@
|
|
|
8
8
|
|
|
9
9
|
把 **TencentDB Agent Memory**(腾讯云开源的四层记忆系统,原为 OpenClaw 插件)移植进 DeepSeek Harness。
|
|
10
10
|
|
|
11
|
-
## 兼容性(v0.3.
|
|
11
|
+
## 兼容性(v0.3.6)
|
|
12
|
+
|
|
13
|
+
已在 DSH `0.1.7-rc.2`(Web)与 `0.2.0-rc.2`(桌面端运行时)的一次性 Profile 验证。桌面端使用独立的 `desktop` Profile;其他预发布版本暂未验证。
|
|
14
|
+
|
|
15
|
+
## 桌面端安装
|
|
16
|
+
|
|
17
|
+
在桌面端的“插件”页面安装,或使用桌面端“应用 → 管理 dsh 命令”注册的命令:
|
|
18
|
+
|
|
19
|
+
```powershell
|
|
20
|
+
dsh plugin --profile desktop add dsh-tdai-memory@0.3.6
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
重启桌面端以加载客户端插件。配置位于 `$DSH_HOME/profiles/desktop`。
|
|
12
24
|
|
|
13
|
-
适配 DSH `0.1.7-rc.1`(npm `next`);npm `latest` 是 `0.1.5-rc.3`。
|
|
14
|
-
设置保存在当前 Profile patch,Web 端使用 `configForms`。
|
|
15
|
-
旧宿主请使用插件旧版;alpha 构建仍标记 `unknown`。
|
|
16
25
|
|
|
17
26
|
## 能力
|
|
18
27
|
|
package/THIRD-PARTY-NOTICES.md
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
|
-
# Third-party notices
|
|
2
|
-
|
|
3
|
-
## TencentDB Agent Memory core (vendored in `vendor/tdai`)
|
|
4
|
-
|
|
5
|
-
`vendor/tdai` is a TypeScript-compiled copy of the host-neutral core of
|
|
6
|
-
[TencentCloud/TencentDB-Agent-Memory](https://github.com/TencentCloud/TencentDB-Agent-Memory)
|
|
7
|
-
(`tdai-memory-openclaw-plugin`, npm: `@tencentdb-agent-memory/memory-tencentdb`),
|
|
8
|
-
compiled with `tsc -p dsh-tsconfig.json` (see the upstream repo for source).
|
|
9
|
-
Its runtime dependencies are declared in this package's `dependencies`.
|
|
10
|
-
|
|
11
|
-
MIT License — Copyright (c) 2026 TencentCloud
|
|
12
|
-
|
|
13
|
-
## Runtime dependencies
|
|
14
|
-
|
|
15
|
-
- `ai` / `@ai-sdk/openai` — Vercel AI SDK (Apache-2.0)
|
|
16
|
-
- `@node-rs/jieba` — Rust jieba bindings (MIT)
|
|
17
|
-
- `sqlite-vec` — vector search extension for SQLite (Apache-2.0 / MIT)
|
|
18
|
-
- `json5` — JSON5 parser (MIT)
|
|
19
|
-
- `undici` — HTTP client (MIT)
|
|
20
|
-
- `@tencentdb-agent-memory/tcvdb-text` — TencentDB text utilities (MIT)
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
## TencentDB Agent Memory core (vendored in `vendor/tdai`)
|
|
4
|
+
|
|
5
|
+
`vendor/tdai` is a TypeScript-compiled copy of the host-neutral core of
|
|
6
|
+
[TencentCloud/TencentDB-Agent-Memory](https://github.com/TencentCloud/TencentDB-Agent-Memory)
|
|
7
|
+
(`tdai-memory-openclaw-plugin`, npm: `@tencentdb-agent-memory/memory-tencentdb`),
|
|
8
|
+
compiled with `tsc -p dsh-tsconfig.json` (see the upstream repo for source).
|
|
9
|
+
Its runtime dependencies are declared in this package's `dependencies`.
|
|
10
|
+
|
|
11
|
+
MIT License — Copyright (c) 2026 TencentCloud
|
|
12
|
+
|
|
13
|
+
## Runtime dependencies
|
|
14
|
+
|
|
15
|
+
- `ai` / `@ai-sdk/openai` — Vercel AI SDK (Apache-2.0)
|
|
16
|
+
- `@node-rs/jieba` — Rust jieba bindings (MIT)
|
|
17
|
+
- `sqlite-vec` — vector search extension for SQLite (Apache-2.0 / MIT)
|
|
18
|
+
- `json5` — JSON5 parser (MIT)
|
|
19
|
+
- `undici` — HTTP client (MIT)
|
|
20
|
+
- `@tencentdb-agent-memory/tcvdb-text` — TencentDB text utilities (MIT)
|
package/cordis.patch.yml
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
# dsh-tdai-memory — bundle patch layer
|
|
2
|
-
#
|
|
3
|
-
# Declares the plugin as a Cordis loader entry, making the package a standard
|
|
4
|
-
# profile bundle: `dsh plugin --profile <p> add dsh-tdai-memory` installs and
|
|
5
|
-
# mounts it without manual cordis.patch.yml edits.
|
|
6
|
-
- insert:
|
|
7
|
-
- id: tdai-memory
|
|
8
|
-
name: 'dsh-tdai-memory'
|
|
1
|
+
# dsh-tdai-memory — bundle patch layer
|
|
2
|
+
#
|
|
3
|
+
# Declares the plugin as a Cordis loader entry, making the package a standard
|
|
4
|
+
# profile bundle: `dsh plugin --profile <p> add dsh-tdai-memory` installs and
|
|
5
|
+
# mounts it without manual cordis.patch.yml edits.
|
|
6
|
+
- insert:
|
|
7
|
+
- id: tdai-memory
|
|
8
|
+
name: 'dsh-tdai-memory'
|
package/index.js
CHANGED
|
@@ -37,7 +37,7 @@ const Config = z.object({
|
|
|
37
37
|
/** TDAI data directory; default: ~/.memory-tencentdb/memory-tdai. */
|
|
38
38
|
dataDir: z.string().default(""),
|
|
39
39
|
/** LLM used for L1/L2/L3 extraction (OpenAI-compatible). Falls back to TDAI_LLM_* env. */
|
|
40
|
-
llm: z.object({
|
|
40
|
+
llm: z.object({
|
|
41
41
|
baseUrl: z.string().default(""),
|
|
42
42
|
apiKey: z.string().default("").role("secret"),
|
|
43
43
|
model: z.string().default(""),
|
|
@@ -52,34 +52,34 @@ const Config = z.object({
|
|
|
52
52
|
sessionHeaderName: z.string().default("x-opencode-session"),
|
|
53
53
|
/** Fixed session id for LLM requests; empty = persistent auto id. */
|
|
54
54
|
sessionId: z.string().default(""),
|
|
55
|
-
}).default({}),
|
|
55
|
+
}).default({}),
|
|
56
56
|
/** Embedding endpoint (OpenAI-compatible /v1/embeddings). */
|
|
57
|
-
embedding: z.object({
|
|
57
|
+
embedding: z.object({
|
|
58
58
|
baseUrl: z.string().default("http://127.0.0.1:8088/v1"),
|
|
59
59
|
apiKey: z.string().default("").role("secret"),
|
|
60
60
|
model: z.string().default("Qwen3-Embedding-0.6B"),
|
|
61
61
|
dimensions: z.number().default(1024),
|
|
62
62
|
sendDimensions: z.boolean().default(false),
|
|
63
|
-
}).default({}),
|
|
63
|
+
}).default({}),
|
|
64
64
|
/** Capture conversations into L0. */
|
|
65
65
|
captureEnabled: z.boolean().default(true),
|
|
66
66
|
/** L1 extraction pipeline (facts from conversations). */
|
|
67
|
-
extraction: z.object({
|
|
67
|
+
extraction: z.object({
|
|
68
68
|
enabled: z.boolean().default(true),
|
|
69
69
|
/** Conflict detection before storing; extra LLM call, disable if flaky.
|
|
70
70
|
* Off by default: dedup LLM output parsing is flaky (README matches). */
|
|
71
71
|
enableDedup: z.boolean().default(false),
|
|
72
|
-
}).default({}),
|
|
72
|
+
}).default({}),
|
|
73
73
|
/** Recall injection settings. */
|
|
74
|
-
recall: z.object({
|
|
74
|
+
recall: z.object({
|
|
75
75
|
enabled: z.boolean().default(true),
|
|
76
76
|
maxResults: z.number().default(5),
|
|
77
77
|
scoreThreshold: z.number().default(0.3),
|
|
78
78
|
timeoutMs: z.number().default(3000),
|
|
79
|
-
}).default({}),
|
|
79
|
+
}).default({}),
|
|
80
80
|
/** Register the two search tools. */
|
|
81
81
|
toolsEnabled: z.boolean().default(true),
|
|
82
|
-
}).volatile();
|
|
82
|
+
}).volatile();
|
|
83
83
|
|
|
84
84
|
/**
|
|
85
85
|
* Whether a derived message is real human input.
|
|
@@ -390,9 +390,9 @@ function apply(ctx, config) {
|
|
|
390
390
|
// `installSettingsSection` export (the provider now lives at ctx.settings).
|
|
391
391
|
// Inline the same logic via ctx.inject(["settings"]) — works on both
|
|
392
392
|
// 0.1.1 (module export wrapper) and 0.1.2 (ctx.settings) hosts.
|
|
393
|
-
ctx.on("settings/document-updated", (id) => {
|
|
394
|
-
if (id === NS) ctx.logger.warn("[tdai-memory] settings updated; restart to apply (TdaiCore is built at startup)");
|
|
395
|
-
});
|
|
393
|
+
ctx.on("settings/document-updated", (id) => {
|
|
394
|
+
if (id === NS) ctx.logger.warn("[tdai-memory] settings updated; restart to apply (TdaiCore is built at startup)");
|
|
395
|
+
});
|
|
396
396
|
|
|
397
397
|
// Fallback when no settings service ever mounts: build from the entry
|
|
398
398
|
// config. One-shot — if the settings mount arrives later, its build
|
|
@@ -401,7 +401,7 @@ function apply(ctx, config) {
|
|
|
401
401
|
setTimeout(() => {
|
|
402
402
|
if (!builtOnce) {
|
|
403
403
|
builtOnce = true;
|
|
404
|
-
void build(typeof config.get === "function" ? config.get() : config);
|
|
404
|
+
void build(typeof config.get === "function" ? config.get() : config);
|
|
405
405
|
}
|
|
406
406
|
}, 500);
|
|
407
407
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "dsh-tdai-memory",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.6",
|
|
4
4
|
"description": "DeepSeek Harness 的 TencentDB Agent Memory 移植:L0 对话捕获 → L1 结构化记忆提取 → L2 场景/L3 画像,自动召回注入 + 记忆/对话搜索工具;复用现有 ~/.memory-tencentdb/memory-tdai 数据;附 Web UI 设置栏。",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"platform": "web"
|
|
18
18
|
},
|
|
19
19
|
"compatibility": {
|
|
20
|
-
"dsh": ">=0.1.7-rc.1 <0.2.0",
|
|
20
|
+
"dsh": ">=0.1.7-rc.1 <0.2.0 || 0.2.0-rc.2",
|
|
21
21
|
"dshReleases": {
|
|
22
22
|
"0.1.5-rc.2": "incompatible",
|
|
23
23
|
"0.1.5-rc.3": "incompatible",
|
|
@@ -25,11 +25,14 @@
|
|
|
25
25
|
"0.1.6-alpha.2": "unknown",
|
|
26
26
|
"0.1.7-alpha.1": "unknown",
|
|
27
27
|
"0.1.7-alpha.2": "unknown",
|
|
28
|
-
"0.1.7-rc.1": "compatible"
|
|
28
|
+
"0.1.7-rc.1": "compatible",
|
|
29
|
+
"0.1.7-rc.2": "compatible",
|
|
30
|
+
"0.2.0-rc.2": "compatible"
|
|
29
31
|
},
|
|
30
32
|
"node": ">=22.18",
|
|
31
33
|
"profiles": [
|
|
32
|
-
"web"
|
|
34
|
+
"web",
|
|
35
|
+
"desktop"
|
|
33
36
|
]
|
|
34
37
|
}
|
|
35
38
|
},
|
|
@@ -79,8 +82,8 @@
|
|
|
79
82
|
},
|
|
80
83
|
"peerDependencies": {
|
|
81
84
|
"react": "^18.2.0",
|
|
82
|
-
"@deepseek-ai/dsh-tools": "^0.1.7-rc.1",
|
|
83
|
-
"@deepseek-ai/dsh-settings": "^0.1.7-rc.1",
|
|
85
|
+
"@deepseek-ai/dsh-tools": "^0.1.7-rc.1 || 0.2.0-rc.2",
|
|
86
|
+
"@deepseek-ai/dsh-settings": "^0.1.7-rc.1 || 0.2.0-rc.2",
|
|
84
87
|
"node-llama-cpp": "^3.19.1"
|
|
85
88
|
},
|
|
86
89
|
"peerDependenciesMeta": {
|
package/recall-inject.js
CHANGED
|
@@ -1,86 +1,86 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* tdai-recall-inject — agent-plane recall injection row for dsh-tdai-memory.
|
|
3
|
-
*
|
|
4
|
-
* Mounted INSIDE an agent preset (agent.cordis.yml), this row listens on the
|
|
5
|
-
* `system-prompt/assemble` waterfall in the preset's scope, so it runs for
|
|
6
|
-
* every assembly of every agent joined to that preset (agent -> preset scope
|
|
7
|
-
* chain). It asks the `tdaiMemory` service (provided by the root-level
|
|
8
|
-
* dsh-tdai-memory plugin) for relevant long-term memories and appends them
|
|
9
|
-
* as dynamic context sections (user-role snapshots):
|
|
10
|
-
*
|
|
11
|
-
* - tdai:recall — relevant L1 memories for the current user message
|
|
12
|
-
* - tdai:profile — stable persona / scene navigation context
|
|
13
|
-
*
|
|
14
|
-
* The recall call is bounded by `recall.timeoutMs`; failures degrade to no
|
|
15
|
-
* injection and never break the assembly.
|
|
16
|
-
*/
|
|
17
|
-
import z from "@deepseek-ai/schemastery";
|
|
18
|
-
|
|
19
|
-
/** Cordis plugin name. */
|
|
20
|
-
const name = "tdai-recall-inject";
|
|
21
|
-
/** Services consumed: the prompt registry and the memory core service. */
|
|
22
|
-
const inject = ["systemPrompt", "tdaiMemory"];
|
|
23
|
-
|
|
24
|
-
/** Runtime schema for the recall-inject row. */
|
|
25
|
-
const Config = z.object({
|
|
26
|
-
/** Skip recall when the user message is shorter than this. */
|
|
27
|
-
minUserTextChars: z.number().default(1),
|
|
28
|
-
/** Per-session cache TTL for identical user text (ms). */
|
|
29
|
-
cacheTtlMs: z.number().default(30_000),
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
function apply(ctx, config) {
|
|
33
|
-
const cache = new Map();
|
|
34
|
-
|
|
35
|
-
ctx.on("system-prompt/assemble", async (assembly, context) => {
|
|
36
|
-
try {
|
|
37
|
-
const agent = context?.agent;
|
|
38
|
-
if (!agent) return assembly;
|
|
39
|
-
const session = agent.session;
|
|
40
|
-
if (!session) return assembly;
|
|
41
|
-
// Only real human input triggers recall. DSH injects synthetic
|
|
42
|
-
// user-role context (system reminders, runtime context, skill
|
|
43
|
-
// catalogs, …) whose source.kind is not "user"; recalling on those
|
|
44
|
-
// would pollute the prompt with irrelevant memories (issue #1).
|
|
45
|
-
const lastUser = [...session.deriveMessages()]
|
|
46
|
-
.reverse()
|
|
47
|
-
.find((m) => m.role === "user" && (m.source == null || m.source.kind === "user"));
|
|
48
|
-
if (!lastUser) return assembly;
|
|
49
|
-
const text = extractText(lastUser.content);
|
|
50
|
-
if (!text || text.length < config.minUserTextChars) return assembly;
|
|
51
|
-
|
|
52
|
-
const now = Date.now();
|
|
53
|
-
const cached = cache.get(session.id);
|
|
54
|
-
if (cached && cached.text === text && now - cached.ts < config.cacheTtlMs) {
|
|
55
|
-
return injectRecall(assembly, cached.result);
|
|
56
|
-
}
|
|
57
|
-
const result = await ctx.tdaiMemory
|
|
58
|
-
.handleBeforeRecall(text, session.id)
|
|
59
|
-
.catch(() => null);
|
|
60
|
-
cache.set(session.id, { text, result: result ?? {}, ts: now });
|
|
61
|
-
return injectRecall(assembly, result ?? {});
|
|
62
|
-
} catch {
|
|
63
|
-
return assembly;
|
|
64
|
-
}
|
|
65
|
-
});
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function extractText(content) {
|
|
69
|
-
if (typeof content === "string") return content.trim();
|
|
70
|
-
if (!Array.isArray(content)) return "";
|
|
71
|
-
return content
|
|
72
|
-
.filter((b) => b && b.type === "text" && typeof b.text === "string")
|
|
73
|
-
.map((b) => b.text)
|
|
74
|
-
.join("\n")
|
|
75
|
-
.trim();
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
function injectRecall(assembly, result) {
|
|
79
|
-
const extras = [];
|
|
80
|
-
if (result.prependContext) extras.push({ name: "tdai:recall", order: 1000, text: result.prependContext });
|
|
81
|
-
if (result.appendSystemContext) extras.push({ name: "tdai:profile", order: 1001, text: result.appendSystemContext });
|
|
82
|
-
if (extras.length === 0) return assembly;
|
|
83
|
-
return { ...assembly, contexts: [...assembly.contexts, ...extras] };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
export { Config, apply, inject, name };
|
|
1
|
+
/**
|
|
2
|
+
* tdai-recall-inject — agent-plane recall injection row for dsh-tdai-memory.
|
|
3
|
+
*
|
|
4
|
+
* Mounted INSIDE an agent preset (agent.cordis.yml), this row listens on the
|
|
5
|
+
* `system-prompt/assemble` waterfall in the preset's scope, so it runs for
|
|
6
|
+
* every assembly of every agent joined to that preset (agent -> preset scope
|
|
7
|
+
* chain). It asks the `tdaiMemory` service (provided by the root-level
|
|
8
|
+
* dsh-tdai-memory plugin) for relevant long-term memories and appends them
|
|
9
|
+
* as dynamic context sections (user-role snapshots):
|
|
10
|
+
*
|
|
11
|
+
* - tdai:recall — relevant L1 memories for the current user message
|
|
12
|
+
* - tdai:profile — stable persona / scene navigation context
|
|
13
|
+
*
|
|
14
|
+
* The recall call is bounded by `recall.timeoutMs`; failures degrade to no
|
|
15
|
+
* injection and never break the assembly.
|
|
16
|
+
*/
|
|
17
|
+
import z from "@deepseek-ai/schemastery";
|
|
18
|
+
|
|
19
|
+
/** Cordis plugin name. */
|
|
20
|
+
const name = "tdai-recall-inject";
|
|
21
|
+
/** Services consumed: the prompt registry and the memory core service. */
|
|
22
|
+
const inject = ["systemPrompt", "tdaiMemory"];
|
|
23
|
+
|
|
24
|
+
/** Runtime schema for the recall-inject row. */
|
|
25
|
+
const Config = z.object({
|
|
26
|
+
/** Skip recall when the user message is shorter than this. */
|
|
27
|
+
minUserTextChars: z.number().default(1),
|
|
28
|
+
/** Per-session cache TTL for identical user text (ms). */
|
|
29
|
+
cacheTtlMs: z.number().default(30_000),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
function apply(ctx, config) {
|
|
33
|
+
const cache = new Map();
|
|
34
|
+
|
|
35
|
+
ctx.on("system-prompt/assemble", async (assembly, context) => {
|
|
36
|
+
try {
|
|
37
|
+
const agent = context?.agent;
|
|
38
|
+
if (!agent) return assembly;
|
|
39
|
+
const session = agent.session;
|
|
40
|
+
if (!session) return assembly;
|
|
41
|
+
// Only real human input triggers recall. DSH injects synthetic
|
|
42
|
+
// user-role context (system reminders, runtime context, skill
|
|
43
|
+
// catalogs, …) whose source.kind is not "user"; recalling on those
|
|
44
|
+
// would pollute the prompt with irrelevant memories (issue #1).
|
|
45
|
+
const lastUser = [...session.deriveMessages()]
|
|
46
|
+
.reverse()
|
|
47
|
+
.find((m) => m.role === "user" && (m.source == null || m.source.kind === "user"));
|
|
48
|
+
if (!lastUser) return assembly;
|
|
49
|
+
const text = extractText(lastUser.content);
|
|
50
|
+
if (!text || text.length < config.minUserTextChars) return assembly;
|
|
51
|
+
|
|
52
|
+
const now = Date.now();
|
|
53
|
+
const cached = cache.get(session.id);
|
|
54
|
+
if (cached && cached.text === text && now - cached.ts < config.cacheTtlMs) {
|
|
55
|
+
return injectRecall(assembly, cached.result);
|
|
56
|
+
}
|
|
57
|
+
const result = await ctx.tdaiMemory
|
|
58
|
+
.handleBeforeRecall(text, session.id)
|
|
59
|
+
.catch(() => null);
|
|
60
|
+
cache.set(session.id, { text, result: result ?? {}, ts: now });
|
|
61
|
+
return injectRecall(assembly, result ?? {});
|
|
62
|
+
} catch {
|
|
63
|
+
return assembly;
|
|
64
|
+
}
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function extractText(content) {
|
|
69
|
+
if (typeof content === "string") return content.trim();
|
|
70
|
+
if (!Array.isArray(content)) return "";
|
|
71
|
+
return content
|
|
72
|
+
.filter((b) => b && b.type === "text" && typeof b.text === "string")
|
|
73
|
+
.map((b) => b.text)
|
|
74
|
+
.join("\n")
|
|
75
|
+
.trim();
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function injectRecall(assembly, result) {
|
|
79
|
+
const extras = [];
|
|
80
|
+
if (result.prependContext) extras.push({ name: "tdai:recall", order: 1000, text: result.prependContext });
|
|
81
|
+
if (result.appendSystemContext) extras.push({ name: "tdai:profile", order: 1001, text: result.appendSystemContext });
|
|
82
|
+
if (extras.length === 0) return assembly;
|
|
83
|
+
return { ...assembly, contexts: [...assembly.contexts, ...extras] };
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export { Config, apply, inject, name };
|
|
@@ -1,15 +1,15 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* TDAI Adapters — barrel re-export for all host adapter implementations.
|
|
3
|
-
*
|
|
4
|
-
* Each adapter translates a specific host environment's API into
|
|
5
|
-
* the host-neutral HostAdapter interface consumed by TdaiCore.
|
|
6
|
-
*
|
|
7
|
-
* Directory structure:
|
|
8
|
-
* adapters/
|
|
9
|
-
* ├── openclaw/ — OpenClaw plugin host (in-process, runEmbeddedPiAgent)
|
|
10
|
-
* └── standalone/ — Gateway / Hermes sidecar (HTTP, OpenAI-compatible API)
|
|
11
|
-
*/
|
|
12
|
-
// OpenClaw adapter
|
|
13
|
-
export { OpenClawHostAdapter, OpenClawLLMRunner, OpenClawLLMRunnerFactory } from "./openclaw/index.js";
|
|
14
|
-
// Standalone adapter
|
|
15
|
-
export { StandaloneHostAdapter, StandaloneLLMRunner, StandaloneLLMRunnerFactory } from "./standalone/index.js";
|
|
1
|
+
/**
|
|
2
|
+
* TDAI Adapters — barrel re-export for all host adapter implementations.
|
|
3
|
+
*
|
|
4
|
+
* Each adapter translates a specific host environment's API into
|
|
5
|
+
* the host-neutral HostAdapter interface consumed by TdaiCore.
|
|
6
|
+
*
|
|
7
|
+
* Directory structure:
|
|
8
|
+
* adapters/
|
|
9
|
+
* ├── openclaw/ — OpenClaw plugin host (in-process, runEmbeddedPiAgent)
|
|
10
|
+
* └── standalone/ — Gateway / Hermes sidecar (HTTP, OpenAI-compatible API)
|
|
11
|
+
*/
|
|
12
|
+
// OpenClaw adapter
|
|
13
|
+
export { OpenClawHostAdapter, OpenClawLLMRunner, OpenClawLLMRunnerFactory } from "./openclaw/index.js";
|
|
14
|
+
// Standalone adapter
|
|
15
|
+
export { StandaloneHostAdapter, StandaloneLLMRunner, StandaloneLLMRunnerFactory } from "./standalone/index.js";
|
|
@@ -1,83 +1,83 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenClawHostAdapter — translates OpenClaw's plugin API into TDAI Core's
|
|
3
|
-
* unified HostAdapter interface.
|
|
4
|
-
*
|
|
5
|
-
* This is the "thin shell" that keeps OpenClaw-specific dependencies
|
|
6
|
-
* (OpenClawPluginApi, pluginConfig, resolveStateDir, event system)
|
|
7
|
-
* confined to the adapter layer while TDAI Core remains host-neutral.
|
|
8
|
-
*
|
|
9
|
-
* Usage (in index.ts):
|
|
10
|
-
* const adapter = new OpenClawHostAdapter({ api, pluginDataDir, config });
|
|
11
|
-
* const core = new TdaiCore({ hostAdapter: adapter, config: parsedConfig });
|
|
12
|
-
*/
|
|
13
|
-
import { OpenClawLLMRunnerFactory } from "./llm-runner.js";
|
|
14
|
-
// ============================
|
|
15
|
-
// OpenClawHostAdapter
|
|
16
|
-
// ============================
|
|
17
|
-
export class OpenClawHostAdapter {
|
|
18
|
-
hostType = "openclaw";
|
|
19
|
-
api;
|
|
20
|
-
pluginDataDir;
|
|
21
|
-
openclawConfig;
|
|
22
|
-
runnerFactory;
|
|
23
|
-
constructor(opts) {
|
|
24
|
-
this.api = opts.api;
|
|
25
|
-
this.pluginDataDir = opts.pluginDataDir;
|
|
26
|
-
this.openclawConfig = opts.openclawConfig;
|
|
27
|
-
this.runnerFactory = new OpenClawLLMRunnerFactory({
|
|
28
|
-
config: opts.openclawConfig,
|
|
29
|
-
agentRuntime: opts.api.runtime.agent,
|
|
30
|
-
logger: opts.api.logger,
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Build a RuntimeContext from the current OpenClaw session.
|
|
35
|
-
*
|
|
36
|
-
* In OpenClaw, sessionKey and sessionId come from the event/ctx objects
|
|
37
|
-
* passed to hooks. This method returns a context with sensible defaults;
|
|
38
|
-
* callers can override sessionKey/sessionId per-hook invocation using
|
|
39
|
-
* `buildRuntimeContextForSession()`.
|
|
40
|
-
*/
|
|
41
|
-
getRuntimeContext() {
|
|
42
|
-
return {
|
|
43
|
-
userId: "default_user",
|
|
44
|
-
sessionId: "",
|
|
45
|
-
sessionKey: "",
|
|
46
|
-
platform: "openclaw",
|
|
47
|
-
workspaceDir: process.cwd(),
|
|
48
|
-
dataDir: this.pluginDataDir,
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
/**
|
|
52
|
-
* Build a RuntimeContext for a specific session (used per-hook).
|
|
53
|
-
*
|
|
54
|
-
* This is an OpenClaw-specific convenience that merges session-level
|
|
55
|
-
* identifiers from hook ctx into the base context.
|
|
56
|
-
*/
|
|
57
|
-
buildRuntimeContextForSession(sessionKey, sessionId) {
|
|
58
|
-
return {
|
|
59
|
-
...this.getRuntimeContext(),
|
|
60
|
-
sessionKey,
|
|
61
|
-
sessionId: sessionId ?? "",
|
|
62
|
-
};
|
|
63
|
-
}
|
|
64
|
-
getLogger() {
|
|
65
|
-
return this.api.logger;
|
|
66
|
-
}
|
|
67
|
-
getLLMRunnerFactory() {
|
|
68
|
-
return this.runnerFactory;
|
|
69
|
-
}
|
|
70
|
-
// -- OpenClaw-specific accessors (for index.ts bridge) --------------------
|
|
71
|
-
/** Get the raw OpenClaw plugin API (for legacy callers during migration). */
|
|
72
|
-
getPluginApi() {
|
|
73
|
-
return this.api;
|
|
74
|
-
}
|
|
75
|
-
/** Get the OpenClaw config object (for legacy callers during migration). */
|
|
76
|
-
getOpenClawConfig() {
|
|
77
|
-
return this.openclawConfig;
|
|
78
|
-
}
|
|
79
|
-
/** Get the resolved plugin data directory. */
|
|
80
|
-
getPluginDataDir() {
|
|
81
|
-
return this.pluginDataDir;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
1
|
+
/**
|
|
2
|
+
* OpenClawHostAdapter — translates OpenClaw's plugin API into TDAI Core's
|
|
3
|
+
* unified HostAdapter interface.
|
|
4
|
+
*
|
|
5
|
+
* This is the "thin shell" that keeps OpenClaw-specific dependencies
|
|
6
|
+
* (OpenClawPluginApi, pluginConfig, resolveStateDir, event system)
|
|
7
|
+
* confined to the adapter layer while TDAI Core remains host-neutral.
|
|
8
|
+
*
|
|
9
|
+
* Usage (in index.ts):
|
|
10
|
+
* const adapter = new OpenClawHostAdapter({ api, pluginDataDir, config });
|
|
11
|
+
* const core = new TdaiCore({ hostAdapter: adapter, config: parsedConfig });
|
|
12
|
+
*/
|
|
13
|
+
import { OpenClawLLMRunnerFactory } from "./llm-runner.js";
|
|
14
|
+
// ============================
|
|
15
|
+
// OpenClawHostAdapter
|
|
16
|
+
// ============================
|
|
17
|
+
export class OpenClawHostAdapter {
|
|
18
|
+
hostType = "openclaw";
|
|
19
|
+
api;
|
|
20
|
+
pluginDataDir;
|
|
21
|
+
openclawConfig;
|
|
22
|
+
runnerFactory;
|
|
23
|
+
constructor(opts) {
|
|
24
|
+
this.api = opts.api;
|
|
25
|
+
this.pluginDataDir = opts.pluginDataDir;
|
|
26
|
+
this.openclawConfig = opts.openclawConfig;
|
|
27
|
+
this.runnerFactory = new OpenClawLLMRunnerFactory({
|
|
28
|
+
config: opts.openclawConfig,
|
|
29
|
+
agentRuntime: opts.api.runtime.agent,
|
|
30
|
+
logger: opts.api.logger,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* Build a RuntimeContext from the current OpenClaw session.
|
|
35
|
+
*
|
|
36
|
+
* In OpenClaw, sessionKey and sessionId come from the event/ctx objects
|
|
37
|
+
* passed to hooks. This method returns a context with sensible defaults;
|
|
38
|
+
* callers can override sessionKey/sessionId per-hook invocation using
|
|
39
|
+
* `buildRuntimeContextForSession()`.
|
|
40
|
+
*/
|
|
41
|
+
getRuntimeContext() {
|
|
42
|
+
return {
|
|
43
|
+
userId: "default_user",
|
|
44
|
+
sessionId: "",
|
|
45
|
+
sessionKey: "",
|
|
46
|
+
platform: "openclaw",
|
|
47
|
+
workspaceDir: process.cwd(),
|
|
48
|
+
dataDir: this.pluginDataDir,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Build a RuntimeContext for a specific session (used per-hook).
|
|
53
|
+
*
|
|
54
|
+
* This is an OpenClaw-specific convenience that merges session-level
|
|
55
|
+
* identifiers from hook ctx into the base context.
|
|
56
|
+
*/
|
|
57
|
+
buildRuntimeContextForSession(sessionKey, sessionId) {
|
|
58
|
+
return {
|
|
59
|
+
...this.getRuntimeContext(),
|
|
60
|
+
sessionKey,
|
|
61
|
+
sessionId: sessionId ?? "",
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
getLogger() {
|
|
65
|
+
return this.api.logger;
|
|
66
|
+
}
|
|
67
|
+
getLLMRunnerFactory() {
|
|
68
|
+
return this.runnerFactory;
|
|
69
|
+
}
|
|
70
|
+
// -- OpenClaw-specific accessors (for index.ts bridge) --------------------
|
|
71
|
+
/** Get the raw OpenClaw plugin API (for legacy callers during migration). */
|
|
72
|
+
getPluginApi() {
|
|
73
|
+
return this.api;
|
|
74
|
+
}
|
|
75
|
+
/** Get the OpenClaw config object (for legacy callers during migration). */
|
|
76
|
+
getOpenClawConfig() {
|
|
77
|
+
return this.openclawConfig;
|
|
78
|
+
}
|
|
79
|
+
/** Get the resolved plugin data directory. */
|
|
80
|
+
getPluginDataDir() {
|
|
81
|
+
return this.pluginDataDir;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* OpenClaw adapter — barrel exports.
|
|
3
|
-
*/
|
|
4
|
-
export { OpenClawHostAdapter } from "./host-adapter.js";
|
|
5
|
-
export { OpenClawLLMRunner, OpenClawLLMRunnerFactory } from "./llm-runner.js";
|
|
1
|
+
/**
|
|
2
|
+
* OpenClaw adapter — barrel exports.
|
|
3
|
+
*/
|
|
4
|
+
export { OpenClawHostAdapter } from "./host-adapter.js";
|
|
5
|
+
export { OpenClawLLMRunner, OpenClawLLMRunnerFactory } from "./llm-runner.js";
|