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/api/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/api/index.ts
|
|
9
|
-
import { Hono as
|
|
9
|
+
import { Hono as Hono11 } from "hono";
|
|
10
10
|
|
|
11
11
|
// src/apps/server/api/sessions.ts
|
|
12
12
|
import { Hono } from "hono";
|
|
@@ -12570,10 +12570,25 @@ sessionsRouter.get("/", async (c) => {
|
|
|
12570
12570
|
id: event.sessionId,
|
|
12571
12571
|
startedAt: event.timestamp,
|
|
12572
12572
|
eventCount: 1,
|
|
12573
|
-
lastEventAt: event.timestamp
|
|
12573
|
+
lastEventAt: event.timestamp,
|
|
12574
|
+
promptPreview: event.eventType === "user_prompt" ? event.content.slice(0, 160) : "",
|
|
12575
|
+
firstUserPromptAt: event.eventType === "user_prompt" ? event.timestamp : null,
|
|
12576
|
+
toolCount: event.eventType === "tool_observation" ? 1 : 0,
|
|
12577
|
+
responseCount: event.eventType === "agent_response" ? 1 : 0,
|
|
12578
|
+
eventTypeCounts: { [event.eventType || "unknown"]: 1 },
|
|
12579
|
+
source: typeof event.metadata?.source === "string" ? event.metadata.source : null
|
|
12574
12580
|
});
|
|
12575
12581
|
} else {
|
|
12576
12582
|
existing.eventCount++;
|
|
12583
|
+
const eventType = event.eventType || "unknown";
|
|
12584
|
+
existing.eventTypeCounts[eventType] = (existing.eventTypeCounts[eventType] || 0) + 1;
|
|
12585
|
+
if (event.eventType === "tool_observation") existing.toolCount++;
|
|
12586
|
+
if (event.eventType === "agent_response") existing.responseCount++;
|
|
12587
|
+
if (event.eventType === "user_prompt" && (!existing.firstUserPromptAt || event.timestamp < existing.firstUserPromptAt)) {
|
|
12588
|
+
existing.firstUserPromptAt = event.timestamp;
|
|
12589
|
+
existing.promptPreview = event.content.slice(0, 160);
|
|
12590
|
+
}
|
|
12591
|
+
if (!existing.source && typeof event.metadata?.source === "string") existing.source = event.metadata.source;
|
|
12577
12592
|
if (event.timestamp < existing.startedAt) {
|
|
12578
12593
|
existing.startedAt = event.timestamp;
|
|
12579
12594
|
}
|
|
@@ -14616,7 +14631,7 @@ turnsRouter.get("/", async (c) => {
|
|
|
14616
14631
|
if (!sessionId) {
|
|
14617
14632
|
return c.json({ error: "sessionId is required" }, 400);
|
|
14618
14633
|
}
|
|
14619
|
-
const memoryService =
|
|
14634
|
+
const memoryService = getLightweightServiceFromQuery(c);
|
|
14620
14635
|
try {
|
|
14621
14636
|
await memoryService.initialize();
|
|
14622
14637
|
const turns = await memoryService.getSessionTurns(sessionId, { limit, offset });
|
|
@@ -14650,7 +14665,7 @@ turnsRouter.get("/", async (c) => {
|
|
|
14650
14665
|
});
|
|
14651
14666
|
turnsRouter.get("/:turnId", async (c) => {
|
|
14652
14667
|
const { turnId } = c.req.param();
|
|
14653
|
-
const memoryService =
|
|
14668
|
+
const memoryService = getLightweightServiceFromQuery(c);
|
|
14654
14669
|
try {
|
|
14655
14670
|
await memoryService.initialize();
|
|
14656
14671
|
const events = await memoryService.getEventsByTurn(turnId);
|
|
@@ -14725,6 +14740,58 @@ import * as fs10 from "fs";
|
|
|
14725
14740
|
import * as path12 from "path";
|
|
14726
14741
|
import * as os7 from "os";
|
|
14727
14742
|
var projectsRouter = new Hono7();
|
|
14743
|
+
projectsRouter.get("/:hash/detail", async (c) => {
|
|
14744
|
+
const { hash } = c.req.param();
|
|
14745
|
+
const registry = loadSessionRegistry();
|
|
14746
|
+
const projectPath = getRegisteredProjectPath(registry, hash);
|
|
14747
|
+
const storagePath = resolveProjectStoragePath(hash);
|
|
14748
|
+
const memoryService = new MemoryService({
|
|
14749
|
+
storagePath,
|
|
14750
|
+
projectHash: hash,
|
|
14751
|
+
...projectPath ? { projectPath } : {},
|
|
14752
|
+
readOnly: true,
|
|
14753
|
+
lightweightMode: true,
|
|
14754
|
+
analyticsEnabled: false,
|
|
14755
|
+
sharedStoreConfig: DISABLED_SHARED_STORE_CONFIG
|
|
14756
|
+
});
|
|
14757
|
+
try {
|
|
14758
|
+
await memoryService.initialize();
|
|
14759
|
+
const [stats, recentEvents, retrieval, outbox] = await Promise.all([
|
|
14760
|
+
memoryService.getStats(),
|
|
14761
|
+
memoryService.getRecentEvents(1e3),
|
|
14762
|
+
memoryService.getRetrievalTraceStats(),
|
|
14763
|
+
memoryService.getOutboxStats()
|
|
14764
|
+
]);
|
|
14765
|
+
const eventSummary = summarizeProjectEvents(recentEvents);
|
|
14766
|
+
return c.json({
|
|
14767
|
+
project: {
|
|
14768
|
+
hash,
|
|
14769
|
+
projectName: projectPath ? path12.basename(projectPath) : `unknown (${hash})`,
|
|
14770
|
+
registered: Boolean(projectPath)
|
|
14771
|
+
},
|
|
14772
|
+
storage: {
|
|
14773
|
+
eventCount: stats.totalEvents,
|
|
14774
|
+
vectorCount: stats.vectorCount,
|
|
14775
|
+
levels: stats.levelStats || []
|
|
14776
|
+
},
|
|
14777
|
+
sessions: eventSummary.sessions,
|
|
14778
|
+
eventTypes: eventSummary.eventTypes,
|
|
14779
|
+
sources: eventSummary.sources,
|
|
14780
|
+
activity: eventSummary.activity,
|
|
14781
|
+
retrieval: {
|
|
14782
|
+
totalQueries: retrieval.totalQueries,
|
|
14783
|
+
avgCandidateCount: retrieval.avgCandidateCount,
|
|
14784
|
+
avgSelectedCount: retrieval.avgSelectedCount,
|
|
14785
|
+
selectionRate: retrieval.selectionRate
|
|
14786
|
+
},
|
|
14787
|
+
outbox: summarizeProjectOutbox(outbox)
|
|
14788
|
+
});
|
|
14789
|
+
} catch {
|
|
14790
|
+
return c.json({ error: "Project detail unavailable" }, 500);
|
|
14791
|
+
} finally {
|
|
14792
|
+
await memoryService.shutdown();
|
|
14793
|
+
}
|
|
14794
|
+
});
|
|
14728
14795
|
projectsRouter.get("/", async (c) => {
|
|
14729
14796
|
try {
|
|
14730
14797
|
const projectsDir = path12.join(os7.homedir(), ".claude-code", "memory", "projects");
|
|
@@ -14764,6 +14831,57 @@ projectsRouter.get("/", async (c) => {
|
|
|
14764
14831
|
return c.json({ projects: [], error: error.message }, 500);
|
|
14765
14832
|
}
|
|
14766
14833
|
});
|
|
14834
|
+
function getRegisteredProjectPath(registry, hash) {
|
|
14835
|
+
for (const entry of Object.values(registry.sessions)) {
|
|
14836
|
+
if (entry.projectHash === hash) {
|
|
14837
|
+
return entry.projectPath;
|
|
14838
|
+
}
|
|
14839
|
+
}
|
|
14840
|
+
return void 0;
|
|
14841
|
+
}
|
|
14842
|
+
function summarizeProjectEvents(events) {
|
|
14843
|
+
const sessions = /* @__PURE__ */ new Set();
|
|
14844
|
+
const eventTypes = {};
|
|
14845
|
+
const sources = {};
|
|
14846
|
+
let firstEventAt = null;
|
|
14847
|
+
let lastEventAt = null;
|
|
14848
|
+
for (const event of events) {
|
|
14849
|
+
if (event.sessionId) sessions.add(event.sessionId);
|
|
14850
|
+
incrementBucket(eventTypes, event.eventType || "unknown");
|
|
14851
|
+
const source = typeof event.metadata?.source === "string" ? event.metadata.source : "unknown";
|
|
14852
|
+
incrementBucket(sources, source || "unknown");
|
|
14853
|
+
const timestamp = normalizeIsoTimestamp(event.timestamp);
|
|
14854
|
+
if (!timestamp) continue;
|
|
14855
|
+
if (!firstEventAt || timestamp < firstEventAt) firstEventAt = timestamp;
|
|
14856
|
+
if (!lastEventAt || timestamp > lastEventAt) lastEventAt = timestamp;
|
|
14857
|
+
}
|
|
14858
|
+
return {
|
|
14859
|
+
sessions: { total: sessions.size },
|
|
14860
|
+
eventTypes,
|
|
14861
|
+
sources,
|
|
14862
|
+
activity: { firstEventAt, lastEventAt }
|
|
14863
|
+
};
|
|
14864
|
+
}
|
|
14865
|
+
function summarizeProjectOutbox(outbox) {
|
|
14866
|
+
const embedding = outbox.embedding || {};
|
|
14867
|
+
const vector = outbox.vector || {};
|
|
14868
|
+
return {
|
|
14869
|
+
pending: (embedding.pending || 0) + (vector.pending || 0),
|
|
14870
|
+
processing: (embedding.processing || 0) + (vector.processing || 0),
|
|
14871
|
+
failed: (embedding.failed || 0) + (vector.failed || 0),
|
|
14872
|
+
stuckProcessing: (embedding.stuckProcessing || 0) + (vector.stuckProcessing || 0)
|
|
14873
|
+
};
|
|
14874
|
+
}
|
|
14875
|
+
function incrementBucket(bucket, rawLabel) {
|
|
14876
|
+
const label = rawLabel.replace(/[^a-zA-Z0-9_.:-]/g, "_").slice(0, 80) || "unknown";
|
|
14877
|
+
bucket[label] = (bucket[label] || 0) + 1;
|
|
14878
|
+
}
|
|
14879
|
+
function normalizeIsoTimestamp(value) {
|
|
14880
|
+
if (!value) return null;
|
|
14881
|
+
const date = value instanceof Date ? value : new Date(value);
|
|
14882
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
14883
|
+
return date.toISOString();
|
|
14884
|
+
}
|
|
14767
14885
|
function formatBytes(bytes) {
|
|
14768
14886
|
if (bytes === 0) return "0 B";
|
|
14769
14887
|
const k = 1024;
|
|
@@ -15052,7 +15170,101 @@ function streamClaudeResponse(prompt, stream) {
|
|
|
15052
15170
|
|
|
15053
15171
|
// src/apps/server/api/health.ts
|
|
15054
15172
|
import { Hono as Hono9 } from "hono";
|
|
15173
|
+
import { spawnSync } from "child_process";
|
|
15055
15174
|
var healthRouter = new Hono9();
|
|
15175
|
+
function hasEnvSignal(name) {
|
|
15176
|
+
const value = process.env[name];
|
|
15177
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
15178
|
+
}
|
|
15179
|
+
function claudeAuthSignal() {
|
|
15180
|
+
return hasEnvSignal("ANTHROPIC_API_KEY") || hasEnvSignal("CLAUDE_API_KEY") || hasEnvSignal("CLAUDE_CODE_OAUTH_TOKEN") ? "env-present" : "not-detected";
|
|
15181
|
+
}
|
|
15182
|
+
function detectClaudeCli() {
|
|
15183
|
+
const result = spawnSync("claude", ["--version"], {
|
|
15184
|
+
encoding: "utf-8",
|
|
15185
|
+
timeout: 3e3
|
|
15186
|
+
});
|
|
15187
|
+
const authSignal = claudeAuthSignal();
|
|
15188
|
+
const errorCode = result.error?.code;
|
|
15189
|
+
if (errorCode === "ENOENT") {
|
|
15190
|
+
return { status: "missing", command: "claude", authSignal };
|
|
15191
|
+
}
|
|
15192
|
+
if (result.status === 0) {
|
|
15193
|
+
const version = String(result.stdout || "").split("\n")[0]?.trim().slice(0, 80) || void 0;
|
|
15194
|
+
return { status: "available", command: "claude", authSignal, ...version ? { version } : {} };
|
|
15195
|
+
}
|
|
15196
|
+
return { status: "error", command: "claude", authSignal };
|
|
15197
|
+
}
|
|
15198
|
+
function detectEmbeddingBackend() {
|
|
15199
|
+
const disabled = ["1", "true", "yes"].includes(String(process.env.CLAUDE_MEMORY_DISABLE_VECTOR || "").toLowerCase());
|
|
15200
|
+
return {
|
|
15201
|
+
status: disabled ? "disabled" : "enabled",
|
|
15202
|
+
backend: "@huggingface/transformers"
|
|
15203
|
+
};
|
|
15204
|
+
}
|
|
15205
|
+
function aggregateOutbox(outbox) {
|
|
15206
|
+
const pending = (outbox.embedding?.pending || 0) + (outbox.vector?.pending || 0);
|
|
15207
|
+
const processing = (outbox.embedding?.processing || 0) + (outbox.vector?.processing || 0);
|
|
15208
|
+
const failed = (outbox.embedding?.failed || 0) + (outbox.vector?.failed || 0);
|
|
15209
|
+
const stuckProcessing = (outbox.embedding?.stuckProcessing || 0) + (outbox.vector?.stuckProcessing || 0);
|
|
15210
|
+
return { pending, processing, failed, stuckProcessing };
|
|
15211
|
+
}
|
|
15212
|
+
healthRouter.get("/setup", async (c) => {
|
|
15213
|
+
const memoryService = getLightweightServiceFromQuery(c);
|
|
15214
|
+
try {
|
|
15215
|
+
await memoryService.initialize();
|
|
15216
|
+
const [stats, outbox] = await Promise.all([
|
|
15217
|
+
memoryService.getStats(),
|
|
15218
|
+
memoryService.getOutboxStats()
|
|
15219
|
+
]);
|
|
15220
|
+
const outboxTotals = aggregateOutbox(outbox);
|
|
15221
|
+
const claudeCli = detectClaudeCli();
|
|
15222
|
+
const embeddings = detectEmbeddingBackend();
|
|
15223
|
+
const recommendations = [];
|
|
15224
|
+
if (claudeCli.status !== "available") {
|
|
15225
|
+
recommendations.push("Install or authenticate Claude CLI to enable Ask Memory assistant responses.");
|
|
15226
|
+
}
|
|
15227
|
+
if (embeddings.status === "disabled") {
|
|
15228
|
+
recommendations.push("Enable vector embeddings to improve semantic retrieval and memory usefulness.");
|
|
15229
|
+
}
|
|
15230
|
+
if (outboxTotals.failed > 0 || outboxTotals.stuckProcessing > 0) {
|
|
15231
|
+
recommendations.push("Run vector health recovery before trusting retrieval freshness.");
|
|
15232
|
+
}
|
|
15233
|
+
let status = "ok";
|
|
15234
|
+
if (claudeCli.status !== "available" || embeddings.status === "disabled") {
|
|
15235
|
+
status = "needs-setup";
|
|
15236
|
+
} else if (outboxTotals.failed > 0 || outboxTotals.stuckProcessing > 0) {
|
|
15237
|
+
status = "needs-attention";
|
|
15238
|
+
}
|
|
15239
|
+
const scoped = Boolean(c.req.query("project") || c.req.query("projectId"));
|
|
15240
|
+
return c.json({
|
|
15241
|
+
status,
|
|
15242
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15243
|
+
setup: {
|
|
15244
|
+
scope: scoped ? "project" : "global",
|
|
15245
|
+
storage: {
|
|
15246
|
+
status: "ok",
|
|
15247
|
+
totalEvents: stats.totalEvents,
|
|
15248
|
+
vectorCount: stats.vectorCount
|
|
15249
|
+
},
|
|
15250
|
+
outbox: outboxTotals
|
|
15251
|
+
},
|
|
15252
|
+
providers: {
|
|
15253
|
+
claudeCli,
|
|
15254
|
+
embeddings
|
|
15255
|
+
},
|
|
15256
|
+
recommendations
|
|
15257
|
+
});
|
|
15258
|
+
} catch {
|
|
15259
|
+
return c.json({
|
|
15260
|
+
status: "error",
|
|
15261
|
+
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
15262
|
+
error: "Setup health check failed"
|
|
15263
|
+
}, 500);
|
|
15264
|
+
} finally {
|
|
15265
|
+
await memoryService.shutdown();
|
|
15266
|
+
}
|
|
15267
|
+
});
|
|
15056
15268
|
healthRouter.get("/", async (c) => {
|
|
15057
15269
|
const memoryService = getLightweightServiceFromQuery(c);
|
|
15058
15270
|
try {
|
|
@@ -15144,8 +15356,230 @@ healthRouter.post("/recover", async (c) => {
|
|
|
15144
15356
|
}
|
|
15145
15357
|
});
|
|
15146
15358
|
|
|
15359
|
+
// src/apps/server/api/playground.ts
|
|
15360
|
+
import { Hono as Hono10 } from "hono";
|
|
15361
|
+
var playgroundRouter = new Hono10();
|
|
15362
|
+
function normalizeWindowSize(value) {
|
|
15363
|
+
const parsed = typeof value === "number" ? value : Number.parseInt(String(value ?? ""), 10);
|
|
15364
|
+
if (!Number.isFinite(parsed)) return 3;
|
|
15365
|
+
return Math.max(1, Math.min(10, Math.trunc(parsed)));
|
|
15366
|
+
}
|
|
15367
|
+
function disclosureOptions(options = {}) {
|
|
15368
|
+
const {
|
|
15369
|
+
windowSize: _windowSize,
|
|
15370
|
+
selectedResultId: _selectedResultId,
|
|
15371
|
+
...rest
|
|
15372
|
+
} = options;
|
|
15373
|
+
return rest;
|
|
15374
|
+
}
|
|
15375
|
+
var SOURCE_EVENT_PREVIEW_CHARS = 500;
|
|
15376
|
+
var SOURCE_EVENT_LIMIT = 3;
|
|
15377
|
+
function asRecord(value) {
|
|
15378
|
+
return value && typeof value === "object" ? value : null;
|
|
15379
|
+
}
|
|
15380
|
+
function safeSourcePreview(value) {
|
|
15381
|
+
const raw = typeof value === "string" ? value : String(value ?? "");
|
|
15382
|
+
const sanitized = String(sanitizeGovernanceAuditValue(raw));
|
|
15383
|
+
return sanitized.length > SOURCE_EVENT_PREVIEW_CHARS ? `${sanitized.slice(0, SOURCE_EVENT_PREVIEW_CHARS)}...` : sanitized;
|
|
15384
|
+
}
|
|
15385
|
+
function sanitizeSourceEvent(event) {
|
|
15386
|
+
const record = asRecord(event);
|
|
15387
|
+
if (!record) {
|
|
15388
|
+
return { preview: safeSourcePreview(event) };
|
|
15389
|
+
}
|
|
15390
|
+
const safe = {};
|
|
15391
|
+
if (record.id !== void 0) safe.id = sanitizeGovernanceAuditValue(record.id, "id");
|
|
15392
|
+
if (record.eventId !== void 0) safe.eventId = sanitizeGovernanceAuditValue(record.eventId, "eventId");
|
|
15393
|
+
if (record.sessionId !== void 0) safe.sessionId = sanitizeGovernanceAuditValue(record.sessionId, "sessionId");
|
|
15394
|
+
if (record.eventType !== void 0) safe.eventType = sanitizeGovernanceAuditValue(record.eventType, "eventType");
|
|
15395
|
+
if (record.timestamp !== void 0) safe.timestamp = sanitizeGovernanceAuditValue(record.timestamp, "timestamp");
|
|
15396
|
+
const previewSource = record.content ?? record.preview ?? "";
|
|
15397
|
+
safe.preview = safeSourcePreview(previewSource);
|
|
15398
|
+
if (typeof record.content === "string") {
|
|
15399
|
+
safe.contentLength = record.content.length;
|
|
15400
|
+
}
|
|
15401
|
+
return safe;
|
|
15402
|
+
}
|
|
15403
|
+
function sanitizeStringArray(value) {
|
|
15404
|
+
if (!Array.isArray(value)) return void 0;
|
|
15405
|
+
return value.map((item) => safeSourcePreview(item).slice(0, 160)).filter((item) => item.length > 0);
|
|
15406
|
+
}
|
|
15407
|
+
function sanitizePlaygroundSource(source) {
|
|
15408
|
+
const record = asRecord(source);
|
|
15409
|
+
if (!record) return source;
|
|
15410
|
+
const safe = {};
|
|
15411
|
+
if (record.sourceRef !== void 0) safe.sourceRef = sanitizeGovernanceAuditValue(record.sourceRef, "sourceRef");
|
|
15412
|
+
if (record.sourceType !== void 0) safe.sourceType = sanitizeGovernanceAuditValue(record.sourceType, "sourceType");
|
|
15413
|
+
if (record.retrievalLayer !== void 0) safe.retrievalLayer = sanitizeGovernanceAuditValue(record.retrievalLayer, "retrievalLayer");
|
|
15414
|
+
if (record.summary !== void 0) safe.summary = sanitizeGovernanceAuditValue(record.summary, "summary");
|
|
15415
|
+
const eventIds = sanitizeStringArray(record.eventIds);
|
|
15416
|
+
if (eventIds) safe.eventIds = eventIds;
|
|
15417
|
+
const rawEvents = Array.isArray(record.rawEvents) ? record.rawEvents : [];
|
|
15418
|
+
safe.rawEvents = rawEvents.slice(0, SOURCE_EVENT_LIMIT).map(sanitizeSourceEvent);
|
|
15419
|
+
if (rawEvents.length > SOURCE_EVENT_LIMIT) {
|
|
15420
|
+
safe.omittedRawEventCount = rawEvents.length - SOURCE_EVENT_LIMIT;
|
|
15421
|
+
}
|
|
15422
|
+
if (record.primaryEvent !== void 0) safe.primaryEvent = sanitizeSourceEvent(record.primaryEvent);
|
|
15423
|
+
if (record.rawEvent !== void 0) safe.rawEvent = sanitizeSourceEvent(record.rawEvent);
|
|
15424
|
+
if (record.event !== void 0) safe.event = sanitizeSourceEvent(record.event);
|
|
15425
|
+
return safe;
|
|
15426
|
+
}
|
|
15427
|
+
function sanitizeScore(value) {
|
|
15428
|
+
const score = typeof value === "number" ? value : Number(value);
|
|
15429
|
+
return Number.isFinite(score) ? score : void 0;
|
|
15430
|
+
}
|
|
15431
|
+
function sanitizeReasons(value) {
|
|
15432
|
+
return sanitizeStringArray(value)?.slice(0, 8) ?? [];
|
|
15433
|
+
}
|
|
15434
|
+
function sanitizeSearchResult(result) {
|
|
15435
|
+
const record = asRecord(result);
|
|
15436
|
+
if (!record) return { snippet: safeSourcePreview(result) };
|
|
15437
|
+
const safe = {};
|
|
15438
|
+
if (record.id !== void 0) safe.id = sanitizeGovernanceAuditValue(record.id, "id");
|
|
15439
|
+
if (record.sourceRef !== void 0) safe.sourceRef = sanitizeGovernanceAuditValue(record.sourceRef, "sourceRef");
|
|
15440
|
+
if (record.resultType !== void 0) safe.resultType = sanitizeGovernanceAuditValue(record.resultType, "resultType");
|
|
15441
|
+
const score = sanitizeScore(record.score);
|
|
15442
|
+
if (score !== void 0) safe.score = score;
|
|
15443
|
+
safe.snippet = safeSourcePreview(record.snippet ?? record.preview ?? record.summary ?? "");
|
|
15444
|
+
if (record.preview !== void 0) safe.preview = safeSourcePreview(record.preview);
|
|
15445
|
+
safe.reasons = sanitizeReasons(record.reasons);
|
|
15446
|
+
return safe;
|
|
15447
|
+
}
|
|
15448
|
+
var PLAYGROUND_META_FIELDS = [
|
|
15449
|
+
"total",
|
|
15450
|
+
"totalMatches",
|
|
15451
|
+
"usedVector",
|
|
15452
|
+
"usedKeyword",
|
|
15453
|
+
"usedRerank",
|
|
15454
|
+
"queryTimeMs",
|
|
15455
|
+
"strategy"
|
|
15456
|
+
];
|
|
15457
|
+
function copySafeMetaValue(target, source, field) {
|
|
15458
|
+
const value = source[field];
|
|
15459
|
+
if (typeof value === "number" || typeof value === "boolean") {
|
|
15460
|
+
target[field] = value;
|
|
15461
|
+
} else if (typeof value === "string") {
|
|
15462
|
+
target[field] = safeSourcePreview(value).slice(0, 160);
|
|
15463
|
+
}
|
|
15464
|
+
}
|
|
15465
|
+
function sanitizeMeta(meta) {
|
|
15466
|
+
const record = asRecord(meta);
|
|
15467
|
+
if (!record) return void 0;
|
|
15468
|
+
const safe = {};
|
|
15469
|
+
for (const field of PLAYGROUND_META_FIELDS) {
|
|
15470
|
+
copySafeMetaValue(safe, record, field);
|
|
15471
|
+
}
|
|
15472
|
+
return safe;
|
|
15473
|
+
}
|
|
15474
|
+
function sanitizePlaygroundSearch(search) {
|
|
15475
|
+
const record = asRecord(search);
|
|
15476
|
+
if (!record) return search;
|
|
15477
|
+
const safe = {
|
|
15478
|
+
results: Array.isArray(record.results) ? record.results.map(sanitizeSearchResult) : []
|
|
15479
|
+
};
|
|
15480
|
+
const meta = sanitizeMeta(record.meta);
|
|
15481
|
+
if (meta) safe.meta = meta;
|
|
15482
|
+
return safe;
|
|
15483
|
+
}
|
|
15484
|
+
function sanitizeExpansionFact(fact) {
|
|
15485
|
+
const record = asRecord(fact);
|
|
15486
|
+
if (!record) return { snippet: safeSourcePreview(fact) };
|
|
15487
|
+
const safe = {};
|
|
15488
|
+
if (record.id !== void 0) safe.id = sanitizeGovernanceAuditValue(record.id, "id");
|
|
15489
|
+
if (record.eventId !== void 0) safe.eventId = sanitizeGovernanceAuditValue(record.eventId, "eventId");
|
|
15490
|
+
if (record.sourceRef !== void 0) safe.sourceRef = sanitizeGovernanceAuditValue(record.sourceRef, "sourceRef");
|
|
15491
|
+
if (record.eventType !== void 0) safe.eventType = sanitizeGovernanceAuditValue(record.eventType, "eventType");
|
|
15492
|
+
if (record.sessionId !== void 0) safe.sessionId = sanitizeGovernanceAuditValue(record.sessionId, "sessionId");
|
|
15493
|
+
if (record.timestamp !== void 0) safe.timestamp = sanitizeGovernanceAuditValue(record.timestamp, "timestamp");
|
|
15494
|
+
const preview = safeSourcePreview(record.snippet ?? record.summary ?? record.preview ?? record.content ?? "");
|
|
15495
|
+
safe.snippet = preview;
|
|
15496
|
+
safe.preview = preview;
|
|
15497
|
+
return safe;
|
|
15498
|
+
}
|
|
15499
|
+
function sanitizeRelatedSource(source) {
|
|
15500
|
+
const record = asRecord(source);
|
|
15501
|
+
if (!record) return { preview: safeSourcePreview(source) };
|
|
15502
|
+
const safe = {};
|
|
15503
|
+
if (record.sourceRef !== void 0) safe.sourceRef = sanitizeGovernanceAuditValue(record.sourceRef, "sourceRef");
|
|
15504
|
+
const eventIds = sanitizeStringArray(record.eventIds);
|
|
15505
|
+
if (eventIds) safe.eventIds = eventIds;
|
|
15506
|
+
const previewSource = record.snippet ?? record.summary ?? record.preview;
|
|
15507
|
+
if (previewSource !== void 0) safe.preview = safeSourcePreview(previewSource);
|
|
15508
|
+
return safe;
|
|
15509
|
+
}
|
|
15510
|
+
function sanitizePlaygroundExpansion(expansion) {
|
|
15511
|
+
const record = asRecord(expansion);
|
|
15512
|
+
if (!record) return expansion;
|
|
15513
|
+
const safe = {};
|
|
15514
|
+
if (record.target !== void 0) safe.target = sanitizeSearchResult(record.target);
|
|
15515
|
+
safe.surroundingFacts = Array.isArray(record.surroundingFacts) ? record.surroundingFacts.slice(0, 12).map(sanitizeExpansionFact) : [];
|
|
15516
|
+
safe.relatedSources = Array.isArray(record.relatedSources) ? record.relatedSources.slice(0, 12).map(sanitizeRelatedSource) : [];
|
|
15517
|
+
return safe;
|
|
15518
|
+
}
|
|
15519
|
+
function selectedResultIdFrom(searchResult, requested) {
|
|
15520
|
+
if (requested && searchResult.results?.some((r) => r.id === requested || r.sourceRef === requested)) {
|
|
15521
|
+
return requested;
|
|
15522
|
+
}
|
|
15523
|
+
const first = searchResult.results?.[0];
|
|
15524
|
+
return first?.id ?? first?.sourceRef ?? null;
|
|
15525
|
+
}
|
|
15526
|
+
playgroundRouter.post("/dry-run", async (c) => {
|
|
15527
|
+
let body;
|
|
15528
|
+
try {
|
|
15529
|
+
body = await c.req.json();
|
|
15530
|
+
} catch {
|
|
15531
|
+
return c.json({ error: "Invalid JSON body" }, 400);
|
|
15532
|
+
}
|
|
15533
|
+
const query = body.query?.trim();
|
|
15534
|
+
if (!query) {
|
|
15535
|
+
return c.json({ error: "Query is required" }, 400);
|
|
15536
|
+
}
|
|
15537
|
+
const useFastStrategy = body.options?.strategy === "fast";
|
|
15538
|
+
const memoryService = useFastStrategy ? getLightweightServiceFromQuery(c) : getServiceFromQuery(c);
|
|
15539
|
+
const replayTrace = [];
|
|
15540
|
+
try {
|
|
15541
|
+
await memoryService.initialize();
|
|
15542
|
+
replayTrace.push("search");
|
|
15543
|
+
const search = await memoryService.searchDisclosure(query, disclosureOptions(body.options));
|
|
15544
|
+
const selectedResultId = selectedResultIdFrom(search, body.options?.selectedResultId);
|
|
15545
|
+
const safeSearch = sanitizePlaygroundSearch(search);
|
|
15546
|
+
if (!selectedResultId) {
|
|
15547
|
+
replayTrace.push("no-results");
|
|
15548
|
+
return c.json({
|
|
15549
|
+
dryRun: true,
|
|
15550
|
+
mutated: false,
|
|
15551
|
+
query,
|
|
15552
|
+
selectedResultId: null,
|
|
15553
|
+
search: safeSearch,
|
|
15554
|
+
expansion: null,
|
|
15555
|
+
source: null,
|
|
15556
|
+
replayTrace
|
|
15557
|
+
});
|
|
15558
|
+
}
|
|
15559
|
+
const windowSize = normalizeWindowSize(body.options?.windowSize);
|
|
15560
|
+
replayTrace.push(`expand:${selectedResultId}`);
|
|
15561
|
+
const expansion = sanitizePlaygroundExpansion(await memoryService.expandDisclosure(selectedResultId, { windowSize }));
|
|
15562
|
+
replayTrace.push(`source:${selectedResultId}`);
|
|
15563
|
+
const source = sanitizePlaygroundSource(await memoryService.sourceDisclosure(selectedResultId));
|
|
15564
|
+
return c.json({
|
|
15565
|
+
dryRun: true,
|
|
15566
|
+
mutated: false,
|
|
15567
|
+
query,
|
|
15568
|
+
selectedResultId,
|
|
15569
|
+
search: safeSearch,
|
|
15570
|
+
expansion,
|
|
15571
|
+
source,
|
|
15572
|
+
replayTrace
|
|
15573
|
+
});
|
|
15574
|
+
} catch (error) {
|
|
15575
|
+
return c.json({ error: safeSourcePreview(error.message), dryRun: true, mutated: false, replayTrace }, 500);
|
|
15576
|
+
} finally {
|
|
15577
|
+
await memoryService.shutdown();
|
|
15578
|
+
}
|
|
15579
|
+
});
|
|
15580
|
+
|
|
15147
15581
|
// src/apps/server/api/index.ts
|
|
15148
|
-
var apiRouter = new
|
|
15582
|
+
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);
|
|
15149
15583
|
export {
|
|
15150
15584
|
apiRouter
|
|
15151
15585
|
};
|