claude-memory-layer 1.0.48 → 1.0.50

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.
@@ -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 Hono11 } from "hono";
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 Hono10 } from "hono";
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 = getServiceFromQuery(c);
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 = getServiceFromQuery(c);
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 Hono10().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);
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 Hono11();
15755
+ const app2 = new Hono12();
15322
15756
  const authOptions = createAuthOptions(options);
15323
15757
  app2.use("*", cors());
15324
15758
  app2.use("*", logger());