@memorax/memorax-code 0.1.7 → 0.1.9
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 +36 -5
- package/bin/memorax-code-setup.mjs +0 -3
- package/docs/configuration.md +1 -3
- package/docs/troubleshooting.md +9 -8
- package/lib/memorax-code-backend/dist/app/backend-server.js +2 -29
- package/lib/memorax-code-backend/dist/app/memory-observability.js +1 -4
- package/lib/memorax-code-backend/dist/app/state.js +1 -12
- package/lib/memorax-code-backend/dist/clients/claude/transcript-turn.js +0 -182
- package/lib/memorax-code-backend/dist/clients/codex/rollout-turn.js +52 -2
- package/lib/memorax-code-backend/dist/clients/opencode/message-turn.js +68 -2
- package/lib/memorax-code-backend/dist/lifecycle/backend/service.js +0 -5
- package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +1 -14
- package/lib/memorax-code-backend/dist/memory/automatic-writeback.js +8 -5
- package/lib/memorax-code-backend/dist/memory/project.js +4 -26
- package/lib/memorax-code-backend/dist/memory/quota-notice.js +79 -19
- package/lib/memorax-code-backend/dist/provider/memorax/adapter.js +1 -4
- package/lib/memorax-code-backend/dist/provider/memorax/http.js +0 -28
- package/lib/memorax-code-backend/dist/transport/http/request.js +1 -39
- package/lib/memorax-code-backend/package.json +2 -3
- package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +0 -6
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +1 -1
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +0 -6
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +1 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +1 -1
- package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
- package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
- package/lib/memorax-code-codex-adapter/package.json +1 -1
- package/lib/memorax-code-codex-adapter/skills/memorax-code/SKILL.md +0 -6
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +1 -1
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +1 -1
- package/lib/memorax-code-dsh-adapter/package.json +1 -1
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/SKILL.md +0 -6
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-add.md +1 -1
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-search.md +1 -1
- package/lib/memorax-code-opencode-adapter/package.json +1 -1
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/SKILL.md +0 -6
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-add.md +1 -1
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-search.md +1 -1
- package/lib/memorax-code-opencode-adapter/src/plugin.mjs +121 -16
- package/lib/memorax-code-opencode-adapter/src/repo-memory-server-runner.mjs +2 -0
- package/package.json +1 -1
- package/lib/memorax-code-backend/dist/clients/codex/effective-prompt.js +0 -54
- package/lib/memorax-code-backend/dist/memory/writeback-reconciler.js +0 -244
- package/lib/memorax-code-backend/dist/memory/writeback-task-projection.js +0 -377
- package/lib/memorax-code-backend/dist/repository/readiness.js +0 -188
- package/lib/memorax-code-backend/dist/shared/incremental-jsonl-projection.js +0 -271
- package/lib/memorax-code-backend/dist/viewer/history/claude-transcript.js +0 -648
- package/lib/memorax-code-backend/dist/viewer/history/session-title.js +0 -93
- package/lib/memorax-code-backend/dist/viewer/http/public-routes.js +0 -146
- package/lib/memorax-code-backend/dist/viewer/model.js +0 -1
- package/lib/memorax-code-backend/dist/viewer/projection/activity.js +0 -113
- package/lib/memorax-code-backend/dist/viewer/projection/cache.js +0 -69
- package/lib/memorax-code-backend/dist/viewer/projection/event-identity.js +0 -3
- package/lib/memorax-code-backend/dist/viewer/projection/history.js +0 -79
- package/lib/memorax-code-backend/dist/viewer/projection/observability.js +0 -8
- package/lib/memorax-code-backend/dist/viewer/projection/redaction.js +0 -52
- package/lib/memorax-code-backend/dist/viewer/projection/turn-reference.js +0 -12
- package/lib/memorax-code-backend/dist/viewer/projection/user.js +0 -168
- package/lib/memorax-code-backend/dist/viewer/projection/writeback-status.js +0 -97
- package/lib/memorax-code-backend/dist/viewer/store.js +0 -991
- package/lib/memorax-code-backend/dist/viewer/ui/icon.js +0 -1
- package/lib/memorax-code-backend/dist/viewer/ui/logo.js +0 -1
- package/lib/memorax-code-backend/dist/viewer/ui/user-html.js +0 -117
|
@@ -276,11 +276,7 @@ async function executeMemoraxCodeStart(serviceOptions, argv, backendUrl, memorax
|
|
|
276
276
|
...(opencodeAdapter ? { opencodeAdapter } : {}),
|
|
277
277
|
};
|
|
278
278
|
}
|
|
279
|
-
const
|
|
280
|
-
...serviceOptions,
|
|
281
|
-
claudeProjectsRoot: claudeProjectsRootForStart(argv, claudeAdapter),
|
|
282
|
-
};
|
|
283
|
-
const backend = await startBackendService(backendStartOptions);
|
|
279
|
+
const backend = await startBackendService(serviceOptions);
|
|
284
280
|
if (!backend.ok) {
|
|
285
281
|
const disabledCodex = codexAdapter
|
|
286
282
|
? await codexAdapterLifecycle.disable({ argv, serviceOptions })
|
|
@@ -314,15 +310,6 @@ async function executeMemoraxCodeStart(serviceOptions, argv, backendUrl, memorax
|
|
|
314
310
|
...(opencodeAdapter ? { opencodeAdapter } : {}),
|
|
315
311
|
};
|
|
316
312
|
}
|
|
317
|
-
function claudeProjectsRootForStart(argv, claudeAdapter) {
|
|
318
|
-
if (claudeAdapter?.ok === false || claudeAdapter?.enabled !== true)
|
|
319
|
-
return false;
|
|
320
|
-
const claudeHome = argValue(argv, "--claude-home")?.trim()
|
|
321
|
-
|| process.env.CLAUDE_CONFIG_DIR?.trim()
|
|
322
|
-
|| process.env.CLAUDE_HOME?.trim()
|
|
323
|
-
|| join(homedir(), ".claude");
|
|
324
|
-
return join(resolve(claudeHome), "projects");
|
|
325
|
-
}
|
|
326
313
|
function expectedBackendUrl(serviceOptions) {
|
|
327
314
|
return preflightBackendServiceStart(serviceOptions).url;
|
|
328
315
|
}
|
|
@@ -2,7 +2,7 @@ import { createHash } from "node:crypto";
|
|
|
2
2
|
import { createMemoryWritebackBufferRuntime, } from "./writeback-buffer.js";
|
|
3
3
|
import { memoryWritebackAddParts, } from "./writeback-chunk.js";
|
|
4
4
|
import { invokeMemoraxMemoryProvider, } from "../provider/memorax/adapter.js";
|
|
5
|
-
import { memoryWritebackBufferEnabled, memoryWritebackEnabled, memoryWritebackMaxMessageChars, } from "../provider/memorax/config.js";
|
|
5
|
+
import { memoraxConfigFromEnv, memoryWritebackBufferEnabled, memoryWritebackEnabled, memoryWritebackMaxMessageChars, } from "../provider/memorax/config.js";
|
|
6
6
|
import { hasMeaningfulMemoryPayloadText, redactMemoryPayloadText, } from "./payload-redaction.js";
|
|
7
7
|
const WRITEBACK_DEDUPE_TTL_MS = 5 * 60 * 1000;
|
|
8
8
|
const WRITEBACK_DEDUPE_MAX = 1024;
|
|
@@ -190,6 +190,7 @@ async function enqueueAutomaticMemoryWritebackAsync(state, decision, options) {
|
|
|
190
190
|
const parts = memoryWritebackAddParts(decision, options.env ?? process.env);
|
|
191
191
|
for (const [index, part] of parts.entries()) {
|
|
192
192
|
for (let attempt = 1; attempt <= AUTOMATIC_MEMORY_WRITEBACK_MAX_ATTEMPTS; attempt += 1) {
|
|
193
|
+
const configResult = memoraxConfigFromEnv(options.env);
|
|
193
194
|
const response = await invokeMemoraxMemoryProvider({
|
|
194
195
|
sessionId: decision.sessionKey,
|
|
195
196
|
prompt: part.messages.find((message) => message.role === "user")?.content ?? "",
|
|
@@ -208,6 +209,7 @@ async function enqueueAutomaticMemoryWritebackAsync(state, decision, options) {
|
|
|
208
209
|
...(part.chunk ? { chunk: part.chunk } : {}),
|
|
209
210
|
},
|
|
210
211
|
}, {
|
|
212
|
+
...(configResult.ok ? { config: configResult.config } : {}),
|
|
211
213
|
env: options.env,
|
|
212
214
|
fetchImpl: options.fetchImpl,
|
|
213
215
|
observability: options.memoryObservability,
|
|
@@ -245,8 +247,9 @@ async function enqueueAutomaticMemoryWritebackAsync(state, decision, options) {
|
|
|
245
247
|
httpStatus: response.ok ? undefined : response.httpStatus,
|
|
246
248
|
});
|
|
247
249
|
if (response.ok) {
|
|
248
|
-
if (response.result.quota)
|
|
249
|
-
queueQuotaNoticeBestEffort(state, response.result.quota);
|
|
250
|
+
if (response.result.quota && configResult.ok) {
|
|
251
|
+
queueQuotaNoticeBestEffort(state, configResult.config, response.result.quota);
|
|
252
|
+
}
|
|
250
253
|
break;
|
|
251
254
|
}
|
|
252
255
|
if (!retrying) {
|
|
@@ -287,9 +290,9 @@ function trackAutomaticMemoryWriteback(state, writeback) {
|
|
|
287
290
|
state.inFlight.add(writeback);
|
|
288
291
|
void writeback.then(() => state.inFlight.delete(writeback), () => state.inFlight.delete(writeback));
|
|
289
292
|
}
|
|
290
|
-
function queueQuotaNoticeBestEffort(state, quota) {
|
|
293
|
+
function queueQuotaNoticeBestEffort(state, config, quota) {
|
|
291
294
|
try {
|
|
292
|
-
state.queueQuotaNotice?.(quota);
|
|
295
|
+
state.queueQuotaNotice?.(config, quota);
|
|
293
296
|
}
|
|
294
297
|
catch {
|
|
295
298
|
state.diagnosticLogger("memory.automatic_writeback.quota_notice_queue_failed", {});
|
|
@@ -1,19 +1,14 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { existsSync, realpathSync } from "node:fs";
|
|
3
3
|
import { basename, dirname, isAbsolute, join, resolve } from "node:path";
|
|
4
|
-
export const UNCLASSIFIED_PROJECT_ID = "__unclassified__";
|
|
5
|
-
const PROJECT_ROOT_CACHE_CAPACITY = 512;
|
|
6
4
|
const projectsByCwd = new Map();
|
|
7
|
-
const projectRootsById = new Map();
|
|
8
5
|
export function resolveMemoryProject(cwd) {
|
|
9
6
|
if (!cwd || !isAbsolute(cwd))
|
|
10
7
|
return undefined;
|
|
11
8
|
const normalizedCwd = normalizePath(cwd);
|
|
12
9
|
const cached = projectsByCwd.get(normalizedCwd);
|
|
13
|
-
if (cached)
|
|
14
|
-
|
|
15
|
-
return cached.identity;
|
|
16
|
-
}
|
|
10
|
+
if (cached)
|
|
11
|
+
return cached;
|
|
17
12
|
let current = normalizedCwd;
|
|
18
13
|
while (true) {
|
|
19
14
|
if (existsSync(join(current, ".git"))) {
|
|
@@ -23,8 +18,7 @@ export function resolveMemoryProject(cwd) {
|
|
|
23
18
|
projectId: `repo:${createHash("sha256").update(root).digest("hex").slice(0, 32)}`,
|
|
24
19
|
projectLabel: isSafeProjectLabel(label) ? label : "Repository",
|
|
25
20
|
};
|
|
26
|
-
projectsByCwd.set(normalizedCwd,
|
|
27
|
-
rememberProjectRoot(identity.projectId, root);
|
|
21
|
+
projectsByCwd.set(normalizedCwd, identity);
|
|
28
22
|
return identity;
|
|
29
23
|
}
|
|
30
24
|
const parent = dirname(current);
|
|
@@ -34,12 +28,6 @@ export function resolveMemoryProject(cwd) {
|
|
|
34
28
|
}
|
|
35
29
|
return undefined;
|
|
36
30
|
}
|
|
37
|
-
export function memoryProjectRoot(projectId) {
|
|
38
|
-
const root = projectRootsById.get(projectId);
|
|
39
|
-
if (root)
|
|
40
|
-
rememberProjectRoot(projectId, root);
|
|
41
|
-
return root;
|
|
42
|
-
}
|
|
43
31
|
export function memoryProjectFromUnknown(value) {
|
|
44
32
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
45
33
|
return undefined;
|
|
@@ -56,7 +44,7 @@ export function memoryProjectFromUnknown(value) {
|
|
|
56
44
|
return undefined;
|
|
57
45
|
return { projectId, projectLabel };
|
|
58
46
|
}
|
|
59
|
-
|
|
47
|
+
function isMemoryProjectId(value) {
|
|
60
48
|
return /^repo:[a-f0-9]{32}$/.test(value);
|
|
61
49
|
}
|
|
62
50
|
function isSafeProjectLabel(value) {
|
|
@@ -74,16 +62,6 @@ function normalizePath(value) {
|
|
|
74
62
|
return absolute;
|
|
75
63
|
}
|
|
76
64
|
}
|
|
77
|
-
function rememberProjectRoot(projectId, root) {
|
|
78
|
-
projectRootsById.delete(projectId);
|
|
79
|
-
projectRootsById.set(projectId, root);
|
|
80
|
-
while (projectRootsById.size > PROJECT_ROOT_CACHE_CAPACITY) {
|
|
81
|
-
const oldest = projectRootsById.keys().next().value;
|
|
82
|
-
if (typeof oldest !== "string")
|
|
83
|
-
break;
|
|
84
|
-
projectRootsById.delete(oldest);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
65
|
function rawString(value) {
|
|
88
66
|
return typeof value === "string" ? value : "";
|
|
89
67
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import { withJsonFileLockAsync, } from "../../../memorax-code-adapter-common/src/config-utils.mjs";
|
|
4
|
+
import { loadTrialCredentialRecord, } from "../../../memorax-code-adapter-common/src/credentials/trial-credential-store.mjs";
|
|
4
5
|
import { readJsonRuntimeRecord, writePrivateJsonRecord, } from "../../../memorax-code-adapter-common/src/runtime-record.mjs";
|
|
5
6
|
import { MEMORAX_ACCOUNT_URL, } from "../../../memorax-code-adapter-common/src/memorax-defaults.mjs";
|
|
6
7
|
import { defaultMemoraxCodeHome } from "../config/memorax-code.js";
|
|
@@ -18,8 +19,10 @@ const QUOTA_NOTICE_STATE_KEYS = new Set([
|
|
|
18
19
|
const SHA256_PATTERN = /^[0-9a-f]{64}$/;
|
|
19
20
|
export const claimQuotaNotice = async (config, quota, options = {}) => {
|
|
20
21
|
const env = options.env ?? process.env;
|
|
22
|
+
const memoraxCodeHome = defaultMemoraxCodeHome(env);
|
|
21
23
|
const transitionState = options.transitionState ?? transitionQuotaNoticeState;
|
|
22
24
|
const timeoutMs = positiveTimeout(options.timeoutMs);
|
|
25
|
+
const deadlineAt = Date.now() + timeoutMs;
|
|
23
26
|
const controller = new AbortController();
|
|
24
27
|
const level = quotaNoticeLevel(quota);
|
|
25
28
|
const initial = initialQuotaNoticeState(config);
|
|
@@ -43,7 +46,7 @@ export const claimQuotaNotice = async (config, quota, options = {}) => {
|
|
|
43
46
|
claimed = true;
|
|
44
47
|
return { ...current, [noticeField]: level };
|
|
45
48
|
}, {
|
|
46
|
-
memoraxCodeHome
|
|
49
|
+
memoraxCodeHome,
|
|
47
50
|
signal: controller.signal,
|
|
48
51
|
timeoutMs: LOCK_TIMEOUT_MS,
|
|
49
52
|
}),
|
|
@@ -64,29 +67,63 @@ export const claimQuotaNotice = async (config, quota, options = {}) => {
|
|
|
64
67
|
if (timeout)
|
|
65
68
|
clearTimeout(timeout);
|
|
66
69
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
+
if (!claimed || level === undefined)
|
|
71
|
+
return undefined;
|
|
72
|
+
let markId;
|
|
73
|
+
if (quota.limit === ANONYMOUS_QUOTA_LIMIT) {
|
|
74
|
+
const remainingMs = deadlineAt - Date.now();
|
|
75
|
+
if (remainingMs > 0) {
|
|
76
|
+
let credentialTimeout;
|
|
77
|
+
try {
|
|
78
|
+
const credential = await Promise.race([
|
|
79
|
+
(options.loadTrialCredential ?? loadTrialCredentialRecord)({
|
|
80
|
+
memoraxCodeHome,
|
|
81
|
+
env,
|
|
82
|
+
runtime: { timeoutMs: remainingMs },
|
|
83
|
+
}),
|
|
84
|
+
new Promise((_resolve, reject) => {
|
|
85
|
+
credentialTimeout = setTimeout(() => reject(new Error("Quota notice credential lookup timed out")), remainingMs);
|
|
86
|
+
}),
|
|
87
|
+
]);
|
|
88
|
+
if (credential?.state === "ready" && credential.api_key === config.apiKey) {
|
|
89
|
+
markId = credential.mark_id;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
options.diagnosticLogger?.("memorax_quota_notice.mark_id_load_failed", {});
|
|
94
|
+
}
|
|
95
|
+
finally {
|
|
96
|
+
if (credentialTimeout)
|
|
97
|
+
clearTimeout(credentialTimeout);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
return quotaNotice(quota, level, config.memoryOutputLanguage, markId);
|
|
70
102
|
};
|
|
71
103
|
export function createPendingQuotaNoticeRuntime(options = {}) {
|
|
72
104
|
const { claimQuotaNotice: claim = claimQuotaNotice, ...noticeOptions } = options;
|
|
73
|
-
let
|
|
105
|
+
let pendingNotice;
|
|
74
106
|
let closed = false;
|
|
75
107
|
return {
|
|
76
|
-
queue(quota) {
|
|
108
|
+
queue(config, quota) {
|
|
77
109
|
if (closed || quota.featureCode !== "memory_write")
|
|
78
110
|
return;
|
|
79
|
-
if (!
|
|
80
|
-
|
|
111
|
+
if (!pendingNotice || quotaRemainingRatio(quota) < quotaRemainingRatio(pendingNotice.quota)) {
|
|
112
|
+
pendingNotice = {
|
|
113
|
+
connectionFingerprint: quotaNoticeConnectionFingerprint(config),
|
|
114
|
+
quota: { ...quota },
|
|
115
|
+
};
|
|
81
116
|
}
|
|
82
117
|
},
|
|
83
118
|
async claim(config) {
|
|
84
|
-
if (closed || !
|
|
119
|
+
if (closed || !pendingNotice)
|
|
120
|
+
return undefined;
|
|
121
|
+
const notice = pendingNotice;
|
|
122
|
+
pendingNotice = undefined;
|
|
123
|
+
if (notice.connectionFingerprint !== quotaNoticeConnectionFingerprint(config))
|
|
85
124
|
return undefined;
|
|
86
|
-
const quota = pendingQuota;
|
|
87
|
-
pendingQuota = undefined;
|
|
88
125
|
try {
|
|
89
|
-
return await claim(config, quota, noticeOptions);
|
|
126
|
+
return await claim(config, notice.quota, noticeOptions);
|
|
90
127
|
}
|
|
91
128
|
catch {
|
|
92
129
|
options.diagnosticLogger?.("memorax_quota_notice.claim_failed", {});
|
|
@@ -95,7 +132,7 @@ export function createPendingQuotaNoticeRuntime(options = {}) {
|
|
|
95
132
|
},
|
|
96
133
|
close() {
|
|
97
134
|
closed = true;
|
|
98
|
-
|
|
135
|
+
pendingNotice = undefined;
|
|
99
136
|
},
|
|
100
137
|
};
|
|
101
138
|
}
|
|
@@ -142,15 +179,18 @@ function readQuotaNoticeState(path) {
|
|
|
142
179
|
function initialQuotaNoticeState(config) {
|
|
143
180
|
return {
|
|
144
181
|
version: QUOTA_NOTICE_STATE_VERSION,
|
|
145
|
-
connection_fingerprint:
|
|
146
|
-
.update(config.baseUrl)
|
|
147
|
-
.update("\0")
|
|
148
|
-
.update(config.apiKey)
|
|
149
|
-
.digest("hex"),
|
|
182
|
+
connection_fingerprint: quotaNoticeConnectionFingerprint(config),
|
|
150
183
|
last_notified_write_level: null,
|
|
151
184
|
last_notified_search_level: null,
|
|
152
185
|
};
|
|
153
186
|
}
|
|
187
|
+
function quotaNoticeConnectionFingerprint(config) {
|
|
188
|
+
return createHash("sha256")
|
|
189
|
+
.update(config.baseUrl)
|
|
190
|
+
.update("\0")
|
|
191
|
+
.update(config.apiKey)
|
|
192
|
+
.digest("hex");
|
|
193
|
+
}
|
|
154
194
|
function positiveTimeout(value) {
|
|
155
195
|
return typeof value === "number" && Number.isSafeInteger(value) && value > 0
|
|
156
196
|
? value
|
|
@@ -172,7 +212,7 @@ function quotaRemainingRatio(quota) {
|
|
|
172
212
|
function validNoticeLevel(value) {
|
|
173
213
|
return value === null || NOTICE_LEVELS.some((level) => value === level);
|
|
174
214
|
}
|
|
175
|
-
function quotaNotice(quota, level, language) {
|
|
215
|
+
function quotaNotice(quota, level, language, markId) {
|
|
176
216
|
if (language === "zh") {
|
|
177
217
|
const quotaName = quota.featureCode === "memory_write" ? "记忆写入" : "记忆搜索";
|
|
178
218
|
const quotaStatus = level === 0 ? "已用完" : `剩余不超过 ${level}%`;
|
|
@@ -182,8 +222,18 @@ function quotaNotice(quota, level, language) {
|
|
|
182
222
|
`请访问 ${MEMORAX_ACCOUNT_URL} 查看额度或管理账户。`,
|
|
183
223
|
].join(" ");
|
|
184
224
|
}
|
|
225
|
+
if (markId) {
|
|
226
|
+
return [
|
|
227
|
+
`额度提醒:您的 MemoraX Code ${quotaName}额度${quotaStatus}。`,
|
|
228
|
+
"游客模式有效期为 90 天。",
|
|
229
|
+
`请访问 ${MEMORAX_ACCOUNT_URL} 查看额度、注册或管理账户。`,
|
|
230
|
+
`当前匿名身份的 Mark ID:\`${markId}\`。`,
|
|
231
|
+
"请妥善保管。",
|
|
232
|
+
].join(" ");
|
|
233
|
+
}
|
|
185
234
|
return [
|
|
186
235
|
`额度提醒:您的 MemoraX Code ${quotaName}额度${quotaStatus}。`,
|
|
236
|
+
"游客模式有效期为 90 天。",
|
|
187
237
|
`请访问 ${MEMORAX_ACCOUNT_URL} 查看额度、注册或管理账户。`,
|
|
188
238
|
"若本机使用的是尚未注册的匿名身份,可以在本机终端运行 `memorax-code account --show-mark-id` 获取用于认领当前身份的 Mark ID。",
|
|
189
239
|
"请妥善保管,不要在聊天中分享。",
|
|
@@ -199,8 +249,18 @@ function quotaNotice(quota, level, language) {
|
|
|
199
249
|
`Visit ${MEMORAX_ACCOUNT_URL} to view your quota or manage your account.`,
|
|
200
250
|
].join(" ");
|
|
201
251
|
}
|
|
252
|
+
if (markId) {
|
|
253
|
+
return [
|
|
254
|
+
`Quota reminder: Your MemoraX Code ${quotaName} quota ${quotaStatus}.`,
|
|
255
|
+
"Guest mode is available for 90 days.",
|
|
256
|
+
`Visit ${MEMORAX_ACCOUNT_URL} to view your quota, register, or manage your account.`,
|
|
257
|
+
`Mark ID for the current anonymous identity: \`${markId}\`.`,
|
|
258
|
+
"Keep it private.",
|
|
259
|
+
].join(" ");
|
|
260
|
+
}
|
|
202
261
|
return [
|
|
203
262
|
`Quota reminder: Your MemoraX Code ${quotaName} quota ${quotaStatus}.`,
|
|
263
|
+
"Guest mode is available for 90 days.",
|
|
204
264
|
`Visit ${MEMORAX_ACCOUNT_URL} to view your quota, register, or manage your account.`,
|
|
205
265
|
"If this device uses an unregistered anonymous identity, run `memorax-code account --show-mark-id` in your local terminal to retrieve the Mark ID used to claim it.",
|
|
206
266
|
"Keep it private and do not share it in chat.",
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { MEMORAX_DEFAULT_MEMORY_OUTPUT_LANGUAGE, MEMORAX_PROVIDER_ID, clampInteger, memoraxAddOptionsFromContext, memoraxConfigFromEnv, parseScore, } from "./config.js";
|
|
2
|
-
import {
|
|
2
|
+
import { memoraxInvocationFailure, postMemoraxJson, } from "./http.js";
|
|
3
3
|
import { repositoryMemoryScopeKind, } from "../../repository/scope.js";
|
|
4
4
|
import { isRecord } from "../../shared/record.js";
|
|
5
5
|
const SLOT_RESULT_SCHEMA_VERSION = "slot-invocation-result.preview.v1";
|
|
@@ -132,9 +132,6 @@ export async function callMemoSearch(config, payload, fetchImpl = fetch) {
|
|
|
132
132
|
export async function callMemoAdd(config, payload, fetchImpl = fetch) {
|
|
133
133
|
return postMemoraxJson(config, "/v1/memories/add", payload, fetchImpl);
|
|
134
134
|
}
|
|
135
|
-
export async function callMemoAddStatus(config, taskId, fetchImpl = fetch) {
|
|
136
|
-
return getMemoraxJson(config, `/v1/memories/add/status/${encodeURIComponent(taskId)}`, fetchImpl);
|
|
137
|
-
}
|
|
138
135
|
function recordMemoryObservabilityEvent(options, event) {
|
|
139
136
|
const observability = options.observability;
|
|
140
137
|
if (!observability?.recordEvent)
|
|
@@ -38,34 +38,6 @@ export async function postMemoraxJson(config, path, payload, fetchImpl) {
|
|
|
38
38
|
clearTimeout(timeout);
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
|
-
export async function getMemoraxJson(config, path, fetchImpl) {
|
|
42
|
-
const controller = new AbortController();
|
|
43
|
-
const timeout = setTimeout(() => controller.abort(), config.timeoutMs);
|
|
44
|
-
try {
|
|
45
|
-
const response = await fetchImpl(`${config.baseUrl}${path}`, {
|
|
46
|
-
method: "GET",
|
|
47
|
-
headers: {
|
|
48
|
-
Authorization: `Token ${config.apiKey}`,
|
|
49
|
-
},
|
|
50
|
-
signal: controller.signal,
|
|
51
|
-
});
|
|
52
|
-
if (!response.ok) {
|
|
53
|
-
const retryAfterMs = parseRetryAfterMs(response.headers.get("retry-after"));
|
|
54
|
-
await response.arrayBuffer().catch(() => undefined);
|
|
55
|
-
throw createMemoraxRequestError(`MemoraX HTTP ${response.status}`, "http", {
|
|
56
|
-
status: response.status,
|
|
57
|
-
retryAfterMs,
|
|
58
|
-
});
|
|
59
|
-
}
|
|
60
|
-
return await response.json().catch(() => null);
|
|
61
|
-
}
|
|
62
|
-
catch (error) {
|
|
63
|
-
throw normalizeMemoraxRequestError(error, controller.signal.aborted);
|
|
64
|
-
}
|
|
65
|
-
finally {
|
|
66
|
-
clearTimeout(timeout);
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
41
|
export function memoraxInvocationFailure(error) {
|
|
70
42
|
const requestError = error instanceof Error ? error : undefined;
|
|
71
43
|
return {
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { backendEnv } from "../../config/backend-env.js";
|
|
2
2
|
const DEFAULT_MAX_JSON_BODY_BYTES = 16 * 1024 * 1024;
|
|
3
|
-
const MEMORY_VIEWER_SESSION_COOKIE = "memorax_code_memory_viewer_session";
|
|
4
3
|
export class HttpRequestError extends Error {
|
|
5
4
|
statusCode;
|
|
6
5
|
constructor(statusCode, message) {
|
|
@@ -57,44 +56,7 @@ export function authorized(state, req, url) {
|
|
|
57
56
|
?? (typeof headerToken === "string" ? headerToken : undefined)
|
|
58
57
|
?? url.searchParams.get("token")
|
|
59
58
|
?? undefined;
|
|
60
|
-
|
|
61
|
-
return true;
|
|
62
|
-
if (url.pathname === "/memory-viewer"
|
|
63
|
-
|| url.pathname.startsWith("/memory-viewer/")) {
|
|
64
|
-
const viewerCookie = cookieValue(req.headers.cookie, MEMORY_VIEWER_SESSION_COOKIE);
|
|
65
|
-
if (viewerCookie === state.authToken)
|
|
66
|
-
return true;
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
export function memoryViewerSessionCookieHeader(state, req, url) {
|
|
71
|
-
if (!state.authToken
|
|
72
|
-
|| req.method !== "GET"
|
|
73
|
-
|| url.pathname !== "/memory-viewer")
|
|
74
|
-
return undefined;
|
|
75
|
-
const supplied = url.searchParams.get("token");
|
|
76
|
-
const authorization = req.headers.authorization;
|
|
77
|
-
const bearer = authorization?.startsWith("Bearer ") ? authorization.slice("Bearer ".length) : undefined;
|
|
78
|
-
if (supplied !== state.authToken && bearer !== state.authToken)
|
|
79
|
-
return undefined;
|
|
80
|
-
const secure = Boolean(req.socket?.encrypted);
|
|
81
|
-
return `${MEMORY_VIEWER_SESSION_COOKIE}=${encodeURIComponent(state.authToken)}; HttpOnly; SameSite=Strict; Path=/memory-viewer${secure ? "; Secure" : ""}`;
|
|
82
|
-
}
|
|
83
|
-
function cookieValue(header, name) {
|
|
84
|
-
if (!header)
|
|
85
|
-
return undefined;
|
|
86
|
-
for (const part of header.split(";")) {
|
|
87
|
-
const [rawName, ...rawValue] = part.trim().split("=");
|
|
88
|
-
if (rawName !== name)
|
|
89
|
-
continue;
|
|
90
|
-
try {
|
|
91
|
-
return decodeURIComponent(rawValue.join("="));
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
return undefined;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
return undefined;
|
|
59
|
+
return token === state.authToken;
|
|
98
60
|
}
|
|
99
61
|
export function statusCodeFromError(error) {
|
|
100
62
|
if (error instanceof HttpRequestError)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@memorax-code/backend",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
4
4
|
"private": true,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "Local memory integration, lifecycle, trace, and diagnostics for MemoraX Code.",
|
|
@@ -13,8 +13,7 @@
|
|
|
13
13
|
"dev": "tsc --watch --preserveWatchOutput",
|
|
14
14
|
"dev:backend": "node --watch dist/server.js",
|
|
15
15
|
"typecheck": "tsc --noEmit",
|
|
16
|
-
"test": "npm run build && node --test --test-timeout=5000 --test-force-exit \"test/**/*.test.mjs\""
|
|
17
|
-
"test:memory-viewer": "npm run build && node --test --test-timeout=5000 --test-force-exit test/viewer/http/memory-viewer-routes.test.mjs"
|
|
16
|
+
"test": "npm run build && node --test --test-timeout=5000 --test-force-exit \"test/**/*.test.mjs\""
|
|
18
17
|
},
|
|
19
18
|
"devDependencies": {
|
|
20
19
|
"@types/node": "^20.0.0",
|
|
@@ -82,10 +82,4 @@ Apply instructions in this order: system and developer instructions, `AGENTS.md`
|
|
|
82
82
|
|
|
83
83
|
For MemoraX Code coding memory, use `memorax-cli` exactly as described by the selected reference. Invoke this skill as `$memorax-code` in Codex or `/memorax-code` in Claude Code. In OpenCode, ask the agent to use the `memorax-code` skill by name. These invocation forms are not shell commands. `memorax-code` is the lifecycle CLI and must not be used for memory search or add. Do not call MemoraX HTTP endpoints directly.
|
|
84
84
|
|
|
85
|
-
When explaining Memory Viewer, describe only `/memory-viewer`. It is a
|
|
86
|
-
content-free local summary and must not expose conversation or memory text,
|
|
87
|
-
session or turn identifiers, paths, or trace details. The page never queries
|
|
88
|
-
MemoraX directly; it projects client-qualified local activity and keeps each
|
|
89
|
-
supported harness isolated.
|
|
90
|
-
|
|
91
85
|
Never store secrets, credentials, `.env` content, sensitive personal data, raw transcripts, hidden tests, exact patches, temporary target commits, or unsafe destructive commands. Do not announce internal routing or reference loading.
|
|
@@ -83,6 +83,6 @@ Do not add speculation, assistant-only praise, or a current instruction merely b
|
|
|
83
83
|
|
|
84
84
|
## Output
|
|
85
85
|
|
|
86
|
-
If a successful Add returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or
|
|
86
|
+
If a successful Add returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or anonymous-account claim details. A guest reminder may already contain the local Mark ID; present it once as part of the reminder, but do not repeat or separately quote it. Never run `memorax-code account --show-mark-id` for the user or ask for its output. Treat the reminder as user-facing operational output, not coding memory content, and continue the current task.
|
|
87
87
|
|
|
88
88
|
After an add request is accepted, confirm briefly that it was submitted for processing and identify the reusable coding lesson at a high level. If add is disabled or fails, report the issue and continue without bypassing the CLI.
|
|
@@ -88,6 +88,6 @@ Do not apply this retry to `memorax-cli add`, authentication or configuration fa
|
|
|
88
88
|
|
|
89
89
|
## Output
|
|
90
90
|
|
|
91
|
-
If a successful Search returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or
|
|
91
|
+
If a successful Search returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or anonymous-account claim details. A guest reminder may already contain the local Mark ID; present it once as part of the reminder, but do not repeat or separately quote it. Never run `memorax-code account --show-mark-id` for the user or ask for its output. Treat the reminder as user-facing operational output, not recalled memory, and continue the current task.
|
|
92
92
|
|
|
93
93
|
Mention only an invariant, pitfall, convention, or validation idea that materially affects the answer. Ground claims about current implementation behavior in live code and checks.
|
|
@@ -82,10 +82,4 @@ Apply instructions in this order: system and developer instructions, `AGENTS.md`
|
|
|
82
82
|
|
|
83
83
|
For MemoraX Code coding memory, use `memorax-cli` exactly as described by the selected reference. Invoke this skill as `$memorax-code` in Codex or `/memorax-code` in Claude Code. In OpenCode, ask the agent to use the `memorax-code` skill by name. These invocation forms are not shell commands. `memorax-code` is the lifecycle CLI and must not be used for memory search or add. Do not call MemoraX HTTP endpoints directly.
|
|
84
84
|
|
|
85
|
-
When explaining Memory Viewer, describe only `/memory-viewer`. It is a
|
|
86
|
-
content-free local summary and must not expose conversation or memory text,
|
|
87
|
-
session or turn identifiers, paths, or trace details. The page never queries
|
|
88
|
-
MemoraX directly; it projects client-qualified local activity and keeps each
|
|
89
|
-
supported harness isolated.
|
|
90
|
-
|
|
91
85
|
Never store secrets, credentials, `.env` content, sensitive personal data, raw transcripts, hidden tests, exact patches, temporary target commits, or unsafe destructive commands. Do not announce internal routing or reference loading.
|
|
@@ -83,6 +83,6 @@ Do not add speculation, assistant-only praise, or a current instruction merely b
|
|
|
83
83
|
|
|
84
84
|
## Output
|
|
85
85
|
|
|
86
|
-
If a successful Add returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or
|
|
86
|
+
If a successful Add returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or anonymous-account claim details. A guest reminder may already contain the local Mark ID; present it once as part of the reminder, but do not repeat or separately quote it. Never run `memorax-code account --show-mark-id` for the user or ask for its output. Treat the reminder as user-facing operational output, not coding memory content, and continue the current task.
|
|
87
87
|
|
|
88
88
|
After an add request is accepted, confirm briefly that it was submitted for processing and identify the reusable coding lesson at a high level. If add is disabled or fails, report the issue and continue without bypassing the CLI.
|
|
@@ -88,6 +88,6 @@ Do not apply this retry to `memorax-cli add`, authentication or configuration fa
|
|
|
88
88
|
|
|
89
89
|
## Output
|
|
90
90
|
|
|
91
|
-
If a successful Search returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or
|
|
91
|
+
If a successful Search returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or anonymous-account claim details. A guest reminder may already contain the local Mark ID; present it once as part of the reminder, but do not repeat or separately quote it. Never run `memorax-code account --show-mark-id` for the user or ask for its output. Treat the reminder as user-facing operational output, not recalled memory, and continue the current task.
|
|
92
92
|
|
|
93
93
|
Mention only an invariant, pitfall, convention, or validation idea that materially affects the answer. Ground claims about current implementation behavior in live code and checks.
|
|
@@ -82,10 +82,4 @@ Apply instructions in this order: system and developer instructions, `AGENTS.md`
|
|
|
82
82
|
|
|
83
83
|
For MemoraX Code coding memory, use `memorax-cli` exactly as described by the selected reference. Invoke this skill as `$memorax-code` in Codex or `/memorax-code` in Claude Code. In OpenCode, ask the agent to use the `memorax-code` skill by name. These invocation forms are not shell commands. `memorax-code` is the lifecycle CLI and must not be used for memory search or add. Do not call MemoraX HTTP endpoints directly.
|
|
84
84
|
|
|
85
|
-
When explaining Memory Viewer, describe only `/memory-viewer`. It is a
|
|
86
|
-
content-free local summary and must not expose conversation or memory text,
|
|
87
|
-
session or turn identifiers, paths, or trace details. The page never queries
|
|
88
|
-
MemoraX directly; it projects client-qualified local activity and keeps each
|
|
89
|
-
supported harness isolated.
|
|
90
|
-
|
|
91
85
|
Never store secrets, credentials, `.env` content, sensitive personal data, raw transcripts, hidden tests, exact patches, temporary target commits, or unsafe destructive commands. Do not announce internal routing or reference loading.
|
|
@@ -83,6 +83,6 @@ Do not add speculation, assistant-only praise, or a current instruction merely b
|
|
|
83
83
|
|
|
84
84
|
## Output
|
|
85
85
|
|
|
86
|
-
If a successful Add returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or
|
|
86
|
+
If a successful Add returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or anonymous-account claim details. A guest reminder may already contain the local Mark ID; present it once as part of the reminder, but do not repeat or separately quote it. Never run `memorax-code account --show-mark-id` for the user or ask for its output. Treat the reminder as user-facing operational output, not coding memory content, and continue the current task.
|
|
87
87
|
|
|
88
88
|
After an add request is accepted, confirm briefly that it was submitted for processing and identify the reusable coding lesson at a high level. If add is disabled or fails, report the issue and continue without bypassing the CLI.
|
|
@@ -88,6 +88,6 @@ Do not apply this retry to `memorax-cli add`, authentication or configuration fa
|
|
|
88
88
|
|
|
89
89
|
## Output
|
|
90
90
|
|
|
91
|
-
If a successful Search returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or
|
|
91
|
+
If a successful Search returns `quotaNotice`, or prints it as a quota-reminder line in the default CLI output, present the complete reminder once and prominently before the normal result summary. Do not reduce it to only a percentage or omit its account URL or anonymous-account claim details. A guest reminder may already contain the local Mark ID; present it once as part of the reminder, but do not repeat or separately quote it. Never run `memorax-code account --show-mark-id` for the user or ask for its output. Treat the reminder as user-facing operational output, not recalled memory, and continue the current task.
|
|
92
92
|
|
|
93
93
|
Mention only an invariant, pitfall, convention, or validation idea that materially affects the answer. Ground claims about current implementation behavior in live code and checks.
|