@remnic/core 9.3.645 → 9.3.647
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/access-cli.js +18 -17
- package/dist/access-cli.js.map +1 -1
- package/dist/access-http.js +13 -13
- package/dist/access-mcp.js +10 -10
- package/dist/access-schema.js +3 -3
- package/dist/access-service.js +8 -8
- package/dist/adapters/index.js +4 -4
- package/dist/adapters/registry.js +2 -2
- package/dist/{capsule-crypto-YO5QJ6L3.js → capsule-crypto-GWVG7LGC.js} +2 -2
- package/dist/{chunk-6YLJZ2EU.js → chunk-3D6L7CEP.js} +92 -29
- package/dist/chunk-3D6L7CEP.js.map +1 -0
- package/dist/{chunk-3SF7QNKF.js → chunk-6BNFVP7Y.js} +2 -2
- package/dist/{chunk-AGNBY3VG.js → chunk-APJQ6UEA.js} +4 -4
- package/dist/{chunk-HRXCPKN4.js → chunk-APWJRJFW.js} +2 -2
- package/dist/{chunk-VDX2J7OX.js → chunk-BUKK5SWA.js} +2 -2
- package/dist/{chunk-T22TZJWJ.js → chunk-DC66QVL2.js} +2 -2
- package/dist/chunk-EVWIEEKZ.js +315 -0
- package/dist/chunk-EVWIEEKZ.js.map +1 -0
- package/dist/{chunk-IZXOO6NP.js → chunk-FAYDM5WD.js} +2 -2
- package/dist/{chunk-Y2XKJJEI.js → chunk-IMWFHBG2.js} +10 -33
- package/dist/chunk-IMWFHBG2.js.map +1 -0
- package/dist/{chunk-W5ISYSIL.js → chunk-L7W5YW6Y.js} +4 -4
- package/dist/{chunk-DGNQRNLL.js → chunk-NT5TINK5.js} +2 -2
- package/dist/{chunk-BXLOS5AJ.js → chunk-OWHERGF2.js} +2 -2
- package/dist/{chunk-CDHRUNBX.js → chunk-QKE4LHNR.js} +2 -2
- package/dist/{chunk-BTRL6GG7.js → chunk-RAELB5NX.js} +5 -5
- package/dist/{chunk-2QSZNTDO.js → chunk-RKNJBZ55.js} +4 -4
- package/dist/chunk-S4DDLTPX.js +140 -0
- package/dist/chunk-S4DDLTPX.js.map +1 -0
- package/dist/{chunk-MBT3ASYV.js → chunk-TA4LQ5SR.js} +12 -12
- package/dist/{chunk-KVUG7FF7.js → chunk-U55D5UD5.js} +7 -7
- package/dist/{chunk-DQEMWVMT.js → chunk-UVYI6VIX.js} +1 -1
- package/dist/{chunk-D2B22JDF.js → chunk-WEPMT6SC.js} +7 -7
- package/dist/{chunk-IHG27THY.js → chunk-XUGQQPGO.js} +77 -61
- package/dist/chunk-XUGQQPGO.js.map +1 -0
- package/dist/{chunk-YOXCJP3A.js → chunk-ZPPFKVSD.js} +3 -3
- package/dist/cli.js +30 -28
- package/dist/{first-start-migration-FF7YFGRP.js → first-start-migration-PG5HBC3K.js} +4 -4
- package/dist/index.d.ts +2 -0
- package/dist/index.js +77 -63
- package/dist/index.js.map +1 -1
- package/dist/lcm/engine.js +3 -3
- package/dist/lcm/index.js +11 -11
- package/dist/namespaces/migrate.js +5 -5
- package/dist/namespaces/search.js +4 -4
- package/dist/operator-toolkit.js +6 -6
- package/dist/orchestrator.js +14 -13
- package/dist/resume-bundles.js +3 -2
- package/dist/schemas.d.ts +22 -22
- package/dist/search/factory.js +3 -3
- package/dist/search/index.js +7 -7
- package/dist/session-identity.d.ts +118 -0
- package/dist/session-identity.js +21 -0
- package/dist/session-identity.js.map +1 -0
- package/dist/session-transcript-migration.d.ts +65 -0
- package/dist/session-transcript-migration.js +16 -0
- package/dist/session-transcript-migration.js.map +1 -0
- package/dist/summarizer.js +2 -1
- package/dist/transcript.d.ts +18 -6
- package/dist/transcript.js +2 -1
- package/dist/transfer/backup.js +2 -2
- package/dist/transfer/capsule-export.js +2 -2
- package/dist/transfer/capsule-import.js +2 -2
- package/dist/transfer/import-sqlite.js +2 -2
- package/dist/transfer/types.d.ts +12 -12
- package/package.json +1 -1
- package/src/cli.ts +79 -0
- package/src/index.ts +18 -0
- package/src/session-identity.test.ts +130 -0
- package/src/session-identity.ts +281 -0
- package/src/session-transcript-migration.test.ts +350 -0
- package/src/session-transcript-migration.ts +527 -0
- package/src/summarizer.ts +15 -35
- package/src/transcript-session-identity.test.ts +424 -0
- package/src/transcript.test.ts +110 -0
- package/src/transcript.ts +121 -73
- package/dist/chunk-6YLJZ2EU.js.map +0 -1
- package/dist/chunk-IHG27THY.js.map +0 -1
- package/dist/chunk-Y2XKJJEI.js.map +0 -1
- /package/dist/{capsule-crypto-YO5QJ6L3.js.map → capsule-crypto-GWVG7LGC.js.map} +0 -0
- /package/dist/{chunk-3SF7QNKF.js.map → chunk-6BNFVP7Y.js.map} +0 -0
- /package/dist/{chunk-AGNBY3VG.js.map → chunk-APJQ6UEA.js.map} +0 -0
- /package/dist/{chunk-HRXCPKN4.js.map → chunk-APWJRJFW.js.map} +0 -0
- /package/dist/{chunk-VDX2J7OX.js.map → chunk-BUKK5SWA.js.map} +0 -0
- /package/dist/{chunk-T22TZJWJ.js.map → chunk-DC66QVL2.js.map} +0 -0
- /package/dist/{chunk-IZXOO6NP.js.map → chunk-FAYDM5WD.js.map} +0 -0
- /package/dist/{chunk-W5ISYSIL.js.map → chunk-L7W5YW6Y.js.map} +0 -0
- /package/dist/{chunk-DGNQRNLL.js.map → chunk-NT5TINK5.js.map} +0 -0
- /package/dist/{chunk-BXLOS5AJ.js.map → chunk-OWHERGF2.js.map} +0 -0
- /package/dist/{chunk-CDHRUNBX.js.map → chunk-QKE4LHNR.js.map} +0 -0
- /package/dist/{chunk-BTRL6GG7.js.map → chunk-RAELB5NX.js.map} +0 -0
- /package/dist/{chunk-2QSZNTDO.js.map → chunk-RKNJBZ55.js.map} +0 -0
- /package/dist/{chunk-MBT3ASYV.js.map → chunk-TA4LQ5SR.js.map} +0 -0
- /package/dist/{chunk-KVUG7FF7.js.map → chunk-U55D5UD5.js.map} +0 -0
- /package/dist/{chunk-DQEMWVMT.js.map → chunk-UVYI6VIX.js.map} +0 -0
- /package/dist/{chunk-D2B22JDF.js.map → chunk-WEPMT6SC.js.map} +0 -0
- /package/dist/{chunk-YOXCJP3A.js.map → chunk-ZPPFKVSD.js.map} +0 -0
- /package/dist/{first-start-migration-FF7YFGRP.js.map → first-start-migration-PG5HBC3K.js.map} +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/summarizer.ts"],"sourcesContent":["import { mkdir, readFile, writeFile, readdir } from \"node:fs/promises\";\nimport path from \"node:path\";\nimport { z } from \"zod\";\nimport { log } from \"./logger.js\";\nimport { LocalLlmClient } from \"./local-llm.js\";\nimport { FallbackLlmClient, fallbackLlmRuntimeContextFromConfig, gatewayTaskChainOptions } from \"./fallback-llm.js\";\nimport { ModelRegistry } from \"./model-registry.js\";\nimport { extractJsonCandidates } from \"./json-extract.js\";\nimport type { HourlySummary, TranscriptEntry, PluginConfig, GatewayConfig } from \"./types.js\";\nimport type { TranscriptManager } from \"./transcript.js\";\nimport { readSummarySnapshot, upsertSummarySnapshot, writeSummarySnapshot } from \"./summary-snapshot.js\";\nimport {\n encodeStoragePathSegment,\n resolveSafeStoragePath,\n} from \"./storage-paths.js\";\nimport { sessionStoragePaths } from \"./session-identity.js\";\n\n// Schema for LLM summary output\nconst HourlySummarySchema = z.object({\n bullets: z\n .array(z.string().trim().min(1))\n .min(1)\n .describe(\"3-5 bullet points summarizing the hour's activity\"),\n});\n\ntype HourlySummaryResult = z.infer<typeof HourlySummarySchema>;\n\nconst HourlySummaryExtendedSchema = z.object({\n topics: z.array(z.string()).default([]),\n decisions: z.array(z.string()).default([]),\n actionItems: z.array(z.string()).default([]),\n rejected: z.array(z.string()).default([]),\n});\n\ntype HourlySummaryExtendedResult = z.infer<typeof HourlySummaryExtendedSchema>;\n\ntype HourlySummaryExtendedMeta = {\n userTurns: number;\n assistantTurns: number;\n toolCalls: number;\n toolCounts: Record<string, number>;\n};\n\nfunction escapeRegExp(value: string): string {\n return value.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n}\n\nexport class HourlySummarizer {\n private summariesDir: string;\n private config: PluginConfig;\n private localLlm: LocalLlmClient;\n private fallbackLlm: FallbackLlmClient;\n private modelRegistry: ModelRegistry;\n private transcript?: TranscriptManager;\n\n constructor(config: PluginConfig, gatewayConfig?: GatewayConfig, modelRegistry?: ModelRegistry, transcript?: TranscriptManager) {\n this.config = config;\n this.summariesDir = path.join(config.memoryDir, \"summaries\", \"hourly\");\n this.modelRegistry = modelRegistry ?? new ModelRegistry(config.memoryDir);\n this.transcript = transcript;\n\n // Initialize local LLM client with shared model registry\n this.localLlm = new LocalLlmClient(config, this.modelRegistry);\n\n // Initialize fallback client with gateway config\n this.fallbackLlm = new FallbackLlmClient(\n gatewayConfig,\n fallbackLlmRuntimeContextFromConfig(config),\n );\n\n if (!gatewayConfig?.agents?.defaults?.model?.primary && !config.localLlmEnabled && config.modelSource !== \"gateway\") {\n log.warn(\"no gateway default AI and local LLM disabled — hourly summarization disabled\");\n }\n }\n\n private get useGatewayModelSource(): boolean {\n return this.config.modelSource === \"gateway\";\n }\n\n private get shouldUseLocalLlm(): boolean {\n return this.config.localLlmEnabled && !this.useGatewayModelSource;\n }\n\n private withGatewayAgent(options: import(\"./fallback-llm.js\").FallbackLlmOptions): import(\"./fallback-llm.js\").FallbackLlmOptions {\n if (!this.useGatewayModelSource) return options;\n // Shared resolution (taskModelChain > gatewayAgentId) — gotcha #22. Issue #1365.\n return { ...options, ...gatewayTaskChainOptions(this.config) };\n }\n\n async initialize(): Promise<void> {\n await mkdir(this.summariesDir, { recursive: true });\n log.info(\"hourly summarizer initialized\");\n }\n\n private async summarySessionDir(sessionKey: string): Promise<string> {\n return resolveSafeStoragePath(\n this.summariesDir,\n encodeStoragePathSegment(sessionKey, \"session\"),\n );\n }\n\n private async legacySummarySessionDir(sessionKey: string): Promise<string | null> {\n if (sessionKey.includes(\"\\0\")) return null;\n try {\n return await resolveSafeStoragePath(this.summariesDir, sessionKey);\n } catch {\n return null;\n }\n }\n\n private summaryDateString(hour: string): string {\n const dateStr = hour.slice(0, 10);\n if (!/^\\d{4}-\\d{2}-\\d{2}$/.test(dateStr)) {\n throw new Error(`invalid hourly summary timestamp: ${hour}`);\n }\n return dateStr;\n }\n\n // Generate summary for a specific hour and session\n async generateSummary(\n sessionKey: string,\n hourStart: Date,\n entries: TranscriptEntry[]\n ): Promise<HourlySummary | null> {\n if (entries.length === 0) return null;\n\n // Format entries for the LLM\n const conversation = entries\n .map((e) => `[${e.role}] ${e.content}`)\n .join(\"\\n\\n\");\n\n if (this.config.hourlySummariesExtendedEnabled) {\n const extended = await this.generateExtended(sessionKey, hourStart, conversation, entries);\n if (!extended) return null;\n const meta: HourlySummaryExtendedMeta = {\n userTurns: entries.filter((e) => e.role === \"user\").length,\n assistantTurns: entries.filter((e) => e.role === \"assistant\").length,\n toolCalls: extended._meta.toolCalls,\n toolCounts: extended._meta.toolCounts,\n };\n // Keep HourlySummary surface stable; encode \"topics\" as bullets for recall injection.\n const base: HourlySummary = {\n hour: hourStart.toISOString(),\n sessionKey,\n bullets: extended.topics.length > 0 ? extended.topics.slice(0, 5) : [\"(summary generated)\"],\n turnCount: entries.length,\n generatedAt: new Date().toISOString(),\n };\n const withExtras = base as any;\n withExtras._extended = extended;\n withExtras._extendedMeta = meta;\n return base;\n }\n\n const hourIso = hourStart.toISOString();\n const startTime = Date.now();\n\n // Try local LLM first if enabled\n if (this.shouldUseLocalLlm) {\n try {\n const localResult = await this.generateWithLocalLlm(conversation);\n if (localResult) {\n const durationMs = Date.now() - startTime;\n log.debug(\n `generated hourly summary for ${sessionKey} at ${hourIso} in ${durationMs}ms using local LLM`\n );\n return {\n hour: hourIso,\n sessionKey,\n bullets: localResult.bullets,\n turnCount: entries.length,\n generatedAt: new Date().toISOString(),\n };\n }\n // Local failed, fall back if allowed\n if (!this.config.localLlmFallback) {\n log.warn(\"summary generation: local LLM failed and fallback disabled\");\n return null;\n }\n log.info(\"summary generation: local LLM unavailable, falling back to gateway default AI\");\n } catch (err) {\n if (!this.config.localLlmFallback) {\n log.warn(\"summary generation: local LLM error and fallback disabled:\", err);\n return null;\n }\n log.info(\"summary generation: local LLM error, falling back to gateway default AI:\", err);\n }\n }\n\n // Fall back to gateway's default AI\n log.info(\"summary generation: falling back to gateway default AI\");\n\n try {\n const messages = [\n {\n role: \"system\" as const,\n content: `You are a conversation summarization system. Summarize the following conversation transcript into 3-5 concise bullet points.\n\nGuidelines:\n- Focus on what was accomplished, decided, or discussed\n- Include specific topics, projects, or entities mentioned\n- Note any significant user requests or agent actions\n- Keep bullets brief but informative (1-2 sentences each)\n- Skip trivial greetings or meta-conversation\n- Use present tense for ongoing work, past for completed items\n\nRespond with valid JSON matching this schema:\n{\n \"bullets\": [\"bullet 1\", \"bullet 2\", \"bullet 3\"]\n}`,\n },\n { role: \"user\" as const, content: `Summarize this conversation:\\n\\n${conversation}` },\n ];\n\n const result = await this.fallbackLlm.parseWithSchema(\n messages,\n HourlySummarySchema,\n this.withGatewayAgent({ temperature: 0.3, maxTokens: 8192 }),\n );\n\n const durationMs = Date.now() - startTime;\n\n if (result) {\n log.debug(\n `generated hourly summary for ${sessionKey} at ${hourIso} in ${durationMs}ms via fallback`,\n );\n return {\n hour: hourIso,\n sessionKey,\n bullets: result.bullets,\n turnCount: entries.length,\n generatedAt: new Date().toISOString(),\n };\n }\n\n log.warn(\"summary generation fallback returned no parsed output\");\n return null;\n } catch (err) {\n log.error(\"summary generation fallback failed\", err);\n return null;\n }\n }\n\n private async generateExtended(\n sessionKey: string,\n hourStart: Date,\n conversation: string,\n entries: TranscriptEntry[],\n ): Promise<(HourlySummaryExtendedResult & { _meta: HourlySummaryExtendedMeta }) | null> {\n const hourIso = hourStart.toISOString();\n const startTime = Date.now();\n\n const hourEnd = new Date(hourStart.getTime() + 60 * 60 * 1000);\n let toolCounts: Record<string, number> = {};\n if (this.config.hourlySummariesIncludeToolStats && this.transcript) {\n const uses = await this.transcript.readToolUse(sessionKey, hourStart, hourEnd);\n for (const u of uses) toolCounts[u.tool] = (toolCounts[u.tool] ?? 0) + 1;\n }\n\n const sys = `You are a conversation summarization system.\\n\\nSummarize the hour into structured sections.\\n\\nReturn valid JSON matching:\\n{\\n \\\"topics\\\": [\\\"...\\\"],\\n \\\"decisions\\\": [\\\"...\\\"],\\n \\\"actionItems\\\": [\\\"...\\\"],\\n \\\"rejected\\\": [\\\"...\\\"]\\n}\\n\\nGuidelines:\\n- Prefer concrete topics and decisions.\\n- Action items should be imperative.\\n- Rejected ideas are things that were explicitly discarded or reversed.\\n- If there are none for a section, return an empty array.\\n`;\n\n const toolStatsLine = Object.keys(toolCounts).length > 0\n ? `Tools used (counts): ${Object.entries(toolCounts).sort((a,b)=>b[1]-a[1]).slice(0,10).map(([k,v])=>`${k}=${v}`).join(\", \")}\\n\\n`\n : \"\";\n const userTurns = entries.filter((e) => e.role === \"user\").length;\n const assistantTurns = entries.filter((e) => e.role === \"assistant\").length;\n const toolCalls = Object.values(toolCounts).reduce((a,b)=>a+b,0);\n const statsLine = `Stats: userTurns=${userTurns}, assistantTurns=${assistantTurns}, toolCalls=${toolCalls}\\n\\n`;\n\n const user = `Hour: ${hourIso}\\nSession: ${sessionKey}\\n\\n${statsLine}${toolStatsLine}Conversation:\\n${conversation}\\n`;\n\n // Try local LLM first if enabled\n if (this.shouldUseLocalLlm) {\n try {\n const contextSizes = this.modelRegistry.calculateContextSizes(this.config.localLlmModel, this.config.localLlmMaxContext);\n const truncated = user.length > contextSizes.maxInputChars ? user.slice(0, contextSizes.maxInputChars) + \"\\n\\n[truncated]\" : user;\n const response = await this.localLlm.chatCompletion(\n [\n { role: \"system\", content: \"Output valid JSON only.\" },\n { role: \"user\", content: sys + \"\\n\\n\" + truncated },\n ],\n {\n temperature: 0.2,\n maxTokens: contextSizes.maxOutputTokens,\n operation: \"hourly_summary_extended\",\n priority: \"background\",\n },\n );\n if (response?.content) {\n const content = response.content.trim();\n for (const candidate of extractJsonCandidates(content)) {\n try {\n const parsed = JSON.parse(candidate);\n const result = HourlySummaryExtendedSchema.parse(parsed);\n log.debug(\n `generated extended hourly summary for ${sessionKey} at ${hourIso} in ${Date.now() - startTime}ms (local)`,\n );\n return { ...result, _meta: { userTurns, assistantTurns, toolCalls, toolCounts } };\n } catch {\n // keep trying candidates\n }\n }\n }\n } catch (err) {\n if (!this.config.localLlmFallback) return null;\n log.info(\"extended summary: local failed, falling back:\", err);\n }\n }\n\n try {\n const result = await this.fallbackLlm.parseWithSchema(\n [\n { role: \"system\" as const, content: sys },\n { role: \"user\" as const, content: user },\n ],\n HourlySummaryExtendedSchema,\n this.withGatewayAgent({ temperature: 0.2, maxTokens: 2048 }),\n );\n if (result) {\n log.debug(`generated extended hourly summary for ${sessionKey} at ${hourIso} in ${Date.now() - startTime}ms (fallback)`);\n return { ...result, _meta: { userTurns, assistantTurns, toolCalls, toolCounts } };\n }\n return null;\n } catch (err) {\n log.error(\"extended summary generation failed\", err);\n return null;\n }\n }\n\n /**\n * Generate summary using local LLM with JSON mode.\n * Uses dynamic context sizing based on model capabilities.\n */\n private async generateWithLocalLlm(conversation: string): Promise<HourlySummaryResult | null> {\n // Get dynamic context sizes based on model capabilities\n const contextSizes = this.modelRegistry.calculateContextSizes(\n this.config.localLlmModel,\n this.config.localLlmMaxContext\n );\n log.debug(`Summarizer model context: ${contextSizes.description}`);\n\n const maxConversationChars = contextSizes.maxInputChars;\n const truncatedConversation = conversation.length > maxConversationChars\n ? conversation.slice(0, maxConversationChars) + \"\\n\\n[truncated]\"\n : conversation;\n\n const instructions = `You are a conversation summarization system. Summarize the following conversation transcript into 3-5 concise bullet points.\n\nGuidelines:\n- Focus on what was accomplished, decided, or discussed\n- Include specific topics, projects, or entities mentioned\n- Note any significant user requests or agent actions\n- Keep bullets brief but informative (1-2 sentences each)\n- Skip trivial greetings or meta-conversation\n- Use present tense for ongoing work, past for completed items\n\nRespond with valid JSON matching this schema:\n{\n \"bullets\": [\"bullet 1\", \"bullet 2\", \"bullet 3\"]\n}`;\n\n const fullPrompt = `${instructions}\\n\\nConversation to summarize:\\n${truncatedConversation}`;\n\n const response = await this.localLlm.chatCompletion(\n [\n { role: \"system\", content: \"You are a conversation summarization system. Output valid JSON only.\" },\n { role: \"user\", content: fullPrompt },\n ],\n {\n temperature: 0.3,\n maxTokens: contextSizes.maxOutputTokens,\n operation: \"hourly_summary\",\n priority: \"background\",\n },\n );\n\n if (!response?.content) {\n return null;\n }\n\n try {\n // Parse JSON response\n const content = response.content.trim();\n for (const candidate of extractJsonCandidates(content)) {\n try {\n const parsed = JSON.parse(candidate);\n return HourlySummarySchema.parse(parsed);\n } catch {\n // keep trying candidates\n }\n }\n return null;\n } catch (err) {\n log.warn(\"local LLM summary: failed to parse JSON response:\", err);\n return null;\n }\n }\n\n // Save summary to file\n async saveSummary(summary: HourlySummary): Promise<void> {\n const sessionDir = await this.summarySessionDir(summary.sessionKey);\n await mkdir(sessionDir, { recursive: true });\n\n // Format date as YYYY-MM-DD for the filename\n const dateStr = this.summaryDateString(summary.hour);\n const filePath = await resolveSafeStoragePath(sessionDir, `${dateStr}.md`);\n\n // Format hour as HH:00 for display\n const hourStr = summary.hour.slice(11, 13);\n\n // Build markdown content\n const lines: string[] = [];\n\n // Check if file exists to append or create\n let existingContent = \"\";\n try {\n existingContent = await readFile(filePath, \"utf-8\");\n } catch {\n // File doesn't exist yet, will create new\n }\n\n // Check if this hour already exists (idempotent)\n const hourHeader = `## ${hourStr}:00`;\n if (existingContent.includes(hourHeader)) {\n // Replace existing hour section\n const headerMatch = new RegExp(`(^|\\\\n)${escapeRegExp(hourHeader)}\\\\n`).exec(existingContent);\n const sectionStart = headerMatch\n ? headerMatch.index + headerMatch[1].length\n : existingContent.indexOf(hourHeader);\n const nextHeaderPattern = /\\n## \\d{2}:00\\n/g;\n nextHeaderPattern.lastIndex = sectionStart + hourHeader.length;\n const nextHeader = nextHeaderPattern.exec(existingContent);\n const beforeHour = existingContent.slice(0, sectionStart);\n const afterHour = nextHeader\n ? existingContent.slice(nextHeader.index + 1)\n : \"\";\n const newSection = this.formatHourSection(summary, hourHeader);\n existingContent = beforeHour + newSection.trimEnd() + (afterHour ? \"\\n\\n\" + afterHour : \"\\n\");\n\n await writeFile(filePath, existingContent, \"utf-8\");\n log.debug(`updated hourly summary for ${summary.sessionKey} at ${hourStr}:00`);\n } else {\n // Append new hour section\n const newSection = this.formatHourSection(summary, hourHeader);\n\n if (existingContent) {\n // Add to existing file\n await writeFile(filePath, existingContent.trimEnd() + \"\\n\\n\" + newSection, \"utf-8\");\n } else {\n // Create new file with header\n const header = `# Hourly Summaries — ${dateStr}\\n\\n*Session: ${summary.sessionKey}*\\n`;\n await writeFile(filePath, header + \"\\n\" + newSection, \"utf-8\");\n }\n log.debug(`saved hourly summary for ${summary.sessionKey} at ${hourStr}:00`);\n }\n try {\n await upsertSummarySnapshot(this.config.memoryDir, summary);\n } catch (error) {\n log.warn(\n `hourly summarizer: failed to update summary snapshot for ${summary.sessionKey} (fail-open): ${String(error)}`,\n );\n }\n }\n\n private formatHourSection(summary: HourlySummary, hourHeader: string): string {\n const ext = (summary as any)._extended as (HourlySummaryExtendedResult & { _meta?: HourlySummaryExtendedMeta }) | undefined;\n const meta = (summary as any)._extendedMeta as HourlySummaryExtendedMeta | undefined;\n const lines: string[] = [hourHeader, \"\"];\n\n if (this.config.hourlySummariesExtendedEnabled && ext) {\n lines.push(\"### Topics Discussed\");\n for (const t of ext.topics) lines.push(`- ${t}`);\n lines.push(\"\");\n lines.push(\"### Decisions Made\");\n for (const d of ext.decisions) lines.push(`- ${d}`);\n lines.push(\"\");\n lines.push(\"### Action Items\");\n for (const a of ext.actionItems) lines.push(`- ${a}`);\n lines.push(\"\");\n lines.push(\"### Rejected Ideas / Reversals\");\n for (const r of ext.rejected) lines.push(`- ${r}`);\n lines.push(\"\");\n if (meta && Object.keys(meta.toolCounts).length > 0) {\n lines.push(\"### Tools Used\");\n const top = Object.entries(meta.toolCounts)\n .sort((a, b) => b[1] - a[1])\n .slice(0, 12);\n for (const [name, count] of top) lines.push(`- ${name}: ${count}`);\n lines.push(\"\");\n }\n lines.push(\"### Stats\");\n lines.push(`- Turns: ${summary.turnCount}`);\n if (meta) {\n lines.push(`- User turns: ${meta.userTurns}`);\n lines.push(`- Assistant turns: ${meta.assistantTurns}`);\n lines.push(`- Tool calls: ${meta.toolCalls}`);\n }\n lines.push(\"\");\n return lines.join(\"\\n\");\n }\n\n for (const bullet of summary.bullets) lines.push(`- ${bullet}`);\n lines.push(` *(${summary.turnCount} turns)*`);\n lines.push(\"\");\n return lines.join(\"\\n\");\n }\n\n // Read recent summaries for recall injection\n async readRecent(sessionKey: string, hours: number): Promise<HourlySummary[]> {\n try {\n const cutoffTime = Date.now() - hours * 60 * 60 * 1000;\n\n const snapshot = await readSummarySnapshot(this.config.memoryDir, sessionKey);\n if (snapshot) {\n return snapshot\n .filter((s) => new Date(s.hour).getTime() >= cutoffTime)\n .sort((a, b) => new Date(b.hour).getTime() - new Date(a.hour).getTime());\n }\n\n const summaries: HourlySummary[] = [];\n const encodedDir = await this.summarySessionDir(sessionKey);\n const legacyDir = await this.legacySummarySessionDir(sessionKey);\n const seenDirs = new Set<string>();\n\n for (const sessionDir of [encodedDir, legacyDir]) {\n if (!sessionDir || seenDirs.has(sessionDir)) continue;\n seenDirs.add(sessionDir);\n const parsed = await this.readSummaryDir(sessionDir, sessionKey);\n summaries.push(...parsed);\n }\n\n const byHour = new Map<string, HourlySummary>();\n for (const summary of summaries) {\n if (!byHour.has(summary.hour)) byHour.set(summary.hour, summary);\n }\n\n const sortedSummaries = Array.from(byHour.values()).sort(\n (a, b) => new Date(b.hour).getTime() - new Date(a.hour).getTime(),\n );\n\n // Filter to recent hours while materializing the full parsed history.\n const recent = sortedSummaries.filter(\n (s) => new Date(s.hour).getTime() >= cutoffTime,\n );\n\n if (sortedSummaries.length > 0) {\n try {\n await writeSummarySnapshot(\n this.config.memoryDir,\n sessionKey,\n sortedSummaries,\n );\n } catch (error) {\n log.warn(\n `hourly summarizer: failed to materialize summary snapshot for ${sessionKey} (fail-open): ${String(error)}`,\n );\n }\n }\n\n return recent;\n } catch {\n // Directory doesn't exist or error reading\n return [];\n }\n }\n\n private async readSummaryDir(\n sessionDir: string,\n sessionKey: string,\n ): Promise<HourlySummary[]> {\n let files: string[];\n try {\n files = await readdir(sessionDir);\n } catch {\n return [];\n }\n\n const summaries: HourlySummary[] = [];\n const mdFiles = files.filter((f) => f.endsWith(\".md\"));\n for (const file of mdFiles) {\n const filePath = await resolveSafeStoragePath(sessionDir, file).catch(() => null);\n if (filePath === null) continue;\n\n try {\n const content = await readFile(filePath, \"utf-8\");\n summaries.push(...this.parseSummaryFile(content, sessionKey, file));\n } catch {\n // Skip unreadable summary files.\n }\n }\n\n return summaries;\n }\n\n private parseSummaryFile(\n content: string,\n sessionKey: string,\n filename: string\n ): HourlySummary[] {\n const summaries: HourlySummary[] = [];\n\n // Extract date from filename (YYYY-MM-DD.md)\n const dateMatch = filename.match(/^(\\d{4}-\\d{2}-\\d{2})\\.md$/);\n if (!dateMatch) return summaries;\n const dateStr = dateMatch[1];\n\n // Split by hour sections\n const hourSections = content.split(/\\n## (\\d{2}):00\\n/);\n\n // First element is the header, skip it\n for (let i = 1; i < hourSections.length; i += 2) {\n const hourStr = hourSections[i];\n const sectionContent = hourSections[i + 1] || \"\";\n\n // Parse bullets\n const bullets: string[] = [];\n const lines = sectionContent.split(\"\\n\");\n let turnCount = 0;\n\n let inTopics = false;\n let sawExtendedTopicsHeader = false;\n for (const line of lines) {\n if (line.startsWith(\"### Topics\")) {\n inTopics = true;\n sawExtendedTopicsHeader = true;\n continue;\n }\n if (line.startsWith(\"### \") && !line.startsWith(\"### Topics\")) {\n inTopics = false;\n }\n const bulletMatch = line.match(/^- (.+)$/);\n if (bulletMatch) {\n // For extended format, only treat topic bullets as recall bullets.\n if (!sawExtendedTopicsHeader || inTopics) bullets.push(bulletMatch[1]);\n }\n const turnMatch = line.match(/\\((\\d+) turns?\\)/);\n if (turnMatch) turnCount = parseInt(turnMatch[1], 10);\n }\n\n if (bullets.length > 0) {\n summaries.push({\n hour: `${dateStr}T${hourStr}:00:00.000Z`,\n sessionKey,\n bullets,\n turnCount,\n generatedAt: \"\", // Not stored in file, not needed for recall\n });\n }\n }\n\n return summaries;\n }\n\n // Format summaries for recall injection\n formatForRecall(summaries: HourlySummary[], maxCount: number): string {\n if (summaries.length === 0) return \"\";\n\n const limited = summaries.slice(0, maxCount);\n const lines: string[] = [`## Recent Activity (last ${limited.length} hours)`];\n\n for (const summary of limited) {\n const hourStr = summary.hour.slice(11, 16); // HH:MM\n for (const bullet of summary.bullets) {\n lines.push(`- ${hourStr}: ${bullet}`);\n }\n }\n\n return lines.join(\"\\n\");\n }\n\n // Main entry point for cron job\n async runHourly(): Promise<void> {\n log.debug(\"running hourly summary generation\");\n\n // Get active sessions from transcript\n const sessions = await this.getActiveSessions();\n\n for (const sessionKey of sessions) {\n // Calculate the hour we want to summarize (previous hour)\n const now = new Date();\n const hourStart = new Date(now.getTime() - 60 * 60 * 1000);\n hourStart.setMinutes(0, 0, 0);\n const hourEnd = new Date(hourStart.getTime() + 60 * 60 * 1000);\n\n // Get entries for this session in the target hour\n const entries = await this.getTranscriptEntries(sessionKey, hourStart, hourEnd);\n\n if (entries.length === 0) {\n log.debug(`no transcript entries for ${sessionKey} at ${hourStart.toISOString()}`);\n continue;\n }\n\n // Generate and save summary\n const summary = await this.generateSummary(sessionKey, hourStart, entries);\n if (summary) {\n await this.saveSummary(summary);\n log.info(`generated hourly summary for ${sessionKey} (${entries.length} turns)`);\n }\n }\n }\n\n // Get list of active sessions from transcript directory\n private async getActiveSessions(): Promise<string[]> {\n const transcriptDir = await resolveSafeStoragePath(\n this.config.memoryDir,\n \"transcripts\",\n ).catch(() => null);\n if (transcriptDir === null) return [];\n\n try {\n const sessionKeys = new Set<string>();\n const typeEntries = await readdir(transcriptDir, { withFileTypes: true });\n for (const typeEnt of typeEntries) {\n if (!typeEnt.isDirectory()) continue;\n const typeDir = await resolveSafeStoragePath(transcriptDir, typeEnt.name).catch(() => null);\n if (typeDir === null) continue;\n const idEntries = await readdir(typeDir, { withFileTypes: true });\n for (const idEnt of idEntries) {\n if (!idEnt.isDirectory()) continue;\n const chanDir = await resolveSafeStoragePath(typeDir, idEnt.name).catch(() => null);\n if (chanDir === null) continue;\n const files = (await readdir(chanDir)).filter((f) => f.endsWith(\".jsonl\")).sort();\n for (const file of files) {\n const transcriptPath = await resolveSafeStoragePath(chanDir, file).catch(() => null);\n if (transcriptPath === null) continue;\n await this.collectTranscriptSessionKeys(transcriptPath, sessionKeys);\n }\n }\n }\n return Array.from(sessionKeys);\n } catch {\n return [];\n }\n }\n\n private async collectTranscriptSessionKeys(\n transcriptPath: string,\n sessionKeys: Set<string>,\n ): Promise<void> {\n try {\n const raw = await readFile(transcriptPath, \"utf-8\");\n for (const line of raw.split(\"\\n\")) {\n if (!line.trim()) continue;\n try {\n const entry = JSON.parse(line) as TranscriptEntry;\n if (typeof entry.sessionKey === \"string\" && entry.sessionKey.length > 0) {\n sessionKeys.add(entry.sessionKey);\n }\n } catch {\n // ignore malformed transcript lines\n }\n }\n } catch {\n // ignore unreadable transcript files\n }\n }\n\n // Get transcript entries for a session within a time range\n private async getTranscriptEntries(\n sessionKey: string,\n startTime: Date,\n endTime: Date\n ): Promise<TranscriptEntry[]> {\n // Shared session-identity layer (issue #1496, rule #22). Arbitrary keys\n // route to `session/<hash>`; legacy `agent:<id>:...` keep their paths. The\n // shared helper also supplies the read-back-only candidate dirs (mirrors\n // TranscriptManager) so legacy `other/default` AND old `parts.length >= 3`\n // data stays discoverable here too.\n const paths = sessionStoragePaths(sessionKey);\n\n try {\n const transcriptRoot = path.join(this.config.memoryDir, \"transcripts\");\n const candidateDirs = new Set(\n [paths.dir, paths.alternateDir, paths.legacyDir, ...paths.readbackDirs].filter(\n (dir): dir is string => typeof dir === \"string\" && dir.length > 0,\n ),\n );\n const entries: TranscriptEntry[] = [];\n // Dedup identical raw rows that a partially-applied migration may have\n // left in both the primary dir and a read-back dir (issue #1496, cursor\n // review). Exact raw JSONL line is the stable identity.\n const seenRawLines = new Set<string>();\n\n // Read all daily transcript files in the directory\n for (const candidateDir of candidateDirs) {\n const transcriptDir = await resolveSafeStoragePath(transcriptRoot, candidateDir).catch(() => null);\n if (transcriptDir === null) continue;\n\n const files = await readdir(transcriptDir).catch(() => []);\n for (const file of files) {\n if (!file.endsWith(\".jsonl\")) continue;\n\n const transcriptPath = await resolveSafeStoragePath(transcriptDir, file).catch(() => null);\n if (transcriptPath === null) continue;\n\n try {\n const content = await readFile(transcriptPath, \"utf-8\");\n const lines = content.trim().split(\"\\n\");\n\n for (const line of lines) {\n if (!line.trim()) continue;\n try {\n const entry = JSON.parse(line) as TranscriptEntry;\n const entryTime = new Date(entry.timestamp).getTime();\n\n if (\n entry.sessionKey === sessionKey &&\n entryTime >= startTime.getTime() &&\n entryTime < endTime.getTime() &&\n !seenRawLines.has(line)\n ) {\n seenRawLines.add(line);\n entries.push(entry);\n }\n } catch {\n // Skip malformed lines\n }\n }\n } catch {\n // Skip unreadable files\n }\n }\n }\n\n return entries;\n } catch {\n return [];\n }\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
} from "./chunk-GDB4J2H3.js";
|
|
9
9
|
import {
|
|
10
10
|
importCapsule
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-NT5TINK5.js";
|
|
12
12
|
import {
|
|
13
13
|
toRecallExplainJson
|
|
14
14
|
} from "./chunk-IENGGY2C.js";
|
|
@@ -52,7 +52,7 @@ import {
|
|
|
52
52
|
} from "./chunk-BEMWL2FZ.js";
|
|
53
53
|
import {
|
|
54
54
|
namespaceCollectionName
|
|
55
|
-
} from "./chunk-
|
|
55
|
+
} from "./chunk-APWJRJFW.js";
|
|
56
56
|
import {
|
|
57
57
|
namespaceIdentityFromToken
|
|
58
58
|
} from "./chunk-ZFXCQPNO.js";
|
|
@@ -136,7 +136,7 @@ import {
|
|
|
136
136
|
} from "./chunk-VFUEZZBS.js";
|
|
137
137
|
import {
|
|
138
138
|
exportCapsule
|
|
139
|
-
} from "./chunk-
|
|
139
|
+
} from "./chunk-BUKK5SWA.js";
|
|
140
140
|
import {
|
|
141
141
|
OFFLINE_SYNC_SNAPSHOT_FORMAT,
|
|
142
142
|
applyOfflineSyncChangeset,
|
|
@@ -4649,4 +4649,4 @@ export {
|
|
|
4649
4649
|
shapeMemorySummary,
|
|
4650
4650
|
EngramAccessService
|
|
4651
4651
|
};
|
|
4652
|
-
//# sourceMappingURL=chunk-
|
|
4652
|
+
//# sourceMappingURL=chunk-L7W5YW6Y.js.map
|
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
decryptCapsuleFileInMemory,
|
|
9
9
|
isEncryptedCapsuleFile
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-UVYI6VIX.js";
|
|
11
11
|
import {
|
|
12
12
|
assertIsDirectoryNotSymlink,
|
|
13
13
|
assertRealpathInsideRoot,
|
|
@@ -227,4 +227,4 @@ function mintForkId(capsuleId, originalId, now) {
|
|
|
227
227
|
export {
|
|
228
228
|
importCapsule
|
|
229
229
|
};
|
|
230
|
-
//# sourceMappingURL=chunk-
|
|
230
|
+
//# sourceMappingURL=chunk-NT5TINK5.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-765K3SAT.js";
|
|
4
4
|
import {
|
|
5
5
|
encryptCapsuleFile
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-UVYI6VIX.js";
|
|
7
7
|
import {
|
|
8
8
|
computeTransferOutputRel,
|
|
9
9
|
isTransferPathExcluded
|
|
@@ -109,4 +109,4 @@ async function enforceRetention(outDirAbs, retentionDays) {
|
|
|
109
109
|
export {
|
|
110
110
|
backupMemoryDir
|
|
111
111
|
};
|
|
112
|
-
//# sourceMappingURL=chunk-
|
|
112
|
+
//# sourceMappingURL=chunk-OWHERGF2.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
isValidCapsuleSince
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-BUKK5SWA.js";
|
|
4
4
|
import {
|
|
5
5
|
CAPSULE_ID_PATTERN
|
|
6
6
|
} from "./chunk-WEHSQBFR.js";
|
|
@@ -432,4 +432,4 @@ export {
|
|
|
432
432
|
actionConfidenceRequestSchema,
|
|
433
433
|
validateRequest
|
|
434
434
|
};
|
|
435
|
-
//# sourceMappingURL=chunk-
|
|
435
|
+
//# sourceMappingURL=chunk-QKE4LHNR.js.map
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import {
|
|
2
2
|
AdapterRegistry
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-RKNJBZ55.js";
|
|
4
4
|
import {
|
|
5
5
|
WearablesInputError
|
|
6
6
|
} from "./chunk-7WV3F5DQ.js";
|
|
7
7
|
import {
|
|
8
8
|
EngramMcpServer
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-ZPPFKVSD.js";
|
|
10
10
|
import {
|
|
11
11
|
EngramAccessInputError
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-L7W5YW6Y.js";
|
|
13
13
|
import {
|
|
14
14
|
projectTagProjectId
|
|
15
15
|
} from "./chunk-6GIKAUTN.js";
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
} from "./chunk-2ODBA7MQ.js";
|
|
31
31
|
import {
|
|
32
32
|
validateRequest
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-QKE4LHNR.js";
|
|
34
34
|
import {
|
|
35
35
|
OFFLINE_SYNC_APPLY_MAX_BODY_BYTES,
|
|
36
36
|
OFFLINE_SYNC_FILE_CONTENT_MAX_CHUNK_BYTES,
|
|
@@ -2023,4 +2023,4 @@ function positiveIntQueryParam(value, label) {
|
|
|
2023
2023
|
export {
|
|
2024
2024
|
EngramAccessHttpServer
|
|
2025
2025
|
};
|
|
2026
|
-
//# sourceMappingURL=chunk-
|
|
2026
|
+
//# sourceMappingURL=chunk-RAELB5NX.js.map
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ClaudeCodeAdapter
|
|
3
|
+
} from "./chunk-J64TK33U.js";
|
|
1
4
|
import {
|
|
2
5
|
CodexAdapter
|
|
3
6
|
} from "./chunk-RSUYKGGZ.js";
|
|
@@ -7,9 +10,6 @@ import {
|
|
|
7
10
|
import {
|
|
8
11
|
ReplitAdapter
|
|
9
12
|
} from "./chunk-TMSXWOBZ.js";
|
|
10
|
-
import {
|
|
11
|
-
ClaudeCodeAdapter
|
|
12
|
-
} from "./chunk-J64TK33U.js";
|
|
13
13
|
|
|
14
14
|
// src/adapters/registry.ts
|
|
15
15
|
var AdapterRegistry = class {
|
|
@@ -53,4 +53,4 @@ var AdapterRegistry = class {
|
|
|
53
53
|
export {
|
|
54
54
|
AdapterRegistry
|
|
55
55
|
};
|
|
56
|
-
//# sourceMappingURL=chunk-
|
|
56
|
+
//# sourceMappingURL=chunk-RKNJBZ55.js.map
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import {
|
|
2
|
+
encodeStoragePathSegment,
|
|
3
|
+
encodeStoragePathSegmentWithHash,
|
|
4
|
+
isSafeLegacyPathSegment,
|
|
5
|
+
storagePathHash
|
|
6
|
+
} from "./chunk-FF4KLI5W.js";
|
|
7
|
+
|
|
8
|
+
// src/session-identity.ts
|
|
9
|
+
import path from "path";
|
|
10
|
+
var SESSION_CHANNEL_TYPE = "session";
|
|
11
|
+
var LEGACY_FALLBACK_CHANNEL_TYPE = "other";
|
|
12
|
+
var LEGACY_FALLBACK_CHANNEL_ID = "default";
|
|
13
|
+
var DISPLAY_LABEL_MAX_LENGTH = 64;
|
|
14
|
+
function parseSessionIdentity(sessionKey) {
|
|
15
|
+
const canonicalSessionKey = typeof sessionKey === "string" ? sessionKey : "";
|
|
16
|
+
const legacy = parseLegacyChannelIdentity(canonicalSessionKey);
|
|
17
|
+
if (legacy) {
|
|
18
|
+
return {
|
|
19
|
+
channelType: legacy.channelType,
|
|
20
|
+
channelId: legacy.channelId,
|
|
21
|
+
displayLabel: safeDisplayLabel(canonicalSessionKey),
|
|
22
|
+
canonicalSessionKey,
|
|
23
|
+
legacy: true
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
return {
|
|
27
|
+
channelType: SESSION_CHANNEL_TYPE,
|
|
28
|
+
channelId: storagePathHash(canonicalSessionKey),
|
|
29
|
+
displayLabel: safeDisplayLabel(canonicalSessionKey),
|
|
30
|
+
canonicalSessionKey,
|
|
31
|
+
legacy: false
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
function sessionStoragePaths(sessionKey) {
|
|
35
|
+
const identity = parseSessionIdentity(sessionKey);
|
|
36
|
+
const { channelType, channelId, canonicalSessionKey } = identity;
|
|
37
|
+
const dir = path.join(encodeStoragePathSegment(channelType), encodeStoragePathSegment(channelId));
|
|
38
|
+
const alternateDir = path.join(
|
|
39
|
+
encodeStoragePathSegmentWithHash(channelType),
|
|
40
|
+
`${encodeStoragePathSegmentWithHash(channelId)}--session-${storagePathHash(canonicalSessionKey)}`
|
|
41
|
+
);
|
|
42
|
+
let legacyDir;
|
|
43
|
+
if (isSafeLegacyPathSegment(channelType) && isSafeLegacyPathSegment(channelId)) {
|
|
44
|
+
const candidate = path.join(channelType, channelId);
|
|
45
|
+
if (candidate !== dir) {
|
|
46
|
+
legacyDir = candidate;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const readbackDirs = [];
|
|
50
|
+
if (!identity.legacy) {
|
|
51
|
+
const seen = /* @__PURE__ */ new Set([dir]);
|
|
52
|
+
if (alternateDir) seen.add(alternateDir);
|
|
53
|
+
if (legacyDir) seen.add(legacyDir);
|
|
54
|
+
for (const candidate of [OTHER_DEFAULT_READBACK_DIR, legacyParserReadbackDir(canonicalSessionKey)]) {
|
|
55
|
+
if (!candidate || seen.has(candidate)) continue;
|
|
56
|
+
seen.add(candidate);
|
|
57
|
+
readbackDirs.push(candidate);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return { channelType, channelId, dir, alternateDir, legacyDir, readbackDirs };
|
|
61
|
+
}
|
|
62
|
+
var OTHER_DEFAULT_READBACK_DIR = path.join(
|
|
63
|
+
LEGACY_FALLBACK_CHANNEL_TYPE,
|
|
64
|
+
LEGACY_FALLBACK_CHANNEL_ID
|
|
65
|
+
);
|
|
66
|
+
function legacyParserReadbackDir(sessionKey) {
|
|
67
|
+
if (typeof sessionKey !== "string" || sessionKey.length === 0) return void 0;
|
|
68
|
+
const parts = sessionKey.split(":");
|
|
69
|
+
if (parts.length < 3) return void 0;
|
|
70
|
+
const channelType = parts[2];
|
|
71
|
+
if (!channelType || channelType.length === 0) return void 0;
|
|
72
|
+
let channelId = LEGACY_FALLBACK_CHANNEL_ID;
|
|
73
|
+
if (channelType === "main") {
|
|
74
|
+
channelId = "default";
|
|
75
|
+
} else if (channelType === "discord" && parts.length >= 5 && parts[3] === "channel") {
|
|
76
|
+
channelId = parts[4];
|
|
77
|
+
} else if (channelType === "slack" && parts.length >= 5 && parts[3] === "channel") {
|
|
78
|
+
channelId = parts[4];
|
|
79
|
+
} else if (channelType === "cron" && parts.length >= 4) {
|
|
80
|
+
channelId = parts[3];
|
|
81
|
+
} else if (parts.length >= 4) {
|
|
82
|
+
channelId = parts[3];
|
|
83
|
+
}
|
|
84
|
+
if (!channelId || channelId.length === 0) {
|
|
85
|
+
channelId = LEGACY_FALLBACK_CHANNEL_ID;
|
|
86
|
+
}
|
|
87
|
+
return path.join(encodeStoragePathSegment(channelType), encodeStoragePathSegment(channelId));
|
|
88
|
+
}
|
|
89
|
+
function parseLegacyChannelIdentity(sessionKey) {
|
|
90
|
+
if (sessionKey.length === 0) return void 0;
|
|
91
|
+
const parts = sessionKey.split(":");
|
|
92
|
+
if (parts.length < 3) return void 0;
|
|
93
|
+
if (parts[0] !== "agent") return void 0;
|
|
94
|
+
if (!parts[1] || parts[1].length === 0) return void 0;
|
|
95
|
+
const channelType = parts[2];
|
|
96
|
+
if (!channelType || channelType.length === 0) return void 0;
|
|
97
|
+
let channelId = LEGACY_FALLBACK_CHANNEL_ID;
|
|
98
|
+
if (channelType === "main") {
|
|
99
|
+
channelId = "default";
|
|
100
|
+
} else if (channelType === "discord" && parts.length >= 5 && parts[3] === "channel") {
|
|
101
|
+
channelId = parts[4];
|
|
102
|
+
} else if (channelType === "slack" && parts.length >= 5 && parts[3] === "channel") {
|
|
103
|
+
channelId = parts[4];
|
|
104
|
+
} else if (channelType === "cron" && parts.length >= 4) {
|
|
105
|
+
channelId = parts[3];
|
|
106
|
+
} else if (parts.length >= 4) {
|
|
107
|
+
channelId = parts[3];
|
|
108
|
+
}
|
|
109
|
+
if (!channelId || channelId.length === 0) {
|
|
110
|
+
channelId = LEGACY_FALLBACK_CHANNEL_ID;
|
|
111
|
+
}
|
|
112
|
+
return { channelType, channelId };
|
|
113
|
+
}
|
|
114
|
+
function safeDisplayLabel(sessionKey) {
|
|
115
|
+
if (sessionKey.length === 0) return "(empty)";
|
|
116
|
+
let out = "";
|
|
117
|
+
for (const char of sessionKey) {
|
|
118
|
+
const code = char.codePointAt(0) ?? 0;
|
|
119
|
+
if (code <= 31 || code === 127) continue;
|
|
120
|
+
if (char === "/" || char === "\\") {
|
|
121
|
+
out += "_";
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
out += char;
|
|
125
|
+
}
|
|
126
|
+
const trimmed = out.trim();
|
|
127
|
+
if (trimmed.length === 0) return "(empty)";
|
|
128
|
+
return trimmed.length > DISPLAY_LABEL_MAX_LENGTH ? `${trimmed.slice(0, DISPLAY_LABEL_MAX_LENGTH - 1)}\u2026` : trimmed;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export {
|
|
132
|
+
SESSION_CHANNEL_TYPE,
|
|
133
|
+
LEGACY_FALLBACK_CHANNEL_TYPE,
|
|
134
|
+
LEGACY_FALLBACK_CHANNEL_ID,
|
|
135
|
+
parseSessionIdentity,
|
|
136
|
+
sessionStoragePaths,
|
|
137
|
+
OTHER_DEFAULT_READBACK_DIR,
|
|
138
|
+
legacyParserReadbackDir
|
|
139
|
+
};
|
|
140
|
+
//# sourceMappingURL=chunk-S4DDLTPX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/session-identity.ts"],"sourcesContent":["import path from \"node:path\";\nimport {\n encodeStoragePathSegment,\n encodeStoragePathSegmentWithHash,\n isSafeLegacyPathSegment,\n storagePathHash,\n} from \"./storage-paths.js\";\n\n/**\n * Shared session-identity / transcript-pathing layer (issue #1496).\n *\n * This module is the SINGLE source of truth for turning a `sessionKey` into a\n * channel identity and a set of deterministic, collision-resistant storage\n * paths. Every subsystem that previously re-parsed session keys (transcript\n * pathing, tool-usage pathing, hourly summaries, session auditing, conversation\n * indexing) MUST route through `parseSessionIdentity()` / `sessionStoragePaths()`\n * so behavior stays identical across paths (rule #22, #39).\n *\n * Two shapes are recognized:\n *\n * 1. Legacy `agent:<agentId>:<channelType>:...` keys keep their existing\n * readable channel identity:\n * - agent:<id>:main → type=\"main\", id=\"default\"\n * - agent:<id>:discord:channel:<chanId> → type=\"discord\", id=\"<chanId>\"\n * - agent:<id>:slack:channel:<chanId> → type=\"slack\", id=\"<chanId>\"\n * - agent:<id>:cron:<jobId> → type=\"cron\", id=\"<jobId>\"\n * - agent:<id>:<other>[:<id>] → type=\"<other>\", id=\"<id?>\"\n *\n * 2. Arbitrary / non-legacy keys (e.g. `pi-geek:abc123`) become first-class\n * from the FIRST write — they NEVER start life under `other/default`:\n * channelType = \"session\"\n * channelId = storagePathHash(sessionKey) (collision-resistant)\n * yielding `transcripts/session/<hash>/YYYY-MM-DD.jsonl`.\n *\n * The reserved `\"session\"` channel type is what makes arbitrary keys isolated\n * and auditable. Legacy data under `other/default` (and any legacy channel\n * directory) remains READABLE via `legacyDir` / `alternateDir` candidates.\n */\n\n/** Channel type reserved for first-class arbitrary (non-legacy) session keys. */\nexport const SESSION_CHANNEL_TYPE = \"session\";\n\n/** Legacy fallback channel identity for un-parseable keys (read-back only). */\nexport const LEGACY_FALLBACK_CHANNEL_TYPE = \"other\";\nexport const LEGACY_FALLBACK_CHANNEL_ID = \"default\";\n\n/** Max characters of the raw session key kept in a human-readable display label. */\nconst DISPLAY_LABEL_MAX_LENGTH = 64;\n\nexport interface SessionIdentity {\n /** Channel type — legacy channel type for known shapes, else `\"session\"`. */\n channelType: string;\n /** Channel id — legacy channel id for known shapes, else the key hash. */\n channelId: string;\n /** Human-readable, non-authoritative label derived from the raw key. */\n displayLabel: string;\n /** The original session key, unchanged. */\n canonicalSessionKey: string;\n /** True when the key matched a known legacy `agent:<id>:...` shape. */\n legacy: boolean;\n}\n\nexport interface SessionStoragePaths {\n /** Channel type (mirrors {@link SessionIdentity.channelType}). */\n channelType: string;\n /** Channel id (mirrors {@link SessionIdentity.channelId}). */\n channelId: string;\n /** Primary encoded storage subdirectory: `<encodedType>/<encodedId>`. */\n dir: string;\n /**\n * Collision-resistant alternate subdirectory that always embeds the full\n * session-key hash. Used both as a write-time collision escape hatch and as a\n * read-back candidate for data written before this layer existed.\n */\n alternateDir: string;\n /**\n * Un-encoded `<type>/<id>` directory for reading data written by older\n * builds that did not URL-encode path segments. Undefined when it would be\n * identical to `dir` or when either segment is path-unsafe.\n */\n legacyDir?: string;\n /**\n * Ordered, de-duplicated list of READ-BACK-ONLY directories where an older\n * build may have stranded this key's data. NEW writes never target these —\n * they exist purely so pre-#1496 transcripts/tool-usage stay discoverable\n * (and migratable). Populated only for non-legacy keys. Includes:\n * 1. the shared `other/default` fallback (every arbitrary key landed here);\n * 2. the directory the OLD `parts.length >= 3` parser would have chosen\n * (e.g. `foo:bar:baz` → `baz/default`, `foo:bar:baz:qux` → `baz/qux`).\n * See {@link legacyParserReadbackDir}.\n */\n readbackDirs: string[];\n}\n\n/**\n * Parse a session key into a stable channel identity.\n *\n * Pure and deterministic — no filesystem access. Same input always yields the\n * same identity, which is what makes arbitrary keys collision-resistant from\n * the first write.\n */\nexport function parseSessionIdentity(sessionKey: string): SessionIdentity {\n const canonicalSessionKey = typeof sessionKey === \"string\" ? sessionKey : \"\";\n const legacy = parseLegacyChannelIdentity(canonicalSessionKey);\n if (legacy) {\n return {\n channelType: legacy.channelType,\n channelId: legacy.channelId,\n displayLabel: safeDisplayLabel(canonicalSessionKey),\n canonicalSessionKey,\n legacy: true,\n };\n }\n\n return {\n channelType: SESSION_CHANNEL_TYPE,\n channelId: storagePathHash(canonicalSessionKey),\n displayLabel: safeDisplayLabel(canonicalSessionKey),\n canonicalSessionKey,\n legacy: false,\n };\n}\n\n/**\n * Resolve the storage path pieces for a session key. Wraps\n * {@link parseSessionIdentity} so transcript, tool-usage, and summary writers\n * share one implementation (rule #22).\n */\nexport function sessionStoragePaths(sessionKey: string): SessionStoragePaths {\n const identity = parseSessionIdentity(sessionKey);\n const { channelType, channelId, canonicalSessionKey } = identity;\n\n const dir = path.join(encodeStoragePathSegment(channelType), encodeStoragePathSegment(channelId));\n\n const alternateDir = path.join(\n encodeStoragePathSegmentWithHash(channelType),\n `${encodeStoragePathSegmentWithHash(channelId)}--session-${storagePathHash(canonicalSessionKey)}`\n );\n\n let legacyDir: string | undefined;\n if (isSafeLegacyPathSegment(channelType) && isSafeLegacyPathSegment(channelId)) {\n const candidate = path.join(channelType, channelId);\n if (candidate !== dir) {\n legacyDir = candidate;\n }\n }\n\n // Read-back-only candidates are only relevant for non-legacy keys: legacy\n // `agent:<id>:...` keys still resolve to their original channel directory, so\n // nothing about their on-disk location moved.\n const readbackDirs: string[] = [];\n if (!identity.legacy) {\n const seen = new Set<string>([dir]);\n if (alternateDir) seen.add(alternateDir);\n if (legacyDir) seen.add(legacyDir);\n for (const candidate of [OTHER_DEFAULT_READBACK_DIR, legacyParserReadbackDir(canonicalSessionKey)]) {\n if (!candidate || seen.has(candidate)) continue;\n seen.add(candidate);\n readbackDirs.push(candidate);\n }\n }\n\n return { channelType, channelId, dir, alternateDir, legacyDir, readbackDirs };\n}\n\n/**\n * The shared `other/default` directory every arbitrary key was routed into by\n * builds predating issue #1496. Exposed so transcript/tool-usage/summary read\n * paths and the migration scanner all agree on the same fallback location.\n */\nexport const OTHER_DEFAULT_READBACK_DIR = path.join(\n LEGACY_FALLBACK_CHANNEL_TYPE,\n LEGACY_FALLBACK_CHANNEL_ID\n);\n\n/**\n * Reconstruct the transcript/tool-usage directory the OLD `getTranscriptPath`\n * parser (pre-#1496) would have produced for a key the NEW parser reclassifies\n * as a first-class `session/<hash>` identity.\n *\n * The OLD parser treated ANY key with `parts.length >= 3` as legacy — it did\n * NOT require a leading `agent` segment — so an arbitrary key like\n * `foo:bar:baz` was stored under `baz/default` and `foo:bar:baz:qux` under\n * `baz/qux`. Those directories must stay readable (and migratable) for existing\n * installs even though the key now writes to `session/<hash>` (Thread B / codex\n * review on PR #1504). Path segments are encoded exactly as the old `dir` was\n * built (`encodeStoragePathSegment`) so the candidate matches the bytes on disk.\n *\n * Returns `undefined` when the key has fewer than three colon parts (the old\n * parser would have used the `other/default` fallback, already covered), when\n * the channel type is empty, or when the result is unsafe.\n */\nexport function legacyParserReadbackDir(sessionKey: string): string | undefined {\n if (typeof sessionKey !== \"string\" || sessionKey.length === 0) return undefined;\n const parts = sessionKey.split(\":\");\n if (parts.length < 3) return undefined;\n\n const channelType = parts[2];\n if (!channelType || channelType.length === 0) return undefined;\n\n // Mirror the OLD parser's channelId derivation for parts.length >= 3 keys.\n let channelId = LEGACY_FALLBACK_CHANNEL_ID;\n if (channelType === \"main\") {\n channelId = \"default\";\n } else if (channelType === \"discord\" && parts.length >= 5 && parts[3] === \"channel\") {\n channelId = parts[4];\n } else if (channelType === \"slack\" && parts.length >= 5 && parts[3] === \"channel\") {\n channelId = parts[4];\n } else if (channelType === \"cron\" && parts.length >= 4) {\n channelId = parts[3];\n } else if (parts.length >= 4) {\n channelId = parts[3];\n }\n if (!channelId || channelId.length === 0) {\n channelId = LEGACY_FALLBACK_CHANNEL_ID;\n }\n\n return path.join(encodeStoragePathSegment(channelType), encodeStoragePathSegment(channelId));\n}\n\n/**\n * Parse the legacy `agent:<agentId>:<channelType>:...` shape. Returns\n * `undefined` for anything that is not a recognized legacy key, so the caller\n * can route it to a first-class `session/<hash>` identity instead.\n *\n * A legacy key must start with the literal `agent` segment and a non-empty\n * agent id; otherwise an arbitrary key like `pi-geek:abc123` would be\n * misread as `channelType = \"abc123\"`.\n */\nfunction parseLegacyChannelIdentity(sessionKey: string): { channelType: string; channelId: string } | undefined {\n if (sessionKey.length === 0) return undefined;\n const parts = sessionKey.split(\":\");\n if (parts.length < 3) return undefined;\n if (parts[0] !== \"agent\") return undefined;\n if (!parts[1] || parts[1].length === 0) return undefined;\n\n const channelType = parts[2];\n if (!channelType || channelType.length === 0) return undefined;\n\n let channelId = LEGACY_FALLBACK_CHANNEL_ID;\n if (channelType === \"main\") {\n channelId = \"default\";\n } else if (channelType === \"discord\" && parts.length >= 5 && parts[3] === \"channel\") {\n channelId = parts[4];\n } else if (channelType === \"slack\" && parts.length >= 5 && parts[3] === \"channel\") {\n channelId = parts[4];\n } else if (channelType === \"cron\" && parts.length >= 4) {\n channelId = parts[3];\n } else if (parts.length >= 4) {\n channelId = parts[3];\n }\n\n if (!channelId || channelId.length === 0) {\n channelId = LEGACY_FALLBACK_CHANNEL_ID;\n }\n\n return { channelType, channelId };\n}\n\n/**\n * A short, human-readable label for dashboards/audits. Never used for storage\n * routing — `channelId` (the hash) owns isolation. Control characters and path\n * separators are stripped so the label is safe to render.\n */\nfunction safeDisplayLabel(sessionKey: string): string {\n if (sessionKey.length === 0) return \"(empty)\";\n let out = \"\";\n for (const char of sessionKey) {\n const code = char.codePointAt(0) ?? 0;\n // Drop ASCII control characters (0x00-0x1F) and DEL (0x7F).\n if (code <= 0x1f || code === 0x7f) continue;\n if (char === \"/\" || char === \"\\\\\") {\n out += \"_\";\n continue;\n }\n out += char;\n }\n const trimmed = out.trim();\n if (trimmed.length === 0) return \"(empty)\";\n return trimmed.length > DISPLAY_LABEL_MAX_LENGTH ? `${trimmed.slice(0, DISPLAY_LABEL_MAX_LENGTH - 1)}…` : trimmed;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -19,7 +19,7 @@ import {
|
|
|
19
19
|
} from "./chunk-666A3MOW.js";
|
|
20
20
|
import {
|
|
21
21
|
LcmEngine
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-WEPMT6SC.js";
|
|
23
23
|
import {
|
|
24
24
|
chunkTranscriptEntries
|
|
25
25
|
} from "./chunk-TECVW3JP.js";
|
|
@@ -38,6 +38,9 @@ import {
|
|
|
38
38
|
applyUtilityRankingRuntimeDelta,
|
|
39
39
|
loadUtilityRuntimeValues
|
|
40
40
|
} from "./chunk-LXOM6IQU.js";
|
|
41
|
+
import {
|
|
42
|
+
TierMigrationExecutor
|
|
43
|
+
} from "./chunk-VOUOLGIP.js";
|
|
41
44
|
import {
|
|
42
45
|
decideTierTransition
|
|
43
46
|
} from "./chunk-S75M5ZRK.js";
|
|
@@ -47,6 +50,9 @@ import {
|
|
|
47
50
|
import {
|
|
48
51
|
extractTopics
|
|
49
52
|
} from "./chunk-UHGBNIOS.js";
|
|
53
|
+
import {
|
|
54
|
+
HourlySummarizer
|
|
55
|
+
} from "./chunk-IMWFHBG2.js";
|
|
50
56
|
import {
|
|
51
57
|
buildTargetedFactRecallSection,
|
|
52
58
|
shouldRecallTargetedFactEvidence
|
|
@@ -59,12 +65,6 @@ import {
|
|
|
59
65
|
import {
|
|
60
66
|
isValidAsOf
|
|
61
67
|
} from "./chunk-MDYG7VI7.js";
|
|
62
|
-
import {
|
|
63
|
-
TierMigrationExecutor
|
|
64
|
-
} from "./chunk-VOUOLGIP.js";
|
|
65
|
-
import {
|
|
66
|
-
HourlySummarizer
|
|
67
|
-
} from "./chunk-Y2XKJJEI.js";
|
|
68
68
|
import {
|
|
69
69
|
SessionObserverState
|
|
70
70
|
} from "./chunk-MHQC2WU2.js";
|
|
@@ -231,7 +231,7 @@ import {
|
|
|
231
231
|
} from "./chunk-ZRWB5D4H.js";
|
|
232
232
|
import {
|
|
233
233
|
TranscriptManager
|
|
234
|
-
} from "./chunk-
|
|
234
|
+
} from "./chunk-XUGQQPGO.js";
|
|
235
235
|
import {
|
|
236
236
|
NamespaceStorageRouter
|
|
237
237
|
} from "./chunk-D6WVJIS3.js";
|
|
@@ -317,11 +317,11 @@ import {
|
|
|
317
317
|
} from "./chunk-BEMWL2FZ.js";
|
|
318
318
|
import {
|
|
319
319
|
NamespaceSearchRouter
|
|
320
|
-
} from "./chunk-
|
|
320
|
+
} from "./chunk-APWJRJFW.js";
|
|
321
321
|
import {
|
|
322
322
|
createConversationIndexRuntime,
|
|
323
323
|
createSearchBackend
|
|
324
|
-
} from "./chunk-
|
|
324
|
+
} from "./chunk-U55D5UD5.js";
|
|
325
325
|
import {
|
|
326
326
|
NoopSearchBackend
|
|
327
327
|
} from "./chunk-CYEPCZN5.js";
|
|
@@ -3355,7 +3355,7 @@ var Orchestrator = class _Orchestrator {
|
|
|
3355
3355
|
if (signal.aborted) return;
|
|
3356
3356
|
if (this.config.lifecyclePolicyEnabled && this.config.qmdTierMigrationEnabled) {
|
|
3357
3357
|
try {
|
|
3358
|
-
const { runFirstStartMigration } = await import("./first-start-migration-
|
|
3358
|
+
const { runFirstStartMigration } = await import("./first-start-migration-PG5HBC3K.js");
|
|
3359
3359
|
const result = await runFirstStartMigration({
|
|
3360
3360
|
storage: this.storage,
|
|
3361
3361
|
config: this.config,
|
|
@@ -14302,4 +14302,4 @@ export {
|
|
|
14302
14302
|
resolvePersistedMemoryRelativePath,
|
|
14303
14303
|
Orchestrator
|
|
14304
14304
|
};
|
|
14305
|
-
//# sourceMappingURL=chunk-
|
|
14305
|
+
//# sourceMappingURL=chunk-TA4LQ5SR.js.map
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
NoopSearchBackend
|
|
3
|
+
} from "./chunk-CYEPCZN5.js";
|
|
4
|
+
import {
|
|
5
|
+
OramaBackend
|
|
6
|
+
} from "./chunk-DCGT4FPP.js";
|
|
1
7
|
import {
|
|
2
8
|
RemoteSearchBackend
|
|
3
9
|
} from "./chunk-JOASJWQR.js";
|
|
@@ -10,12 +16,6 @@ import {
|
|
|
10
16
|
import {
|
|
11
17
|
MeilisearchBackend
|
|
12
18
|
} from "./chunk-RG3LBSGH.js";
|
|
13
|
-
import {
|
|
14
|
-
NoopSearchBackend
|
|
15
|
-
} from "./chunk-CYEPCZN5.js";
|
|
16
|
-
import {
|
|
17
|
-
OramaBackend
|
|
18
|
-
} from "./chunk-DCGT4FPP.js";
|
|
19
19
|
import {
|
|
20
20
|
createConversationIndexBackend
|
|
21
21
|
} from "./chunk-7OGJQP7T.js";
|
|
@@ -170,4 +170,4 @@ export {
|
|
|
170
170
|
createConversationSearchBackend,
|
|
171
171
|
createConversationIndexRuntime
|
|
172
172
|
};
|
|
173
|
-
//# sourceMappingURL=chunk-
|
|
173
|
+
//# sourceMappingURL=chunk-U55D5UD5.js.map
|
|
@@ -1,3 +1,9 @@
|
|
|
1
|
+
import {
|
|
2
|
+
LcmSummarizer
|
|
3
|
+
} from "./chunk-5VDJMYTF.js";
|
|
4
|
+
import {
|
|
5
|
+
LcmArchive
|
|
6
|
+
} from "./chunk-HP5FMB6L.js";
|
|
1
7
|
import {
|
|
2
8
|
LcmDag
|
|
3
9
|
} from "./chunk-7N4KAIGN.js";
|
|
@@ -11,12 +17,6 @@ import {
|
|
|
11
17
|
ensureLcmStateDir,
|
|
12
18
|
openLcmDatabase
|
|
13
19
|
} from "./chunk-MAV46GWQ.js";
|
|
14
|
-
import {
|
|
15
|
-
LcmSummarizer
|
|
16
|
-
} from "./chunk-5VDJMYTF.js";
|
|
17
|
-
import {
|
|
18
|
-
LcmArchive
|
|
19
|
-
} from "./chunk-HP5FMB6L.js";
|
|
20
20
|
import {
|
|
21
21
|
log
|
|
22
22
|
} from "./chunk-2ODBA7MQ.js";
|
|
@@ -513,4 +513,4 @@ export {
|
|
|
513
513
|
extractLcmConfig,
|
|
514
514
|
LcmEngine
|
|
515
515
|
};
|
|
516
|
-
//# sourceMappingURL=chunk-
|
|
516
|
+
//# sourceMappingURL=chunk-WEPMT6SC.js.map
|