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.
@@ -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 Hono10 } from "hono";
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 = getServiceFromQuery(c);
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 = getServiceFromQuery(c);
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 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);
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
  };