@memorax/memorax-code 0.1.8 → 0.1.10
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 +10 -0
- package/bin/memorax-code-codebuddy.mjs +4 -0
- package/bin/memorax-code-setup.mjs +182 -65
- package/bin/memorax-code.mjs +74 -45
- package/docs/configuration.md +82 -22
- package/docs/troubleshooting.md +69 -5
- package/lib/automatic-update.mjs +233 -0
- package/lib/memorax-code-adapter-common/src/automatic-update-state.d.mts +24 -0
- package/lib/memorax-code-adapter-common/src/automatic-update-state.mjs +73 -0
- package/lib/memorax-code-adapter-common/src/clients/codebuddy-command.mjs +82 -0
- package/lib/memorax-code-adapter-common/src/config-utils.d.mts +2 -0
- package/lib/memorax-code-adapter-common/src/config-utils.mjs +11 -0
- package/lib/memorax-code-adapter-common/src/hooks/client-hook-launcher.mjs +3 -0
- package/lib/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +45 -10
- package/lib/memorax-code-adapter-common/src/repo-memory/repo-memory-job-worker.mjs +88 -3
- package/lib/memorax-code-adapter-common/src/windows-cli-invocation.mjs +11 -1
- package/lib/memorax-code-backend/dist/app/backend-server.js +2 -2
- package/lib/memorax-code-backend/dist/app/memory-observability.js +2 -2
- package/lib/memorax-code-backend/dist/clients/codebuddy/jsonl-history.js +208 -0
- package/lib/memorax-code-backend/dist/clients/codebuddy/lifecycle.js +44 -0
- package/lib/memorax-code-backend/dist/clients/codebuddy/memory-hook-runtime.js +250 -0
- package/lib/memorax-code-backend/dist/clients/codebuddy/turn-id.js +16 -0
- package/lib/memorax-code-backend/dist/clients/codex/plugin-install.js +129 -7
- package/lib/memorax-code-backend/dist/clients/codex/rollout-turn.js +52 -2
- package/lib/memorax-code-backend/dist/config/memorax-code.js +14 -1
- package/lib/memorax-code-backend/dist/entrypoints/backend-cli.js +43 -6
- package/lib/memorax-code-backend/dist/lifecycle/active-clients.js +3 -0
- package/lib/memorax-code-backend/dist/lifecycle/automatic-update-scheduler.js +133 -0
- package/lib/memorax-code-backend/dist/lifecycle/client-plugin-removal.js +14 -2
- package/lib/memorax-code-backend/dist/lifecycle/client-selection.js +6 -4
- package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +48 -9
- package/lib/memorax-code-backend/dist/memory/cli.js +4 -2
- package/lib/memorax-code-backend/dist/memory/hook-command.js +31 -1
- 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/shared/windows-cli-invocation.js +10 -1
- package/lib/memorax-code-backend/dist/trace/config.js +12 -0
- package/lib/memorax-code-backend/dist/trace/context.js +21 -1
- package/lib/memorax-code-backend/dist/trace/store.js +12 -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/runtime-hooks/ensure-backend.mjs +2 -0
- package/lib/memorax-code-claude-adapter/src/plugin-install.mjs +3 -0
- 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/memorax-code-adapter-common/src/automatic-update-state.d.mts +24 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/automatic-update-state.mjs +73 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/clients/codebuddy-command.mjs +82 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.d.mts +2 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/config-utils.mjs +11 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/client-hook-launcher.mjs +3 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/hooks/ensure-backend-runner.mjs +45 -10
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/repo-memory/repo-memory-job-worker.mjs +88 -3
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/memorax-code-adapter-common/src/windows-cli-invocation.mjs +11 -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/runtime-hooks/ensure-backend.mjs +2 -0
- package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/src/plugin-install.mjs +3 -0
- package/lib/memorax-code-codebuddy-adapter/.codebuddy-plugin/plugin.json +7 -0
- package/lib/memorax-code-codebuddy-adapter/hooks/common-runtime.mjs +13 -0
- package/lib/memorax-code-codebuddy-adapter/hooks/hooks.json +38 -0
- package/lib/memorax-code-codebuddy-adapter/hooks/repo-memory-job.mjs +40 -0
- package/lib/memorax-code-codebuddy-adapter/hooks/runtime-hook.mjs +279 -0
- package/lib/memorax-code-codebuddy-adapter/package.json +9 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/SKILL.md +85 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/agents/claude.yaml +10 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/agents/openai.yaml +7 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/defaults.json +12 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-add.md +88 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/memorax-search.md +93 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/personal-read.md +46 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/personal-write.md +120 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-build.md +319 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-read.md +103 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-templates.md +390 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/references/repo-update.md +127 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/collect_all.py +579 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/detect_updates.py +919 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/git_commit_facets.py +222 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/github_resource_facets.py +512 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/gitlab_resource_facets.py +517 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/prepare_repo_memory.py +411 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/user_profile_memory.py +528 -0
- package/lib/memorax-code-codebuddy-adapter/skills/memorax-code/scripts/validate_memory.py +248 -0
- package/lib/memorax-code-codebuddy-adapter/src/cli.mjs +58 -0
- package/lib/memorax-code-codebuddy-adapter/src/config.mjs +277 -0
- package/lib/memorax-code-codebuddy-adapter/src/hook-manifest.mjs +47 -0
- package/lib/memorax-code-codebuddy-adapter/src/runtime-observation.mjs +65 -0
- 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/runtime-hooks/ensure-backend.mjs +4 -1
- package/lib/memorax-code-dsh-adapter/package.json +2 -1
- package/lib/memorax-code-dsh-adapter/src/profile-lifecycle.mjs +1 -0
- package/lib/memorax-code-opencode-adapter/package.json +1 -1
- package/lib/npm-invocation.mjs +51 -8
- package/lib/resolve-codebuddy-command.mjs +112 -0
- package/lib/run-entrypoint.mjs +25 -1
- package/lib/windows-cli-invocation.mjs +11 -1
- package/lib/windows-user-path.mjs +218 -0
- package/package.json +5 -3
|
@@ -0,0 +1,250 @@
|
|
|
1
|
+
import { readCodeBuddyInterruptedTranscriptTurn, readCodeBuddyTranscriptTurn, } from "./jsonl-history.js";
|
|
2
|
+
import { retrieveAutomaticMemoryContext } from "../../memory/automatic-retrieval.js";
|
|
3
|
+
import { createAutomaticMemoryWritebackRuntime } from "../../memory/automatic-writeback.js";
|
|
4
|
+
import { claimQuotaNotice, createPendingQuotaNoticeRuntime, } from "../../memory/quota-notice.js";
|
|
5
|
+
import { createMemoryTurnCoordinator } from "../../memory/turn-coordinator.js";
|
|
6
|
+
import { createRepositoryMemorySessionRuntime, resolvedRepoMemoryWorktree } from "../../memory/repository-session.js";
|
|
7
|
+
import { traceContextFromCodeBuddyHookBody } from "../../trace/context.js";
|
|
8
|
+
import { markCurrentCodeBuddyTurnOutcome, readOpenTraceTurn, recordCodeBuddyTraceEvent, traceTurnEventId, writeCurrentCodeBuddyTurn, } from "../../trace/store.js";
|
|
9
|
+
export function createCodeBuddyMemoryHookRuntime(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 automatic = options.turnCoordinator ? undefined : options.automaticWriteback ? { enqueue: options.automaticWriteback, discardForScopeUpgrade: () => 0, drain: async () => undefined, close: () => undefined } : createAutomaticMemoryWritebackRuntime({ diagnosticLogger: options.diagnosticLogger, queueQuotaNotice: pendingQuotaNotice.queue });
|
|
18
|
+
const coordinator = options.turnCoordinator ?? createMemoryTurnCoordinator({ automaticWriteback: automatic.enqueue, now, ttlMs: options.ttlMs, maxEntries: options.maxEntries, cleanupIntervalMs: options.cleanupIntervalMs });
|
|
19
|
+
const repository = options.repositoryMemorySession ?? createRepositoryMemorySessionRuntime({ onScopeUpgrade: automatic?.discardForScopeUpgrade });
|
|
20
|
+
return {
|
|
21
|
+
async recordTurnStart(command) {
|
|
22
|
+
await reconcilePreviousInterruptedTurn(coordinator, command, options, now);
|
|
23
|
+
const memory = await resolveRepository(command, options, repository);
|
|
24
|
+
const traceContext = traceContextFromCodeBuddyHookBody(command, new Date(now()).toISOString());
|
|
25
|
+
coordinator.recordTurnStart({ client: "codebuddy", sessionId: command.sessionId, clientTurnId: command.turnId, cwd: command.cwd, workspaceKind: command.workspaceKind, transcriptPath: command.transcriptPath, createdAt: now(), traceContext, repositoryMemory: memory });
|
|
26
|
+
await recordTraceBestEffort("codebuddy_memory_hook.turn_start_event", recordCodeBuddyTraceEvent({
|
|
27
|
+
eventId: traceTurnEventId(traceContext, "turn_start"),
|
|
28
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
29
|
+
env: options.env,
|
|
30
|
+
traceContext,
|
|
31
|
+
type: "turn_start",
|
|
32
|
+
source: "unknown",
|
|
33
|
+
operation: "query",
|
|
34
|
+
ok: true,
|
|
35
|
+
request: { prompt: command.prompt, cwd: command.cwd, transcriptPath: command.transcriptPath },
|
|
36
|
+
}), options.diagnosticLogger);
|
|
37
|
+
await recordTraceBestEffort("codebuddy_memory_hook.current_turn_write", writeCurrentCodeBuddyTurn(traceContext, {
|
|
38
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
39
|
+
env: options.env,
|
|
40
|
+
now: () => new Date(now()),
|
|
41
|
+
}), options.diagnosticLogger);
|
|
42
|
+
const repoMemoryWorktree = resolvedRepoMemoryWorktree(memory);
|
|
43
|
+
const pendingUserNotice = memory.ok
|
|
44
|
+
? await pendingQuotaNotice.claim(memory.memory.config)
|
|
45
|
+
: undefined;
|
|
46
|
+
const retrieval = await retrieveAutomaticMemoryContext({ diagnosticLogger: options.diagnosticLogger, claimQuotaNotice: options.claimQuotaNotice ?? claimQuotaNotice, env: options.env ?? process.env, fetchImpl: options.fetchImpl, memoryObservability: options.memoryObservability, memoryObservabilitySource: "codebuddy_hook_retrieval", query: command.prompt, repositoryMemory: memory, sessionKey: command.sessionId, traceContext: traceContextFromCodeBuddyHookBody(command) });
|
|
47
|
+
const userNotice = [pendingUserNotice, retrieval.userNotice].filter(Boolean).join("\n");
|
|
48
|
+
return { ok: true, ...(repoMemoryWorktree ? { repoMemoryWorktree } : {}), ...(retrieval.context ? { additionalContext: retrieval.context } : {}), ...(userNotice ? { userNotice } : {}) };
|
|
49
|
+
},
|
|
50
|
+
async writeback(command) {
|
|
51
|
+
const entry = coordinator.getTurn({ client: "codebuddy", sessionId: command.sessionId, clientTurnId: command.turnId });
|
|
52
|
+
if (!command.sessionId)
|
|
53
|
+
return { ok: true, scheduled: false, reason: "missing_session_id" };
|
|
54
|
+
if (!command.turnId)
|
|
55
|
+
return { ok: true, scheduled: false, reason: "turn_id_missing" };
|
|
56
|
+
if (entry?.transcriptPath && entry.transcriptPath !== command.transcriptPath) {
|
|
57
|
+
await recordCodeBuddyTurnEnd(options, traceContextForWriteback(command, entry), undefined, "transcript_path_mismatch");
|
|
58
|
+
return { ok: true, scheduled: false, reason: "transcript_path_mismatch" };
|
|
59
|
+
}
|
|
60
|
+
const traceContext = traceContextForWriteback(command, entry);
|
|
61
|
+
const transcript = await readWithRetry({ transcriptPath: command.transcriptPath, sessionId: command.sessionId, turnId: command.turnId }, options);
|
|
62
|
+
if (!transcript.ok) {
|
|
63
|
+
await recordCodeBuddyTurnEnd(options, traceContext, undefined, transcript.reason);
|
|
64
|
+
return { ok: true, scheduled: false, reason: transcript.reason };
|
|
65
|
+
}
|
|
66
|
+
await recordCodeBuddyTurnEnd(options, traceContext, transcript.turn);
|
|
67
|
+
const memory = await resolveRepository({ ...command, cwd: command.cwd ?? entry?.cwd, workspaceKind: command.workspaceKind ?? entry?.workspaceKind }, options, repository);
|
|
68
|
+
const completed = await coordinator.completeMaterializedTurn({ key: { client: "codebuddy", sessionId: command.sessionId, clientTurnId: command.turnId }, metadata: entry, resolveRepositoryMemory: async () => memory, userText: transcript.turn.userPrompt, assistantText: transcript.turn.assistantReply, writeback: { client: "codebuddy", sessionKey: command.sessionId, env: options.env ?? process.env, fetchImpl: options.fetchImpl, memoryObservability: options.memoryObservability, memoryObservabilitySource: "codebuddy_hook_writeback", traceContext: traceContextFromCodeBuddyHookBody(command) } });
|
|
69
|
+
await recordCodeBuddyTurnMaterialization(options, traceContext, transcript.turn);
|
|
70
|
+
return completed.scheduled ? { ok: true, scheduled: true } : { ok: true, scheduled: false, reason: completed.reason };
|
|
71
|
+
},
|
|
72
|
+
size() { return coordinator.size("codebuddy"); },
|
|
73
|
+
close() { if (!options.turnCoordinator)
|
|
74
|
+
coordinator.close(); if (!options.repositoryMemorySession)
|
|
75
|
+
repository.close(); automatic?.close?.(); if (ownsPendingQuotaNotice)
|
|
76
|
+
pendingQuotaNotice.close(); },
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
async function resolveRepository(command, options, repository) {
|
|
80
|
+
return repository.resolve({ client: "codebuddy", sessionId: command.sessionId, workspaceRoot: command.cwd, workspaceKind: command.workspaceKind, memoraxCodeHome: options.memoraxCodeHome ?? options.env?.MEMORAX_CODE_HOME, env: options.env });
|
|
81
|
+
}
|
|
82
|
+
async function readWithRetry(input, options) {
|
|
83
|
+
const attempts = options.transcriptReadAttempts ?? 6;
|
|
84
|
+
let result = await readCodeBuddyTranscriptTurn(input);
|
|
85
|
+
for (let i = 1; i < attempts && !result.ok && ["transcript_unavailable", "turn_not_found", "user_prompt_missing", "assistant_message_missing"].includes(result.reason); i += 1) {
|
|
86
|
+
await new Promise((resolve) => setTimeout(resolve, options.transcriptRetryDelayMs ?? 100));
|
|
87
|
+
result = await readCodeBuddyTranscriptTurn(input);
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
}
|
|
91
|
+
function traceContextForWriteback(command, entry) {
|
|
92
|
+
return traceContextFromCodeBuddyHookBody({
|
|
93
|
+
...command,
|
|
94
|
+
cwd: command.cwd ?? entry?.cwd,
|
|
95
|
+
workspaceKind: command.workspaceKind ?? entry?.workspaceKind,
|
|
96
|
+
}) ?? entry?.traceContext;
|
|
97
|
+
}
|
|
98
|
+
async function recordCodeBuddyTurnEnd(options, traceContext, turn, failureReason) {
|
|
99
|
+
const outcome = turn ? "completed" : failureReason === "assistant_message_missing" ? "interrupted" : undefined;
|
|
100
|
+
const recorded = await recordTraceBestEffort("codebuddy_memory_hook.turn_end_event", recordCodeBuddyTraceEvent({
|
|
101
|
+
eventId: traceTurnEventId(traceContext, "turn_end"),
|
|
102
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
103
|
+
env: options.env,
|
|
104
|
+
traceContext,
|
|
105
|
+
type: "turn_end",
|
|
106
|
+
source: turn ? "codebuddy-hook" : "codebuddy-transcript",
|
|
107
|
+
operation: "reply",
|
|
108
|
+
ok: Boolean(turn),
|
|
109
|
+
...(outcome ? { outcome } : {}),
|
|
110
|
+
...(turn ? {
|
|
111
|
+
activities: turn.activities,
|
|
112
|
+
sessionTurnIndex: turn.sessionTurnIndex,
|
|
113
|
+
request: { prompt: turn.userPrompt },
|
|
114
|
+
response: { assistantMessage: turn.assistantReply },
|
|
115
|
+
} : {
|
|
116
|
+
error: failureReason,
|
|
117
|
+
}),
|
|
118
|
+
}), options.diagnosticLogger);
|
|
119
|
+
if (outcome) {
|
|
120
|
+
await recordTraceBestEffort("codebuddy_memory_hook.current_turn_close", markCurrentCodeBuddyTurnOutcome(traceContext, outcome, { memoraxCodeHome: options.memoraxCodeHome, env: options.env }), options.diagnosticLogger);
|
|
121
|
+
}
|
|
122
|
+
return recorded;
|
|
123
|
+
}
|
|
124
|
+
async function recordCodeBuddyTurnMaterialization(options, traceContext, turn) {
|
|
125
|
+
const originalEventId = traceTurnEventId(traceContext, "turn_end");
|
|
126
|
+
if (!originalEventId)
|
|
127
|
+
return;
|
|
128
|
+
await recordTraceBestEffort("codebuddy_memory_hook.turn_materialized_event", recordCodeBuddyTraceEvent({
|
|
129
|
+
eventId: traceTurnEventId(traceContext, "turn_materialized"),
|
|
130
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
131
|
+
env: options.env,
|
|
132
|
+
traceContext,
|
|
133
|
+
type: "turn_materialized",
|
|
134
|
+
source: "codebuddy-transcript",
|
|
135
|
+
operation: "reply",
|
|
136
|
+
ok: true,
|
|
137
|
+
outcome: "completed",
|
|
138
|
+
activities: turn.activities,
|
|
139
|
+
sessionTurnIndex: turn.sessionTurnIndex,
|
|
140
|
+
request: { original_event_id: originalEventId, prompt: turn.userPrompt },
|
|
141
|
+
response: { assistantMessage: turn.assistantReply },
|
|
142
|
+
}), options.diagnosticLogger);
|
|
143
|
+
}
|
|
144
|
+
async function reconcilePreviousInterruptedTurn(coordinator, currentTurn, options, now) {
|
|
145
|
+
const candidate = await previousInterruptedTurnCandidate(coordinator, currentTurn, options, now);
|
|
146
|
+
if (!candidate)
|
|
147
|
+
return;
|
|
148
|
+
const transcript = await readCodeBuddyInterruptedTranscriptTurn({
|
|
149
|
+
transcriptPath: candidate.transcriptPath,
|
|
150
|
+
sessionId: candidate.sessionId,
|
|
151
|
+
turnId: candidate.turnId,
|
|
152
|
+
});
|
|
153
|
+
if (!transcript.ok)
|
|
154
|
+
return;
|
|
155
|
+
await recordCodeBuddyInterruptedTurnEnd(options, candidate.traceContext, transcript.turn);
|
|
156
|
+
coordinator.discardTurn({
|
|
157
|
+
client: "codebuddy",
|
|
158
|
+
sessionId: candidate.sessionId,
|
|
159
|
+
clientTurnId: candidate.turnId,
|
|
160
|
+
}, "interrupted");
|
|
161
|
+
options.diagnosticLogger?.("codebuddy_memory_hook.interrupted_turn_reconciled", {
|
|
162
|
+
sessionId: candidate.sessionId,
|
|
163
|
+
turnId: candidate.turnId,
|
|
164
|
+
assistantChars: transcript.turn.assistantReply.length,
|
|
165
|
+
activityCount: transcript.turn.activities.length,
|
|
166
|
+
sessionTurnIndex: transcript.turn.sessionTurnIndex,
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
async function previousInterruptedTurnCandidate(coordinator, currentTurn, options, now) {
|
|
170
|
+
const open = await readOpenTraceTurn({
|
|
171
|
+
client: "codebuddy",
|
|
172
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
173
|
+
env: options.env,
|
|
174
|
+
expectedSessionId: currentTurn.sessionId,
|
|
175
|
+
allowStale: true,
|
|
176
|
+
now: () => new Date(now()),
|
|
177
|
+
});
|
|
178
|
+
if (!open.ok && open.reason === "closed")
|
|
179
|
+
return undefined;
|
|
180
|
+
if (open.ok && open.traceContext.turnId && open.traceContext.turnId !== currentTurn.turnId) {
|
|
181
|
+
const turnId = open.traceContext.turnId;
|
|
182
|
+
const cached = coordinator.getTurn({ client: "codebuddy", sessionId: currentTurn.sessionId, clientTurnId: turnId });
|
|
183
|
+
return {
|
|
184
|
+
sessionId: currentTurn.sessionId,
|
|
185
|
+
turnId,
|
|
186
|
+
transcriptPath: cached?.transcriptPath
|
|
187
|
+
?? open.traceContext.transcriptPath
|
|
188
|
+
?? currentTurn.transcriptPath,
|
|
189
|
+
traceContext: cached?.traceContext ?? open.traceContext,
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
const cached = coordinator.latestTurn({
|
|
193
|
+
client: "codebuddy",
|
|
194
|
+
sessionId: currentTurn.sessionId,
|
|
195
|
+
excludeClientTurnId: currentTurn.turnId,
|
|
196
|
+
});
|
|
197
|
+
if (!cached)
|
|
198
|
+
return undefined;
|
|
199
|
+
const transcriptPath = cached.transcriptPath ?? currentTurn.transcriptPath;
|
|
200
|
+
const traceContext = cached.traceContext ?? traceContextFromCodeBuddyHookBody({
|
|
201
|
+
sessionId: cached.sessionId,
|
|
202
|
+
turnId: cached.clientTurnId,
|
|
203
|
+
cwd: cached.cwd,
|
|
204
|
+
workspaceKind: cached.workspaceKind,
|
|
205
|
+
transcriptPath,
|
|
206
|
+
}, new Date(cached.createdAt).toISOString());
|
|
207
|
+
if (!traceContext)
|
|
208
|
+
return undefined;
|
|
209
|
+
return {
|
|
210
|
+
sessionId: cached.sessionId,
|
|
211
|
+
turnId: cached.clientTurnId,
|
|
212
|
+
transcriptPath,
|
|
213
|
+
traceContext,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
async function recordCodeBuddyInterruptedTurnEnd(options, traceContext, turn) {
|
|
217
|
+
const recorded = await recordTraceBestEffort("codebuddy_memory_hook.interrupted_turn_end_event", recordCodeBuddyTraceEvent({
|
|
218
|
+
eventId: traceTurnEventId(traceContext, "turn_end"),
|
|
219
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
220
|
+
env: options.env,
|
|
221
|
+
traceContext,
|
|
222
|
+
type: "turn_end",
|
|
223
|
+
source: "codebuddy-transcript",
|
|
224
|
+
operation: "reply",
|
|
225
|
+
ok: true,
|
|
226
|
+
outcome: "interrupted",
|
|
227
|
+
activities: turn.activities,
|
|
228
|
+
sessionTurnIndex: turn.sessionTurnIndex,
|
|
229
|
+
request: { prompt: turn.userPrompt },
|
|
230
|
+
response: { assistantMessage: turn.assistantReply },
|
|
231
|
+
}), options.diagnosticLogger);
|
|
232
|
+
if (!recorded || (!recorded.written && recorded.reason !== "duplicate_event"))
|
|
233
|
+
return;
|
|
234
|
+
await recordTraceBestEffort("codebuddy_memory_hook.interrupted_current_turn_close", markCurrentCodeBuddyTurnOutcome(traceContext, "interrupted", {
|
|
235
|
+
memoraxCodeHome: options.memoraxCodeHome,
|
|
236
|
+
env: options.env,
|
|
237
|
+
}), options.diagnosticLogger);
|
|
238
|
+
}
|
|
239
|
+
async function recordTraceBestEffort(label, promise, diagnosticLogger) {
|
|
240
|
+
try {
|
|
241
|
+
return await promise;
|
|
242
|
+
}
|
|
243
|
+
catch (error) {
|
|
244
|
+
diagnosticLogger?.("codebuddy_trace.write_failed", {
|
|
245
|
+
label,
|
|
246
|
+
error: error instanceof Error ? error.message : String(error),
|
|
247
|
+
});
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
export function parseCodeBuddyTurnId(input) {
|
|
3
|
+
const prefix = `${input.sessionId}:`;
|
|
4
|
+
if (!input.turnId.startsWith(prefix))
|
|
5
|
+
return undefined;
|
|
6
|
+
const match = /^(0|[1-9]\d*):([0-9a-f]{64})$/.exec(input.turnId.slice(prefix.length));
|
|
7
|
+
if (!match)
|
|
8
|
+
return undefined;
|
|
9
|
+
const boundary = Number(match[1]);
|
|
10
|
+
if (!Number.isSafeInteger(boundary))
|
|
11
|
+
return undefined;
|
|
12
|
+
return { boundary, promptDigest: match[2] };
|
|
13
|
+
}
|
|
14
|
+
export function codeBuddyPromptDigest(prompt) {
|
|
15
|
+
return createHash("sha256").update(prompt.trim()).digest("hex");
|
|
16
|
+
}
|
|
@@ -11,7 +11,9 @@ import { confirmHookTrust, listMemoraxCodeHooks, trustCodexPluginHookSelection,
|
|
|
11
11
|
import { resolveWindowsCliInvocation } from "../../shared/windows-cli-invocation.js";
|
|
12
12
|
const PLUGIN_NAME = "memorax-code-codex-adapter";
|
|
13
13
|
const CLI_MARKETPLACE_NAME = "memorax-code";
|
|
14
|
+
const PLUGIN_ID = `${PLUGIN_NAME}@${CLI_MARKETPLACE_NAME}`;
|
|
14
15
|
const ADAPTER_COMMON_NAME = "memorax-code-adapter-common";
|
|
16
|
+
const PLUGIN_LIST_TIMEOUT_MS = 10_000;
|
|
15
17
|
export async function installCodexPlugin(options = {}) {
|
|
16
18
|
const home = resolveHome(options.homeDir);
|
|
17
19
|
const codexHome = resolveCodexHome(options.codexHome, home);
|
|
@@ -124,14 +126,25 @@ export async function activateCodexPlugin(options = {}) {
|
|
|
124
126
|
if (!existsSync(join(cliMarketplaceRoot, ".agents", "plugins", "marketplace.json"))) {
|
|
125
127
|
await stageCodexCliMarketplace(install);
|
|
126
128
|
}
|
|
129
|
+
const registrationBefore = await inspectCodexPluginRegistration({
|
|
130
|
+
...options,
|
|
131
|
+
codexHome: install.codexHome,
|
|
132
|
+
codexCommand,
|
|
133
|
+
workspace,
|
|
134
|
+
});
|
|
127
135
|
let marketplaceAdd;
|
|
128
136
|
let pluginAdd;
|
|
129
|
-
if (
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
137
|
+
if (!registrationBefore.registered || !registrationBefore.enabled) {
|
|
138
|
+
if (registrationBefore.available) {
|
|
139
|
+
marketplaceAdd = skippedPluginCommand("marketplace_registration_preserved");
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
marketplaceAdd = await runCommand(codexCommand, ["plugin", "marketplace", "add", cliMarketplaceRoot, "--json"], { cwd: workspace, env });
|
|
143
|
+
if (!marketplaceAdd.ok) {
|
|
144
|
+
throw new Error(`codex plugin marketplace add failed: ${marketplaceAdd.stderr || marketplaceAdd.stdout || "unknown error"}`);
|
|
145
|
+
}
|
|
133
146
|
}
|
|
134
|
-
pluginAdd = await runCommand(codexCommand, ["plugin", "add",
|
|
147
|
+
pluginAdd = await runCommand(codexCommand, ["plugin", "add", PLUGIN_ID, "--json"], { cwd: workspace, env });
|
|
135
148
|
if (!pluginAdd.ok) {
|
|
136
149
|
throw new Error(`codex plugin add failed: ${pluginAdd.stderr || pluginAdd.stdout || "unknown error"}`);
|
|
137
150
|
}
|
|
@@ -140,6 +153,19 @@ export async function activateCodexPlugin(options = {}) {
|
|
|
140
153
|
marketplaceAdd = skippedPluginCommand("versioned_installation_preserved");
|
|
141
154
|
pluginAdd = skippedPluginCommand("versioned_installation_preserved");
|
|
142
155
|
}
|
|
156
|
+
const registration = await inspectCodexPluginRegistration({
|
|
157
|
+
...options,
|
|
158
|
+
codexHome: install.codexHome,
|
|
159
|
+
codexCommand,
|
|
160
|
+
workspace,
|
|
161
|
+
});
|
|
162
|
+
const expectedVersion = stringField(await readJsonRecord(join(install.pluginSourcePath, ".codex-plugin", "plugin.json")), "version");
|
|
163
|
+
if (!registration.registered || !registration.enabled) {
|
|
164
|
+
throw new Error("Codex plugin registration was not enabled after activation");
|
|
165
|
+
}
|
|
166
|
+
if (!expectedVersion || registration.version !== expectedVersion) {
|
|
167
|
+
throw new Error(`Codex plugin registration version does not match ${expectedVersion ?? "the installed plugin"}`);
|
|
168
|
+
}
|
|
143
169
|
await removePersonalMarketplaceEntry(bootstrapMarketplacePath);
|
|
144
170
|
const hooks = await listMemoraxCodeHooks(codexCommand, workspace, env);
|
|
145
171
|
if (hooks.length === 0) {
|
|
@@ -163,12 +189,37 @@ export async function activateCodexPlugin(options = {}) {
|
|
|
163
189
|
workspace,
|
|
164
190
|
marketplaceAdd,
|
|
165
191
|
pluginAdd,
|
|
192
|
+
registrationBefore,
|
|
193
|
+
registration,
|
|
166
194
|
hooks,
|
|
167
195
|
trustedHooks: hooks.length,
|
|
168
196
|
configPath,
|
|
169
197
|
startsBackend: false,
|
|
170
198
|
};
|
|
171
199
|
}
|
|
200
|
+
export async function inspectCodexPluginRegistration(options = {}) {
|
|
201
|
+
const home = resolveHome(options.homeDir);
|
|
202
|
+
const codexHome = resolveCodexHome(options.codexHome, home);
|
|
203
|
+
const codexCommand = options.codexCommand ?? process.env.CODEX_CLI_PATH ?? "codex";
|
|
204
|
+
const workspace = resolve(options.workspace ?? process.cwd());
|
|
205
|
+
const result = await runCommand(codexCommand, ["plugin", "list", "--available", "--json"], {
|
|
206
|
+
cwd: workspace,
|
|
207
|
+
env: { ...process.env, HOME: home, CODEX_HOME: codexHome },
|
|
208
|
+
timeoutMs: PLUGIN_LIST_TIMEOUT_MS,
|
|
209
|
+
});
|
|
210
|
+
if (!result.ok) {
|
|
211
|
+
throw new Error(`codex plugin list failed: ${result.stderr || result.stdout || "unknown error"}`);
|
|
212
|
+
}
|
|
213
|
+
const state = parseCodexPluginList(result.stdout);
|
|
214
|
+
return {
|
|
215
|
+
ok: true,
|
|
216
|
+
action: "codex-plugin-registration",
|
|
217
|
+
codexHome,
|
|
218
|
+
codexCommand,
|
|
219
|
+
workspace,
|
|
220
|
+
...state,
|
|
221
|
+
};
|
|
222
|
+
}
|
|
172
223
|
export async function removeCodexPlugin(options = {}) {
|
|
173
224
|
const home = resolveHome(options.homeDir);
|
|
174
225
|
const codexHome = resolveCodexHome(options.codexHome, home);
|
|
@@ -368,10 +419,12 @@ function mjsFiles(root) {
|
|
|
368
419
|
}
|
|
369
420
|
async function writePluginMetadata(pluginSourcePath, codexCommand) {
|
|
370
421
|
const normalizedCodexCommand = nonEmpty(codexCommand);
|
|
422
|
+
const npmExecPath = nonEmpty(process.env.MEMORAX_CODE_NPM_EXEC_PATH);
|
|
371
423
|
const metadata = {
|
|
372
424
|
version: 1,
|
|
373
425
|
memoraxCodeCommand: process.argv[1],
|
|
374
426
|
...(normalizedCodexCommand ? { codexCommand: normalizedCodexCommand } : {}),
|
|
427
|
+
...(npmExecPath ? { npmExecPath } : {}),
|
|
375
428
|
writtenAt: new Date().toISOString(),
|
|
376
429
|
};
|
|
377
430
|
await writeFile(join(pluginSourcePath, ".memorax-code-package.json"), `${JSON.stringify(metadata, null, 2)}\n`, "utf8");
|
|
@@ -447,6 +500,55 @@ async function readMarketplace(marketplacePath) {
|
|
|
447
500
|
function skippedPluginCommand(reason) {
|
|
448
501
|
return { ok: true, stdout: "", stderr: "", skipped: true, reason };
|
|
449
502
|
}
|
|
503
|
+
function parseCodexPluginList(stdout) {
|
|
504
|
+
let parsed;
|
|
505
|
+
try {
|
|
506
|
+
parsed = JSON.parse(stdout);
|
|
507
|
+
}
|
|
508
|
+
catch {
|
|
509
|
+
throw new Error("codex plugin list returned invalid JSON");
|
|
510
|
+
}
|
|
511
|
+
if (!isRecord(parsed) || !Array.isArray(parsed.installed) || !Array.isArray(parsed.available)) {
|
|
512
|
+
throw new Error("codex plugin list returned an invalid registry payload");
|
|
513
|
+
}
|
|
514
|
+
const installed = matchingPluginEntry(parsed.installed, "installed");
|
|
515
|
+
const available = matchingPluginEntry(parsed.available, "available");
|
|
516
|
+
if (!installed) {
|
|
517
|
+
return {
|
|
518
|
+
available: available !== undefined,
|
|
519
|
+
registered: false,
|
|
520
|
+
enabled: false,
|
|
521
|
+
};
|
|
522
|
+
}
|
|
523
|
+
const version = typeof installed.version === "string" ? nonEmpty(installed.version) : undefined;
|
|
524
|
+
if (installed.installed !== true
|
|
525
|
+
|| typeof installed.enabled !== "boolean"
|
|
526
|
+
|| !version) {
|
|
527
|
+
throw new Error("codex plugin list returned an invalid MemoraX Code registration");
|
|
528
|
+
}
|
|
529
|
+
return {
|
|
530
|
+
available: true,
|
|
531
|
+
registered: true,
|
|
532
|
+
enabled: installed.enabled,
|
|
533
|
+
version,
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
function matchingPluginEntry(entries, collection) {
|
|
537
|
+
const matches = entries.filter((entry) => isRecord(entry) && (entry.pluginId === PLUGIN_ID
|
|
538
|
+
|| (entry.name === PLUGIN_NAME && entry.marketplaceName === CLI_MARKETPLACE_NAME)));
|
|
539
|
+
if (matches.length > 1) {
|
|
540
|
+
throw new Error(`codex plugin list returned duplicate MemoraX Code ${collection} entries`);
|
|
541
|
+
}
|
|
542
|
+
const entry = matches[0];
|
|
543
|
+
if (!entry)
|
|
544
|
+
return undefined;
|
|
545
|
+
if (entry.pluginId !== PLUGIN_ID
|
|
546
|
+
|| entry.name !== PLUGIN_NAME
|
|
547
|
+
|| entry.marketplaceName !== CLI_MARKETPLACE_NAME) {
|
|
548
|
+
throw new Error(`codex plugin list returned an invalid MemoraX Code ${collection} entry`);
|
|
549
|
+
}
|
|
550
|
+
return entry;
|
|
551
|
+
}
|
|
450
552
|
function pluginEntry(sourcePath) {
|
|
451
553
|
return {
|
|
452
554
|
name: PLUGIN_NAME,
|
|
@@ -495,13 +597,33 @@ function runCommand(command, args, options) {
|
|
|
495
597
|
});
|
|
496
598
|
let stdout = "";
|
|
497
599
|
let stderr = "";
|
|
600
|
+
let settled = false;
|
|
601
|
+
let timeout;
|
|
602
|
+
const finish = (result) => {
|
|
603
|
+
if (settled)
|
|
604
|
+
return;
|
|
605
|
+
settled = true;
|
|
606
|
+
if (timeout)
|
|
607
|
+
clearTimeout(timeout);
|
|
608
|
+
resolveResult(result);
|
|
609
|
+
};
|
|
610
|
+
if (options.timeoutMs) {
|
|
611
|
+
timeout = setTimeout(() => {
|
|
612
|
+
child.kill();
|
|
613
|
+
finish({
|
|
614
|
+
ok: false,
|
|
615
|
+
stdout,
|
|
616
|
+
stderr: stderr || `command timed out after ${options.timeoutMs}ms`,
|
|
617
|
+
});
|
|
618
|
+
}, options.timeoutMs);
|
|
619
|
+
}
|
|
498
620
|
child.stdout.on("data", (chunk) => { stdout += String(chunk); });
|
|
499
621
|
child.stderr.on("data", (chunk) => { stderr += String(chunk); });
|
|
500
622
|
child.on("error", (error) => {
|
|
501
|
-
|
|
623
|
+
finish({ ok: false, stdout, stderr: stderr || error.message });
|
|
502
624
|
});
|
|
503
625
|
child.on("close", (code) => {
|
|
504
|
-
|
|
626
|
+
finish({ ok: code === 0, stdout, stderr });
|
|
505
627
|
});
|
|
506
628
|
});
|
|
507
629
|
}
|
|
@@ -32,6 +32,8 @@ export function codexRolloutTurnFromJsonLines(transcript, input) {
|
|
|
32
32
|
if (!codexRolloutSessionMatches(scan, input.sessionId)) {
|
|
33
33
|
return { ok: false, reason: "transcript_session_mismatch" };
|
|
34
34
|
}
|
|
35
|
+
if (scan.turnMetadataMismatch)
|
|
36
|
+
return { ok: false, reason: "turn_metadata_mismatch" };
|
|
35
37
|
if (!scan.targetSeen)
|
|
36
38
|
return { ok: false, reason: "turn_not_found" };
|
|
37
39
|
if (!scan.userPrompt)
|
|
@@ -48,6 +50,8 @@ export function codexInterruptedRolloutTurnFromJsonLines(transcript, input) {
|
|
|
48
50
|
if (!codexRolloutSessionMatches(scan, input.sessionId)) {
|
|
49
51
|
return { ok: false, reason: "transcript_session_mismatch" };
|
|
50
52
|
}
|
|
53
|
+
if (scan.turnMetadataMismatch)
|
|
54
|
+
return { ok: false, reason: "turn_metadata_mismatch" };
|
|
51
55
|
if (!scan.targetSeen)
|
|
52
56
|
return { ok: false, reason: "turn_not_found" };
|
|
53
57
|
if (!scan.userPrompt)
|
|
@@ -74,9 +78,12 @@ function scanCodexRolloutTurn(transcript, targetTurnId) {
|
|
|
74
78
|
let targetBoundarySeen = false;
|
|
75
79
|
let activeTurnId;
|
|
76
80
|
let targetSeen = false;
|
|
81
|
+
let turnMetadataMismatch = false;
|
|
77
82
|
let userPrompt;
|
|
78
83
|
let assistantReply;
|
|
79
84
|
const visibleAssistantMessages = [];
|
|
85
|
+
let responseItemUserPrompt;
|
|
86
|
+
let responseItemAssistantReply;
|
|
80
87
|
let interrupted = false;
|
|
81
88
|
let interruptedAt;
|
|
82
89
|
let rolledBack = false;
|
|
@@ -145,6 +152,26 @@ function scanCodexRolloutTurn(transcript, targetTurnId) {
|
|
|
145
152
|
const activities = activityCandidatesFromResponseItem(payload);
|
|
146
153
|
if (activities.length > 0)
|
|
147
154
|
targetActivityGroups.push(activities);
|
|
155
|
+
if (stringValue(payload.type) === "message") {
|
|
156
|
+
const role = stringValue(payload.role);
|
|
157
|
+
const message = responseItemMessageText(payload.content, role);
|
|
158
|
+
const authoritativeMessage = role === "user"
|
|
159
|
+
|| (role === "assistant" && payload.phase === "final_answer");
|
|
160
|
+
if (authoritativeMessage && message) {
|
|
161
|
+
const responseTurnId = responseItemTurnId(payload);
|
|
162
|
+
if (responseTurnId && responseTurnId !== activeTurnId) {
|
|
163
|
+
turnMetadataMismatch = true;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (role === "user") {
|
|
167
|
+
userMessageTurnIds.add(activeTurnId);
|
|
168
|
+
responseItemUserPrompt = message;
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
171
|
+
responseItemAssistantReply = message;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
148
175
|
}
|
|
149
176
|
continue;
|
|
150
177
|
}
|
|
@@ -231,8 +258,9 @@ function scanCodexRolloutTurn(transcript, targetTurnId) {
|
|
|
231
258
|
ambiguousSessionMetadata,
|
|
232
259
|
composite,
|
|
233
260
|
targetSeen,
|
|
234
|
-
|
|
235
|
-
|
|
261
|
+
turnMetadataMismatch,
|
|
262
|
+
userPrompt: responseItemUserPrompt ?? userPrompt,
|
|
263
|
+
assistantReply: responseItemAssistantReply ?? assistantReply,
|
|
236
264
|
visibleAssistantMessages,
|
|
237
265
|
interrupted,
|
|
238
266
|
interruptedAt,
|
|
@@ -245,6 +273,28 @@ function scanCodexRolloutTurn(transcript, targetTurnId) {
|
|
|
245
273
|
userMessageTurnIds,
|
|
246
274
|
};
|
|
247
275
|
}
|
|
276
|
+
function responseItemTurnId(payload) {
|
|
277
|
+
const metadata = isRecord(payload.internal_chat_message_metadata_passthrough)
|
|
278
|
+
? payload.internal_chat_message_metadata_passthrough
|
|
279
|
+
: undefined;
|
|
280
|
+
return metadata
|
|
281
|
+
? stringValue(metadata.turn_id) ?? stringValue(metadata.turnId)
|
|
282
|
+
: undefined;
|
|
283
|
+
}
|
|
284
|
+
function responseItemMessageText(value, role) {
|
|
285
|
+
if (!Array.isArray(value))
|
|
286
|
+
return undefined;
|
|
287
|
+
const expectedType = role === "user" ? "input_text" : role === "assistant" ? "output_text" : undefined;
|
|
288
|
+
if (!expectedType)
|
|
289
|
+
return undefined;
|
|
290
|
+
const parts = value.flatMap((item) => {
|
|
291
|
+
if (!isRecord(item) || stringValue(item.type) !== expectedType)
|
|
292
|
+
return [];
|
|
293
|
+
const text = nonBlankString(item.text);
|
|
294
|
+
return text ? [text] : [];
|
|
295
|
+
});
|
|
296
|
+
return parts.length > 0 ? parts.join("\n") : undefined;
|
|
297
|
+
}
|
|
248
298
|
function codexRolloutSessionMatches(scan, sessionId) {
|
|
249
299
|
return scan.authoritySessionId === sessionId && !scan.ambiguousSessionMetadata;
|
|
250
300
|
}
|
|
@@ -70,6 +70,10 @@ export function renderDefaultMemoraxCodeConfig() {
|
|
|
70
70
|
"enabled = true # Enable local OpenCode session memory trace collection.",
|
|
71
71
|
"capture_content = true # Store content in local OpenCode trace events.",
|
|
72
72
|
"",
|
|
73
|
+
"[trace.codebuddy]",
|
|
74
|
+
"enabled = true # Enable local CodeBuddy session memory trace collection.",
|
|
75
|
+
"capture_content = true # Store content in local CodeBuddy trace events.",
|
|
76
|
+
"",
|
|
73
77
|
].join("\n");
|
|
74
78
|
}
|
|
75
79
|
export async function seedMissingMemoraxCodeConfig(memoraxCodeHome = defaultMemoraxCodeHome(process.env)) {
|
|
@@ -146,12 +150,14 @@ function normalizeMemoraxCodeConfig(value) {
|
|
|
146
150
|
const traceClaude = recordValue(trace?.claude);
|
|
147
151
|
const traceDsh = recordValue(trace?.dsh);
|
|
148
152
|
const traceOpenCode = recordValue(trace?.opencode);
|
|
153
|
+
const traceCodeBuddy = recordValue(trace?.codebuddy);
|
|
149
154
|
return (prune({
|
|
150
155
|
clients: prune({
|
|
151
156
|
codex: booleanField(clients, "codex"),
|
|
152
157
|
claude: booleanField(clients, "claude"),
|
|
153
158
|
dsh: booleanField(clients, "dsh"),
|
|
154
159
|
opencode: booleanField(clients, "opencode"),
|
|
160
|
+
codebuddy: booleanField(clients, "codebuddy"),
|
|
155
161
|
}),
|
|
156
162
|
memorax: prune({
|
|
157
163
|
endpoint: stringField(memorax, "endpoint"),
|
|
@@ -230,6 +236,13 @@ function normalizeMemoraxCodeConfig(value) {
|
|
|
230
236
|
max_event_chars: numberField(traceOpenCode, "max_event_chars"),
|
|
231
237
|
max_file_bytes: numberField(traceOpenCode, "max_file_bytes"),
|
|
232
238
|
}),
|
|
239
|
+
codebuddy: prune({
|
|
240
|
+
enabled: booleanField(traceCodeBuddy, "enabled"),
|
|
241
|
+
capture_content: booleanField(traceCodeBuddy, "capture_content"),
|
|
242
|
+
retention_days: numberField(traceCodeBuddy, "retention_days"),
|
|
243
|
+
max_event_chars: numberField(traceCodeBuddy, "max_event_chars"),
|
|
244
|
+
max_file_bytes: numberField(traceCodeBuddy, "max_file_bytes"),
|
|
245
|
+
}),
|
|
233
246
|
}),
|
|
234
247
|
}) ?? {});
|
|
235
248
|
}
|
|
@@ -242,7 +255,7 @@ function validateRawLifecycleConfig(value, path) {
|
|
|
242
255
|
const clients = tableValue(rawClients);
|
|
243
256
|
if (!clients)
|
|
244
257
|
throw invalidLifecycleConfig(path, "clients must be a table");
|
|
245
|
-
for (const field of ["codex", "claude", "dsh", "opencode"]) {
|
|
258
|
+
for (const field of ["codex", "claude", "dsh", "opencode", "codebuddy"]) {
|
|
246
259
|
if (clients[field] !== undefined && typeof clients[field] !== "boolean") {
|
|
247
260
|
throw invalidLifecycleConfig(path, `clients.${field} must be a boolean`);
|
|
248
261
|
}
|