claude-memory-layer 1.0.49 → 1.0.51
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/dist/cli/index.js +442 -8
- package/dist/cli/index.js.map +4 -4
- package/dist/mcp/index.js +5 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/server/api/index.js +439 -5
- package/dist/server/api/index.js.map +4 -4
- package/dist/server/index.js +441 -7
- package/dist/server/index.js.map +4 -4
- package/dist/ui/assets/js/bootstrap.js +63 -1
- package/dist/ui/assets/js/disclosure.js +158 -10
- package/dist/ui/assets/js/modals.js +39 -18
- package/dist/ui/assets/js/overview.js +99 -9
- package/dist/ui/assets/js/state.js +28 -1
- package/dist/ui/assets/js/views.js +626 -15
- package/dist/ui/index.html +130 -0
- package/dist/ui/style.css +460 -0
- package/package.json +3 -1
package/dist/server/index.js
CHANGED
|
@@ -6,7 +6,7 @@ const __filename = fileURLToPath(import.meta.url);
|
|
|
6
6
|
const __dirname = dirname(__filename);
|
|
7
7
|
|
|
8
8
|
// src/apps/server/index.ts
|
|
9
|
-
import { Hono as
|
|
9
|
+
import { Hono as Hono12 } from "hono";
|
|
10
10
|
import { cors } from "hono/cors";
|
|
11
11
|
import { getCookie, setCookie, deleteCookie } from "hono/cookie";
|
|
12
12
|
import { logger } from "hono/logger";
|
|
@@ -18,7 +18,7 @@ import { createHmac, timingSafeEqual } from "crypto";
|
|
|
18
18
|
import { fileURLToPath as fileUrlToPath } from "url";
|
|
19
19
|
|
|
20
20
|
// src/apps/server/api/index.ts
|
|
21
|
-
import { Hono as
|
|
21
|
+
import { Hono as Hono11 } from "hono";
|
|
22
22
|
|
|
23
23
|
// src/apps/server/api/sessions.ts
|
|
24
24
|
import { Hono } from "hono";
|
|
@@ -12582,10 +12582,25 @@ sessionsRouter.get("/", async (c) => {
|
|
|
12582
12582
|
id: event.sessionId,
|
|
12583
12583
|
startedAt: event.timestamp,
|
|
12584
12584
|
eventCount: 1,
|
|
12585
|
-
lastEventAt: event.timestamp
|
|
12585
|
+
lastEventAt: event.timestamp,
|
|
12586
|
+
promptPreview: event.eventType === "user_prompt" ? event.content.slice(0, 160) : "",
|
|
12587
|
+
firstUserPromptAt: event.eventType === "user_prompt" ? event.timestamp : null,
|
|
12588
|
+
toolCount: event.eventType === "tool_observation" ? 1 : 0,
|
|
12589
|
+
responseCount: event.eventType === "agent_response" ? 1 : 0,
|
|
12590
|
+
eventTypeCounts: { [event.eventType || "unknown"]: 1 },
|
|
12591
|
+
source: typeof event.metadata?.source === "string" ? event.metadata.source : null
|
|
12586
12592
|
});
|
|
12587
12593
|
} else {
|
|
12588
12594
|
existing.eventCount++;
|
|
12595
|
+
const eventType = event.eventType || "unknown";
|
|
12596
|
+
existing.eventTypeCounts[eventType] = (existing.eventTypeCounts[eventType] || 0) + 1;
|
|
12597
|
+
if (event.eventType === "tool_observation") existing.toolCount++;
|
|
12598
|
+
if (event.eventType === "agent_response") existing.responseCount++;
|
|
12599
|
+
if (event.eventType === "user_prompt" && (!existing.firstUserPromptAt || event.timestamp < existing.firstUserPromptAt)) {
|
|
12600
|
+
existing.firstUserPromptAt = event.timestamp;
|
|
12601
|
+
existing.promptPreview = event.content.slice(0, 160);
|
|
12602
|
+
}
|
|
12603
|
+
if (!existing.source && typeof event.metadata?.source === "string") existing.source = event.metadata.source;
|
|
12589
12604
|
if (event.timestamp < existing.startedAt) {
|
|
12590
12605
|
existing.startedAt = event.timestamp;
|
|
12591
12606
|
}
|
|
@@ -14628,7 +14643,7 @@ turnsRouter.get("/", async (c) => {
|
|
|
14628
14643
|
if (!sessionId) {
|
|
14629
14644
|
return c.json({ error: "sessionId is required" }, 400);
|
|
14630
14645
|
}
|
|
14631
|
-
const memoryService =
|
|
14646
|
+
const memoryService = getLightweightServiceFromQuery(c);
|
|
14632
14647
|
try {
|
|
14633
14648
|
await memoryService.initialize();
|
|
14634
14649
|
const turns = await memoryService.getSessionTurns(sessionId, { limit, offset });
|
|
@@ -14662,7 +14677,7 @@ turnsRouter.get("/", async (c) => {
|
|
|
14662
14677
|
});
|
|
14663
14678
|
turnsRouter.get("/:turnId", async (c) => {
|
|
14664
14679
|
const { turnId } = c.req.param();
|
|
14665
|
-
const memoryService =
|
|
14680
|
+
const memoryService = getLightweightServiceFromQuery(c);
|
|
14666
14681
|
try {
|
|
14667
14682
|
await memoryService.initialize();
|
|
14668
14683
|
const events = await memoryService.getEventsByTurn(turnId);
|
|
@@ -14737,6 +14752,58 @@ import * as fs10 from "fs";
|
|
|
14737
14752
|
import * as path12 from "path";
|
|
14738
14753
|
import * as os7 from "os";
|
|
14739
14754
|
var projectsRouter = new Hono7();
|
|
14755
|
+
projectsRouter.get("/:hash/detail", async (c) => {
|
|
14756
|
+
const { hash } = c.req.param();
|
|
14757
|
+
const registry = loadSessionRegistry();
|
|
14758
|
+
const projectPath = getRegisteredProjectPath(registry, hash);
|
|
14759
|
+
const storagePath = resolveProjectStoragePath(hash);
|
|
14760
|
+
const memoryService = new MemoryService({
|
|
14761
|
+
storagePath,
|
|
14762
|
+
projectHash: hash,
|
|
14763
|
+
...projectPath ? { projectPath } : {},
|
|
14764
|
+
readOnly: true,
|
|
14765
|
+
lightweightMode: true,
|
|
14766
|
+
analyticsEnabled: false,
|
|
14767
|
+
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
14768
|
+
});
|
|
14769
|
+
try {
|
|
14770
|
+
await memoryService.initialize();
|
|
14771
|
+
const [stats, recentEvents, retrieval, outbox] = await Promise.all([
|
|
14772
|
+
memoryService.getStats(),
|
|
14773
|
+
memoryService.getRecentEvents(1e3),
|
|
14774
|
+
memoryService.getRetrievalTraceStats(),
|
|
14775
|
+
memoryService.getOutboxStats()
|
|
14776
|
+
]);
|
|
14777
|
+
const eventSummary = summarizeProjectEvents(recentEvents);
|
|
14778
|
+
return c.json({
|
|
14779
|
+
project: {
|
|
14780
|
+
hash,
|
|
14781
|
+
projectName: projectPath ? path12.basename(projectPath) : `unknown (${hash})`,
|
|
14782
|
+
registered: Boolean(projectPath)
|
|
14783
|
+
},
|
|
14784
|
+
storage: {
|
|
14785
|
+
eventCount: stats.totalEvents,
|
|
14786
|
+
vectorCount: stats.vectorCount,
|
|
14787
|
+
levels: stats.levelStats || []
|
|
14788
|
+
},
|
|
14789
|
+
sessions: eventSummary.sessions,
|
|
14790
|
+
eventTypes: eventSummary.eventTypes,
|
|
14791
|
+
sources: eventSummary.sources,
|
|
14792
|
+
activity: eventSummary.activity,
|
|
14793
|
+
retrieval: {
|
|
14794
|
+
totalQueries: retrieval.totalQueries,
|
|
14795
|
+
avgCandidateCount: retrieval.avgCandidateCount,
|
|
14796
|
+
avgSelectedCount: retrieval.avgSelectedCount,
|
|
14797
|
+
selectionRate: retrieval.selectionRate
|
|
14798
|
+
},
|
|
14799
|
+
outbox: summarizeProjectOutbox(outbox)
|
|
14800
|
+
});
|
|
14801
|
+
} catch {
|
|
14802
|
+
return c.json({ error: "Project detail unavailable" }, 500);
|
|
14803
|
+
} finally {
|
|
14804
|
+
await memoryService.shutdown();
|
|
14805
|
+
}
|
|
14806
|
+
});
|
|
14740
14807
|
projectsRouter.get("/", async (c) => {
|
|
14741
14808
|
try {
|
|
14742
14809
|
const projectsDir = path12.join(os7.homedir(), ".claude-code", "memory", "projects");
|
|
@@ -14776,6 +14843,57 @@ projectsRouter.get("/", async (c) => {
|
|
|
14776
14843
|
return c.json({ projects: [], error: error.message }, 500);
|
|
14777
14844
|
}
|
|
14778
14845
|
});
|
|
14846
|
+
function getRegisteredProjectPath(registry, hash) {
|
|
14847
|
+
for (const entry of Object.values(registry.sessions)) {
|
|
14848
|
+
if (entry.projectHash === hash) {
|
|
14849
|
+
return entry.projectPath;
|
|
14850
|
+
}
|
|
14851
|
+
}
|
|
14852
|
+
return void 0;
|
|
14853
|
+
}
|
|
14854
|
+
function summarizeProjectEvents(events) {
|
|
14855
|
+
const sessions = /* @__PURE__ */ new Set();
|
|
14856
|
+
const eventTypes = {};
|
|
14857
|
+
const sources = {};
|
|
14858
|
+
let firstEventAt = null;
|
|
14859
|
+
let lastEventAt = null;
|
|
14860
|
+
for (const event of events) {
|
|
14861
|
+
if (event.sessionId) sessions.add(event.sessionId);
|
|
14862
|
+
incrementBucket(eventTypes, event.eventType || "unknown");
|
|
14863
|
+
const source = typeof event.metadata?.source === "string" ? event.metadata.source : "unknown";
|
|
14864
|
+
incrementBucket(sources, source || "unknown");
|
|
14865
|
+
const timestamp = normalizeIsoTimestamp(event.timestamp);
|
|
14866
|
+
if (!timestamp) continue;
|
|
14867
|
+
if (!firstEventAt || timestamp < firstEventAt) firstEventAt = timestamp;
|
|
14868
|
+
if (!lastEventAt || timestamp > lastEventAt) lastEventAt = timestamp;
|
|
14869
|
+
}
|
|
14870
|
+
return {
|
|
14871
|
+
sessions: { total: sessions.size },
|
|
14872
|
+
eventTypes,
|
|
14873
|
+
sources,
|
|
14874
|
+
activity: { firstEventAt, lastEventAt }
|
|
14875
|
+
};
|
|
14876
|
+
}
|
|
14877
|
+
function summarizeProjectOutbox(outbox) {
|
|
14878
|
+
const embedding = outbox.embedding || {};
|
|
14879
|
+
const vector = outbox.vector || {};
|
|
14880
|
+
return {
|
|
14881
|
+
pending: (embedding.pending || 0) + (vector.pending || 0),
|
|
14882
|
+
processing: (embedding.processing || 0) + (vector.processing || 0),
|
|
14883
|
+
failed: (embedding.failed || 0) + (vector.failed || 0),
|
|
14884
|
+
stuckProcessing: (embedding.stuckProcessing || 0) + (vector.stuckProcessing || 0)
|
|
14885
|
+
};
|
|
14886
|
+
}
|
|
14887
|
+
function incrementBucket(bucket, rawLabel) {
|
|
14888
|
+
const label = rawLabel.replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 80) || "unknown";
|
|
14889
|
+
bucket[label] = (bucket[label] || 0) + 1;
|
|
14890
|
+
}
|
|
14891
|
+
function normalizeIsoTimestamp(value) {
|
|
14892
|
+
if (!value) return null;
|
|
14893
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
14894
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
14895
|
+
return date.toISOString();
|
|
14896
|
+
}
|
|
14779
14897
|
function formatBytes(bytes) {
|
|
14780
14898
|
if (bytes === 0) return "0 B";
|
|
14781
14899
|
const k = 1024;
|
|
@@ -15064,7 +15182,101 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
15064
15182
|
|
|
15065
15183
|
// src/apps/server/api/health.ts
|
|
15066
15184
|
import { Hono as Hono9 } from "hono";
|
|
15185
|
+
import { spawnSync } from "child_process";
|
|
15067
15186
|
var healthRouter = new Hono9();
|
|
15187
|
+
function hasEnvSignal(name) {
|
|
15188
|
+
const value = process.env[name];
|
|
15189
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
15190
|
+
}
|
|
15191
|
+
function claudeAuthSignal() {
|
|
15192
|
+
return hasEnvSignal("ANTHROPIC_API_KEY") || hasEnvSignal("CLAUDE_API_KEY") || hasEnvSignal("CLAUDE_CODE_OAUTH_TOKEN") ? "env-present" : "not-detected";
|
|
15193
|
+
}
|
|
15194
|
+
function detectClaudeCli() {
|
|
15195
|
+
const result = spawnSync("claude", ["--version"], {
|
|
15196
|
+
encoding: "utf-8",
|
|
15197
|
+
timeout: 3e3
|
|
15198
|
+
});
|
|
15199
|
+
const authSignal = claudeAuthSignal();
|
|
15200
|
+
const errorCode = result.error?.code;
|
|
15201
|
+
if (errorCode === "ENOENT") {
|
|
15202
|
+
return { status: "missing", command: "claude", authSignal };
|
|
15203
|
+
}
|
|
15204
|
+
if (result.status === 0) {
|
|
15205
|
+
const version = String(result.stdout || "").split("\n")[0]?.trim().slice(0, 80) || void 0;
|
|
15206
|
+
return { status: "available", command: "claude", authSignal, ...version ? { version } : {} };
|
|
15207
|
+
}
|
|
15208
|
+
return { status: "error", command: "claude", authSignal };
|
|
15209
|
+
}
|
|
15210
|
+
function detectEmbeddingBackend() {
|
|
15211
|
+
const disabled = ["1", "true", "yes"].includes(String(process.env.CLAUDE_MEMORY_DISABLE_VECTOR || "").toLowerCase());
|
|
15212
|
+
return {
|
|
15213
|
+
status: disabled ? "disabled" : "enabled",
|
|
15214
|
+
backend: "@huggingface/transformers"
|
|
15215
|
+
};
|
|
15216
|
+
}
|
|
15217
|
+
function aggregateOutbox(outbox) {
|
|
15218
|
+
const pending = (outbox.embedding?.pending || 0) + (outbox.vector?.pending || 0);
|
|
15219
|
+
const processing = (outbox.embedding?.processing || 0) + (outbox.vector?.processing || 0);
|
|
15220
|
+
const failed = (outbox.embedding?.failed || 0) + (outbox.vector?.failed || 0);
|
|
15221
|
+
const stuckProcessing = (outbox.embedding?.stuckProcessing || 0) + (outbox.vector?.stuckProcessing || 0);
|
|
15222
|
+
return { pending, processing, failed, stuckProcessing };
|
|
15223
|
+
}
|
|
15224
|
+
healthRouter.get("/setup", async (c) => {
|
|
15225
|
+
const memoryService = getLightweightServiceFromQuery(c);
|
|
15226
|
+
try {
|
|
15227
|
+
await memoryService.initialize();
|
|
15228
|
+
const [stats, outbox] = await Promise.all([
|
|
15229
|
+
memoryService.getStats(),
|
|
15230
|
+
memoryService.getOutboxStats()
|
|
15231
|
+
]);
|
|
15232
|
+
const outboxTotals = aggregateOutbox(outbox);
|
|
15233
|
+
const claudeCli = detectClaudeCli();
|
|
15234
|
+
const embeddings = detectEmbeddingBackend();
|
|
15235
|
+
const recommendations = [];
|
|
15236
|
+
if (claudeCli.status !== "available") {
|
|
15237
|
+
recommendations.push("Install or authenticate Claude CLI to enable Ask Memory assistant responses.");
|
|
15238
|
+
}
|
|
15239
|
+
if (embeddings.status === "disabled") {
|
|
15240
|
+
recommendations.push("Enable vector embeddings to improve semantic retrieval and memory usefulness.");
|
|
15241
|
+
}
|
|
15242
|
+
if (outboxTotals.failed > 0 || outboxTotals.stuckProcessing > 0) {
|
|
15243
|
+
recommendations.push("Run vector health recovery before trusting retrieval freshness.");
|
|
15244
|
+
}
|
|
15245
|
+
let status = "ok";
|
|
15246
|
+
if (claudeCli.status !== "available" || embeddings.status === "disabled") {
|
|
15247
|
+
status = "needs-setup";
|
|
15248
|
+
} else if (outboxTotals.failed > 0 || outboxTotals.stuckProcessing > 0) {
|
|
15249
|
+
status = "needs-attention";
|
|
15250
|
+
}
|
|
15251
|
+
const scoped = Boolean(c.req.query("project") || c.req.query("projectId"));
|
|
15252
|
+
return c.json({
|
|
15253
|
+
status,
|
|
15254
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15255
|
+
setup: {
|
|
15256
|
+
scope: scoped ? "project" : "global",
|
|
15257
|
+
storage: {
|
|
15258
|
+
status: "ok",
|
|
15259
|
+
totalEvents: stats.totalEvents,
|
|
15260
|
+
vectorCount: stats.vectorCount
|
|
15261
|
+
},
|
|
15262
|
+
outbox: outboxTotals
|
|
15263
|
+
},
|
|
15264
|
+
providers: {
|
|
15265
|
+
claudeCli,
|
|
15266
|
+
embeddings
|
|
15267
|
+
},
|
|
15268
|
+
recommendations
|
|
15269
|
+
});
|
|
15270
|
+
} catch {
|
|
15271
|
+
return c.json({
|
|
15272
|
+
status: "error",
|
|
15273
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15274
|
+
error: "Setup health check failed"
|
|
15275
|
+
}, 500);
|
|
15276
|
+
} finally {
|
|
15277
|
+
await memoryService.shutdown();
|
|
15278
|
+
}
|
|
15279
|
+
});
|
|
15068
15280
|
healthRouter.get("/", async (c) => {
|
|
15069
15281
|
const memoryService = getLightweightServiceFromQuery(c);
|
|
15070
15282
|
try {
|
|
@@ -15156,8 +15368,230 @@ healthRouter.post("/recover", async (c) => {
|
|
|
15156
15368
|
}
|
|
15157
15369
|
});
|
|
15158
15370
|
|
|
15371
|
+
// src/apps/server/api/playground.ts
|
|
15372
|
+
import { Hono as Hono10 } from "hono";
|
|
15373
|
+
var playgroundRouter = new Hono10();
|
|
15374
|
+
function normalizeWindowSize(value) {
|
|
15375
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
|
|
15376
|
+
if (!Number.isFinite(parsed)) return 3;
|
|
15377
|
+
return Math.max(1, Math.min(10, Math.trunc(parsed)));
|
|
15378
|
+
}
|
|
15379
|
+
function disclosureOptions(options = {}) {
|
|
15380
|
+
const {
|
|
15381
|
+
windowSize: _windowSize,
|
|
15382
|
+
selectedResultId: _selectedResultId,
|
|
15383
|
+
...rest
|
|
15384
|
+
} = options;
|
|
15385
|
+
return rest;
|
|
15386
|
+
}
|
|
15387
|
+
var SOURCE_EVENT_PREVIEW_CHARS = 500;
|
|
15388
|
+
var SOURCE_EVENT_LIMIT = 3;
|
|
15389
|
+
function asRecord(value) {
|
|
15390
|
+
return value && typeof value === "object" ? value : null;
|
|
15391
|
+
}
|
|
15392
|
+
function safeSourcePreview(value) {
|
|
15393
|
+
const raw = typeof value === "string" ? value : String(value ?? "");
|
|
15394
|
+
const sanitized = String(sanitizeGovernanceAuditValue(raw));
|
|
15395
|
+
return sanitized.length > SOURCE_EVENT_PREVIEW_CHARS ? `${sanitized.slice(0, SOURCE_EVENT_PREVIEW_CHARS)}...` : sanitized;
|
|
15396
|
+
}
|
|
15397
|
+
function sanitizeSourceEvent(event) {
|
|
15398
|
+
const record = asRecord(event);
|
|
15399
|
+
if (!record) {
|
|
15400
|
+
return { preview: safeSourcePreview(event) };
|
|
15401
|
+
}
|
|
15402
|
+
const safe = {};
|
|
15403
|
+
if (record.id !== void 0) safe.id = sanitizeGovernanceAuditValue(record.id, "id");
|
|
15404
|
+
if (record.eventId !== void 0) safe.eventId = sanitizeGovernanceAuditValue(record.eventId, "eventId");
|
|
15405
|
+
if (record.sessionId !== void 0) safe.sessionId = sanitizeGovernanceAuditValue(record.sessionId, "sessionId");
|
|
15406
|
+
if (record.eventType !== void 0) safe.eventType = sanitizeGovernanceAuditValue(record.eventType, "eventType");
|
|
15407
|
+
if (record.timestamp !== void 0) safe.timestamp = sanitizeGovernanceAuditValue(record.timestamp, "timestamp");
|
|
15408
|
+
const previewSource = record.content ?? record.preview ?? "";
|
|
15409
|
+
safe.preview = safeSourcePreview(previewSource);
|
|
15410
|
+
if (typeof record.content === "string") {
|
|
15411
|
+
safe.contentLength = record.content.length;
|
|
15412
|
+
}
|
|
15413
|
+
return safe;
|
|
15414
|
+
}
|
|
15415
|
+
function sanitizeStringArray(value) {
|
|
15416
|
+
if (!Array.isArray(value)) return void 0;
|
|
15417
|
+
return value.map((item) => safeSourcePreview(item).slice(0, 160)).filter((item) => item.length > 0);
|
|
15418
|
+
}
|
|
15419
|
+
function sanitizePlaygroundSource(source) {
|
|
15420
|
+
const record = asRecord(source);
|
|
15421
|
+
if (!record) return source;
|
|
15422
|
+
const safe = {};
|
|
15423
|
+
if (record.sourceRef !== void 0) safe.sourceRef = sanitizeGovernanceAuditValue(record.sourceRef, "sourceRef");
|
|
15424
|
+
if (record.sourceType !== void 0) safe.sourceType = sanitizeGovernanceAuditValue(record.sourceType, "sourceType");
|
|
15425
|
+
if (record.retrievalLayer !== void 0) safe.retrievalLayer = sanitizeGovernanceAuditValue(record.retrievalLayer, "retrievalLayer");
|
|
15426
|
+
if (record.summary !== void 0) safe.summary = sanitizeGovernanceAuditValue(record.summary, "summary");
|
|
15427
|
+
const eventIds = sanitizeStringArray(record.eventIds);
|
|
15428
|
+
if (eventIds) safe.eventIds = eventIds;
|
|
15429
|
+
const rawEvents = Array.isArray(record.rawEvents) ? record.rawEvents : [];
|
|
15430
|
+
safe.rawEvents = rawEvents.slice(0, SOURCE_EVENT_LIMIT).map(sanitizeSourceEvent);
|
|
15431
|
+
if (rawEvents.length > SOURCE_EVENT_LIMIT) {
|
|
15432
|
+
safe.omittedRawEventCount = rawEvents.length - SOURCE_EVENT_LIMIT;
|
|
15433
|
+
}
|
|
15434
|
+
if (record.primaryEvent !== void 0) safe.primaryEvent = sanitizeSourceEvent(record.primaryEvent);
|
|
15435
|
+
if (record.rawEvent !== void 0) safe.rawEvent = sanitizeSourceEvent(record.rawEvent);
|
|
15436
|
+
if (record.event !== void 0) safe.event = sanitizeSourceEvent(record.event);
|
|
15437
|
+
return safe;
|
|
15438
|
+
}
|
|
15439
|
+
function sanitizeScore(value) {
|
|
15440
|
+
const score = typeof value === "number" ? value : Number(value);
|
|
15441
|
+
return Number.isFinite(score) ? score : void 0;
|
|
15442
|
+
}
|
|
15443
|
+
function sanitizeReasons(value) {
|
|
15444
|
+
return sanitizeStringArray(value)?.slice(0, 8) ?? [];
|
|
15445
|
+
}
|
|
15446
|
+
function sanitizeSearchResult(result) {
|
|
15447
|
+
const record = asRecord(result);
|
|
15448
|
+
if (!record) return { snippet: safeSourcePreview(result) };
|
|
15449
|
+
const safe = {};
|
|
15450
|
+
if (record.id !== void 0) safe.id = sanitizeGovernanceAuditValue(record.id, "id");
|
|
15451
|
+
if (record.sourceRef !== void 0) safe.sourceRef = sanitizeGovernanceAuditValue(record.sourceRef, "sourceRef");
|
|
15452
|
+
if (record.resultType !== void 0) safe.resultType = sanitizeGovernanceAuditValue(record.resultType, "resultType");
|
|
15453
|
+
const score = sanitizeScore(record.score);
|
|
15454
|
+
if (score !== void 0) safe.score = score;
|
|
15455
|
+
safe.snippet = safeSourcePreview(record.snippet ?? record.preview ?? record.summary ?? "");
|
|
15456
|
+
if (record.preview !== void 0) safe.preview = safeSourcePreview(record.preview);
|
|
15457
|
+
safe.reasons = sanitizeReasons(record.reasons);
|
|
15458
|
+
return safe;
|
|
15459
|
+
}
|
|
15460
|
+
var PLAYGROUND_META_FIELDS = [
|
|
15461
|
+
"total",
|
|
15462
|
+
"totalMatches",
|
|
15463
|
+
"usedVector",
|
|
15464
|
+
"usedKeyword",
|
|
15465
|
+
"usedRerank",
|
|
15466
|
+
"queryTimeMs",
|
|
15467
|
+
"strategy"
|
|
15468
|
+
];
|
|
15469
|
+
function copySafeMetaValue(target, source, field) {
|
|
15470
|
+
const value = source[field];
|
|
15471
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
15472
|
+
target[field] = value;
|
|
15473
|
+
} else if (typeof value === "string") {
|
|
15474
|
+
target[field] = safeSourcePreview(value).slice(0, 160);
|
|
15475
|
+
}
|
|
15476
|
+
}
|
|
15477
|
+
function sanitizeMeta(meta) {
|
|
15478
|
+
const record = asRecord(meta);
|
|
15479
|
+
if (!record) return void 0;
|
|
15480
|
+
const safe = {};
|
|
15481
|
+
for (const field of PLAYGROUND_META_FIELDS) {
|
|
15482
|
+
copySafeMetaValue(safe, record, field);
|
|
15483
|
+
}
|
|
15484
|
+
return safe;
|
|
15485
|
+
}
|
|
15486
|
+
function sanitizePlaygroundSearch(search) {
|
|
15487
|
+
const record = asRecord(search);
|
|
15488
|
+
if (!record) return search;
|
|
15489
|
+
const safe = {
|
|
15490
|
+
results: Array.isArray(record.results) ? record.results.map(sanitizeSearchResult) : []
|
|
15491
|
+
};
|
|
15492
|
+
const meta = sanitizeMeta(record.meta);
|
|
15493
|
+
if (meta) safe.meta = meta;
|
|
15494
|
+
return safe;
|
|
15495
|
+
}
|
|
15496
|
+
function sanitizeExpansionFact(fact) {
|
|
15497
|
+
const record = asRecord(fact);
|
|
15498
|
+
if (!record) return { snippet: safeSourcePreview(fact) };
|
|
15499
|
+
const safe = {};
|
|
15500
|
+
if (record.id !== void 0) safe.id = sanitizeGovernanceAuditValue(record.id, "id");
|
|
15501
|
+
if (record.eventId !== void 0) safe.eventId = sanitizeGovernanceAuditValue(record.eventId, "eventId");
|
|
15502
|
+
if (record.sourceRef !== void 0) safe.sourceRef = sanitizeGovernanceAuditValue(record.sourceRef, "sourceRef");
|
|
15503
|
+
if (record.eventType !== void 0) safe.eventType = sanitizeGovernanceAuditValue(record.eventType, "eventType");
|
|
15504
|
+
if (record.sessionId !== void 0) safe.sessionId = sanitizeGovernanceAuditValue(record.sessionId, "sessionId");
|
|
15505
|
+
if (record.timestamp !== void 0) safe.timestamp = sanitizeGovernanceAuditValue(record.timestamp, "timestamp");
|
|
15506
|
+
const preview = safeSourcePreview(record.snippet ?? record.summary ?? record.preview ?? record.content ?? "");
|
|
15507
|
+
safe.snippet = preview;
|
|
15508
|
+
safe.preview = preview;
|
|
15509
|
+
return safe;
|
|
15510
|
+
}
|
|
15511
|
+
function sanitizeRelatedSource(source) {
|
|
15512
|
+
const record = asRecord(source);
|
|
15513
|
+
if (!record) return { preview: safeSourcePreview(source) };
|
|
15514
|
+
const safe = {};
|
|
15515
|
+
if (record.sourceRef !== void 0) safe.sourceRef = sanitizeGovernanceAuditValue(record.sourceRef, "sourceRef");
|
|
15516
|
+
const eventIds = sanitizeStringArray(record.eventIds);
|
|
15517
|
+
if (eventIds) safe.eventIds = eventIds;
|
|
15518
|
+
const previewSource = record.snippet ?? record.summary ?? record.preview;
|
|
15519
|
+
if (previewSource !== void 0) safe.preview = safeSourcePreview(previewSource);
|
|
15520
|
+
return safe;
|
|
15521
|
+
}
|
|
15522
|
+
function sanitizePlaygroundExpansion(expansion) {
|
|
15523
|
+
const record = asRecord(expansion);
|
|
15524
|
+
if (!record) return expansion;
|
|
15525
|
+
const safe = {};
|
|
15526
|
+
if (record.target !== void 0) safe.target = sanitizeSearchResult(record.target);
|
|
15527
|
+
safe.surroundingFacts = Array.isArray(record.surroundingFacts) ? record.surroundingFacts.slice(0, 12).map(sanitizeExpansionFact) : [];
|
|
15528
|
+
safe.relatedSources = Array.isArray(record.relatedSources) ? record.relatedSources.slice(0, 12).map(sanitizeRelatedSource) : [];
|
|
15529
|
+
return safe;
|
|
15530
|
+
}
|
|
15531
|
+
function selectedResultIdFrom(searchResult, requested) {
|
|
15532
|
+
if (requested && searchResult.results?.some((r) => r.id === requested || r.sourceRef === requested)) {
|
|
15533
|
+
return requested;
|
|
15534
|
+
}
|
|
15535
|
+
const first = searchResult.results?.[0];
|
|
15536
|
+
return first?.id ?? first?.sourceRef ?? null;
|
|
15537
|
+
}
|
|
15538
|
+
playgroundRouter.post("/dry-run", async (c) => {
|
|
15539
|
+
let body;
|
|
15540
|
+
try {
|
|
15541
|
+
body = await c.req.json();
|
|
15542
|
+
} catch {
|
|
15543
|
+
return c.json({ error: "Invalid JSON body" }, 400);
|
|
15544
|
+
}
|
|
15545
|
+
const query = body.query?.trim();
|
|
15546
|
+
if (!query) {
|
|
15547
|
+
return c.json({ error: "Query is required" }, 400);
|
|
15548
|
+
}
|
|
15549
|
+
const useFastStrategy = body.options?.strategy === "fast";
|
|
15550
|
+
const memoryService = useFastStrategy ? getLightweightServiceFromQuery(c) : getServiceFromQuery(c);
|
|
15551
|
+
const replayTrace = [];
|
|
15552
|
+
try {
|
|
15553
|
+
await memoryService.initialize();
|
|
15554
|
+
replayTrace.push("search");
|
|
15555
|
+
const search = await memoryService.searchDisclosure(query, disclosureOptions(body.options));
|
|
15556
|
+
const selectedResultId = selectedResultIdFrom(search, body.options?.selectedResultId);
|
|
15557
|
+
const safeSearch = sanitizePlaygroundSearch(search);
|
|
15558
|
+
if (!selectedResultId) {
|
|
15559
|
+
replayTrace.push("no-results");
|
|
15560
|
+
return c.json({
|
|
15561
|
+
dryRun: true,
|
|
15562
|
+
mutated: false,
|
|
15563
|
+
query,
|
|
15564
|
+
selectedResultId: null,
|
|
15565
|
+
search: safeSearch,
|
|
15566
|
+
expansion: null,
|
|
15567
|
+
source: null,
|
|
15568
|
+
replayTrace
|
|
15569
|
+
});
|
|
15570
|
+
}
|
|
15571
|
+
const windowSize = normalizeWindowSize(body.options?.windowSize);
|
|
15572
|
+
replayTrace.push(`expand:${selectedResultId}`);
|
|
15573
|
+
const expansion = sanitizePlaygroundExpansion(await memoryService.expandDisclosure(selectedResultId, { windowSize }));
|
|
15574
|
+
replayTrace.push(`source:${selectedResultId}`);
|
|
15575
|
+
const source = sanitizePlaygroundSource(await memoryService.sourceDisclosure(selectedResultId));
|
|
15576
|
+
return c.json({
|
|
15577
|
+
dryRun: true,
|
|
15578
|
+
mutated: false,
|
|
15579
|
+
query,
|
|
15580
|
+
selectedResultId,
|
|
15581
|
+
search: safeSearch,
|
|
15582
|
+
expansion,
|
|
15583
|
+
source,
|
|
15584
|
+
replayTrace
|
|
15585
|
+
});
|
|
15586
|
+
} catch (error) {
|
|
15587
|
+
return c.json({ error: safeSourcePreview(error.message), dryRun: true, mutated: false, replayTrace }, 500);
|
|
15588
|
+
} finally {
|
|
15589
|
+
await memoryService.shutdown();
|
|
15590
|
+
}
|
|
15591
|
+
});
|
|
15592
|
+
|
|
15159
15593
|
// src/apps/server/api/index.ts
|
|
15160
|
-
var apiRouter = new
|
|
15594
|
+
var apiRouter = new Hono11().route("/sessions", sessionsRouter).route("/events", eventsRouter).route("/search", searchRouter).route("/stats", statsRouter).route("/citations", citationsRouter).route("/turns", turnsRouter).route("/projects", projectsRouter).route("/chat", chatRouter).route("/health", healthRouter).route("/playground", playgroundRouter);
|
|
15161
15595
|
|
|
15162
15596
|
// src/apps/server/index.ts
|
|
15163
15597
|
var moduleDir = path13.dirname(fileUrlToPath(import.meta.url));
|
|
@@ -15318,7 +15752,7 @@ function createAuthOptions(options) {
|
|
|
15318
15752
|
};
|
|
15319
15753
|
}
|
|
15320
15754
|
function createDashboardApp(options = {}) {
|
|
15321
|
-
const app2 = new
|
|
15755
|
+
const app2 = new Hono12();
|
|
15322
15756
|
const authOptions = createAuthOptions(options);
|
|
15323
15757
|
app2.use("*", cors());
|
|
15324
15758
|
app2.use("*", logger());
|