@memorax/memorax-code 0.1.6 → 0.1.8

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.
Files changed (70) hide show
  1. package/README.md +36 -5
  2. package/bin/memorax-code-setup.mjs +0 -3
  3. package/docs/configuration.md +1 -3
  4. package/docs/troubleshooting.md +27 -8
  5. package/lib/memorax-code-backend/dist/app/backend-server.js +2 -29
  6. package/lib/memorax-code-backend/dist/app/memory-observability.js +1 -4
  7. package/lib/memorax-code-backend/dist/app/state.js +1 -12
  8. package/lib/memorax-code-backend/dist/clients/claude/transcript-turn.js +0 -182
  9. package/lib/memorax-code-backend/dist/clients/opencode/message-turn.js +68 -2
  10. package/lib/memorax-code-backend/dist/lifecycle/backend/service.js +1 -5
  11. package/lib/memorax-code-backend/dist/lifecycle/orchestrator.js +1 -14
  12. package/lib/memorax-code-backend/dist/memory/automatic-writeback.js +8 -5
  13. package/lib/memorax-code-backend/dist/memory/project.js +4 -26
  14. package/lib/memorax-code-backend/dist/memory/quota-notice.js +79 -19
  15. package/lib/memorax-code-backend/dist/provider/memorax/adapter.js +1 -4
  16. package/lib/memorax-code-backend/dist/provider/memorax/http.js +0 -28
  17. package/lib/memorax-code-backend/dist/transport/http/request.js +1 -39
  18. package/lib/memorax-code-backend/package.json +2 -3
  19. package/lib/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  20. package/lib/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  21. package/lib/memorax-code-claude-adapter/package.json +1 -1
  22. package/lib/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +0 -6
  23. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  24. package/lib/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +1 -1
  25. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/.claude-plugin/plugin.json +1 -1
  26. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/hooks/runtime-shell.json +1 -1
  27. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/package.json +1 -1
  28. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/SKILL.md +0 -6
  29. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  30. package/lib/memorax-code-claude-marketplace/plugins/memorax-code-claude-adapter/skills/memorax-code/references/memorax-search.md +1 -1
  31. package/lib/memorax-code-codex-adapter/.codex-plugin/plugin.json +1 -1
  32. package/lib/memorax-code-codex-adapter/hooks/runtime-shell.json +1 -1
  33. package/lib/memorax-code-codex-adapter/package.json +1 -1
  34. package/lib/memorax-code-codex-adapter/skills/memorax-code/SKILL.md +0 -6
  35. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  36. package/lib/memorax-code-codex-adapter/skills/memorax-code/references/memorax-search.md +1 -1
  37. package/lib/memorax-code-dsh-adapter/package.json +1 -1
  38. package/lib/memorax-code-dsh-adapter/skills/memorax-code/SKILL.md +0 -6
  39. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  40. package/lib/memorax-code-dsh-adapter/skills/memorax-code/references/memorax-search.md +1 -1
  41. package/lib/memorax-code-opencode-adapter/package.json +1 -1
  42. package/lib/memorax-code-opencode-adapter/skills/memorax-code/SKILL.md +0 -6
  43. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-add.md +1 -1
  44. package/lib/memorax-code-opencode-adapter/skills/memorax-code/references/memorax-search.md +1 -1
  45. package/lib/memorax-code-opencode-adapter/src/plugin.mjs +121 -16
  46. package/lib/memorax-code-opencode-adapter/src/repo-memory-server-runner.mjs +2 -0
  47. package/lib/package-transition.mjs +4 -3
  48. package/package.json +1 -1
  49. package/lib/memorax-code-backend/dist/clients/codex/effective-prompt.js +0 -54
  50. package/lib/memorax-code-backend/dist/memory/writeback-reconciler.js +0 -244
  51. package/lib/memorax-code-backend/dist/memory/writeback-task-projection.js +0 -377
  52. package/lib/memorax-code-backend/dist/repository/readiness.js +0 -188
  53. package/lib/memorax-code-backend/dist/shared/incremental-jsonl-projection.js +0 -271
  54. package/lib/memorax-code-backend/dist/viewer/history/claude-transcript.js +0 -648
  55. package/lib/memorax-code-backend/dist/viewer/history/session-title.js +0 -93
  56. package/lib/memorax-code-backend/dist/viewer/http/public-routes.js +0 -146
  57. package/lib/memorax-code-backend/dist/viewer/model.js +0 -1
  58. package/lib/memorax-code-backend/dist/viewer/projection/activity.js +0 -113
  59. package/lib/memorax-code-backend/dist/viewer/projection/cache.js +0 -69
  60. package/lib/memorax-code-backend/dist/viewer/projection/event-identity.js +0 -3
  61. package/lib/memorax-code-backend/dist/viewer/projection/history.js +0 -79
  62. package/lib/memorax-code-backend/dist/viewer/projection/observability.js +0 -8
  63. package/lib/memorax-code-backend/dist/viewer/projection/redaction.js +0 -52
  64. package/lib/memorax-code-backend/dist/viewer/projection/turn-reference.js +0 -12
  65. package/lib/memorax-code-backend/dist/viewer/projection/user.js +0 -168
  66. package/lib/memorax-code-backend/dist/viewer/projection/writeback-status.js +0 -97
  67. package/lib/memorax-code-backend/dist/viewer/store.js +0 -991
  68. package/lib/memorax-code-backend/dist/viewer/ui/icon.js +0 -1
  69. package/lib/memorax-code-backend/dist/viewer/ui/logo.js +0 -1
  70. package/lib/memorax-code-backend/dist/viewer/ui/user-html.js +0 -117
