@dcrays/mbh-memory 0.1.0
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/README.md +5 -0
- package/cordis.patch.yml +6 -0
- package/package.json +39 -0
- package/plugin/index.d.ts +31 -0
- package/plugin/index.js +264 -0
- package/src/api-client.d.ts +30 -0
- package/src/api-client.js +131 -0
- package/src/config/env-config.d.ts +9 -0
- package/src/config/env-config.js +17 -0
- package/src/index.d.ts +4 -0
- package/src/index.js +4 -0
- package/src/tools.d.ts +3 -0
- package/src/tools.js +37 -0
package/README.md
ADDED
package/cordis.patch.yml
ADDED
package/package.json
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@dcrays/mbh-memory",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "DeepSeek Harness memory plugin for Mobook (production)",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "plugin/index.js",
|
|
7
|
+
"types": "plugin/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./plugin/index.d.ts",
|
|
11
|
+
"default": "./plugin/index.js"
|
|
12
|
+
},
|
|
13
|
+
"./cordis.patch.yml": "./cordis.patch.yml",
|
|
14
|
+
"./package.json": "./package.json"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"plugin/**",
|
|
18
|
+
"src/**",
|
|
19
|
+
"cordis.patch.yml",
|
|
20
|
+
"README.md",
|
|
21
|
+
"!**/*.map"
|
|
22
|
+
],
|
|
23
|
+
"dsh": {
|
|
24
|
+
"bundle": {
|
|
25
|
+
"patch": "./cordis.patch.yml"
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=24.0.0"
|
|
30
|
+
},
|
|
31
|
+
"peerDependencies": {
|
|
32
|
+
"@deepseek-ai/cordis": "^4.0.2",
|
|
33
|
+
"@deepseek-ai/dsh-tools": "^0.1.2-rc.1",
|
|
34
|
+
"@deepseek-ai/schemastery": "^3.18.1"
|
|
35
|
+
},
|
|
36
|
+
"publishConfig": {
|
|
37
|
+
"access": "public"
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Context } from '@deepseek-ai/cordis';
|
|
2
|
+
import { Service } from '@deepseek-ai/cordis';
|
|
3
|
+
import z from '@deepseek-ai/schemastery';
|
|
4
|
+
import { type MemoryApiValue } from '../src/index.js';
|
|
5
|
+
export { DEFAULT_BUILD_ENV } from '../src/config/env-config.js';
|
|
6
|
+
export declare const name = "mbh-memory";
|
|
7
|
+
export declare const inject: string[];
|
|
8
|
+
export interface Config {
|
|
9
|
+
enabled: boolean;
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
authorizationToken: string;
|
|
12
|
+
timeoutMs: number;
|
|
13
|
+
maxMemoryChars: number;
|
|
14
|
+
maxResponseBytes: number;
|
|
15
|
+
}
|
|
16
|
+
export declare const Config: z<Config>;
|
|
17
|
+
declare module '@deepseek-ai/cordis' {
|
|
18
|
+
interface Context {
|
|
19
|
+
mbhMemory: MbhMemoryService;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export declare class MbhMemoryService extends Service {
|
|
23
|
+
private readonly config;
|
|
24
|
+
private readonly operations;
|
|
25
|
+
constructor(ctx: Context, config: Config);
|
|
26
|
+
isEnabled(): boolean;
|
|
27
|
+
isConfigured(): boolean;
|
|
28
|
+
getMemory(signal?: AbortSignal): Promise<MemoryApiValue>;
|
|
29
|
+
setMemory(memory: string, signal?: AbortSignal): Promise<MemoryApiValue>;
|
|
30
|
+
}
|
|
31
|
+
export declare function apply(ctx: Context, config: Config): void;
|
package/plugin/index.js
ADDED
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
import { createRequire as __mobookCreateRequire } from 'node:module'; const require = __mobookCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// dist-npm/plugin/index.js
|
|
4
|
+
import { Service } from "@deepseek-ai/cordis";
|
|
5
|
+
import z from "@deepseek-ai/schemastery";
|
|
6
|
+
|
|
7
|
+
// dist-npm/src/api-client.js
|
|
8
|
+
var MemoryApiError = class extends Error {
|
|
9
|
+
code;
|
|
10
|
+
constructor(code, message, options) {
|
|
11
|
+
super(message, options);
|
|
12
|
+
this.code = code;
|
|
13
|
+
this.name = "MemoryApiError";
|
|
14
|
+
}
|
|
15
|
+
};
|
|
16
|
+
function endpoint(baseUrl, name2) {
|
|
17
|
+
let base;
|
|
18
|
+
try {
|
|
19
|
+
base = new URL(baseUrl.endsWith("/") ? baseUrl : `${baseUrl}/`);
|
|
20
|
+
} catch (error) {
|
|
21
|
+
throw new MemoryApiError("INVALID_CONFIG", "mbh-memory API baseUrl is invalid", { cause: error });
|
|
22
|
+
}
|
|
23
|
+
if (base.protocol !== "http:" && base.protocol !== "https:") {
|
|
24
|
+
throw new MemoryApiError("INVALID_CONFIG", "mbh-memory API baseUrl must use http or https");
|
|
25
|
+
}
|
|
26
|
+
return new URL(name2, base);
|
|
27
|
+
}
|
|
28
|
+
function requestSignal(signal, timeoutMs) {
|
|
29
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
30
|
+
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
31
|
+
}
|
|
32
|
+
async function responseText(response, maxBytes) {
|
|
33
|
+
const declared = Number(response.headers.get("content-length"));
|
|
34
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
35
|
+
await response.body?.cancel().catch(() => void 0);
|
|
36
|
+
throw new MemoryApiError("RESPONSE_TOO_LARGE", `mbh-memory API response exceeds ${maxBytes} bytes`);
|
|
37
|
+
}
|
|
38
|
+
if (!response.body)
|
|
39
|
+
return "";
|
|
40
|
+
const reader = response.body.getReader();
|
|
41
|
+
const decoder = new TextDecoder();
|
|
42
|
+
let size = 0;
|
|
43
|
+
let text = "";
|
|
44
|
+
try {
|
|
45
|
+
for (; ; ) {
|
|
46
|
+
const item = await reader.read();
|
|
47
|
+
if (item.done)
|
|
48
|
+
break;
|
|
49
|
+
size += item.value.byteLength;
|
|
50
|
+
if (size > maxBytes) {
|
|
51
|
+
await reader.cancel().catch(() => void 0);
|
|
52
|
+
throw new MemoryApiError("RESPONSE_TOO_LARGE", `mbh-memory API response exceeds ${maxBytes} bytes`);
|
|
53
|
+
}
|
|
54
|
+
text += decoder.decode(item.value, { stream: true });
|
|
55
|
+
}
|
|
56
|
+
return text + decoder.decode();
|
|
57
|
+
} finally {
|
|
58
|
+
reader.releaseLock();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function jsonValue(text) {
|
|
62
|
+
if (!text.trim())
|
|
63
|
+
return { ok: true };
|
|
64
|
+
try {
|
|
65
|
+
return JSON.parse(text);
|
|
66
|
+
} catch (error) {
|
|
67
|
+
throw new MemoryApiError("INVALID_RESPONSE", "mbh-memory API returned invalid JSON", { cause: error });
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
var MemoryApiClient = class {
|
|
71
|
+
config;
|
|
72
|
+
fetchImpl;
|
|
73
|
+
getUrl;
|
|
74
|
+
setUrl;
|
|
75
|
+
constructor(config, fetchImpl = fetch) {
|
|
76
|
+
this.config = config;
|
|
77
|
+
this.fetchImpl = fetchImpl;
|
|
78
|
+
if (!Number.isSafeInteger(config.timeoutMs) || config.timeoutMs <= 0) {
|
|
79
|
+
throw new MemoryApiError("INVALID_CONFIG", "mbh-memory timeoutMs must be a positive integer");
|
|
80
|
+
}
|
|
81
|
+
if (!Number.isSafeInteger(config.maxMemoryChars) || config.maxMemoryChars <= 0) {
|
|
82
|
+
throw new MemoryApiError("INVALID_CONFIG", "mbh-memory maxMemoryChars must be a positive integer");
|
|
83
|
+
}
|
|
84
|
+
if (!Number.isSafeInteger(config.maxResponseBytes) || config.maxResponseBytes <= 0) {
|
|
85
|
+
throw new MemoryApiError("INVALID_CONFIG", "mbh-memory maxResponseBytes must be a positive integer");
|
|
86
|
+
}
|
|
87
|
+
this.getUrl = endpoint(config.baseUrl, "get_memory");
|
|
88
|
+
this.setUrl = endpoint(config.baseUrl, "set_memory");
|
|
89
|
+
}
|
|
90
|
+
getMemory(signal) {
|
|
91
|
+
return this.request(this.getUrl, {}, signal);
|
|
92
|
+
}
|
|
93
|
+
async setMemory(memory, signal) {
|
|
94
|
+
if (!memory.trim())
|
|
95
|
+
throw new MemoryApiError("INVALID_MEMORY", "memory must not be empty");
|
|
96
|
+
if (memory.length > this.config.maxMemoryChars) {
|
|
97
|
+
throw new MemoryApiError("INVALID_MEMORY", `memory exceeds ${this.config.maxMemoryChars} characters`);
|
|
98
|
+
}
|
|
99
|
+
return this.request(this.setUrl, { memory }, signal);
|
|
100
|
+
}
|
|
101
|
+
async request(url, body, signal) {
|
|
102
|
+
const headers = {
|
|
103
|
+
accept: "application/json",
|
|
104
|
+
"content-type": "application/json"
|
|
105
|
+
};
|
|
106
|
+
const token = this.config.authorizationToken?.trim();
|
|
107
|
+
if (token)
|
|
108
|
+
headers.authorization = `Bearer ${token}`;
|
|
109
|
+
const effectiveSignal = requestSignal(signal, this.config.timeoutMs);
|
|
110
|
+
let response;
|
|
111
|
+
try {
|
|
112
|
+
response = await this.fetchImpl(url, {
|
|
113
|
+
method: "POST",
|
|
114
|
+
headers,
|
|
115
|
+
body: JSON.stringify(body),
|
|
116
|
+
signal: effectiveSignal
|
|
117
|
+
});
|
|
118
|
+
} catch (error) {
|
|
119
|
+
if (signal?.aborted)
|
|
120
|
+
throw error;
|
|
121
|
+
if (effectiveSignal.aborted) {
|
|
122
|
+
throw new MemoryApiError("TIMEOUT", `mbh-memory API request timed out after ${this.config.timeoutMs}ms`, {
|
|
123
|
+
cause: error
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
throw new MemoryApiError("NETWORK_ERROR", "mbh-memory API request failed", { cause: error });
|
|
127
|
+
}
|
|
128
|
+
const text = await responseText(response, this.config.maxResponseBytes);
|
|
129
|
+
if (!response.ok) {
|
|
130
|
+
throw new MemoryApiError("HTTP_ERROR", `mbh-memory API request failed with HTTP ${response.status}`);
|
|
131
|
+
}
|
|
132
|
+
return jsonValue(text);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
// dist-npm/src/tools.js
|
|
137
|
+
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
138
|
+
function render(value) {
|
|
139
|
+
return typeof value === "string" ? value : JSON.stringify(value);
|
|
140
|
+
}
|
|
141
|
+
function createMemoryTools(memory) {
|
|
142
|
+
const getMemory = defineTool({
|
|
143
|
+
name: "get_memory",
|
|
144
|
+
description: "Read the user's long-term memory from the configured Mobook memory API. Use it only when prior preferences, facts, or experience are relevant to the current request.",
|
|
145
|
+
parameters: {},
|
|
146
|
+
output: {
|
|
147
|
+
schema: { type: "json" },
|
|
148
|
+
render: (_args, value) => [{ type: "text", text: render(value) }]
|
|
149
|
+
},
|
|
150
|
+
async execute(_args, exec) {
|
|
151
|
+
return memory.getMemory(exec.signal);
|
|
152
|
+
}
|
|
153
|
+
});
|
|
154
|
+
const setMemory = defineTool({
|
|
155
|
+
name: "set_memory",
|
|
156
|
+
description: "Write one durable memory through the configured Mobook memory API. Use this when the user explicitly asks you to remember something useful for future conversations. Never store passwords, access tokens, API keys, or other secrets.",
|
|
157
|
+
parameters: {
|
|
158
|
+
memory: {
|
|
159
|
+
type: "string",
|
|
160
|
+
required: true,
|
|
161
|
+
description: "A concise, self-contained fact, preference, rule, or reusable experience to remember."
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
output: {
|
|
165
|
+
schema: { type: "json" },
|
|
166
|
+
render: (_args, value) => [{ type: "text", text: render(value) }]
|
|
167
|
+
},
|
|
168
|
+
async execute(args, exec) {
|
|
169
|
+
return memory.setMemory(args.memory, exec.signal);
|
|
170
|
+
}
|
|
171
|
+
});
|
|
172
|
+
return [getMemory, setMemory];
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// dist-npm/src/index.js
|
|
176
|
+
var MBH_MEMORY_SERVICE_KEY = "mbhMemory";
|
|
177
|
+
|
|
178
|
+
// dist-npm/src/config/env-config.js
|
|
179
|
+
var ENV_CONFIG = {
|
|
180
|
+
production: {
|
|
181
|
+
packageName: "@dcrays/mbh-memory",
|
|
182
|
+
channelId: "mbh-memory"
|
|
183
|
+
},
|
|
184
|
+
test: {
|
|
185
|
+
packageName: "@dcrays/mbh-memory-test",
|
|
186
|
+
channelId: "mbh-memory-test"
|
|
187
|
+
},
|
|
188
|
+
uat: {
|
|
189
|
+
packageName: "@dcrays/mbh-memory-uat",
|
|
190
|
+
channelId: "mbh-memory-uat"
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
var DEFAULT_BUILD_ENV = "production";
|
|
194
|
+
var CONFIG_CHANNEL_ID = ENV_CONFIG[DEFAULT_BUILD_ENV].channelId;
|
|
195
|
+
var CONFIG_PLUGIN_PATH = `plugins.${CONFIG_CHANNEL_ID}`;
|
|
196
|
+
|
|
197
|
+
// dist-npm/plugin/index.js
|
|
198
|
+
var name = "@dcrays/mbh-memory";
|
|
199
|
+
var inject = ["tools"];
|
|
200
|
+
var Config = z.object({
|
|
201
|
+
enabled: z.boolean().default(true),
|
|
202
|
+
baseUrl: z.string().default(""),
|
|
203
|
+
authorizationToken: z.string().role("secret").default(""),
|
|
204
|
+
timeoutMs: z.natural().min(1).default(1e4),
|
|
205
|
+
maxMemoryChars: z.natural().min(1).default(2e4),
|
|
206
|
+
maxResponseBytes: z.natural().min(1).default(128 * 1024)
|
|
207
|
+
});
|
|
208
|
+
var MbhMemoryService = class extends Service {
|
|
209
|
+
config;
|
|
210
|
+
operations;
|
|
211
|
+
constructor(ctx, config) {
|
|
212
|
+
super(ctx, MBH_MEMORY_SERVICE_KEY);
|
|
213
|
+
this.config = config;
|
|
214
|
+
this.operations = config.enabled && config.baseUrl.trim() ? new MemoryApiClient({
|
|
215
|
+
baseUrl: config.baseUrl.trim(),
|
|
216
|
+
...config.authorizationToken.trim() ? { authorizationToken: config.authorizationToken.trim() } : {},
|
|
217
|
+
timeoutMs: config.timeoutMs,
|
|
218
|
+
maxMemoryChars: config.maxMemoryChars,
|
|
219
|
+
maxResponseBytes: config.maxResponseBytes
|
|
220
|
+
}) : void 0;
|
|
221
|
+
}
|
|
222
|
+
isEnabled() {
|
|
223
|
+
return this.config.enabled;
|
|
224
|
+
}
|
|
225
|
+
isConfigured() {
|
|
226
|
+
return this.operations !== void 0;
|
|
227
|
+
}
|
|
228
|
+
getMemory(signal) {
|
|
229
|
+
if (!this.operations)
|
|
230
|
+
return Promise.reject(new Error("mbh-memory API is not configured"));
|
|
231
|
+
return this.operations.getMemory(signal);
|
|
232
|
+
}
|
|
233
|
+
setMemory(memory, signal) {
|
|
234
|
+
if (!this.operations)
|
|
235
|
+
return Promise.reject(new Error("mbh-memory API is not configured"));
|
|
236
|
+
return this.operations.setMemory(memory, signal);
|
|
237
|
+
}
|
|
238
|
+
};
|
|
239
|
+
function apply(ctx, config) {
|
|
240
|
+
const service = new MbhMemoryService(ctx, config);
|
|
241
|
+
ctx.effect(() => {
|
|
242
|
+
const logger = ctx.logger(name);
|
|
243
|
+
if (!service.isEnabled())
|
|
244
|
+
return () => void 0;
|
|
245
|
+
if (!service.isConfigured()) {
|
|
246
|
+
logger.info("%s plugin loaded without an API baseUrl; memory tools are unavailable", name);
|
|
247
|
+
return () => void 0;
|
|
248
|
+
}
|
|
249
|
+
const unregister = createMemoryTools(service).map((tool) => ctx.tools.register(tool));
|
|
250
|
+
logger.info("%s plugin loaded with get_memory and set_memory tools", name);
|
|
251
|
+
return () => {
|
|
252
|
+
for (const dispose of unregister.reverse())
|
|
253
|
+
dispose();
|
|
254
|
+
};
|
|
255
|
+
}, `${name} lifecycle`);
|
|
256
|
+
}
|
|
257
|
+
export {
|
|
258
|
+
Config,
|
|
259
|
+
DEFAULT_BUILD_ENV,
|
|
260
|
+
MbhMemoryService,
|
|
261
|
+
apply,
|
|
262
|
+
inject,
|
|
263
|
+
name
|
|
264
|
+
};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export interface MemoryApiClientConfig {
|
|
2
|
+
baseUrl: string;
|
|
3
|
+
authorizationToken?: string;
|
|
4
|
+
timeoutMs: number;
|
|
5
|
+
maxMemoryChars: number;
|
|
6
|
+
maxResponseBytes: number;
|
|
7
|
+
}
|
|
8
|
+
export type MemoryApiValue = null | boolean | number | string | MemoryApiValue[] | {
|
|
9
|
+
[key: string]: MemoryApiValue;
|
|
10
|
+
};
|
|
11
|
+
export interface MemoryOperations {
|
|
12
|
+
getMemory(signal?: AbortSignal): Promise<MemoryApiValue>;
|
|
13
|
+
setMemory(memory: string, signal?: AbortSignal): Promise<MemoryApiValue>;
|
|
14
|
+
}
|
|
15
|
+
export declare class MemoryApiError extends Error {
|
|
16
|
+
readonly code: 'INVALID_CONFIG' | 'INVALID_MEMORY' | 'HTTP_ERROR' | 'INVALID_RESPONSE' | 'RESPONSE_TOO_LARGE' | 'NETWORK_ERROR' | 'TIMEOUT';
|
|
17
|
+
constructor(code: 'INVALID_CONFIG' | 'INVALID_MEMORY' | 'HTTP_ERROR' | 'INVALID_RESPONSE' | 'RESPONSE_TOO_LARGE' | 'NETWORK_ERROR' | 'TIMEOUT', message: string, options?: ErrorOptions);
|
|
18
|
+
}
|
|
19
|
+
type Fetch = (input: string | URL | Request, init?: RequestInit) => Promise<Response>;
|
|
20
|
+
export declare class MemoryApiClient implements MemoryOperations {
|
|
21
|
+
private readonly config;
|
|
22
|
+
private readonly fetchImpl;
|
|
23
|
+
private readonly getUrl;
|
|
24
|
+
private readonly setUrl;
|
|
25
|
+
constructor(config: MemoryApiClientConfig, fetchImpl?: Fetch);
|
|
26
|
+
getMemory(signal?: AbortSignal): Promise<MemoryApiValue>;
|
|
27
|
+
setMemory(memory: string, signal?: AbortSignal): Promise<MemoryApiValue>;
|
|
28
|
+
private request;
|
|
29
|
+
}
|
|
30
|
+
export {};
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
export class MemoryApiError extends Error {
|
|
2
|
+
code;
|
|
3
|
+
constructor(code, message, options) {
|
|
4
|
+
super(message, options);
|
|
5
|
+
this.code = code;
|
|
6
|
+
this.name = 'MemoryApiError';
|
|
7
|
+
}
|
|
8
|
+
}
|
|
9
|
+
function endpoint(baseUrl, name) {
|
|
10
|
+
let base;
|
|
11
|
+
try {
|
|
12
|
+
base = new URL(baseUrl.endsWith('/') ? baseUrl : `${baseUrl}/`);
|
|
13
|
+
}
|
|
14
|
+
catch (error) {
|
|
15
|
+
throw new MemoryApiError('INVALID_CONFIG', 'mbh-memory API baseUrl is invalid', { cause: error });
|
|
16
|
+
}
|
|
17
|
+
if (base.protocol !== 'http:' && base.protocol !== 'https:') {
|
|
18
|
+
throw new MemoryApiError('INVALID_CONFIG', 'mbh-memory API baseUrl must use http or https');
|
|
19
|
+
}
|
|
20
|
+
return new URL(name, base);
|
|
21
|
+
}
|
|
22
|
+
function requestSignal(signal, timeoutMs) {
|
|
23
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
24
|
+
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
25
|
+
}
|
|
26
|
+
async function responseText(response, maxBytes) {
|
|
27
|
+
const declared = Number(response.headers.get('content-length'));
|
|
28
|
+
if (Number.isFinite(declared) && declared > maxBytes) {
|
|
29
|
+
await response.body?.cancel().catch(() => undefined);
|
|
30
|
+
throw new MemoryApiError('RESPONSE_TOO_LARGE', `mbh-memory API response exceeds ${maxBytes} bytes`);
|
|
31
|
+
}
|
|
32
|
+
if (!response.body)
|
|
33
|
+
return '';
|
|
34
|
+
const reader = response.body.getReader();
|
|
35
|
+
const decoder = new TextDecoder();
|
|
36
|
+
let size = 0;
|
|
37
|
+
let text = '';
|
|
38
|
+
try {
|
|
39
|
+
for (;;) {
|
|
40
|
+
const item = await reader.read();
|
|
41
|
+
if (item.done)
|
|
42
|
+
break;
|
|
43
|
+
size += item.value.byteLength;
|
|
44
|
+
if (size > maxBytes) {
|
|
45
|
+
await reader.cancel().catch(() => undefined);
|
|
46
|
+
throw new MemoryApiError('RESPONSE_TOO_LARGE', `mbh-memory API response exceeds ${maxBytes} bytes`);
|
|
47
|
+
}
|
|
48
|
+
text += decoder.decode(item.value, { stream: true });
|
|
49
|
+
}
|
|
50
|
+
return text + decoder.decode();
|
|
51
|
+
}
|
|
52
|
+
finally {
|
|
53
|
+
reader.releaseLock();
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
function jsonValue(text) {
|
|
57
|
+
if (!text.trim())
|
|
58
|
+
return { ok: true };
|
|
59
|
+
try {
|
|
60
|
+
return JSON.parse(text);
|
|
61
|
+
}
|
|
62
|
+
catch (error) {
|
|
63
|
+
throw new MemoryApiError('INVALID_RESPONSE', 'mbh-memory API returned invalid JSON', { cause: error });
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export class MemoryApiClient {
|
|
67
|
+
config;
|
|
68
|
+
fetchImpl;
|
|
69
|
+
getUrl;
|
|
70
|
+
setUrl;
|
|
71
|
+
constructor(config, fetchImpl = fetch) {
|
|
72
|
+
this.config = config;
|
|
73
|
+
this.fetchImpl = fetchImpl;
|
|
74
|
+
if (!Number.isSafeInteger(config.timeoutMs) || config.timeoutMs <= 0) {
|
|
75
|
+
throw new MemoryApiError('INVALID_CONFIG', 'mbh-memory timeoutMs must be a positive integer');
|
|
76
|
+
}
|
|
77
|
+
if (!Number.isSafeInteger(config.maxMemoryChars) || config.maxMemoryChars <= 0) {
|
|
78
|
+
throw new MemoryApiError('INVALID_CONFIG', 'mbh-memory maxMemoryChars must be a positive integer');
|
|
79
|
+
}
|
|
80
|
+
if (!Number.isSafeInteger(config.maxResponseBytes) || config.maxResponseBytes <= 0) {
|
|
81
|
+
throw new MemoryApiError('INVALID_CONFIG', 'mbh-memory maxResponseBytes must be a positive integer');
|
|
82
|
+
}
|
|
83
|
+
this.getUrl = endpoint(config.baseUrl, 'get_memory');
|
|
84
|
+
this.setUrl = endpoint(config.baseUrl, 'set_memory');
|
|
85
|
+
}
|
|
86
|
+
getMemory(signal) {
|
|
87
|
+
return this.request(this.getUrl, {}, signal);
|
|
88
|
+
}
|
|
89
|
+
async setMemory(memory, signal) {
|
|
90
|
+
if (!memory.trim())
|
|
91
|
+
throw new MemoryApiError('INVALID_MEMORY', 'memory must not be empty');
|
|
92
|
+
if (memory.length > this.config.maxMemoryChars) {
|
|
93
|
+
throw new MemoryApiError('INVALID_MEMORY', `memory exceeds ${this.config.maxMemoryChars} characters`);
|
|
94
|
+
}
|
|
95
|
+
return this.request(this.setUrl, { memory }, signal);
|
|
96
|
+
}
|
|
97
|
+
async request(url, body, signal) {
|
|
98
|
+
const headers = {
|
|
99
|
+
accept: 'application/json',
|
|
100
|
+
'content-type': 'application/json'
|
|
101
|
+
};
|
|
102
|
+
const token = this.config.authorizationToken?.trim();
|
|
103
|
+
if (token)
|
|
104
|
+
headers.authorization = `Bearer ${token}`;
|
|
105
|
+
const effectiveSignal = requestSignal(signal, this.config.timeoutMs);
|
|
106
|
+
let response;
|
|
107
|
+
try {
|
|
108
|
+
response = await this.fetchImpl(url, {
|
|
109
|
+
method: 'POST',
|
|
110
|
+
headers,
|
|
111
|
+
body: JSON.stringify(body),
|
|
112
|
+
signal: effectiveSignal
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
catch (error) {
|
|
116
|
+
if (signal?.aborted)
|
|
117
|
+
throw error;
|
|
118
|
+
if (effectiveSignal.aborted) {
|
|
119
|
+
throw new MemoryApiError('TIMEOUT', `mbh-memory API request timed out after ${this.config.timeoutMs}ms`, {
|
|
120
|
+
cause: error
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
throw new MemoryApiError('NETWORK_ERROR', 'mbh-memory API request failed', { cause: error });
|
|
124
|
+
}
|
|
125
|
+
const text = await responseText(response, this.config.maxResponseBytes);
|
|
126
|
+
if (!response.ok) {
|
|
127
|
+
throw new MemoryApiError('HTTP_ERROR', `mbh-memory API request failed with HTTP ${response.status}`);
|
|
128
|
+
}
|
|
129
|
+
return jsonValue(text);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export type BuildEnv = 'production' | 'test' | 'uat';
|
|
2
|
+
export interface EnvConfig {
|
|
3
|
+
packageName: string;
|
|
4
|
+
channelId: string;
|
|
5
|
+
}
|
|
6
|
+
export declare const ENV_CONFIG: Record<BuildEnv, EnvConfig>;
|
|
7
|
+
export declare const DEFAULT_BUILD_ENV: BuildEnv;
|
|
8
|
+
export declare const CONFIG_CHANNEL_ID: string;
|
|
9
|
+
export declare const CONFIG_PLUGIN_PATH: `plugins.${string}`;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export const ENV_CONFIG = {
|
|
2
|
+
production: {
|
|
3
|
+
packageName: '@dcrays/mbh-memory',
|
|
4
|
+
channelId: 'mbh-memory'
|
|
5
|
+
},
|
|
6
|
+
test: {
|
|
7
|
+
packageName: '@dcrays/mbh-memory-test',
|
|
8
|
+
channelId: 'mbh-memory-test'
|
|
9
|
+
},
|
|
10
|
+
uat: {
|
|
11
|
+
packageName: '@dcrays/mbh-memory-uat',
|
|
12
|
+
channelId: 'mbh-memory-uat'
|
|
13
|
+
}
|
|
14
|
+
};
|
|
15
|
+
export const DEFAULT_BUILD_ENV = 'production';
|
|
16
|
+
export const CONFIG_CHANNEL_ID = ENV_CONFIG[DEFAULT_BUILD_ENV].channelId;
|
|
17
|
+
export const CONFIG_PLUGIN_PATH = `plugins.${CONFIG_CHANNEL_ID}`;
|
package/src/index.d.ts
ADDED
package/src/index.js
ADDED
package/src/tools.d.ts
ADDED
package/src/tools.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { defineTool } from '@deepseek-ai/dsh-tools';
|
|
2
|
+
function render(value) {
|
|
3
|
+
return typeof value === 'string' ? value : JSON.stringify(value);
|
|
4
|
+
}
|
|
5
|
+
export function createMemoryTools(memory) {
|
|
6
|
+
const getMemory = defineTool({
|
|
7
|
+
name: 'get_memory',
|
|
8
|
+
description: "Read the user's long-term memory from the configured Mobook memory API. Use it only when prior preferences, facts, or experience are relevant to the current request.",
|
|
9
|
+
parameters: {},
|
|
10
|
+
output: {
|
|
11
|
+
schema: { type: 'json' },
|
|
12
|
+
render: (_args, value) => [{ type: 'text', text: render(value) }]
|
|
13
|
+
},
|
|
14
|
+
async execute(_args, exec) {
|
|
15
|
+
return memory.getMemory(exec.signal);
|
|
16
|
+
}
|
|
17
|
+
});
|
|
18
|
+
const setMemory = defineTool({
|
|
19
|
+
name: 'set_memory',
|
|
20
|
+
description: 'Write one durable memory through the configured Mobook memory API. Use this when the user explicitly asks you to remember something useful for future conversations. Never store passwords, access tokens, API keys, or other secrets.',
|
|
21
|
+
parameters: {
|
|
22
|
+
memory: {
|
|
23
|
+
type: 'string',
|
|
24
|
+
required: true,
|
|
25
|
+
description: 'A concise, self-contained fact, preference, rule, or reusable experience to remember.'
|
|
26
|
+
}
|
|
27
|
+
},
|
|
28
|
+
output: {
|
|
29
|
+
schema: { type: 'json' },
|
|
30
|
+
render: (_args, value) => [{ type: 'text', text: render(value) }]
|
|
31
|
+
},
|
|
32
|
+
async execute(args, exec) {
|
|
33
|
+
return memory.setMemory(args.memory, exec.signal);
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
return [getMemory, setMemory];
|
|
37
|
+
}
|