@memorax/memorax-code 0.1.10 → 0.1.12
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 +7 -4
- package/bin/memorax-code-setup.mjs +136 -34
- package/bin/memorax-code-trae.mjs +4 -0
- package/docs/configuration.md +111 -54
- package/docs/troubleshooting.md +50 -8
- package/lib/memorax-code-backend/dist/clients/claude/transcript-turn.js +2 -2
- package/lib/memorax-code-backend/dist/clients/codex/rollout-turn.js +1 -1
- package/lib/memorax-code-backend/dist/clients/trae/lifecycle.js +53 -0
- package/lib/memorax-code-backend/dist/clients/trae/memory-hook-runtime.js +302 -0
- package/lib/memorax-code-backend/dist/clients/trae/turn-id.js +16 -0
- package/lib/memorax-code-backend/dist/config/memorax-code.js +14 -1
- package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +22 -4
- package/lib/memorax-code-backend/dist/lifecycle/active-clients.js +3 -0
- package/lib/memorax-code-backend/dist/lifecycle/client-plugin-removal.js +14 -2
- package/lib/memorax-code-backend/dist/lifecycle/client-selection.js +5 -3
- package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +62 -9
- package/lib/memorax-code-backend/dist/memory/hook-command.js +34 -2
- package/lib/memorax-code-backend/dist/memory/reminder-trace-recorder.js +5 -1
- package/lib/memorax-code-backend/dist/memory/service.js +12 -0
- package/lib/memorax-code-backend/dist/trace/config.js +12 -1
- package/lib/memorax-code-backend/dist/trace/context.js +26 -1
- package/lib/memorax-code-backend/package.json +1 -1
- 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 +3 -1
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- 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 +3 -1
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-codebuddy-adapter/.codebuddy-plugin/plugin.json +1 -1
- package/lib/memorax-code-codebuddy-adapter/hooks/runtime-hook.mjs +11 -1
- package/lib/memorax-code-codebuddy-adapter/package.json +1 -1
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/SKILL.md +3 -1
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-codebuddy-adapter/src/config.mjs +86 -23
- 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 +3 -1
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-dsh-adapter/package.json +1 -1
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/SKILL.md +3 -1
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-opencode-adapter/package.json +1 -1
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/SKILL.md +3 -1
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-add.md +16 -3
- package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-search.md +15 -2
- package/lib/memorax-code-trae-adapter/hooks/runtime-hook.mjs +340 -0
- package/lib/memorax-code-trae-adapter/package.json +9 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/SKILL.md +87 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/agents/claude.yaml +10 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/agents/openai.yaml +7 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/defaults.json +12 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/memorax-add.md +101 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/memorax-search.md +106 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/personal-read.md +46 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/personal-write.md +120 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-build.md +319 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-read.md +103 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-templates.md +390 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/references/repo-update.md +127 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
- package/lib/memorax-code-trae-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
- package/lib/memorax-code-trae-adapter/src/adapter-paths.mjs +69 -0
- package/lib/memorax-code-trae-adapter/src/cli.mjs +59 -0
- package/lib/memorax-code-trae-adapter/src/config.mjs +630 -0
- package/lib/memorax-code-trae-adapter/src/runtime-observation.mjs +55 -0
- package/lib/resolve-codebuddy-command.mjs +15 -4
- package/lib/run-entrypoint.mjs +6 -0
- package/package.json +5 -3
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
import { createAutomaticMemoryWritebackRuntime, } from "../../memory/automatic-writeback.js";
|
|
2
|
+
import { retrieveAutomaticMemoryContext } from "../../memory/automatic-retrieval.js";
|
|
3
|
+
import { claimQuotaNotice, createPendingQuotaNoticeRuntime, } from "../../memory/quota-notice.js";
|
|
4
|
+
import { createMemoryTurnCoordinator, } from "../../memory/turn-coordinator.js";
|
|
5
|
+
import { createRepositoryMemorySessionRuntime, resolvedRepoMemoryWorktree, } from "../../memory/repository-session.js";
|
|
6
|
+
import { traceContextFromTraeHookBody } from "../../trace/context.js";
|
|
7
|
+
import { markCurrentTraceTurnOutcome, recordTraceEvent, traceTurnEventId, writeCurrentTraceTurn, } from "../../trace/store.js";
|
|
8
|
+
const TRAE_MEMORY_TURN_CLIENT = "trae";
|
|
9
|
+
export function createTraeMemoryHookRuntime(options = {}) {
|
|
10
|
+
const now = options.now ?? (() => Date.now());
|
|
11
|
+
const pendingQuotaNotice = options.pendingQuotaNotice ?? createPendingQuotaNoticeRuntime({
|
|
12
|
+
claimQuotaNotice: options.claimQuotaNotice,
|
|
13
|
+
diagnosticLogger: options.diagnosticLogger,
|
|
14
|
+
env: options.env,
|
|
15
|
+
});
|
|
16
|
+
const ownsPendingQuotaNotice = options.pendingQuotaNotice === undefined;
|
|
17
|
+
const automaticWritebackRuntime = options.turnCoordinator
|
|
18
|
+
? undefined
|
|
19
|
+
: options.automaticWriteback
|
|
20
|
+
? { enqueue: options.automaticWriteback }
|
|
21
|
+
: createAutomaticMemoryWritebackRuntime({
|
|
22
|
+
diagnosticLogger: options.diagnosticLogger,
|
|
23
|
+
queueQuotaNotice: pendingQuotaNotice.queue,
|
|
24
|
+
});
|
|
25
|
+
const turnCoordinator = options.turnCoordinator ?? createMemoryTurnCoordinator({
|
|
26
|
+
automaticWriteback: automaticWritebackRuntime.enqueue,
|
|
27
|
+
now,
|
|
28
|
+
ttlMs: options.ttlMs,
|
|
29
|
+
maxEntries: options.maxEntries,
|
|
30
|
+
cleanupIntervalMs: options.cleanupIntervalMs,
|
|
31
|
+
});
|
|
32
|
+
const ownsTurnCoordinator = options.turnCoordinator === undefined;
|
|
33
|
+
const repositoryMemorySession = options.repositoryMemorySession ?? createRepositoryMemorySessionRuntime({
|
|
34
|
+
onScopeUpgrade: automaticWritebackRuntime?.discardForScopeUpgrade,
|
|
35
|
+
});
|
|
36
|
+
const ownsRepositoryMemorySession = options.repositoryMemorySession === undefined;
|
|
37
|
+
const retrievalTurns = new Set();
|
|
38
|
+
const interruptedTurns = new Set();
|
|
39
|
+
const activeTurns = new Map();
|
|
40
|
+
const turnStartOperations = new Map();
|
|
41
|
+
const runtimeTurnLimit = positiveInteger(options.maxEntries, 256);
|
|
42
|
+
const runtime = {
|
|
43
|
+
async recordTurnStart(command) {
|
|
44
|
+
turnCoordinator.pruneExpired();
|
|
45
|
+
const commandKey = traeRuntimeTurnKey(command.sessionId, command.turnId);
|
|
46
|
+
if (interruptedTurns.has(commandKey))
|
|
47
|
+
return { ok: true };
|
|
48
|
+
const previous = activeTurns.get(command.sessionId) ?? turnCoordinator.latestTurn({
|
|
49
|
+
client: TRAE_MEMORY_TURN_CLIENT,
|
|
50
|
+
sessionId: command.sessionId,
|
|
51
|
+
excludeClientTurnId: command.turnId,
|
|
52
|
+
});
|
|
53
|
+
if (previous && previous.clientTurnId !== command.turnId) {
|
|
54
|
+
await interruptPreviousTurn(turnCoordinator, previous, options, now);
|
|
55
|
+
rememberBounded(interruptedTurns, traeRuntimeTurnKey(previous.sessionId, previous.clientTurnId), runtimeTurnLimit);
|
|
56
|
+
}
|
|
57
|
+
const createdAt = now();
|
|
58
|
+
const traceContext = traceContextFromTraeHookBody(command, new Date(createdAt).toISOString());
|
|
59
|
+
const repositoryMemory = await resolveHookRepositoryMemory(command, options, repositoryMemorySession);
|
|
60
|
+
const repoMemoryWorktree = resolvedRepoMemoryWorktree(repositoryMemory);
|
|
61
|
+
const turn = turnCoordinator.recordTurnStart({
|
|
62
|
+
client: TRAE_MEMORY_TURN_CLIENT,
|
|
63
|
+
sessionId: command.sessionId,
|
|
64
|
+
clientTurnId: command.turnId,
|
|
65
|
+
cwd: command.cwd,
|
|
66
|
+
workspaceKind: command.workspaceKind,
|
|
67
|
+
createdAt,
|
|
68
|
+
traceContext,
|
|
69
|
+
repositoryMemory,
|
|
70
|
+
});
|
|
71
|
+
activeTurns.delete(command.sessionId);
|
|
72
|
+
activeTurns.set(command.sessionId, turn);
|
|
73
|
+
while (activeTurns.size > runtimeTurnLimit) {
|
|
74
|
+
const oldestSessionId = activeTurns.keys().next().value;
|
|
75
|
+
if (typeof oldestSessionId !== "string")
|
|
76
|
+
break;
|
|
77
|
+
activeTurns.delete(oldestSessionId);
|
|
78
|
+
}
|
|
79
|
+
await recordTraceBestEffort("trae_memory.turn_start_event", recordTraceEvent({
|
|
80
|
+
eventId: traceTurnEventId(traceContext, "turn_start"),
|
|
81
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
82
|
+
env: options.env,
|
|
83
|
+
traceContext,
|
|
84
|
+
type: "turn_start",
|
|
85
|
+
source: "trae-hook",
|
|
86
|
+
operation: "query",
|
|
87
|
+
ok: true,
|
|
88
|
+
request: { prompt: command.prompt, cwd: command.cwd },
|
|
89
|
+
}), options.diagnosticLogger);
|
|
90
|
+
await recordTraceBestEffort("trae_memory.current_turn_write", writeCurrentTraceTurn(traceContext, {
|
|
91
|
+
client: "trae",
|
|
92
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
93
|
+
env: options.env,
|
|
94
|
+
now: () => new Date(now()),
|
|
95
|
+
}), options.diagnosticLogger);
|
|
96
|
+
const retrievalKey = JSON.stringify([command.sessionId, command.turnId]);
|
|
97
|
+
if (retrievalTurns.has(retrievalKey)) {
|
|
98
|
+
return { ok: true, ...(repoMemoryWorktree ? { repoMemoryWorktree } : {}) };
|
|
99
|
+
}
|
|
100
|
+
retrievalTurns.add(retrievalKey);
|
|
101
|
+
trimBounded(retrievalTurns, runtimeTurnLimit);
|
|
102
|
+
const pendingUserNotice = repositoryMemory.ok
|
|
103
|
+
? await pendingQuotaNotice.claim(repositoryMemory.memory.config)
|
|
104
|
+
: undefined;
|
|
105
|
+
const retrieval = await retrieveAutomaticMemoryContext({
|
|
106
|
+
diagnosticLogger: options.diagnosticLogger,
|
|
107
|
+
claimQuotaNotice: options.claimQuotaNotice ?? claimQuotaNotice,
|
|
108
|
+
env: options.env ?? process.env,
|
|
109
|
+
fetchImpl: options.fetchImpl,
|
|
110
|
+
memoryObservability: options.memoryObservability,
|
|
111
|
+
memoryObservabilitySource: "trae_hook_retrieval",
|
|
112
|
+
query: command.prompt,
|
|
113
|
+
repositoryMemory,
|
|
114
|
+
sessionKey: command.sessionId,
|
|
115
|
+
traceContext,
|
|
116
|
+
});
|
|
117
|
+
const userNotice = [pendingUserNotice, retrieval.userNotice].filter(Boolean).join("\n");
|
|
118
|
+
return {
|
|
119
|
+
ok: true,
|
|
120
|
+
...(repoMemoryWorktree ? { repoMemoryWorktree } : {}),
|
|
121
|
+
...(retrieval.context ? { additionalContext: retrieval.context } : {}),
|
|
122
|
+
...(userNotice ? { userNotice } : {}),
|
|
123
|
+
};
|
|
124
|
+
},
|
|
125
|
+
async writeback(command) {
|
|
126
|
+
turnCoordinator.pruneExpired();
|
|
127
|
+
const commandKey = traeRuntimeTurnKey(command.sessionId, command.turnId);
|
|
128
|
+
const activeTurn = activeTurns.get(command.sessionId);
|
|
129
|
+
if (interruptedTurns.has(commandKey)
|
|
130
|
+
|| (activeTurn && activeTurn.clientTurnId !== command.turnId)) {
|
|
131
|
+
rememberBounded(interruptedTurns, commandKey, runtimeTurnLimit);
|
|
132
|
+
return { ok: true, scheduled: false, reason: "interrupted" };
|
|
133
|
+
}
|
|
134
|
+
const key = traeTurnKey(command.sessionId, command.turnId);
|
|
135
|
+
const entry = turnCoordinator.getTurn(key) ?? activeTurn;
|
|
136
|
+
const traceContext = entry?.traceContext ?? traceContextFromTraeHookBody(command);
|
|
137
|
+
const repositoryMemory = await resolveHookRepositoryMemory(command, options, repositoryMemorySession);
|
|
138
|
+
await recordCompletedTurn(traceContext, command, options, now);
|
|
139
|
+
const completed = await turnCoordinator.completeMaterializedTurn({
|
|
140
|
+
key,
|
|
141
|
+
metadata: entry,
|
|
142
|
+
resolveRepositoryMemory: async () => repositoryMemory,
|
|
143
|
+
userText: command.prompt,
|
|
144
|
+
assistantText: command.lastAssistantMessage,
|
|
145
|
+
writeback: {
|
|
146
|
+
client: "trae",
|
|
147
|
+
sessionKey: command.sessionId,
|
|
148
|
+
env: options.env ?? process.env,
|
|
149
|
+
fetchImpl: options.fetchImpl,
|
|
150
|
+
memoryObservability: options.memoryObservability,
|
|
151
|
+
memoryObservabilitySource: "trae_hook_writeback",
|
|
152
|
+
traceContext,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
await recordMaterializedTurn(traceContext, command, options);
|
|
156
|
+
if (activeTurn?.clientTurnId === command.turnId)
|
|
157
|
+
activeTurns.delete(command.sessionId);
|
|
158
|
+
options.diagnosticLogger?.("trae_memory.writeback", {
|
|
159
|
+
scheduled: completed.scheduled,
|
|
160
|
+
...(!completed.scheduled ? { reason: completed.reason } : {}),
|
|
161
|
+
sessionId: command.sessionId,
|
|
162
|
+
turnId: command.turnId,
|
|
163
|
+
metadataDisposition: completed.metadataDisposition,
|
|
164
|
+
});
|
|
165
|
+
return completed.scheduled
|
|
166
|
+
? { ok: true, scheduled: true }
|
|
167
|
+
: { ok: true, scheduled: false, reason: completed.reason };
|
|
168
|
+
},
|
|
169
|
+
size() {
|
|
170
|
+
return turnCoordinator.size(TRAE_MEMORY_TURN_CLIENT);
|
|
171
|
+
},
|
|
172
|
+
close() {
|
|
173
|
+
retrievalTurns.clear();
|
|
174
|
+
interruptedTurns.clear();
|
|
175
|
+
activeTurns.clear();
|
|
176
|
+
turnStartOperations.clear();
|
|
177
|
+
if (ownsTurnCoordinator)
|
|
178
|
+
turnCoordinator.close();
|
|
179
|
+
if (ownsRepositoryMemorySession)
|
|
180
|
+
repositoryMemorySession.close();
|
|
181
|
+
automaticWritebackRuntime?.close?.();
|
|
182
|
+
if (ownsPendingQuotaNotice)
|
|
183
|
+
pendingQuotaNotice.close();
|
|
184
|
+
},
|
|
185
|
+
};
|
|
186
|
+
return {
|
|
187
|
+
...runtime,
|
|
188
|
+
recordTurnStart(command) {
|
|
189
|
+
return queueSessionTurnStart(turnStartOperations, command.sessionId, () => runtime.recordTurnStart(command));
|
|
190
|
+
},
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
async function interruptPreviousTurn(coordinator, previous, options, now) {
|
|
194
|
+
await recordTraceBestEffort("trae_memory.interrupted_turn_end", recordTraceEvent({
|
|
195
|
+
eventId: traceTurnEventId(previous.traceContext, "turn_end"),
|
|
196
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
197
|
+
env: options.env,
|
|
198
|
+
traceContext: previous.traceContext,
|
|
199
|
+
type: "turn_end",
|
|
200
|
+
source: "trae-hook",
|
|
201
|
+
operation: "writeback",
|
|
202
|
+
ok: true,
|
|
203
|
+
outcome: "interrupted",
|
|
204
|
+
}), options.diagnosticLogger);
|
|
205
|
+
await recordTraceBestEffort("trae_memory.interrupted_current_turn", markCurrentTraceTurnOutcome(previous.traceContext, "interrupted", {
|
|
206
|
+
client: "trae",
|
|
207
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
208
|
+
env: options.env,
|
|
209
|
+
now: () => new Date(now()),
|
|
210
|
+
}), options.diagnosticLogger);
|
|
211
|
+
coordinator.discardTurn(previous, "interrupted");
|
|
212
|
+
}
|
|
213
|
+
async function recordCompletedTurn(traceContext, command, options, now) {
|
|
214
|
+
await recordTraceBestEffort("trae_memory.turn_end", recordTraceEvent({
|
|
215
|
+
eventId: traceTurnEventId(traceContext, "turn_end"),
|
|
216
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
217
|
+
env: options.env,
|
|
218
|
+
traceContext,
|
|
219
|
+
type: "turn_end",
|
|
220
|
+
source: "trae-hook",
|
|
221
|
+
operation: "writeback",
|
|
222
|
+
ok: true,
|
|
223
|
+
outcome: "completed",
|
|
224
|
+
response: { assistant: command.lastAssistantMessage },
|
|
225
|
+
}), options.diagnosticLogger);
|
|
226
|
+
await recordTraceBestEffort("trae_memory.current_turn_close", markCurrentTraceTurnOutcome(traceContext, "completed", {
|
|
227
|
+
client: "trae",
|
|
228
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
229
|
+
env: options.env,
|
|
230
|
+
now: () => new Date(now()),
|
|
231
|
+
}), options.diagnosticLogger);
|
|
232
|
+
}
|
|
233
|
+
async function recordMaterializedTurn(traceContext, command, options) {
|
|
234
|
+
await recordTraceBestEffort("trae_memory.turn_materialized", recordTraceEvent({
|
|
235
|
+
eventId: traceTurnEventId(traceContext, "turn_materialized"),
|
|
236
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
237
|
+
env: options.env,
|
|
238
|
+
traceContext,
|
|
239
|
+
type: "turn_materialized",
|
|
240
|
+
source: "trae-hook",
|
|
241
|
+
operation: "writeback",
|
|
242
|
+
ok: true,
|
|
243
|
+
request: { prompt: command.prompt },
|
|
244
|
+
response: { assistant: command.lastAssistantMessage },
|
|
245
|
+
}), options.diagnosticLogger);
|
|
246
|
+
}
|
|
247
|
+
async function resolveHookRepositoryMemory(command, options, repository) {
|
|
248
|
+
return await repository.resolve({
|
|
249
|
+
client: "trae",
|
|
250
|
+
sessionId: command.sessionId,
|
|
251
|
+
workspaceRoot: command.cwd,
|
|
252
|
+
workspaceKind: command.workspaceKind,
|
|
253
|
+
memoraxCodeHome: options.memoraxCodeHome ?? options.env?.MEMORAX_CODE_HOME,
|
|
254
|
+
env: options.env,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
function traeTurnKey(sessionId, turnId) {
|
|
258
|
+
return { client: "trae", sessionId, clientTurnId: turnId };
|
|
259
|
+
}
|
|
260
|
+
function traeRuntimeTurnKey(sessionId, turnId) {
|
|
261
|
+
return JSON.stringify([sessionId, turnId]);
|
|
262
|
+
}
|
|
263
|
+
function rememberBounded(values, value, limit) {
|
|
264
|
+
values.add(value);
|
|
265
|
+
trimBounded(values, limit);
|
|
266
|
+
}
|
|
267
|
+
function trimBounded(values, limit) {
|
|
268
|
+
while (values.size > limit) {
|
|
269
|
+
const oldest = values.values().next().value;
|
|
270
|
+
if (typeof oldest !== "string")
|
|
271
|
+
return;
|
|
272
|
+
values.delete(oldest);
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
async function recordTraceBestEffort(label, promise, diagnosticLogger) {
|
|
276
|
+
try {
|
|
277
|
+
await promise;
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
diagnosticLogger?.("trae_trace.write_failed", {
|
|
281
|
+
label,
|
|
282
|
+
error: error instanceof Error ? error.message : String(error),
|
|
283
|
+
});
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
function queueSessionTurnStart(operations, sessionId, operation) {
|
|
287
|
+
const previous = operations.get(sessionId) ?? Promise.resolve();
|
|
288
|
+
const queued = previous
|
|
289
|
+
.catch(() => undefined)
|
|
290
|
+
.then(operation);
|
|
291
|
+
operations.set(sessionId, queued);
|
|
292
|
+
const clear = () => {
|
|
293
|
+
if (operations.get(sessionId) === queued)
|
|
294
|
+
operations.delete(sessionId);
|
|
295
|
+
};
|
|
296
|
+
void queued.then(clear, clear);
|
|
297
|
+
return queued;
|
|
298
|
+
}
|
|
299
|
+
function positiveInteger(value, fallback) {
|
|
300
|
+
const parsed = Number(value);
|
|
301
|
+
return Number.isInteger(parsed) && parsed > 0 ? parsed : fallback;
|
|
302
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export function traePromptDigest(prompt) {
|
|
3
|
+
return createHash("sha256").update(prompt.trim()).digest("hex");
|
|
4
|
+
}
|
|
5
|
+
export function parseTraeTurnId(input) {
|
|
6
|
+
const prefix = `${input.sessionId}:`;
|
|
7
|
+
if (!input.turnId.startsWith(prefix))
|
|
8
|
+
return undefined;
|
|
9
|
+
const match = /^([1-9]\d*):([a-f0-9]{64})$/.exec(input.turnId.slice(prefix.length));
|
|
10
|
+
if (!match)
|
|
11
|
+
return undefined;
|
|
12
|
+
const createdAt = Number(match[1]);
|
|
13
|
+
if (!Number.isSafeInteger(createdAt))
|
|
14
|
+
return undefined;
|
|
15
|
+
return { createdAt, promptDigest: match[2] };
|
|
16
|
+
}
|
|
@@ -74,6 +74,10 @@ export function renderDefaultMemoraxCodeConfig() {
|
|
|
74
74
|
"enabled = true # Enable local CodeBuddy session memory trace collection.",
|
|
75
75
|
"capture_content = true # Store content in local CodeBuddy trace events.",
|
|
76
76
|
"",
|
|
77
|
+
"[trace.trae]",
|
|
78
|
+
"enabled = true # Enable local Trae session memory trace collection.",
|
|
79
|
+
"capture_content = true # Store content in local Trae trace events.",
|
|
80
|
+
"",
|
|
77
81
|
].join("\n");
|
|
78
82
|
}
|
|
79
83
|
export async function seedMissingMemoraxCodeConfig(memoraxCodeHome = defaultMemoraxCodeHome(process.env)) {
|
|
@@ -151,6 +155,7 @@ function normalizeMemoraxCodeConfig(value) {
|
|
|
151
155
|
const traceDsh = recordValue(trace?.dsh);
|
|
152
156
|
const traceOpenCode = recordValue(trace?.opencode);
|
|
153
157
|
const traceCodeBuddy = recordValue(trace?.codebuddy);
|
|
158
|
+
const traceTrae = recordValue(trace?.trae);
|
|
154
159
|
return (prune({
|
|
155
160
|
clients: prune({
|
|
156
161
|
codex: booleanField(clients, "codex"),
|
|
@@ -158,6 +163,7 @@ function normalizeMemoraxCodeConfig(value) {
|
|
|
158
163
|
dsh: booleanField(clients, "dsh"),
|
|
159
164
|
opencode: booleanField(clients, "opencode"),
|
|
160
165
|
codebuddy: booleanField(clients, "codebuddy"),
|
|
166
|
+
trae: booleanField(clients, "trae"),
|
|
161
167
|
}),
|
|
162
168
|
memorax: prune({
|
|
163
169
|
endpoint: stringField(memorax, "endpoint"),
|
|
@@ -243,6 +249,13 @@ function normalizeMemoraxCodeConfig(value) {
|
|
|
243
249
|
max_event_chars: numberField(traceCodeBuddy, "max_event_chars"),
|
|
244
250
|
max_file_bytes: numberField(traceCodeBuddy, "max_file_bytes"),
|
|
245
251
|
}),
|
|
252
|
+
trae: prune({
|
|
253
|
+
enabled: booleanField(traceTrae, "enabled"),
|
|
254
|
+
capture_content: booleanField(traceTrae, "capture_content"),
|
|
255
|
+
retention_days: numberField(traceTrae, "retention_days"),
|
|
256
|
+
max_event_chars: numberField(traceTrae, "max_event_chars"),
|
|
257
|
+
max_file_bytes: numberField(traceTrae, "max_file_bytes"),
|
|
258
|
+
}),
|
|
246
259
|
}),
|
|
247
260
|
}) ?? {});
|
|
248
261
|
}
|
|
@@ -255,7 +268,7 @@ function validateRawLifecycleConfig(value, path) {
|
|
|
255
268
|
const clients = tableValue(rawClients);
|
|
256
269
|
if (!clients)
|
|
257
270
|
throw invalidLifecycleConfig(path, "clients must be a table");
|
|
258
|
-
for (const field of ["codex", "claude", "dsh", "opencode", "codebuddy"]) {
|
|
271
|
+
for (const field of ["codex", "claude", "dsh", "opencode", "codebuddy", "trae"]) {
|
|
259
272
|
if (clients[field] !== undefined && typeof clients[field] !== "boolean") {
|
|
260
273
|
throw invalidLifecycleConfig(path, `clients.${field} must be a boolean`);
|
|
261
274
|
}
|
|
@@ -30,9 +30,9 @@ export function runBackendCli(argv = process.argv) {
|
|
|
30
30
|
`Usage: ${usageName} [${commands}] [--backend-url URL] [--backend-token TOKEN] [--home DIR]`,
|
|
31
31
|
"[--host HOST] [--port PORT] [--rotate] [--show]",
|
|
32
32
|
"[--codex-command CMD]",
|
|
33
|
-
"[--codex-home DIR] [--claude-home DIR] [--dsh-home DIR] [--opencode-config-dir DIR] [--codebuddy-home DIR]",
|
|
33
|
+
"[--codex-home DIR] [--claude-home DIR] [--dsh-home DIR] [--opencode-config-dir DIR] [--codebuddy-home DIR] [--trae-home DIR]",
|
|
34
34
|
"[--dsh-command CMD] [--dsh-adapter-root DIR] [--memorax-code-command CMD]",
|
|
35
|
-
"[--clients codex|claude|dsh|opencode|codebuddy|CLIENT,...|all|none]",
|
|
35
|
+
"[--clients codex|claude|dsh|opencode|codebuddy|trae|CLIENT,...|all|none]",
|
|
36
36
|
"[--json]",
|
|
37
37
|
"[--marketplace-path FILE] [--plugin-source-path DIR] [--claude-command CMD] [--help]",
|
|
38
38
|
"[--yes]",
|
|
@@ -212,6 +212,7 @@ async function startRawBackendServer(state, host, port, argv) {
|
|
|
212
212
|
dshAdapterRoot: argValue(argv, "--dsh-adapter-root"),
|
|
213
213
|
openCodeConfigDir: argValue(argv, "--opencode-config-dir"),
|
|
214
214
|
codeBuddyHome: argValue(argv, "--codebuddy-home"),
|
|
215
|
+
traeHome: argValue(argv, "--trae-home"),
|
|
215
216
|
codexCommand: argValue(argv, "--codex-command"),
|
|
216
217
|
claudeCommand: argValue(argv, "--claude-command"),
|
|
217
218
|
dshCommand: argValue(argv, "--dsh-command"),
|
|
@@ -523,6 +524,8 @@ function printMemoraxCodeStatus(report) {
|
|
|
523
524
|
backendLog(`OpenCode adapter: ${adapterStatusLine(report.opencodeAdapter)}`);
|
|
524
525
|
if (report.codebuddyAdapter)
|
|
525
526
|
backendLog(`CodeBuddy adapter: ${adapterStatusLine(report.codebuddyAdapter)}`);
|
|
527
|
+
if (report.traeAdapter)
|
|
528
|
+
backendLog(`Trae adapter: ${adapterStatusLine(report.traeAdapter)}`);
|
|
526
529
|
if (!suppressBackendGuidance()) {
|
|
527
530
|
for (const line of statusGuidance(report))
|
|
528
531
|
backendLog(line);
|
|
@@ -626,6 +629,8 @@ function printLifecycleResult(report) {
|
|
|
626
629
|
backendLog(`OpenCode adapter: ${adapterStatusLine(report.opencodeAdapter)}`);
|
|
627
630
|
if (report.codebuddyAdapter)
|
|
628
631
|
backendLog(`CodeBuddy adapter: ${adapterStatusLine(report.codebuddyAdapter)}`);
|
|
632
|
+
if (report.traeAdapter)
|
|
633
|
+
backendLog(`Trae adapter: ${adapterStatusLine(report.traeAdapter)}`);
|
|
629
634
|
if (report.codexPlugin) {
|
|
630
635
|
const removed = report.codexPlugin.removedPaths.length;
|
|
631
636
|
const marketplace = report.codexPlugin.marketplaceChanged ? " marketplace=updated" : " marketplace=unchanged";
|
|
@@ -795,6 +800,9 @@ function statusGuidance(report) {
|
|
|
795
800
|
}
|
|
796
801
|
return [
|
|
797
802
|
green("MemoraX Code is ready; sessions with the stable plugin shell use the active Hook runtime on their next user prompt."),
|
|
803
|
+
...(report.traeAdapter?.globalHooksActivationRequired
|
|
804
|
+
? ["Trae requires one manual step: open Trae Settings and enable Global Hooks, then start a new Trae session."]
|
|
805
|
+
: []),
|
|
798
806
|
...(optionalDshUnavailable
|
|
799
807
|
? [`DSH integration is unavailable: ${report.dshAdapter?.reason ?? "not available"}.`]
|
|
800
808
|
: []),
|
|
@@ -841,6 +849,12 @@ function statusGuidance(report) {
|
|
|
841
849
|
"Run `memorax-code start`, then restart or refresh OpenCode.",
|
|
842
850
|
];
|
|
843
851
|
}
|
|
852
|
+
if (report.traeAdapter && !isAdapterReady(report.traeAdapter)) {
|
|
853
|
+
return [
|
|
854
|
+
red("Trae adapter is not enabled."),
|
|
855
|
+
"Run `memorax-code start --clients trae`, then enable Global Hooks in Trae Settings if this is the first setup.",
|
|
856
|
+
];
|
|
857
|
+
}
|
|
844
858
|
return [
|
|
845
859
|
red("MemoraX Code needs attention."),
|
|
846
860
|
"Run `memorax-code status` and `memorax-code logs` for details.",
|
|
@@ -884,9 +898,11 @@ function adapterStatusLine(report) {
|
|
|
884
898
|
const skillStatus = report.codexSkills?.status
|
|
885
899
|
?? report.claudeSkills?.status
|
|
886
900
|
?? report.opencodeSkills?.status
|
|
887
|
-
?? report.codebuddySkills?.status
|
|
901
|
+
?? report.codebuddySkills?.status
|
|
902
|
+
?? report.traeSkills?.status;
|
|
888
903
|
const skills = skillStatus ? ` skills=${skillStatus}` : "";
|
|
889
|
-
const
|
|
904
|
+
const hookStatus = report.codebuddyHooks?.status ?? report.traeHooks?.status;
|
|
905
|
+
const hooks = hookStatus ? ` hook-runtime=${hookStatus}` : "";
|
|
890
906
|
const changed = report.changed === true ? " changed" : "";
|
|
891
907
|
const integration = report.integration ?? report.state?.integration ?? "hooks";
|
|
892
908
|
return `${enabled ? "ok" : "not enabled"} integration=${integration}${skills}${hooks}${changed}`;
|
|
@@ -897,6 +913,8 @@ function selectedLifecycleClientNames(report) {
|
|
|
897
913
|
report.claudeAdapter ? "Claude Code" : undefined,
|
|
898
914
|
report.dshAdapter ? "DSH" : undefined,
|
|
899
915
|
report.opencodeAdapter ? "OpenCode" : undefined,
|
|
916
|
+
report.codebuddyAdapter ? "CodeBuddy/WorkBuddy" : undefined,
|
|
917
|
+
report.traeAdapter ? "Trae" : undefined,
|
|
900
918
|
].filter((name) => name !== undefined);
|
|
901
919
|
}
|
|
902
920
|
function lifecycleClientName(report) {
|
|
@@ -12,6 +12,8 @@ export function readActiveManagedClients(memoraxCodeHome) {
|
|
|
12
12
|
return undefined;
|
|
13
13
|
if (value.codebuddy !== undefined && typeof value.codebuddy !== "boolean")
|
|
14
14
|
return undefined;
|
|
15
|
+
if (value.trae !== undefined && typeof value.trae !== "boolean")
|
|
16
|
+
return undefined;
|
|
15
17
|
if (value.dsh !== undefined && typeof value.dsh !== "boolean")
|
|
16
18
|
return undefined;
|
|
17
19
|
return {
|
|
@@ -22,6 +24,7 @@ export function readActiveManagedClients(memoraxCodeHome) {
|
|
|
22
24
|
dsh: value.dsh === true,
|
|
23
25
|
opencode: value.opencode === true,
|
|
24
26
|
...(value.codebuddy === true ? { codebuddy: true } : {}),
|
|
27
|
+
...(value.trae === true ? { trae: true } : {}),
|
|
25
28
|
};
|
|
26
29
|
}
|
|
27
30
|
catch {
|
|
@@ -9,14 +9,17 @@ export async function prepareClientPluginRemovalCleanup(options = {}) {
|
|
|
9
9
|
const dshProfileLifecycle = await loadDshProfileLifecycle();
|
|
10
10
|
const openCodePluginInstaller = await loadOpenCodePluginInstaller();
|
|
11
11
|
const codeBuddyPluginInstaller = await loadCodeBuddyPluginInstaller();
|
|
12
|
+
const traePluginInstaller = await loadTraePluginInstaller();
|
|
12
13
|
const home = resolveHome(options.homeDir);
|
|
13
14
|
const memoraxCodeHome = resolve(options.memoraxCodeHome ?? process.env.MEMORAX_CODE_HOME ?? join(home, ".memorax-code"));
|
|
14
15
|
const claudeState = await readJsonRecord(join(memoraxCodeHome, "adapters", "claude-code", "state.json"));
|
|
15
16
|
const claudeHome = options.claudeHome ?? stringField(claudeState, "claudeHome");
|
|
17
|
+
const traeState = await readJsonRecord(join(memoraxCodeHome, "adapters", "trae", "state.json"));
|
|
18
|
+
const traeHome = options.traeHome ?? stringField(traeState, "traeHome");
|
|
16
19
|
return async () => {
|
|
17
20
|
try {
|
|
18
21
|
return await withBackendLifecycleLock({ home: memoraxCodeHome }, async () => {
|
|
19
|
-
const [codexPlugin, claudePlugin, dshPlugin, opencodePlugin, codebuddyPlugin] = await Promise.all([
|
|
22
|
+
const [codexPlugin, claudePlugin, dshPlugin, opencodePlugin, codebuddyPlugin, traePlugin] = await Promise.all([
|
|
20
23
|
cleanupCodexAfterBackendRemoval({
|
|
21
24
|
memoraxCodeHome,
|
|
22
25
|
homeDir: home,
|
|
@@ -52,15 +55,20 @@ export async function prepareClientPluginRemovalCleanup(options = {}) {
|
|
|
52
55
|
...(options.codeBuddyHome ? { codeBuddyHome: options.codeBuddyHome } : {}),
|
|
53
56
|
}))
|
|
54
57
|
.catch((error) => removalFailure("codebuddy-plugin-remove", error)),
|
|
58
|
+
traePluginInstaller.removeTraeAdapterInstallation({
|
|
59
|
+
memoraxCodeHome,
|
|
60
|
+
...(traeHome ? { traeHome } : {}),
|
|
61
|
+
}).catch((error) => removalFailure("trae-adapter-remove", error)),
|
|
55
62
|
]);
|
|
56
63
|
return {
|
|
57
|
-
ok: codexPlugin.ok && claudePlugin.ok && dshPlugin.ok && opencodePlugin.ok && codebuddyPlugin.ok,
|
|
64
|
+
ok: codexPlugin.ok && claudePlugin.ok && dshPlugin.ok && opencodePlugin.ok && codebuddyPlugin.ok && traePlugin.ok,
|
|
58
65
|
action: "client-plugin-removal-cleanup",
|
|
59
66
|
codexPlugin,
|
|
60
67
|
claudePlugin,
|
|
61
68
|
dshPlugin,
|
|
62
69
|
opencodePlugin,
|
|
63
70
|
codebuddyPlugin,
|
|
71
|
+
traePlugin,
|
|
64
72
|
};
|
|
65
73
|
});
|
|
66
74
|
}
|
|
@@ -74,6 +82,7 @@ export async function prepareClientPluginRemovalCleanup(options = {}) {
|
|
|
74
82
|
dshPlugin: failure,
|
|
75
83
|
opencodePlugin: failure,
|
|
76
84
|
codebuddyPlugin: failure,
|
|
85
|
+
traePlugin: failure,
|
|
77
86
|
};
|
|
78
87
|
}
|
|
79
88
|
};
|
|
@@ -87,6 +96,9 @@ async function loadOpenCodePluginInstaller() {
|
|
|
87
96
|
async function loadCodeBuddyPluginInstaller() {
|
|
88
97
|
return await import(new URL("../../../memorax-code-codebuddy-adapter/src/config.mjs", import.meta.url).href);
|
|
89
98
|
}
|
|
99
|
+
async function loadTraePluginInstaller() {
|
|
100
|
+
return await import(new URL("../../../memorax-code-trae-adapter/src/config.mjs", import.meta.url).href);
|
|
101
|
+
}
|
|
90
102
|
async function loadDshProfileLifecycle() {
|
|
91
103
|
return await import(new URL("../../../memorax-code-dsh-adapter/src/profile-lifecycle.mjs", import.meta.url).href);
|
|
92
104
|
}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { loadLifecycleMemoraxCodeConfig } from "../config/memorax-code.js";
|
|
2
|
-
const allClients = Object.freeze({ codex: true, claude: true, dsh: true, opencode: true, codebuddy: true });
|
|
2
|
+
const allClients = Object.freeze({ codex: true, claude: true, dsh: true, opencode: true, codebuddy: true, trae: true });
|
|
3
3
|
export function resolveManagedClients(argv, config = {}) {
|
|
4
4
|
const explicit = argValue(argv, "--clients");
|
|
5
5
|
if (explicit !== undefined)
|
|
@@ -13,6 +13,7 @@ export function resolveManagedClients(argv, config = {}) {
|
|
|
13
13
|
dsh: config.clients.dsh !== false,
|
|
14
14
|
opencode: config.clients.opencode === true,
|
|
15
15
|
...(config.clients.codebuddy === true ? { codebuddy: true } : {}),
|
|
16
|
+
...(config.clients.trae === true ? { trae: true } : {}),
|
|
16
17
|
};
|
|
17
18
|
}
|
|
18
19
|
return { codex: true, claude: true, dsh: true, opencode: true };
|
|
@@ -24,8 +25,8 @@ export function parseManagedClients(value) {
|
|
|
24
25
|
if (normalized === "none")
|
|
25
26
|
return { codex: false, claude: false, dsh: false, opencode: false };
|
|
26
27
|
const names = normalized.split(",").map((name) => name.trim()).filter(Boolean);
|
|
27
|
-
if (names.length === 0 || names.some((name) => (name !== "codex" && name !== "claude" && name !== "dsh" && name !== "opencode" && name !== "codebuddy"))) {
|
|
28
|
-
throw new Error(`invalid --clients value: ${value}; expected a comma-separated subset of codex, claude, dsh, opencode, codebuddy, or all or none`);
|
|
28
|
+
if (names.length === 0 || names.some((name) => (name !== "codex" && name !== "claude" && name !== "dsh" && name !== "opencode" && name !== "codebuddy" && name !== "trae"))) {
|
|
29
|
+
throw new Error(`invalid --clients value: ${value}; expected a comma-separated subset of codex, claude, dsh, opencode, codebuddy, trae, or all or none`);
|
|
29
30
|
}
|
|
30
31
|
return {
|
|
31
32
|
codex: names.includes("codex"),
|
|
@@ -33,6 +34,7 @@ export function parseManagedClients(value) {
|
|
|
33
34
|
dsh: names.includes("dsh"),
|
|
34
35
|
opencode: names.includes("opencode"),
|
|
35
36
|
...(names.includes("codebuddy") ? { codebuddy: true } : {}),
|
|
37
|
+
...(names.includes("trae") ? { trae: true } : {}),
|
|
36
38
|
};
|
|
37
39
|
}
|
|
38
40
|
export function loadManagedClientsConfig(memoraxCodeHome) {
|