@@ -1,93 +0,0 @@
1
- import { redactViewerPaths } from "../projection/redaction.js";
2
- const MAX_SESSION_TITLE_LENGTH = 120;
3
- const COMMON_RELATIVE_PATH_ROOTS = new Set([
4
- "app",
5
- "apps",
6
- "assets",
7
- "build",
8
- "config",
9
- "configs",
10
- "crates",
11
- "dist",
12
- "docs",
13
- "lib",
14
- "node_modules",
15
- "packages",
16
- "private",
17
- "public",
18
- "resources",
19
- "scripts",
20
- "src",
21
- "test",
22
- "tests",
23
- "vendor",
24
- ]);
25
- export function earliestMemoryViewerSessionTitleCandidates(allEvents) {
26
- const candidates = new Map();
27
- for (const event of allEvents) {
28
- if (event.type !== "turn_start" || !event.sessionId)
29
- continue;
30
- const title = normalizedMemoryViewerSessionTitle(event.prompt);
31
- if (!title)
32
- continue;
33
- const candidate = {
34
- id: event.id,
35
- client: event.client,
36
- timestamp: event.timestamp,
37
- sessionId: event.sessionId,
38
- title,
39
- };
40
- const key = `${event.client}\u0000${event.sessionId}`;
41
- const current = candidates.get(key);
42
- if (!current || compareSessionTitleCandidates(candidate, current) < 0) {
43
- candidates.set(key, candidate);
44
- }
45
- }
46
- return [...candidates.values()];
47
- }
48
- export function normalizedMemoryViewerSessionTitle(value) {
49
- if (typeof value !== "string")
50
- return undefined;
51
- const title = value
52
- .replace(/[\u0000-\u001f\u007f-\u009f]+/g, " ")
53
- .replace(/\s+/g, " ")
54
- .trim();
55
- if (!title)
56
- return undefined;
57
- if (/^(?:[a-z]:[\\/]|[~.]?[\\/]|file:\/\/)/i.test(title))
58
- return undefined;
59
- const redacted = redactSessionTitlePaths(title).replace(/\s+/g, " ").trim();
60
- if (!redacted || redacted === "[REDACTED]")
61
- return undefined;
62
- const characters = Array.from(redacted);
63
- return characters.length <= MAX_SESSION_TITLE_LENGTH
64
- ? redacted
65
- : `${characters.slice(0, MAX_SESSION_TITLE_LENGTH - 1).join("")}…`;
66
- }
67
- function compareSessionTitleCandidates(left, right) {
68
- const leftTime = Date.parse(left.timestamp);
69
- const rightTime = Date.parse(right.timestamp);
70
- const leftOrder = Number.isFinite(leftTime) ? leftTime : 0;
71
- const rightOrder = Number.isFinite(rightTime) ? rightTime : 0;
72
- return leftOrder - rightOrder || left.id.localeCompare(right.id);
73
- }
74
- function redactSessionTitlePaths(value) {
75
- return redactViewerPaths(value)
76
- .replace(/(^|[\s('"`])(?:~|\.\.?)?[\\/](?![\\/])[^\s)'"`]+/g, "$1[REDACTED]")
77
- .replace(/(^|[\s('"`])\\\\[^\\\s]+\\[^\s)'"`]+/g, "$1[REDACTED]")
78
- .replace(/(^|[\s('"`])([^\s)'"`]*[\\/][^\s)'"`]*)/g, (match, prefix, token) => (likelyPathToken(token) ? `${prefix}[REDACTED]` : match));
79
- }
80
- function likelyPathToken(token) {
81
- if (/^[a-z][a-z0-9+.-]*:\/\//i.test(token))
82
- return false;
83
- if (/^[a-z]:[\\/]/i.test(token))
84
- return true;
85
- const segments = token.split(/[\\/]/).filter(Boolean);
86
- if (segments.length >= 3)
87
- return true;
88
- if (segments.length < 2)
89
- return false;
90
- if (/\.[a-z0-9]{1,16}(?:[?#].*)?$/i.test(segments.at(-1) ?? ""))
91
- return true;
92
- return COMMON_RELATIVE_PATH_ROOTS.has(segments[0]?.toLowerCase() ?? "");
93
- }
@@ -1,146 +0,0 @@
1
- import { createHash } from "node:crypto";
2
- import { MEMORAX_ICON_DATA_URL } from "../ui/icon.js";
3
- import { isMemoryProjectId, UNCLASSIFIED_PROJECT_ID } from "../../memory/project.js";
4
- import { MEMORAX_LOGO_DATA_URL } from "../ui/logo.js";
5
- import { memoryViewerUserHtml } from "../ui/user-html.js";
6
- import { projectMemoryViewerUserData } from "../projection/user.js";
7
- import { listMemoryViewerDataWithHistory } from "../store.js";
8
- import { repoMemoryReadinessForProject, } from "../../repository/readiness.js";
9
- import { json } from "../../transport/http/json.js";
10
- import { memoraxCodeHomeForTrace } from "../../trace/config.js";
11
- const MEMORY_VIEWER_SESSION_ACTIVITY_WINDOW_MS = 72 * 60 * 60 * 1_000;
12
- const MEMORY_VIEWER_ACTIVITY_CUTOFF_GRANULARITY_MS = 60_000;
13
- const MEMORY_VIEWER_LOGO = Buffer.from(MEMORAX_LOGO_DATA_URL.slice(MEMORAX_LOGO_DATA_URL.indexOf(",") + 1), "base64");
14
- const MEMORY_VIEWER_ICON = Buffer.from(MEMORAX_ICON_DATA_URL.slice(MEMORAX_ICON_DATA_URL.indexOf(",") + 1), "base64");
15
- export async function handleMemoryViewerRequest(url, req, res, options = {}) {
16
- if (req.method === "GET" && url.pathname === "/memory-viewer") {
17
- if (!memoryViewerUserClient(url)) {
18
- json(res, 400, { ok: false, error: "client must be codex, claude-code, dsh, or opencode" });
19
- return true;
20
- }
21
- res.writeHead(200, {
22
- "content-type": "text/html; charset=utf-8",
23
- "cache-control": "no-store",
24
- "content-security-policy": "default-src 'self'; script-src 'unsafe-inline'; style-src 'unsafe-inline'; connect-src 'self'; img-src 'self' data:; base-uri 'none'; frame-ancestors 'none'",
25
- "referrer-policy": "no-referrer",
26
- "x-content-type-options": "nosniff",
27
- ...(options.sessionCookie ? { "set-cookie": options.sessionCookie } : {}),
28
- });
29
- res.end(memoryViewerUserHtml());
30
- return true;
31
- }
32
- if (req.method === "GET" && url.pathname === "/memory-viewer/logo") {
33
- res.writeHead(200, {
34
- "content-type": "image/png",
35
- "cache-control": "private, max-age=86400",
36
- "x-content-type-options": "nosniff",
37
- });
38
- res.end(MEMORY_VIEWER_LOGO);
39
- return true;
40
- }
41
- if (req.method === "GET" && url.pathname === "/memory-viewer/favicon.png") {
42
- res.writeHead(200, {
43
- "content-type": "image/png",
44
- "cache-control": "private, max-age=86400",
45
- "x-content-type-options": "nosniff",
46
- });
47
- res.end(MEMORY_VIEWER_ICON);
48
- return true;
49
- }
50
- if (req.method !== "GET" || url.pathname !== "/memory-viewer/api/summary") {
51
- return false;
52
- }
53
- const memoraxCodeHome = viewerMemoraxCodeHome(options);
54
- const selectedClient = memoryViewerUserClient(url);
55
- if (!selectedClient) {
56
- json(res, 400, { ok: false, error: "client must be codex, claude-code, dsh, or opencode" });
57
- return true;
58
- }
59
- const requestedProject = projectFilter(url);
60
- const activeSessionSince = memoryViewerActivityCutoff(options.now?.() ?? Date.now());
61
- const viewerData = await listMemoryViewerDataWithHistory(memoraxCodeHome, {
62
- client: selectedClient.traceClient,
63
- includePendingWritebacks: true,
64
- activeSessionSince,
65
- includeUserProjection: true,
66
- ...(requestedProject.projectId ? { projectId: requestedProject.projectId } : {}),
67
- ...(requestedProject.reject ? { rejectProjectFilter: true } : {}),
68
- }, { claudeProjectsRoot: options.claudeProjectsRoot });
69
- const projection = viewerData.userProjection
70
- ?? projectMemoryViewerUserData(viewerData.events);
71
- const readiness = options.repoMemoryReadiness ?? repoMemoryReadinessForProject;
72
- const projects = await Promise.all(activeMemoryViewerProjects(viewerData.activityProjects, viewerData.activityProjectSessions, activeSessionSince).map(async (project) => ({
73
- projectId: project.projectId,
74
- projectLabel: project.projectLabel,
75
- repoMemory: await readiness(project.projectId, memoraxCodeHome),
76
- })));
77
- conditionalViewerJson(req, res, {
78
- ok: true,
79
- selectedClient: selectedClient.publicClient,
80
- availableClients: ["codex", "claude-code", "dsh", "opencode"],
81
- summary: projection.summary,
82
- activities: projection.activities.slice(0, 100),
83
- projects,
84
- ...(viewerData.selectedProjectId ? { selectedProjectId: viewerData.selectedProjectId } : {}),
85
- ...(viewerData.projectFilterStatus ? { projectFilterStatus: viewerData.projectFilterStatus } : {}),
86
- });
87
- return true;
88
- }
89
- function viewerMemoraxCodeHome(options) {
90
- return options.memoraxCodeHome
91
- ?? (options.env?.MEMORAX_CODE_HOME?.trim() || memoraxCodeHomeForTrace(options.env));
92
- }
93
- function memoryViewerUserClient(url) {
94
- const value = url.searchParams.get("client");
95
- const traceClient = value === null ? "codex" : traceClientFromPublicValue(value.trim().toLowerCase());
96
- return traceClient
97
- ? { traceClient, publicClient: traceClient === "claude" ? "claude-code" : traceClient }
98
- : undefined;
99
- }
100
- function traceClientFromPublicValue(value) {
101
- if (value === "codex" || value === "dsh" || value === "opencode")
102
- return value;
103
- return value === "claude" || value === "claude-code" || value === "cc"
104
- ? "claude"
105
- : undefined;
106
- }
107
- function activeMemoryViewerProjects(projects, projectSessions, activeSessionSince) {
108
- const activeProjectIds = new Set(projectSessions
109
- .filter((entry) => {
110
- const lastSeenAt = Date.parse(entry.lastSeenAt);
111
- return Number.isFinite(lastSeenAt) && lastSeenAt >= activeSessionSince;
112
- })
113
- .map((entry) => entry.projectId));
114
- return projects.filter((entry) => activeProjectIds.has(entry.projectId));
115
- }
116
- function memoryViewerActivityCutoff(now) {
117
- const safeNow = Number.isFinite(now) ? now : Date.now();
118
- return Math.floor(safeNow / MEMORY_VIEWER_ACTIVITY_CUTOFF_GRANULARITY_MS)
119
- * MEMORY_VIEWER_ACTIVITY_CUTOFF_GRANULARITY_MS
120
- - MEMORY_VIEWER_SESSION_ACTIVITY_WINDOW_MS;
121
- }
122
- function conditionalViewerJson(req, res, body) {
123
- const payload = JSON.stringify(body);
124
- const etag = `"${createHash("sha256").update(payload).digest("base64url")}"`;
125
- const headers = {
126
- "cache-control": "private, no-cache",
127
- etag,
128
- };
129
- if (req.headers["if-none-match"] === etag) {
130
- res.writeHead(304, headers);
131
- res.end();
132
- return;
133
- }
134
- res.writeHead(200, { ...headers, "content-type": "application/json" });
135
- res.end(payload);
136
- }
137
- function projectFilter(url) {
138
- const rawProjectId = url.searchParams.get("projectId");
139
- const projectId = (rawProjectId ?? "").trim();
140
- if (projectId === UNCLASSIFIED_PROJECT_ID || isMemoryProjectId(projectId)) {
141
- return { projectId };
142
- }
143
- if (rawProjectId !== null && projectId)
144
- return { reject: true };
145
- return {};
146
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,113 +0,0 @@
1
- import { UNCLASSIFIED_PROJECT_ID } from "../../memory/project.js";
2
- import { memoryViewerEventTurnIds } from "./turn-reference.js";
3
- export function memoryViewerActivitySummary(allEvents) {
4
- let recalledCount = 0;
5
- let addCount = 0;
6
- let searchCount = 0;
7
- for (const event of allEvents) {
8
- if (event.type === "memory_retrieve" || event.type === "memory_cli_search") {
9
- searchCount += 1;
10
- recalledCount += event.itemCount ?? event.results?.length ?? 0;
11
- }
12
- if (event.type === "memory_cli_add" || event.type === "memory_writeback")
13
- addCount += 1;
14
- }
15
- return {
16
- activityCount: allEvents.length,
17
- recalledCount,
18
- addCount,
19
- searchCount,
20
- };
21
- }
22
- export function memoryViewerActivityProjection(allEvents) {
23
- const turns = new Map();
24
- for (const event of allEvents) {
25
- if (!event.sessionId)
26
- continue;
27
- const order = timestampOrder(event.timestamp);
28
- for (const turnId of memoryViewerEventTurnIds(event)) {
29
- const key = `${event.client}\u0000${event.sessionId}\u0000${turnId}`;
30
- const current = turns.get(key);
31
- if (!current) {
32
- turns.set(key, {
33
- firstOrder: order,
34
- firstTimestamp: event.timestamp,
35
- ...(event.type === "turn_start" && Number.isFinite(order)
36
- ? { startOrder: order, startTimestamp: event.timestamp }
37
- : {}),
38
- });
39
- continue;
40
- }
41
- if (Number.isFinite(order)
42
- && (!Number.isFinite(current.firstOrder) || order < current.firstOrder)) {
43
- current.firstOrder = order;
44
- current.firstTimestamp = event.timestamp;
45
- }
46
- if (event.type === "turn_start"
47
- && Number.isFinite(order)
48
- && (current.startOrder === undefined || order < current.startOrder)) {
49
- current.startOrder = order;
50
- current.startTimestamp = event.timestamp;
51
- }
52
- }
53
- }
54
- const eventActivityOrder = new Map();
55
- const entries = new Map();
56
- for (const event of allEvents) {
57
- let turn;
58
- if (event.sessionId) {
59
- for (const turnId of memoryViewerEventTurnIds(event)) {
60
- const candidate = turns.get(`${event.client}\u0000${event.sessionId}\u0000${turnId}`);
61
- if (!candidate)
62
- continue;
63
- const candidateOrder = candidate.startOrder ?? candidate.firstOrder;
64
- const turnOrder = turn ? turn.startOrder ?? turn.firstOrder : Number.NEGATIVE_INFINITY;
65
- if (!turn || candidateOrder > turnOrder)
66
- turn = candidate;
67
- }
68
- }
69
- const activityOrder = turn?.startOrder ?? turn?.firstOrder ?? timestampOrder(event.timestamp);
70
- const activityTimestamp = turn?.startTimestamp ?? turn?.firstTimestamp ?? event.timestamp;
71
- eventActivityOrder.set(event.eventKey, activityOrder);
72
- const projectId = event.projectId ?? UNCLASSIFIED_PROJECT_ID;
73
- const key = `${event.client}\u0000${projectId}\u0000${event.sessionId ?? ""}`;
74
- const current = entries.get(key);
75
- if (!current) {
76
- entries.set(key, {
77
- value: {
78
- client: event.client,
79
- projectId,
80
- ...(event.sessionId ? { sessionId: event.sessionId } : { unscoped: true }),
81
- eventCount: 1,
82
- lastSeenAt: activityTimestamp,
83
- },
84
- lastSeenOrder: activityOrder,
85
- });
86
- continue;
87
- }
88
- const lastSeenAt = activityOrder >= current.lastSeenOrder
89
- ? activityTimestamp
90
- : current.value.lastSeenAt;
91
- entries.set(key, {
92
- value: {
93
- ...current.value,
94
- eventCount: current.value.eventCount + 1,
95
- lastSeenAt,
96
- },
97
- lastSeenOrder: Math.max(current.lastSeenOrder, activityOrder),
98
- });
99
- }
100
- return {
101
- eventActivityOrder,
102
- projectSessions: [...entries.values()]
103
- .map((entry) => entry.value)
104
- .sort((left, right) => (left.projectId.localeCompare(right.projectId)
105
- || left.client.localeCompare(right.client)
106
- || timestampOrder(right.lastSeenAt) - timestampOrder(left.lastSeenAt)
107
- || (left.sessionId ?? "").localeCompare(right.sessionId ?? ""))),
108
- };
109
- }
110
- function timestampOrder(value) {
111
- const parsed = Date.parse(value);
112
- return Number.isFinite(parsed) ? parsed : Number.NEGATIVE_INFINITY;
113
- }
@@ -1,69 +0,0 @@
1
- export const MEMORY_VIEWER_DATA_PROJECTION_CAPACITY = 64;
2
- const dataProjections = new Map();
3
- const sharedDataProjections = new Map();
4
- const projectionHistories = new Map();
5
- export function prepareMemoryViewerDataProjectionHistory(projectionKey, historical) {
6
- if (projectionHistories.get(projectionKey) === historical)
7
- return;
8
- dataProjections.delete(projectionKey);
9
- sharedDataProjections.delete(projectionKey);
10
- projectionHistories.set(projectionKey, historical);
11
- }
12
- export function shareMemoryViewerDataProjectionValue(projectionKey, valueKey, historical, liveVersion, create) {
13
- let projections = sharedDataProjections.get(projectionKey);
14
- if (!projections) {
15
- projections = new Map();
16
- sharedDataProjections.set(projectionKey, projections);
17
- }
18
- const cached = projections.get(valueKey);
19
- if (cached && cached.historical === historical && cached.liveVersion === liveVersion)
20
- return cached.value;
21
- const value = create();
22
- projections.set(valueKey, { historical, liveVersion, value });
23
- return value;
24
- }
25
- export function readMemoryViewerDataProjection(projectionKey, filterKey, historical, liveVersion) {
26
- const projections = dataProjections.get(projectionKey);
27
- const cached = projections?.get(filterKey);
28
- if (!cached || cached.historical !== historical || cached.liveVersion !== liveVersion)
29
- return undefined;
30
- projections?.delete(filterKey);
31
- projections?.set(filterKey, cached);
32
- return cached.data;
33
- }
34
- export function writeMemoryViewerDataProjection(projectionKey, filterKey, historical, liveVersion, data) {
35
- let projections = dataProjections.get(projectionKey);
36
- if (!projections) {
37
- projections = new Map();
38
- dataProjections.set(projectionKey, projections);
39
- }
40
- projections.set(filterKey, { historical, liveVersion, data });
41
- while (projections.size > MEMORY_VIEWER_DATA_PROJECTION_CAPACITY) {
42
- const oldestKey = projections.keys().next().value;
43
- if (!oldestKey)
44
- break;
45
- projections.delete(oldestKey);
46
- }
47
- }
48
- export function clearMemoryViewerDataProjections() {
49
- dataProjections.clear();
50
- sharedDataProjections.clear();
51
- }
52
- export function clearMemoryViewerDataProjectionCache() {
53
- dataProjections.clear();
54
- sharedDataProjections.clear();
55
- projectionHistories.clear();
56
- }
57
- export function memoryViewerDataProjectionCacheSize(projectionKey) {
58
- return dataProjections.get(projectionKey)?.size ?? 0;
59
- }
60
- export function memoryViewerSharedDataProjectionCacheSize(projectionKey) {
61
- return sharedDataProjections.get(projectionKey)?.size ?? 0;
62
- }
63
- export function shouldCacheMemoryViewerDataProjection(filter, data) {
64
- if (filter.sessionId && !data.projectSessions.some((entry) => entry.sessionId === filter.sessionId))
65
- return false;
66
- if (filter.projectId && data.projectFilterStatus !== "resolved")
67
- return false;
68
- return true;
69
- }
@@ -1,3 +0,0 @@
1
- export function memoryViewerEventKey(event) {
2
- return JSON.stringify([event.client ?? "codex", event.sessionId ?? "", event.id]);
3
- }
@@ -1,79 +0,0 @@
1
- import { memoryViewerEventKey } from "./event-identity.js";
2
- /**
3
- * Marks a retained start without an end as interrupted only when a different
4
- * Turn demonstrably started later in the same client session.
5
- *
6
- * This is a Viewer-only projection. It does not synthesize a trace event or
7
- * change transcript and writeback authority.
8
- */
9
- export function foldMemoryViewerSupersededTurnStarts(allEvents) {
10
- const endedTurns = new Set();
11
- const startsBySession = new Map();
12
- for (const event of allEvents) {
13
- if (!event.sessionId || !event.turnId)
14
- continue;
15
- const turnKey = viewerTurnKey(event);
16
- if (event.type === "turn_end") {
17
- endedTurns.add(turnKey);
18
- continue;
19
- }
20
- if (event.type !== "turn_start")
21
- continue;
22
- const startedAt = Date.parse(event.timestamp);
23
- if (!Number.isFinite(startedAt))
24
- continue;
25
- const sessionKey = JSON.stringify([event.client, event.sessionId]);
26
- const starts = startsBySession.get(sessionKey) ?? new Map();
27
- const current = starts.get(turnKey);
28
- if (current === undefined || startedAt < current)
29
- starts.set(turnKey, startedAt);
30
- startsBySession.set(sessionKey, starts);
31
- }
32
- const interruptedTurns = new Set();
33
- for (const starts of startsBySession.values()) {
34
- const latestStartedAt = Math.max(...starts.values());
35
- for (const [turnKey, startedAt] of starts) {
36
- if (!endedTurns.has(turnKey) && startedAt < latestStartedAt) {
37
- interruptedTurns.add(turnKey);
38
- }
39
- }
40
- }
41
- if (interruptedTurns.size === 0)
42
- return [...allEvents];
43
- return allEvents.map((event) => (event.type === "turn_start"
44
- && !event.turnOutcome
45
- && interruptedTurns.has(viewerTurnKey(event))
46
- ? { ...event, turnOutcome: "interrupted" }
47
- : event));
48
- }
49
- export function foldMemoryViewerTurnMaterializations(allEvents) {
50
- const byOriginalEventKey = new Map();
51
- for (const event of allEvents) {
52
- if (event.type === "turn_materialized" && event.originalEventId) {
53
- byOriginalEventKey.set(memoryViewerEventKey({
54
- id: event.originalEventId,
55
- client: event.client,
56
- ...(event.sessionId ? { sessionId: event.sessionId } : {}),
57
- }), event);
58
- }
59
- }
60
- return allEvents
61
- .filter((event) => event.type !== "turn_materialized")
62
- .map((event) => {
63
- if (event.type !== "turn_end")
64
- return event;
65
- const materialized = byOriginalEventKey.get(event.eventKey);
66
- if (!materialized)
67
- return event;
68
- const answer = materialized.answer ?? event.answer;
69
- return {
70
- ...event,
71
- ...(materialized.turnOutcome ? { turnOutcome: materialized.turnOutcome } : {}),
72
- ...(answer ? { answer, content: answer } : {}),
73
- ...(materialized.details === undefined ? {} : { details: materialized.details }),
74
- };
75
- });
76
- }
77
- function viewerTurnKey(event) {
78
- return JSON.stringify([event.client, event.sessionId, event.turnId]);
79
- }
@@ -1,8 +0,0 @@
1
- import { recordMemoryViewerEvent } from "../store.js";
2
- export function memoryViewerObservabilityHook() {
3
- return {
4
- recordEvent(event) {
5
- recordMemoryViewerEvent(event);
6
- },
7
- };
8
- }
@@ -1,52 +0,0 @@
1
- export function sanitizeMemoryViewerDetails(value, depth = 0) {
2
- if (depth > 5)
3
- return "[truncated]";
4
- if (typeof value === "string")
5
- return redactViewerPaths(value.slice(0, 12_000));
6
- if (value === null || typeof value !== "object")
7
- return value;
8
- if (Array.isArray(value)) {
9
- return value.slice(0, 50).map((item) => sanitizeMemoryViewerDetails(item, depth + 1));
10
- }
11
- const output = {};
12
- for (const [key, item] of Object.entries(value)) {
13
- if (/authorization|api[-_]?key|token|secret/i.test(key))
14
- continue;
15
- if (key.toLowerCase() === "route"
16
- && typeof item === "string"
17
- && /^\/api(?:\/[^/\s?#]+)*\/?$/.test(item)) {
18
- output[key] = item;
19
- continue;
20
- }
21
- if (viewerPathLikeKey(key)) {
22
- output[key] = "[REDACTED]";
23
- continue;
24
- }
25
- output[key] = sanitizeMemoryViewerDetails(item, depth + 1);
26
- }
27
- return output;
28
- }
29
- function viewerPathLikeKey(key) {
30
- const normalized = key.replace(/[^a-z0-9]/gi, "").toLowerCase();
31
- return normalized === "cwd"
32
- || normalized === "path"
33
- || normalized.endsWith("path")
34
- || normalized === "root"
35
- || normalized.endsWith("root")
36
- || normalized === "workspace"
37
- || normalized === "workingdirectory";
38
- }
39
- export function redactViewerPaths(value) {
40
- return value
41
- .split(/(\bhttps?:\/\/[^\s)'"`]+)/gi)
42
- .map((segment) => {
43
- if (/^https?:\/\//i.test(segment))
44
- return segment;
45
- return segment
46
- .replace(/\bfile:\/\/[^\s)'"`]+/gi, "[REDACTED]")
47
- .replace(/(^|[\s('"`])\\\\[^\\/\s)'"`]+[\\/][^\s)'"`]+/g, "$1[REDACTED]")
48
- .replace(/\b[A-Za-z]:[\\/][^\s)'"`]+/g, "[REDACTED]")
49
- .replace(/(^|[\s('"`])\/(?!\/)[^\s)'"`]+/g, "$1[REDACTED]");
50
- })
51
- .join("");
52
- }
@@ -1,12 +0,0 @@
1
- export function memoryViewerEventTurnIds(event) {
2
- return [...new Set([
3
- safeTurnId(event.turnId),
4
- ...(event.turnReferences ?? []).map((reference) => safeTurnId(reference.turnId)),
5
- ].filter(Boolean))];
6
- }
7
- function safeTurnId(value) {
8
- const candidate = typeof value === "string" ? value.trim() : "";
9
- if (!candidate || candidate.length > 512 || /[\u0000-\u001f\u007f\\/]/.test(candidate))
10
- return "";
11
- return candidate;
12
- }