@xopcai/xopc 0.0.119 → 0.0.120
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/browser-ext/manifest.json +1 -1
- package/dist/extensions/telegram/xopc.extension.json +1 -1
- package/dist/gateway/static/root/assets/{agent-avatar-dicebear-CU8J6zou.js → agent-avatar-dicebear-APTlX9Sq.js} +1 -1
- package/dist/gateway/static/root/assets/agents-CFW_FS_V.js +2 -0
- package/dist/gateway/static/root/assets/{app-management-settings-panel-B9vfYoEr.js → app-management-settings-panel-DTKc5HHT.js} +1 -1
- package/dist/gateway/static/root/assets/{appearance-settings-ClOzIO24.js → appearance-settings-BTciFrE9.js} +1 -1
- package/dist/gateway/static/root/assets/{apps-page-DU0dz9Zo.js → apps-page-DT52OrbM.js} +1 -1
- package/dist/gateway/static/root/assets/{archive-plugin-BkVIgZzA.js → archive-plugin-CiHaUMII.js} +1 -1
- package/dist/gateway/static/root/assets/{attachment-load-CaCMWh1D.js → attachment-load-t4gSrNjL.js} +1 -1
- package/dist/gateway/static/root/assets/{automation-suggestion-card-DhGDpKP3.js → automation-suggestion-card-CtysjSoQ.js} +1 -1
- package/dist/gateway/static/root/assets/{automations-page-CTedroVo.js → automations-page-B2YvT3ES.js} +1 -1
- package/dist/gateway/static/root/assets/{binary-plugins-B4TO_bsn.js → binary-plugins-BAbSldW5.js} +1 -1
- package/dist/gateway/static/root/assets/{block-editor-DJRWwWNI.js → block-editor-BNr_82xl.js} +1 -1
- package/dist/gateway/static/root/assets/{browser-settings-page-FCDwOOBK.js → browser-settings-page-BQEXPhDU.js} +3 -3
- package/dist/gateway/static/root/assets/{capability-presets-api-Befi89CL.js → capability-presets-api-tzD702Gg.js} +1 -1
- package/dist/gateway/static/root/assets/{capability-presets-settings-panel-_G8eb2vJ.js → capability-presets-settings-panel-AGhTueAc.js} +1 -1
- package/dist/gateway/static/root/assets/{channel-recipient-api-BNSuJb5f.js → channel-recipient-api-DL_KzwGE.js} +1 -1
- package/dist/gateway/static/root/assets/{channels-settings-C3d5JBJX.js → channels-settings-DRTutLlN.js} +1 -1
- package/dist/gateway/static/root/assets/{connectors-page-yaUNvt5u.js → connectors-page-DneJVuge.js} +2 -2
- package/dist/gateway/static/root/assets/dreaming-settings-BEwWA1yf.js +1 -0
- package/dist/gateway/static/root/assets/{extension-debug-page-DYh402XV.js → extension-debug-page-Dl2kO0Sf.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-page-Cr_TizrN.js → extension-page-DmbLPsx_.js} +1 -1
- package/dist/gateway/static/root/assets/{extension-settings-page-BU1KoKRj.js → extension-settings-page-BS2rx40e.js} +1 -1
- package/dist/gateway/static/root/assets/{fetch-Y7AEX4H3.js → fetch-B3ME5Eri.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-config-api-RbMmAOpX.js → gateway-config-api-DI9_BBTo.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-settings-C79wkXef.js → gateway-settings-DAFI_T0W.js} +1 -1
- package/dist/gateway/static/root/assets/{gateway-startup-retry-DiD_Tx4N.js → gateway-startup-retry-CSZQTz6K.js} +1 -1
- package/dist/gateway/static/root/assets/goal-detail-page-DLuz4jC9.js +1 -0
- package/dist/gateway/static/root/assets/{goals-config-api-DqxVi0p5.js → goals-config-api-Bhzqrp2w.js} +1 -1
- package/dist/gateway/static/root/assets/{goals-page-CS0R-Af2.js → goals-page-DTAfv9Ft.js} +2 -2
- package/dist/gateway/static/root/assets/{goals-settings-CSuzZ8Ae.js → goals-settings-CWN2iirQ.js} +1 -1
- package/dist/gateway/static/root/assets/{heartbeat-settings-DXPaYJm3.js → heartbeat-settings-CkkkHvOi.js} +1 -1
- package/dist/gateway/static/root/assets/index-CnjzMw5i.js +191 -0
- package/dist/gateway/static/root/assets/index-E1QinYKp.css +1 -0
- package/dist/gateway/static/root/assets/{keyboard-shortcuts-settings-Dvg53qnd.js → keyboard-shortcuts-settings-kMp08SlX.js} +1 -1
- package/dist/gateway/static/root/assets/{locale-store-DjbfSsfA.js → locale-store-Ba72bFmP.js} +1 -1
- package/dist/gateway/static/root/assets/{logs-page-DLXPcksQ.js → logs-page-CDXDlS_W.js} +2 -2
- package/dist/gateway/static/root/assets/{markdown-split-CpHxbtWJ.js → markdown-split-Bc0w40vz.js} +1 -1
- package/dist/gateway/static/root/assets/{media-plugins-SFPQRSxq.js → media-plugins-DJ_HvgIy.js} +1 -1
- package/dist/gateway/static/root/assets/messages-CISb1HLv.js +3 -0
- package/dist/gateway/static/root/assets/models-hub-panel-BTwxMIgZ.js +2 -0
- package/dist/gateway/static/root/assets/note-detail-page-BBfOeB0m.js +2 -0
- package/dist/gateway/static/root/assets/{note-image-lightbox-BUYURRgn.js → note-image-lightbox-4zntqpZq.js} +1 -1
- package/dist/gateway/static/root/assets/{notes-page-BsIBSFyZ.js → notes-page-VocWOjoK.js} +1 -1
- package/dist/gateway/static/root/assets/{preview-open-alternatives-BHz9iWYA.js → preview-open-alternatives-BbA7sRd_.js} +1 -1
- package/dist/gateway/static/root/assets/remote-access-hub-CXwjuD_Q.js +1 -0
- package/dist/gateway/static/root/assets/{save-bar-controls-CefkOlRg.js → save-bar-controls-Dividh61.js} +1 -1
- package/dist/gateway/static/root/assets/{schedule-field-CmTRoOcE.js → schedule-field-pRheF2CO.js} +1 -1
- package/dist/gateway/static/root/assets/{schema-form-FMKv1vGu.js → schema-form-D6PiPxfr.js} +1 -1
- package/dist/gateway/static/root/assets/sessions-page-CpcVySQK.js +1 -0
- package/dist/gateway/static/root/assets/settings-advanced-gate-BVy_TawT.js +1 -0
- package/dist/gateway/static/root/assets/{settings-collapsible-section-BouO6JTi.js → settings-collapsible-section-BMCOfuE_.js} +1 -1
- package/dist/gateway/static/root/assets/settings-form-section-DBkGwpIN.js +1 -0
- package/dist/gateway/static/root/assets/settings-page-CZpNyCWn.js +1 -0
- package/dist/gateway/static/root/assets/{settings-page-layout-BJ4Gsef5.js → settings-page-layout-NxoW5Y57.js} +1 -1
- package/dist/gateway/static/root/assets/setup-status-panel-MVHLLu_a.js +1 -0
- package/dist/gateway/static/root/assets/{share-preview-page-BDy2btRN.js → share-preview-page-B0suFCfj.js} +1 -1
- package/dist/gateway/static/root/assets/{shares-settings-B18OBmH5.js → shares-settings-C6DBbQGo.js} +1 -1
- package/dist/gateway/static/root/assets/skill-reload-api-6hJmhwSn.js +1 -0
- package/dist/gateway/static/root/assets/{skills-page-Bm2ebkwl.js → skills-page-Im1MA0Iw.js} +1 -1
- package/dist/gateway/static/root/assets/{system-settings-panel-CRngrlnZ.js → system-settings-panel-CrmEnyX4.js} +1 -1
- package/dist/gateway/static/root/assets/{theme-store-CD2wtgZV.js → theme-store-Bt6vAkSB.js} +1 -1
- package/dist/gateway/static/root/assets/{typed-models-lib-BltBc0b-.js → typed-models-lib-BAsGn25g.js} +1 -1
- package/dist/gateway/static/root/assets/{use-channel-catalog-CjN-51e6.js → use-channel-catalog-C7vcAoGb.js} +1 -1
- package/dist/gateway/static/root/assets/{user-profile-settings-panel-DJ1dKwZy.js → user-profile-settings-panel-QAQWGelF.js} +1 -1
- package/dist/gateway/static/root/assets/{utils-B5M5-YvK.js → utils-Dl9efgnV.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-api-key-field-CeEevm7z.js → voice-api-key-field-D-6XueTv.js} +1 -1
- package/dist/gateway/static/root/assets/{voice-player-store-B7ykUlHM.js → voice-player-store-CMC5NOJp.js} +1 -1
- package/dist/gateway/static/root/assets/{workflow-run-setup-panel-C4PzUQjB.js → workflow-run-setup-panel-DVPllR4k.js} +1 -1
- package/dist/gateway/static/root/assets/{workflows-page-BL84_-Ky.js → workflows-page-B94WISTW.js} +2 -2
- package/dist/gateway/static/root/index.html +7 -7
- package/dist/package.js +1 -1
- package/dist/src/agent/context/workspace-templates/AGENTS.md +3 -2
- package/dist/src/agent/memory/builtin-memory-store.d.ts +1 -1
- package/dist/src/agent/memory/builtin-memory-store.js +1 -1
- package/dist/src/agent/memory/builtin-memory-store.js.map +1 -1
- package/dist/src/agent/memory/builtin-provider.d.ts +2 -2
- package/dist/src/agent/memory/builtin-provider.js +2 -2
- package/dist/src/agent/memory/builtin-provider.js.map +1 -1
- package/dist/src/agent/memory/dreaming/light-sweep.d.ts +0 -7
- package/dist/src/agent/memory/dreaming/light-sweep.js +16 -277
- package/dist/src/agent/memory/dreaming/light-sweep.js.map +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.d.ts +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.js +1 -1
- package/dist/src/agent/memory/dreaming/rem-patterns.js.map +1 -1
- package/dist/src/agent/memory/dreaming/short-term-store.d.ts +3 -3
- package/dist/src/agent/memory/dreaming/short-term-store.js +5 -5
- package/dist/src/agent/memory/dreaming/short-term-store.js.map +1 -1
- package/dist/src/agent/memory/dreaming/utils.d.ts +2 -2
- package/dist/src/agent/memory/dreaming/utils.js +2 -2
- package/dist/src/agent/memory/dreaming/utils.js.map +1 -1
- package/dist/src/agent/memory/index.d.ts +0 -2
- package/dist/src/agent/memory/index.js +1 -2
- package/dist/src/agent/prompt/sections/memory-skills.js +11 -10
- package/dist/src/agent/prompt/sections/memory-skills.js.map +1 -1
- package/dist/src/agent/prompt/sections/tooling.js +2 -2
- package/dist/src/agent/prompt/sections/tooling.js.map +1 -1
- package/dist/src/agent/reply/startup-context.js +6 -105
- package/dist/src/agent/reply/startup-context.js.map +1 -1
- package/dist/src/agent/service.d.ts +0 -3
- package/dist/src/agent/service.js +0 -41
- package/dist/src/agent/service.js.map +1 -1
- package/dist/src/agent/tools/memory-tool.js +2 -2
- package/dist/src/agent/tools/memory-tool.js.map +1 -1
- package/dist/src/cli/commands/gateway/lifecycle.js +1 -1
- package/dist/src/cli/commands/init.js +1 -1
- package/dist/src/cli/commands/init.js.map +1 -1
- package/dist/src/cli/templates.js +3 -2
- package/dist/src/cli/templates.js.map +1 -1
- package/dist/src/config/schema.d.ts +2 -2
- package/dist/src/config/schema.js +2 -2
- package/dist/src/config/schema.js.map +1 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js +4 -1
- package/dist/src/gateway/hono/routes/config-patch/misc.js.map +1 -1
- package/dist/src/infra/update-restart.js +1 -1
- package/dist/src/storage/sqlite/row-mappers.js +3 -1
- package/dist/src/storage/sqlite/row-mappers.js.map +1 -1
- package/dist/src/tui/backends/embedded-backend.js +1 -2
- package/dist/src/tui/backends/embedded-backend.js.map +1 -1
- package/package.json +1 -1
- package/dist/gateway/static/root/assets/agents-B0hkpLhP.js +0 -2
- package/dist/gateway/static/root/assets/dreaming-settings-DNrdg1Ve.js +0 -1
- package/dist/gateway/static/root/assets/goal-detail-page-9paXuHdo.js +0 -1
- package/dist/gateway/static/root/assets/index-BoKafJIZ.js +0 -191
- package/dist/gateway/static/root/assets/index-CArhCcYn.css +0 -1
- package/dist/gateway/static/root/assets/messages-CrDHEbss.js +0 -3
- package/dist/gateway/static/root/assets/models-hub-panel-BDF2iyRk.js +0 -2
- package/dist/gateway/static/root/assets/note-detail-page-CSb_hwig.js +0 -2
- package/dist/gateway/static/root/assets/remote-access-hub-CwzSovcS.js +0 -1
- package/dist/gateway/static/root/assets/sessions-page-PHmX-NmA.js +0 -1
- package/dist/gateway/static/root/assets/settings-advanced-gate-BRAeKJN4.js +0 -1
- package/dist/gateway/static/root/assets/settings-form-section-D-4lBl6X.js +0 -1
- package/dist/gateway/static/root/assets/settings-page-KKrbFjy8.js +0 -1
- package/dist/gateway/static/root/assets/setup-status-panel-CrUjHPQo.js +0 -1
- package/dist/gateway/static/root/assets/skill-reload-api-C28-2uKQ.js +0 -1
- package/dist/src/agent/memory/memory-flush.d.ts +0 -27
- package/dist/src/agent/memory/memory-flush.js +0 -124
- package/dist/src/agent/memory/memory-flush.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rem-patterns.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/rem-patterns.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { DREAMING_DIR_RELATIVE, DREAMS_MD_FILENAME, MS_PER_DAY } from './constants.js';\nimport type { DreamingRemConfig } from './config.js';\nimport {\n bumpEntryPhaseSignal,\n loadDreamingStore,\n saveDreamingStore,\n type DreamingStoreEntry,\n} from './short-term-store.js';\nimport { isoDay } from './utils.js';\nimport {\n DREAMING_LAST_RUN_FORMAT_VERSION,\n type DreamingRemLastRun,\n} from './last-run.js';\n\nconst log = createLogger('Dreaming:REM');\n\n// ── Pattern types ──────────────────────────────────────────────────────\n\ntype PatternCluster = {\n representative: DreamingStoreEntry;\n members: DreamingStoreEntry[];\n strength: number;\n /** Shared query hashes across members. */\n sharedQueries: string[];\n /** Distinct source files involved. */\n distinctPaths: string[];\n};\n\n// ── Config defaults ────────────────────────────────────────────────────\n\nfunction resolveConfig(overrides?: Partial<DreamingRemConfig>): DreamingRemConfig {\n return {\n enabled: overrides?.enabled === true,\n cron: typeof overrides?.cron === 'string' ? overrides.cron : '0 5 * * 0',\n lookbackDays: Math.max(1, Math.floor(Number(overrides?.lookbackDays) || 7)),\n limit: Math.max(0, Math.floor(Number(overrides?.limit) || 10)),\n minPatternStrength: Math.max(0, Math.min(1, Number(overrides?.minPatternStrength) || 0.75)),\n };\n}\n\n// ── Core REM pattern discovery ─────────────────────────────────────────\n\n/**\n * REM phase: cross-session pattern discovery.\n *\n * Scans the short-term store for entries that share query hashes or\n * appear across multiple daily files, then clusters them to identify\n * recurring themes/patterns. Bumps `remHits` on touched entries and\n * optionally writes a pattern summary to DREAMS.md.\n *\n * Runs weekly; expensive but insightful.\n */\nexport async function runRemPatterns(params: {\n workspaceDir: string;\n config?: Partial<DreamingRemConfig>;\n now?: Date;\n}): Promise<{\n ok: boolean;\n reason: string;\n patternsDiscovered: number;\n entriesAnalyzed: number;\n}> {\n const cfg = resolveConfig(params.config);\n const now = params.now ?? new Date();\n const startedAt = now.toISOString();\n const runId = `rem:${startedAt}:${process.pid}`;\n const startMs = Date.now();\n const nowMs = now.getTime();\n\n if (!cfg.enabled) {\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: true, reason: 'REM patterns disabled', startMs,\n rem: { patternsDiscovered: 0, entriesAnalyzed: 0 },\n });\n return { ok: true, reason: 'REM patterns disabled', patternsDiscovered: 0, entriesAnalyzed: 0 };\n }\n\n try {\n const { store } = await loadDreamingStore({ workspaceDir: params.workspaceDir });\n\n // Filter entries within the lookback window.\n const cutoffMs = nowMs - cfg.lookbackDays * MS_PER_DAY;\n const recentEntries = Object.values(store.entries ?? {}).filter(\n (entry): entry is DreamingStoreEntry => {\n if (!entry || typeof entry !== 'object') return false;\n if (!entry.lastRecalledAt) return false;\n const lastMs = Date.parse(entry.lastRecalledAt);\n return Number.isFinite(lastMs) && lastMs >= cutoffMs;\n },\n );\n\n if (recentEntries.length < 2) {\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: true, reason: 'not enough recent entries for pattern analysis', startMs,\n rem: { patternsDiscovered: 0, entriesAnalyzed: recentEntries.length },\n });\n return {\n ok: true,\n reason: 'not enough recent entries for pattern analysis',\n patternsDiscovered: 0,\n entriesAnalyzed: recentEntries.length,\n };\n }\n\n // Discover patterns by clustering entries that share query hashes.\n const clusters = discoverPatternClusters(recentEntries, cfg.minPatternStrength);\n const topClusters = clusters.slice(0, cfg.limit);\n\n // Bump remHits on all entries that belong to a discovered pattern.\n const touchedKeys = new Set<string>();\n for (const cluster of topClusters) {\n for (const member of cluster.members) {\n if (!touchedKeys.has(member.key)) {\n touchedKeys.add(member.key);\n const storeEntry = store.entries[member.key];\n if (storeEntry) {\n bumpEntryPhaseSignal(storeEntry, 'remHits');\n }\n }\n }\n }\n\n store.updatedAt = now.toISOString();\n await saveDreamingStore({ workspaceDir: params.workspaceDir, store });\n\n // Write pattern summary to DREAMS.md (append).\n if (topClusters.length > 0) {\n await appendPatternSummary(params.workspaceDir, topClusters, now);\n }\n\n log.info(\n {\n workspaceDir: params.workspaceDir,\n patterns: topClusters.length,\n entriesAnalyzed: recentEntries.length,\n touched: touchedKeys.size,\n },\n 'REM pattern discovery complete',\n );\n\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: true, reason: 'REM patterns complete', startMs,\n rem: { patternsDiscovered: topClusters.length, entriesAnalyzed: recentEntries.length },\n });\n\n return {\n ok: true,\n reason: 'REM patterns complete',\n patternsDiscovered: topClusters.length,\n entriesAnalyzed: recentEntries.length,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage, workspaceDir: params.workspaceDir }, `REM pattern discovery failed: ${errorMessage}`);\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: false, reason: `REM error: ${errorMessage}`, startMs,\n rem: { patternsDiscovered: 0, entriesAnalyzed: 0 }, errorMessage,\n }).catch(() => undefined);\n return { ok: false, reason: errorMessage, patternsDiscovered: 0, entriesAnalyzed: 0 };\n }\n}\n\n// ── Pattern clustering ─────────────────────────────────────────────────\n\n/**\n * Build an inverted index of queryHash → entries, then form clusters\n * where multiple entries share overlapping query hashes. Each cluster's\n * \"strength\" is the ratio of shared queries to total unique queries.\n */\nfunction discoverPatternClusters(\n entries: DreamingStoreEntry[],\n minStrength: number,\n): PatternCluster[] {\n // Build inverted index: queryHash → entry keys.\n const hashToEntries = new Map<string, DreamingStoreEntry[]>();\n for (const entry of entries) {\n for (const queryHash of entry.queryHashes ?? []) {\n const group = hashToEntries.get(queryHash);\n if (group) {\n group.push(entry);\n } else {\n hashToEntries.set(queryHash, [entry]);\n }\n }\n }\n\n // Find query hashes that appear in 2+ distinct entries from different paths.\n const significantHashes: Array<{ hash: string; entries: DreamingStoreEntry[] }> = [];\n for (const [hash, group] of hashToEntries) {\n const uniquePaths = new Set(group.map((e) => e.path));\n if (group.length >= 2 && uniquePaths.size >= 2) {\n significantHashes.push({ hash, entries: group });\n }\n }\n\n if (significantHashes.length === 0) return [];\n\n // Merge overlapping groups into clusters using union-find.\n const keyToCluster = new Map<string, Set<string>>();\n const keyToEntry = new Map<string, DreamingStoreEntry>();\n\n for (const entry of entries) {\n keyToEntry.set(entry.key, entry);\n }\n\n for (const { entries: groupEntries } of significantHashes) {\n const keys = groupEntries.map((e) => e.key);\n // Find or create the cluster for the first key.\n let mergedCluster = keyToCluster.get(keys[0]!);\n if (!mergedCluster) {\n mergedCluster = new Set<string>();\n mergedCluster.add(keys[0]!);\n keyToCluster.set(keys[0]!, mergedCluster);\n }\n // Merge all other keys into this cluster.\n for (const key of keys.slice(1)) {\n const existingCluster = keyToCluster.get(key);\n if (existingCluster && existingCluster !== mergedCluster) {\n for (const existingKey of existingCluster) {\n mergedCluster.add(existingKey);\n keyToCluster.set(existingKey, mergedCluster);\n }\n } else {\n mergedCluster.add(key);\n keyToCluster.set(key, mergedCluster);\n }\n }\n }\n\n // Deduplicate cluster sets.\n const seenClusters = new Set<Set<string>>();\n const rawClusters: Set<string>[] = [];\n for (const cluster of keyToCluster.values()) {\n if (!seenClusters.has(cluster) && cluster.size >= 2) {\n seenClusters.add(cluster);\n rawClusters.push(cluster);\n }\n }\n\n // Score each cluster.\n const scoredClusters: PatternCluster[] = [];\n for (const clusterKeys of rawClusters) {\n const members: DreamingStoreEntry[] = [];\n for (const key of clusterKeys) {\n const entry = keyToEntry.get(key);\n if (entry) members.push(entry);\n }\n if (members.length < 2) continue;\n\n // Shared queries: hashes that appear in 2+ members.\n const hashCounts = new Map<string, number>();\n for (const member of members) {\n for (const hash of member.queryHashes ?? []) {\n hashCounts.set(hash, (hashCounts.get(hash) ?? 0) + 1);\n }\n }\n const sharedQueries = [...hashCounts.entries()]\n .filter(([, count]) => count >= 2)\n .map(([hash]) => hash);\n\n const allUniqueHashes = new Set<string>();\n for (const member of members) {\n for (const hash of member.queryHashes ?? []) allUniqueHashes.add(hash);\n }\n\n const strength = allUniqueHashes.size > 0 ? sharedQueries.length / allUniqueHashes.size : 0;\n if (strength < minStrength) continue;\n\n const distinctPaths = [...new Set(members.map((m) => m.path))];\n\n // Representative: the member with the highest totalSignalCount.\n const representative = members.reduce((best, current) =>\n (current.totalSignalCount ?? 0) > (best.totalSignalCount ?? 0) ? current : best,\n );\n\n scoredClusters.push({\n representative,\n members,\n strength,\n sharedQueries,\n distinctPaths,\n });\n }\n\n // Sort by strength descending, then by member count.\n scoredClusters.sort((a, b) => {\n if (b.strength !== a.strength) return b.strength - a.strength;\n return b.members.length - a.members.length;\n });\n\n return scoredClusters;\n}\n\n// ── DREAMS.md writer ───────────────────────────────────────────────────\n\nasync function appendPatternSummary(\n workspaceDir: string,\n clusters: PatternCluster[],\n now: Date,\n): Promise<void> {\n const dreamsPath = path.join(workspaceDir, DREAMS_MD_FILENAME);\n const existing = await fs.readFile(dreamsPath, 'utf-8').catch((err: unknown) => {\n if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') return '';\n throw err;\n });\n\n const day = isoDay(now);\n const lines: string[] = [];\n\n if (existing.trim().length === 0) {\n lines.push('# Dream Diary', '');\n }\n\n lines.push(`## REM Pattern Discovery — ${day}`, '');\n lines.push(`*${now.toISOString()}*`, '');\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i]!;\n lines.push(\n `### Pattern ${i + 1}: ${cluster.distinctPaths.length} files, strength=${cluster.strength.toFixed(2)}`,\n );\n lines.push('');\n lines.push(`**Files involved:** ${cluster.distinctPaths.join(', ')}`);\n lines.push(`**Shared query themes:** ${cluster.sharedQueries.length} overlapping queries`);\n lines.push(`**Members:** ${cluster.members.length} snippets`);\n lines.push('');\n // Include the representative snippet.\n const rep = cluster.representative;\n lines.push(`> ${rep.snippet?.slice(0, 200) ?? '(no snippet)'}`);\n lines.push(`> — ${rep.path}:${rep.startLine}-${rep.endLine}`);\n lines.push('');\n }\n\n lines.push('---', '');\n\n const separator = existing.trim().length > 0 && !existing.endsWith('\\n') ? '\\n' : '';\n const next = `${existing}${separator}${lines.join('\\n')}`;\n await fs.writeFile(dreamsPath, next, 'utf-8');\n}\n\n// ── Last-run writer ────────────────────────────────────────────────────\n\nasync function writeLastRun(\n workspaceDir: string,\n params: {\n runId: string;\n startedAt: string;\n cfg: DreamingRemConfig;\n ok: boolean;\n reason: string;\n startMs: number;\n rem: DreamingRemLastRun['rem'];\n errorMessage?: string;\n },\n): Promise<void> {\n const finishedAt = new Date().toISOString();\n const durationMs = Math.max(0, Date.now() - params.startMs);\n\n const lastRun: DreamingRemLastRun = {\n version: DREAMING_LAST_RUN_FORMAT_VERSION,\n phase: 'rem',\n runId: params.runId,\n startedAt: params.startedAt,\n finishedAt,\n durationMs,\n ok: params.ok,\n reason: params.reason,\n config: params.cfg,\n rem: params.rem,\n ...(params.errorMessage ? { errorMessage: params.errorMessage } : {}),\n };\n\n const lastRunPath = path.join(workspaceDir, DREAMING_DIR_RELATIVE, 'last-run-rem.json');\n await fs.mkdir(path.dirname(lastRunPath), { recursive: true });\n const tmp = `${lastRunPath}.${process.pid}.${Date.now()}.tmp`;\n await fs.writeFile(tmp, `${JSON.stringify(lastRun, null, 2)}\\n`, 'utf-8');\n await fs.rename(tmp, lastRunPath);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"rem-patterns.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/rem-patterns.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\n\nimport { createLogger } from '../../../utils/logger.js';\nimport { DREAMING_DIR_RELATIVE, DREAMS_MD_FILENAME, MS_PER_DAY } from './constants.js';\nimport type { DreamingRemConfig } from './config.js';\nimport {\n bumpEntryPhaseSignal,\n loadDreamingStore,\n saveDreamingStore,\n type DreamingStoreEntry,\n} from './short-term-store.js';\nimport { isoDay } from './utils.js';\nimport {\n DREAMING_LAST_RUN_FORMAT_VERSION,\n type DreamingRemLastRun,\n} from './last-run.js';\n\nconst log = createLogger('Dreaming:REM');\n\n// ── Pattern types ──────────────────────────────────────────────────────\n\ntype PatternCluster = {\n representative: DreamingStoreEntry;\n members: DreamingStoreEntry[];\n strength: number;\n /** Shared query hashes across members. */\n sharedQueries: string[];\n /** Distinct source files involved. */\n distinctPaths: string[];\n};\n\n// ── Config defaults ────────────────────────────────────────────────────\n\nfunction resolveConfig(overrides?: Partial<DreamingRemConfig>): DreamingRemConfig {\n return {\n enabled: overrides?.enabled === true,\n cron: typeof overrides?.cron === 'string' ? overrides.cron : '0 5 * * 0',\n lookbackDays: Math.max(1, Math.floor(Number(overrides?.lookbackDays) || 7)),\n limit: Math.max(0, Math.floor(Number(overrides?.limit) || 10)),\n minPatternStrength: Math.max(0, Math.min(1, Number(overrides?.minPatternStrength) || 0.75)),\n };\n}\n\n// ── Core REM pattern discovery ─────────────────────────────────────────\n\n/**\n * REM phase: cross-session pattern discovery.\n *\n * Scans the short-term store for entries that share query hashes or\n * appear across multiple indexed memory sources, then clusters them to identify\n * recurring themes/patterns. Bumps `remHits` on touched entries and\n * optionally writes a pattern summary to DREAMS.md.\n *\n * Runs weekly; expensive but insightful.\n */\nexport async function runRemPatterns(params: {\n workspaceDir: string;\n config?: Partial<DreamingRemConfig>;\n now?: Date;\n}): Promise<{\n ok: boolean;\n reason: string;\n patternsDiscovered: number;\n entriesAnalyzed: number;\n}> {\n const cfg = resolveConfig(params.config);\n const now = params.now ?? new Date();\n const startedAt = now.toISOString();\n const runId = `rem:${startedAt}:${process.pid}`;\n const startMs = Date.now();\n const nowMs = now.getTime();\n\n if (!cfg.enabled) {\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: true, reason: 'REM patterns disabled', startMs,\n rem: { patternsDiscovered: 0, entriesAnalyzed: 0 },\n });\n return { ok: true, reason: 'REM patterns disabled', patternsDiscovered: 0, entriesAnalyzed: 0 };\n }\n\n try {\n const { store } = await loadDreamingStore({ workspaceDir: params.workspaceDir });\n\n // Filter entries within the lookback window.\n const cutoffMs = nowMs - cfg.lookbackDays * MS_PER_DAY;\n const recentEntries = Object.values(store.entries ?? {}).filter(\n (entry): entry is DreamingStoreEntry => {\n if (!entry || typeof entry !== 'object') return false;\n if (!entry.lastRecalledAt) return false;\n const lastMs = Date.parse(entry.lastRecalledAt);\n return Number.isFinite(lastMs) && lastMs >= cutoffMs;\n },\n );\n\n if (recentEntries.length < 2) {\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: true, reason: 'not enough recent entries for pattern analysis', startMs,\n rem: { patternsDiscovered: 0, entriesAnalyzed: recentEntries.length },\n });\n return {\n ok: true,\n reason: 'not enough recent entries for pattern analysis',\n patternsDiscovered: 0,\n entriesAnalyzed: recentEntries.length,\n };\n }\n\n // Discover patterns by clustering entries that share query hashes.\n const clusters = discoverPatternClusters(recentEntries, cfg.minPatternStrength);\n const topClusters = clusters.slice(0, cfg.limit);\n\n // Bump remHits on all entries that belong to a discovered pattern.\n const touchedKeys = new Set<string>();\n for (const cluster of topClusters) {\n for (const member of cluster.members) {\n if (!touchedKeys.has(member.key)) {\n touchedKeys.add(member.key);\n const storeEntry = store.entries[member.key];\n if (storeEntry) {\n bumpEntryPhaseSignal(storeEntry, 'remHits');\n }\n }\n }\n }\n\n store.updatedAt = now.toISOString();\n await saveDreamingStore({ workspaceDir: params.workspaceDir, store });\n\n // Write pattern summary to DREAMS.md (append).\n if (topClusters.length > 0) {\n await appendPatternSummary(params.workspaceDir, topClusters, now);\n }\n\n log.info(\n {\n workspaceDir: params.workspaceDir,\n patterns: topClusters.length,\n entriesAnalyzed: recentEntries.length,\n touched: touchedKeys.size,\n },\n 'REM pattern discovery complete',\n );\n\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: true, reason: 'REM patterns complete', startMs,\n rem: { patternsDiscovered: topClusters.length, entriesAnalyzed: recentEntries.length },\n });\n\n return {\n ok: true,\n reason: 'REM patterns complete',\n patternsDiscovered: topClusters.length,\n entriesAnalyzed: recentEntries.length,\n };\n } catch (err) {\n const errorMessage = err instanceof Error ? err.message : String(err);\n log.error({ err, errorMessage, workspaceDir: params.workspaceDir }, `REM pattern discovery failed: ${errorMessage}`);\n await writeLastRun(params.workspaceDir, {\n runId, startedAt, cfg, ok: false, reason: `REM error: ${errorMessage}`, startMs,\n rem: { patternsDiscovered: 0, entriesAnalyzed: 0 }, errorMessage,\n }).catch(() => undefined);\n return { ok: false, reason: errorMessage, patternsDiscovered: 0, entriesAnalyzed: 0 };\n }\n}\n\n// ── Pattern clustering ─────────────────────────────────────────────────\n\n/**\n * Build an inverted index of queryHash → entries, then form clusters\n * where multiple entries share overlapping query hashes. Each cluster's\n * \"strength\" is the ratio of shared queries to total unique queries.\n */\nfunction discoverPatternClusters(\n entries: DreamingStoreEntry[],\n minStrength: number,\n): PatternCluster[] {\n // Build inverted index: queryHash → entry keys.\n const hashToEntries = new Map<string, DreamingStoreEntry[]>();\n for (const entry of entries) {\n for (const queryHash of entry.queryHashes ?? []) {\n const group = hashToEntries.get(queryHash);\n if (group) {\n group.push(entry);\n } else {\n hashToEntries.set(queryHash, [entry]);\n }\n }\n }\n\n // Find query hashes that appear in 2+ distinct entries from different paths.\n const significantHashes: Array<{ hash: string; entries: DreamingStoreEntry[] }> = [];\n for (const [hash, group] of hashToEntries) {\n const uniquePaths = new Set(group.map((e) => e.path));\n if (group.length >= 2 && uniquePaths.size >= 2) {\n significantHashes.push({ hash, entries: group });\n }\n }\n\n if (significantHashes.length === 0) return [];\n\n // Merge overlapping groups into clusters using union-find.\n const keyToCluster = new Map<string, Set<string>>();\n const keyToEntry = new Map<string, DreamingStoreEntry>();\n\n for (const entry of entries) {\n keyToEntry.set(entry.key, entry);\n }\n\n for (const { entries: groupEntries } of significantHashes) {\n const keys = groupEntries.map((e) => e.key);\n // Find or create the cluster for the first key.\n let mergedCluster = keyToCluster.get(keys[0]!);\n if (!mergedCluster) {\n mergedCluster = new Set<string>();\n mergedCluster.add(keys[0]!);\n keyToCluster.set(keys[0]!, mergedCluster);\n }\n // Merge all other keys into this cluster.\n for (const key of keys.slice(1)) {\n const existingCluster = keyToCluster.get(key);\n if (existingCluster && existingCluster !== mergedCluster) {\n for (const existingKey of existingCluster) {\n mergedCluster.add(existingKey);\n keyToCluster.set(existingKey, mergedCluster);\n }\n } else {\n mergedCluster.add(key);\n keyToCluster.set(key, mergedCluster);\n }\n }\n }\n\n // Deduplicate cluster sets.\n const seenClusters = new Set<Set<string>>();\n const rawClusters: Set<string>[] = [];\n for (const cluster of keyToCluster.values()) {\n if (!seenClusters.has(cluster) && cluster.size >= 2) {\n seenClusters.add(cluster);\n rawClusters.push(cluster);\n }\n }\n\n // Score each cluster.\n const scoredClusters: PatternCluster[] = [];\n for (const clusterKeys of rawClusters) {\n const members: DreamingStoreEntry[] = [];\n for (const key of clusterKeys) {\n const entry = keyToEntry.get(key);\n if (entry) members.push(entry);\n }\n if (members.length < 2) continue;\n\n // Shared queries: hashes that appear in 2+ members.\n const hashCounts = new Map<string, number>();\n for (const member of members) {\n for (const hash of member.queryHashes ?? []) {\n hashCounts.set(hash, (hashCounts.get(hash) ?? 0) + 1);\n }\n }\n const sharedQueries = [...hashCounts.entries()]\n .filter(([, count]) => count >= 2)\n .map(([hash]) => hash);\n\n const allUniqueHashes = new Set<string>();\n for (const member of members) {\n for (const hash of member.queryHashes ?? []) allUniqueHashes.add(hash);\n }\n\n const strength = allUniqueHashes.size > 0 ? sharedQueries.length / allUniqueHashes.size : 0;\n if (strength < minStrength) continue;\n\n const distinctPaths = [...new Set(members.map((m) => m.path))];\n\n // Representative: the member with the highest totalSignalCount.\n const representative = members.reduce((best, current) =>\n (current.totalSignalCount ?? 0) > (best.totalSignalCount ?? 0) ? current : best,\n );\n\n scoredClusters.push({\n representative,\n members,\n strength,\n sharedQueries,\n distinctPaths,\n });\n }\n\n // Sort by strength descending, then by member count.\n scoredClusters.sort((a, b) => {\n if (b.strength !== a.strength) return b.strength - a.strength;\n return b.members.length - a.members.length;\n });\n\n return scoredClusters;\n}\n\n// ── DREAMS.md writer ───────────────────────────────────────────────────\n\nasync function appendPatternSummary(\n workspaceDir: string,\n clusters: PatternCluster[],\n now: Date,\n): Promise<void> {\n const dreamsPath = path.join(workspaceDir, DREAMS_MD_FILENAME);\n const existing = await fs.readFile(dreamsPath, 'utf-8').catch((err: unknown) => {\n if ((err as NodeJS.ErrnoException)?.code === 'ENOENT') return '';\n throw err;\n });\n\n const day = isoDay(now);\n const lines: string[] = [];\n\n if (existing.trim().length === 0) {\n lines.push('# Dream Diary', '');\n }\n\n lines.push(`## REM Pattern Discovery — ${day}`, '');\n lines.push(`*${now.toISOString()}*`, '');\n\n for (let i = 0; i < clusters.length; i++) {\n const cluster = clusters[i]!;\n lines.push(\n `### Pattern ${i + 1}: ${cluster.distinctPaths.length} files, strength=${cluster.strength.toFixed(2)}`,\n );\n lines.push('');\n lines.push(`**Files involved:** ${cluster.distinctPaths.join(', ')}`);\n lines.push(`**Shared query themes:** ${cluster.sharedQueries.length} overlapping queries`);\n lines.push(`**Members:** ${cluster.members.length} snippets`);\n lines.push('');\n // Include the representative snippet.\n const rep = cluster.representative;\n lines.push(`> ${rep.snippet?.slice(0, 200) ?? '(no snippet)'}`);\n lines.push(`> — ${rep.path}:${rep.startLine}-${rep.endLine}`);\n lines.push('');\n }\n\n lines.push('---', '');\n\n const separator = existing.trim().length > 0 && !existing.endsWith('\\n') ? '\\n' : '';\n const next = `${existing}${separator}${lines.join('\\n')}`;\n await fs.writeFile(dreamsPath, next, 'utf-8');\n}\n\n// ── Last-run writer ────────────────────────────────────────────────────\n\nasync function writeLastRun(\n workspaceDir: string,\n params: {\n runId: string;\n startedAt: string;\n cfg: DreamingRemConfig;\n ok: boolean;\n reason: string;\n startMs: number;\n rem: DreamingRemLastRun['rem'];\n errorMessage?: string;\n },\n): Promise<void> {\n const finishedAt = new Date().toISOString();\n const durationMs = Math.max(0, Date.now() - params.startMs);\n\n const lastRun: DreamingRemLastRun = {\n version: DREAMING_LAST_RUN_FORMAT_VERSION,\n phase: 'rem',\n runId: params.runId,\n startedAt: params.startedAt,\n finishedAt,\n durationMs,\n ok: params.ok,\n reason: params.reason,\n config: params.cfg,\n rem: params.rem,\n ...(params.errorMessage ? { errorMessage: params.errorMessage } : {}),\n };\n\n const lastRunPath = path.join(workspaceDir, DREAMING_DIR_RELATIVE, 'last-run-rem.json');\n await fs.mkdir(path.dirname(lastRunPath), { recursive: true });\n const tmp = `${lastRunPath}.${process.pid}.${Date.now()}.tmp`;\n await fs.writeFile(tmp, `${JSON.stringify(lastRun, null, 2)}\\n`, 'utf-8');\n await fs.rename(tmp, lastRunPath);\n}\n"],"mappings":"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"}
|
|
@@ -7,8 +7,8 @@ export type DreamingStoreEntry = {
|
|
|
7
7
|
snippet: string;
|
|
8
8
|
/** Number of times this snippet was returned by a memory recall query. */
|
|
9
9
|
recallCount: number;
|
|
10
|
-
/** Number of times recorded from
|
|
11
|
-
|
|
10
|
+
/** Number of times recorded from source-side sweep phases. */
|
|
11
|
+
sourceCount: number;
|
|
12
12
|
/** Number of times replayed from grounded context (agent-initiated). */
|
|
13
13
|
groundedCount: number;
|
|
14
14
|
/** Number of times the light phase touched this entry. */
|
|
@@ -55,7 +55,7 @@ export declare function saveDreamingStore(params: {
|
|
|
55
55
|
workspaceDir: string;
|
|
56
56
|
store: DreamingStore;
|
|
57
57
|
}): Promise<void>;
|
|
58
|
-
type PhaseSignalField = '
|
|
58
|
+
type PhaseSignalField = 'sourceCount' | 'groundedCount' | 'lightHits' | 'remHits';
|
|
59
59
|
/**
|
|
60
60
|
* Increment a phase-specific signal counter on an existing store entry.
|
|
61
61
|
* Also bumps `phaseHitCount` and `totalSignalCount`.
|
|
@@ -8,9 +8,9 @@ import fs from "node:fs/promises";
|
|
|
8
8
|
//#region src/agent/memory/dreaming/short-term-store.ts
|
|
9
9
|
init_logger();
|
|
10
10
|
const log = createLogger("Dreaming:Store");
|
|
11
|
-
function
|
|
11
|
+
function isRecordableMemoryPath(rel) {
|
|
12
12
|
const p = normalizeMemoryPath(rel);
|
|
13
|
-
return
|
|
13
|
+
return p.length > 0 && !p.startsWith("../") && !path.isAbsolute(p);
|
|
14
14
|
}
|
|
15
15
|
function hashQuery(query) {
|
|
16
16
|
return createHash("sha1").update(query.trim().toLowerCase()).digest("hex").slice(0, 12);
|
|
@@ -82,7 +82,7 @@ async function recordDreamingRecalls(params) {
|
|
|
82
82
|
let skipped = 0;
|
|
83
83
|
for (const match of params.matches) {
|
|
84
84
|
const file = typeof match?.file === "string" ? match.file : "";
|
|
85
|
-
if (!file || !
|
|
85
|
+
if (!file || !isRecordableMemoryPath(file)) {
|
|
86
86
|
skipped += 1;
|
|
87
87
|
continue;
|
|
88
88
|
}
|
|
@@ -106,7 +106,7 @@ async function recordDreamingRecalls(params) {
|
|
|
106
106
|
...existing,
|
|
107
107
|
snippet,
|
|
108
108
|
recallCount: Math.max(0, Math.floor(existing.recallCount + 1)),
|
|
109
|
-
|
|
109
|
+
sourceCount: existing.sourceCount ?? 0,
|
|
110
110
|
groundedCount: existing.groundedCount ?? 0,
|
|
111
111
|
lightHits: existing.lightHits ?? 0,
|
|
112
112
|
remHits: existing.remHits ?? 0,
|
|
@@ -124,7 +124,7 @@ async function recordDreamingRecalls(params) {
|
|
|
124
124
|
endLine,
|
|
125
125
|
snippet,
|
|
126
126
|
recallCount: 1,
|
|
127
|
-
|
|
127
|
+
sourceCount: 0,
|
|
128
128
|
groundedCount: 0,
|
|
129
129
|
lightHits: 0,
|
|
130
130
|
remHits: 0,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"short-term-store.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/short-term-store.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { createHash, randomUUID } from 'node:crypto';\n\nimport type { MemorySearchOptions } from '../../prompt/memory/index.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport {\n SHORT_TERM_PROMOTION_LOCK_RELATIVE,\n SHORT_TERM_RECALL_STORE_RELATIVE,\n} from './constants.js';\nimport { buildEntryKey, clamp01, isoDay, normalizeMemoryPath } from './utils.js';\n\nconst log = createLogger('Dreaming:Store');\n\nexport type DreamingStoreEntry = {\n key: string;\n path: string; // workspace-relative: memory/YYYY-MM-DD.md\n startLine: number;\n endLine: number;\n snippet: string;\n\n // ── Signal dimensions ────────────────────────────────────────────────\n /** Number of times this snippet was returned by a memory recall query. */\n recallCount: number;\n /** Number of times recorded from daily log scanning (light sweep). */\n dailyCount: number;\n /** Number of times replayed from grounded context (agent-initiated). */\n groundedCount: number;\n /** Number of times the light phase touched this entry. */\n lightHits: number;\n /** Number of times the REM phase touched this entry. */\n remHits: number;\n /** Cross-phase hit count (light + deep + rem combined touches). */\n phaseHitCount: number;\n /** Weighted aggregate of all signal dimensions. */\n totalSignalCount: number;\n\n // ── Score tracking ───────────────────────────────────────────────────\n totalScore: number;\n maxScore: number;\n queryHashes: string[];\n recallDays: string[];\n\n // ── Timestamps ───────────────────────────────────────────────────────\n firstRecalledAt: string;\n lastRecalledAt: string;\n promotedAt?: string;\n};\n\nexport type DreamingStore = {\n version: 1;\n updatedAt: string;\n entries: Record<string, DreamingStoreEntry>;\n};\n\ntype MemoryMatch = Awaited<ReturnType<typeof import('../../prompt/memory/index.js').memorySearch>>[number];\n\nfunction isDailyWorkspaceMemoryPath(rel: string): boolean {\n const p = normalizeMemoryPath(rel);\n return /^memory\\/\\d{4}-\\d{2}-\\d{2}\\.md$/i.test(p);\n}\n\nfunction hashQuery(query: string): string {\n return createHash('sha1').update(query.trim().toLowerCase()).digest('hex').slice(0, 12);\n}\n\nfunction mergeRecentDistinct(existing: string[], nextValue: string, limit: number): string[] {\n const seen = new Set<string>();\n const normalized = existing\n .filter((v): v is string => typeof v === 'string')\n .map((v) => v.trim())\n .filter((v) => v.length > 0 && !seen.has(v) && (seen.add(v), true));\n if (nextValue && !normalized.includes(nextValue)) {\n normalized.push(nextValue);\n }\n return normalized.length <= limit ? normalized : normalized.slice(-limit);\n}\n\nfunction mergeQueryHashes(existing: string[], nextHash: string): string[] {\n const out = mergeRecentDistinct(existing, nextHash, 32);\n return out;\n}\n\nfunction emptyStore(nowIso: string): DreamingStore {\n return { version: 1, updatedAt: nowIso, entries: {} };\n}\n\nasync function ensureDreamDir(workspaceDir: string): Promise<string> {\n const dir = path.join(workspaceDir, path.dirname(SHORT_TERM_RECALL_STORE_RELATIVE));\n await fs.mkdir(dir, { recursive: true });\n return dir;\n}\n\nasync function readStore(workspaceDir: string, nowIso: string): Promise<DreamingStore> {\n const storePath = path.join(workspaceDir, SHORT_TERM_RECALL_STORE_RELATIVE);\n try {\n const raw = await fs.readFile(storePath, 'utf-8');\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== 'object') return emptyStore(nowIso);\n const rec = parsed as Partial<DreamingStore>;\n if (rec.version !== 1 || !rec.entries || typeof rec.entries !== 'object') {\n return emptyStore(nowIso);\n }\n return {\n version: 1,\n updatedAt: typeof rec.updatedAt === 'string' ? rec.updatedAt : nowIso,\n entries: rec.entries as Record<string, DreamingStoreEntry>,\n };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'ENOENT') return emptyStore(nowIso);\n log.warn({ err, workspaceDir }, 'Failed to read dreaming store; resetting');\n return emptyStore(nowIso);\n }\n}\n\nasync function writeStore(workspaceDir: string, store: DreamingStore): Promise<void> {\n await ensureDreamDir(workspaceDir);\n const storePath = path.join(workspaceDir, SHORT_TERM_RECALL_STORE_RELATIVE);\n const tmp = `${storePath}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;\n await fs.writeFile(tmp, `${JSON.stringify(store, null, 2)}\\n`, 'utf-8');\n await fs.rename(tmp, storePath);\n}\n\nexport async function recordDreamingRecalls(params: {\n workspaceDir: string;\n query: string;\n matches: MemoryMatch[];\n options?: MemorySearchOptions;\n now?: Date;\n}): Promise<{ recorded: number; skipped: number; storePath: string }> {\n const workspaceDir = params.workspaceDir.trim();\n const query = params.query.trim();\n if (!workspaceDir || !query) {\n return { recorded: 0, skipped: params.matches.length, storePath: SHORT_TERM_RECALL_STORE_RELATIVE };\n }\n const now = params.now ?? new Date();\n const nowIso = now.toISOString();\n const dayBucket = isoDay(now);\n const qHash = hashQuery(query);\n\n const store = await readStore(workspaceDir, nowIso);\n\n let recorded = 0;\n let skipped = 0;\n for (const match of params.matches) {\n const file = typeof match?.file === \"string\" ? match.file : \"\";\n if (!file || !isDailyWorkspaceMemoryPath(file)) {\n skipped += 1;\n continue;\n }\n const lines = typeof match?.lines === \"string\" ? match.lines.trim() : \"\";\n const lineNumbers = Array.isArray(match?.lineNumbers) ? match.lineNumbers : [];\n const startLine = Math.max(1, Math.min(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));\n const endLine = Math.max(startLine, Math.max(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));\n if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) {\n skipped += 1;\n continue;\n }\n const score = clamp01(Number(match.score));\n const key = buildEntryKey({ path: file, startLine, endLine });\n const existing = store.entries[key];\n const snippet = lines.length > 0 ? lines.slice(0, 360) : file;\n\n const next: DreamingStoreEntry = existing\n ? {\n ...existing,\n snippet,\n recallCount: Math.max(0, Math.floor(existing.recallCount + 1)),\n dailyCount: existing.dailyCount ?? 0,\n groundedCount: existing.groundedCount ?? 0,\n lightHits: existing.lightHits ?? 0,\n remHits: existing.remHits ?? 0,\n phaseHitCount: existing.phaseHitCount ?? 0,\n totalSignalCount: Math.max(0, (existing.totalSignalCount ?? existing.recallCount ?? 0) + 1),\n totalScore: Math.max(0, existing.totalScore + score),\n maxScore: Math.max(existing.maxScore, score),\n queryHashes: mergeQueryHashes(existing.queryHashes ?? [], qHash),\n recallDays: mergeRecentDistinct(existing.recallDays ?? [], dayBucket, 16),\n lastRecalledAt: nowIso,\n }\n : {\n key,\n path: normalizeMemoryPath(file),\n startLine,\n endLine,\n snippet,\n recallCount: 1,\n dailyCount: 0,\n groundedCount: 0,\n lightHits: 0,\n remHits: 0,\n phaseHitCount: 0,\n totalSignalCount: 1,\n totalScore: score,\n maxScore: score,\n queryHashes: [qHash],\n recallDays: [dayBucket],\n firstRecalledAt: nowIso,\n lastRecalledAt: nowIso,\n };\n\n store.entries[key] = next;\n recorded += 1;\n }\n\n if (recorded > 0) {\n store.updatedAt = nowIso;\n await writeStore(workspaceDir, store);\n }\n\n return {\n recorded,\n skipped,\n storePath: SHORT_TERM_RECALL_STORE_RELATIVE,\n };\n}\n\nexport async function withDreamingPromotionLock<T>(\n workspaceDir: string,\n task: () => Promise<T>,\n): Promise<T> {\n const lockPath = path.join(workspaceDir, SHORT_TERM_PROMOTION_LOCK_RELATIVE);\n await ensureDreamDir(workspaceDir);\n\n const startedAt = Date.now();\n const timeoutMs = 10_000;\n const retryDelayMs = 50;\n\n while (true) {\n try {\n const handle = await fs.open(lockPath, 'wx');\n await handle.writeFile(`${process.pid}:${Date.now()}\\n`, 'utf-8').catch(() => undefined);\n try {\n return await task();\n } finally {\n await handle.close().catch(() => undefined);\n await fs.unlink(lockPath).catch(() => undefined);\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code !== 'EEXIST') {\n throw err;\n }\n if (Date.now() - startedAt >= timeoutMs) {\n throw new Error(`Timed out waiting for dreaming promotion lock at ${lockPath}`);\n }\n await new Promise((r) => setTimeout(r, retryDelayMs));\n }\n }\n}\n\nexport async function loadDreamingStore(params: {\n workspaceDir: string;\n}): Promise<{ store: DreamingStore; storePath: string }> {\n const nowIso = new Date().toISOString();\n const store = await readStore(params.workspaceDir, nowIso);\n return { store, storePath: SHORT_TERM_RECALL_STORE_RELATIVE };\n}\n\nexport async function saveDreamingStore(params: {\n workspaceDir: string;\n store: DreamingStore;\n}): Promise<void> {\n await writeStore(params.workspaceDir, params.store);\n}\n\n// ── Phase-level signal helpers ─────────────────────────────────────────\n\ntype PhaseSignalField = 'dailyCount' | 'groundedCount' | 'lightHits' | 'remHits';\n\n/**\n * Increment a phase-specific signal counter on an existing store entry.\n * Also bumps `phaseHitCount` and `totalSignalCount`.\n * Returns `true` if the entry existed and was updated.\n */\nexport function bumpEntryPhaseSignal(\n entry: DreamingStoreEntry,\n field: PhaseSignalField,\n increment = 1,\n): void {\n entry[field] = (entry[field] ?? 0) + increment;\n entry.phaseHitCount = (entry.phaseHitCount ?? 0) + increment;\n entry.totalSignalCount = (entry.totalSignalCount ?? 0) + increment;\n}\n\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"short-term-store.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/short-term-store.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { createHash, randomUUID } from 'node:crypto';\n\nimport type { MemorySearchOptions } from '../../prompt/memory/index.js';\nimport { createLogger } from '../../../utils/logger.js';\nimport {\n SHORT_TERM_PROMOTION_LOCK_RELATIVE,\n SHORT_TERM_RECALL_STORE_RELATIVE,\n} from './constants.js';\nimport { buildEntryKey, clamp01, isoDay, normalizeMemoryPath } from './utils.js';\n\nconst log = createLogger('Dreaming:Store');\n\nexport type DreamingStoreEntry = {\n key: string;\n path: string;\n startLine: number;\n endLine: number;\n snippet: string;\n\n // ── Signal dimensions ────────────────────────────────────────────────\n /** Number of times this snippet was returned by a memory recall query. */\n recallCount: number;\n /** Number of times recorded from source-side sweep phases. */\n sourceCount: number;\n /** Number of times replayed from grounded context (agent-initiated). */\n groundedCount: number;\n /** Number of times the light phase touched this entry. */\n lightHits: number;\n /** Number of times the REM phase touched this entry. */\n remHits: number;\n /** Cross-phase hit count (light + deep + rem combined touches). */\n phaseHitCount: number;\n /** Weighted aggregate of all signal dimensions. */\n totalSignalCount: number;\n\n // ── Score tracking ───────────────────────────────────────────────────\n totalScore: number;\n maxScore: number;\n queryHashes: string[];\n recallDays: string[];\n\n // ── Timestamps ───────────────────────────────────────────────────────\n firstRecalledAt: string;\n lastRecalledAt: string;\n promotedAt?: string;\n};\n\nexport type DreamingStore = {\n version: 1;\n updatedAt: string;\n entries: Record<string, DreamingStoreEntry>;\n};\n\ntype MemoryMatch = Awaited<ReturnType<typeof import('../../prompt/memory/index.js').memorySearch>>[number];\n\nfunction isRecordableMemoryPath(rel: string): boolean {\n const p = normalizeMemoryPath(rel);\n return p.length > 0 && !p.startsWith('../') && !path.isAbsolute(p);\n}\n\nfunction hashQuery(query: string): string {\n return createHash('sha1').update(query.trim().toLowerCase()).digest('hex').slice(0, 12);\n}\n\nfunction mergeRecentDistinct(existing: string[], nextValue: string, limit: number): string[] {\n const seen = new Set<string>();\n const normalized = existing\n .filter((v): v is string => typeof v === 'string')\n .map((v) => v.trim())\n .filter((v) => v.length > 0 && !seen.has(v) && (seen.add(v), true));\n if (nextValue && !normalized.includes(nextValue)) {\n normalized.push(nextValue);\n }\n return normalized.length <= limit ? normalized : normalized.slice(-limit);\n}\n\nfunction mergeQueryHashes(existing: string[], nextHash: string): string[] {\n const out = mergeRecentDistinct(existing, nextHash, 32);\n return out;\n}\n\nfunction emptyStore(nowIso: string): DreamingStore {\n return { version: 1, updatedAt: nowIso, entries: {} };\n}\n\nasync function ensureDreamDir(workspaceDir: string): Promise<string> {\n const dir = path.join(workspaceDir, path.dirname(SHORT_TERM_RECALL_STORE_RELATIVE));\n await fs.mkdir(dir, { recursive: true });\n return dir;\n}\n\nasync function readStore(workspaceDir: string, nowIso: string): Promise<DreamingStore> {\n const storePath = path.join(workspaceDir, SHORT_TERM_RECALL_STORE_RELATIVE);\n try {\n const raw = await fs.readFile(storePath, 'utf-8');\n const parsed = JSON.parse(raw) as unknown;\n if (!parsed || typeof parsed !== 'object') return emptyStore(nowIso);\n const rec = parsed as Partial<DreamingStore>;\n if (rec.version !== 1 || !rec.entries || typeof rec.entries !== 'object') {\n return emptyStore(nowIso);\n }\n return {\n version: 1,\n updatedAt: typeof rec.updatedAt === 'string' ? rec.updatedAt : nowIso,\n entries: rec.entries as Record<string, DreamingStoreEntry>,\n };\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code === 'ENOENT') return emptyStore(nowIso);\n log.warn({ err, workspaceDir }, 'Failed to read dreaming store; resetting');\n return emptyStore(nowIso);\n }\n}\n\nasync function writeStore(workspaceDir: string, store: DreamingStore): Promise<void> {\n await ensureDreamDir(workspaceDir);\n const storePath = path.join(workspaceDir, SHORT_TERM_RECALL_STORE_RELATIVE);\n const tmp = `${storePath}.${process.pid}.${Date.now()}.${randomUUID()}.tmp`;\n await fs.writeFile(tmp, `${JSON.stringify(store, null, 2)}\\n`, 'utf-8');\n await fs.rename(tmp, storePath);\n}\n\nexport async function recordDreamingRecalls(params: {\n workspaceDir: string;\n query: string;\n matches: MemoryMatch[];\n options?: MemorySearchOptions;\n now?: Date;\n}): Promise<{ recorded: number; skipped: number; storePath: string }> {\n const workspaceDir = params.workspaceDir.trim();\n const query = params.query.trim();\n if (!workspaceDir || !query) {\n return { recorded: 0, skipped: params.matches.length, storePath: SHORT_TERM_RECALL_STORE_RELATIVE };\n }\n const now = params.now ?? new Date();\n const nowIso = now.toISOString();\n const dayBucket = isoDay(now);\n const qHash = hashQuery(query);\n\n const store = await readStore(workspaceDir, nowIso);\n\n let recorded = 0;\n let skipped = 0;\n for (const match of params.matches) {\n const file = typeof match?.file === \"string\" ? match.file : \"\";\n if (!file || !isRecordableMemoryPath(file)) {\n skipped += 1;\n continue;\n }\n const lines = typeof match?.lines === \"string\" ? match.lines.trim() : \"\";\n const lineNumbers = Array.isArray(match?.lineNumbers) ? match.lineNumbers : [];\n const startLine = Math.max(1, Math.min(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));\n const endLine = Math.max(startLine, Math.max(...lineNumbers.filter((n) => Number.isFinite(n) && n > 0)));\n if (!Number.isFinite(startLine) || !Number.isFinite(endLine)) {\n skipped += 1;\n continue;\n }\n const score = clamp01(Number(match.score));\n const key = buildEntryKey({ path: file, startLine, endLine });\n const existing = store.entries[key];\n const snippet = lines.length > 0 ? lines.slice(0, 360) : file;\n\n const next: DreamingStoreEntry = existing\n ? {\n ...existing,\n snippet,\n recallCount: Math.max(0, Math.floor(existing.recallCount + 1)),\n sourceCount: existing.sourceCount ?? 0,\n groundedCount: existing.groundedCount ?? 0,\n lightHits: existing.lightHits ?? 0,\n remHits: existing.remHits ?? 0,\n phaseHitCount: existing.phaseHitCount ?? 0,\n totalSignalCount: Math.max(0, (existing.totalSignalCount ?? existing.recallCount ?? 0) + 1),\n totalScore: Math.max(0, existing.totalScore + score),\n maxScore: Math.max(existing.maxScore, score),\n queryHashes: mergeQueryHashes(existing.queryHashes ?? [], qHash),\n recallDays: mergeRecentDistinct(existing.recallDays ?? [], dayBucket, 16),\n lastRecalledAt: nowIso,\n }\n : {\n key,\n path: normalizeMemoryPath(file),\n startLine,\n endLine,\n snippet,\n recallCount: 1,\n sourceCount: 0,\n groundedCount: 0,\n lightHits: 0,\n remHits: 0,\n phaseHitCount: 0,\n totalSignalCount: 1,\n totalScore: score,\n maxScore: score,\n queryHashes: [qHash],\n recallDays: [dayBucket],\n firstRecalledAt: nowIso,\n lastRecalledAt: nowIso,\n };\n\n store.entries[key] = next;\n recorded += 1;\n }\n\n if (recorded > 0) {\n store.updatedAt = nowIso;\n await writeStore(workspaceDir, store);\n }\n\n return {\n recorded,\n skipped,\n storePath: SHORT_TERM_RECALL_STORE_RELATIVE,\n };\n}\n\nexport async function withDreamingPromotionLock<T>(\n workspaceDir: string,\n task: () => Promise<T>,\n): Promise<T> {\n const lockPath = path.join(workspaceDir, SHORT_TERM_PROMOTION_LOCK_RELATIVE);\n await ensureDreamDir(workspaceDir);\n\n const startedAt = Date.now();\n const timeoutMs = 10_000;\n const retryDelayMs = 50;\n\n while (true) {\n try {\n const handle = await fs.open(lockPath, 'wx');\n await handle.writeFile(`${process.pid}:${Date.now()}\\n`, 'utf-8').catch(() => undefined);\n try {\n return await task();\n } finally {\n await handle.close().catch(() => undefined);\n await fs.unlink(lockPath).catch(() => undefined);\n }\n } catch (err) {\n const code = (err as NodeJS.ErrnoException | undefined)?.code;\n if (code !== 'EEXIST') {\n throw err;\n }\n if (Date.now() - startedAt >= timeoutMs) {\n throw new Error(`Timed out waiting for dreaming promotion lock at ${lockPath}`);\n }\n await new Promise((r) => setTimeout(r, retryDelayMs));\n }\n }\n}\n\nexport async function loadDreamingStore(params: {\n workspaceDir: string;\n}): Promise<{ store: DreamingStore; storePath: string }> {\n const nowIso = new Date().toISOString();\n const store = await readStore(params.workspaceDir, nowIso);\n return { store, storePath: SHORT_TERM_RECALL_STORE_RELATIVE };\n}\n\nexport async function saveDreamingStore(params: {\n workspaceDir: string;\n store: DreamingStore;\n}): Promise<void> {\n await writeStore(params.workspaceDir, params.store);\n}\n\n// ── Phase-level signal helpers ─────────────────────────────────────────\n\ntype PhaseSignalField = 'sourceCount' | 'groundedCount' | 'lightHits' | 'remHits';\n\n/**\n * Increment a phase-specific signal counter on an existing store entry.\n * Also bumps `phaseHitCount` and `totalSignalCount`.\n * Returns `true` if the entry existed and was updated.\n */\nexport function bumpEntryPhaseSignal(\n entry: DreamingStoreEntry,\n field: PhaseSignalField,\n increment = 1,\n): void {\n entry[field] = (entry[field] ?? 0) + increment;\n entry.phaseHitCount = (entry.phaseHitCount ?? 0) + increment;\n entry.totalSignalCount = (entry.totalSignalCount ?? 0) + increment;\n}\n"],"mappings":"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"}
|
|
@@ -4,7 +4,7 @@ type DreamingStoreEntryScoringView = {
|
|
|
4
4
|
totalScore: number;
|
|
5
5
|
lastRecalledAt?: string;
|
|
6
6
|
queryHashes?: readonly string[];
|
|
7
|
-
|
|
7
|
+
sourceCount: number;
|
|
8
8
|
groundedCount: number;
|
|
9
9
|
lightHits: number;
|
|
10
10
|
remHits: number;
|
|
@@ -20,7 +20,7 @@ export declare function buildEntryKey(parts: {
|
|
|
20
20
|
startLine: number;
|
|
21
21
|
endLine: number;
|
|
22
22
|
}): string;
|
|
23
|
-
/** YYYY-MM-DD in local time
|
|
23
|
+
/** YYYY-MM-DD in local time. */
|
|
24
24
|
export declare function isoDay(d: Date): string;
|
|
25
25
|
export declare function clamp01(value: number): number;
|
|
26
26
|
/** Fills in deep-phase defaults; mirrors `resolveDreamingConfig` deep object when only partial overrides are given. */
|
|
@@ -18,7 +18,7 @@ function buildEntryKey(parts) {
|
|
|
18
18
|
const a = Math.max(1, Math.floor(parts.startLine));
|
|
19
19
|
return `${p}#${a}-${Math.max(a, Math.floor(parts.endLine))}`;
|
|
20
20
|
}
|
|
21
|
-
/** YYYY-MM-DD in local time
|
|
21
|
+
/** YYYY-MM-DD in local time. */
|
|
22
22
|
function isoDay(d) {
|
|
23
23
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
24
24
|
}
|
|
@@ -71,7 +71,7 @@ function computeCandidateScore(entry, nowMs, recencyHalfLifeDays) {
|
|
|
71
71
|
const qCount = entry.queryHashes?.length ?? 0;
|
|
72
72
|
const uniqueQueryTerm = Math.min(1, qCount / 6) * DIVERSITY_WEIGHT;
|
|
73
73
|
const signalDiversity = [
|
|
74
|
-
entry.
|
|
74
|
+
entry.sourceCount,
|
|
75
75
|
entry.groundedCount,
|
|
76
76
|
entry.lightHits,
|
|
77
77
|
entry.remHits
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/utils.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { createHash } from 'node:crypto';\n\nimport type { DreamingDeepConfig } from './config.js';\nimport {\n DEFAULT_DEEP_CRON,\n DEFAULT_MAX_AGE_DAYS,\n DEFAULT_RECENCY_HALF_LIFE_DAYS,\n DIVERSITY_DIMENSION_COUNT,\n DIVERSITY_WEIGHT,\n MS_PER_DAY,\n REINFORCEMENT_WEIGHT,\n} from './constants.js';\n// `DreamingStoreEntry` lives in `./short-term-store.js`, which imports value\n// helpers from THIS file — so importing it back creates a circular cycle.\n// `computeCandidateScore` only reads three numeric/string fields; declaring a\n// narrow structural type keeps the helper decoupled.\ntype DreamingStoreEntryScoringView = {\n recallCount: number;\n totalScore: number;\n lastRecalledAt?: string;\n queryHashes?: readonly string[];\n dailyCount: number;\n groundedCount: number;\n lightHits: number;\n remHits: number;\n};\n\n// ── Path + keying ─────────────────────────────────────────────────────\n\n/** Normalize a workspace-relative memory path: forward slashes, no odd ../ escapes at start. */\nexport function normalizeMemoryPath(rel: string): string {\n if (typeof rel !== 'string' || !rel.trim()) return '';\n const s = rel.trim().replace(/\\\\/g, '/');\n return path.posix\n .normalize(s)\n .replace(/^(\\.\\/)+/, '')\n .replace(/\\/+/g, '/');\n}\n\n/**\n * Stable id for a short-term store entry (path + line range).\n * Format: `{normalizedPath}#{start}-{end}` (1-based, inclusive end).\n */\nexport function buildEntryKey(parts: { path: string; startLine: number; endLine: number }): string {\n const p = normalizeMemoryPath(parts.path);\n const a = Math.max(1, Math.floor(parts.startLine));\n const b = Math.max(a, Math.floor(parts.endLine));\n return `${p}#${a}-${b}`;\n}\n\n/** YYYY-MM-DD in local time (for daily `memory/YYYY-MM-DD.md` names). */\nexport function isoDay(d: Date): string {\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\n// ── Numbers + deep config defaults ────────────────────────────────────\n\nexport function clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.max(0, Math.min(1, value));\n}\n\nfunction toPositiveInt(value: unknown, fallback: number): number {\n const num = typeof value === 'string' ? Number(value) : Number(value);\n if (!Number.isFinite(num)) return fallback;\n const floored = Math.floor(num);\n return floored > 0 ? floored : fallback;\n}\n\nfunction toNonNegInt(value: unknown, fallback: number): number {\n const num = typeof value === 'string' ? Number(value) : Number(value);\n if (!Number.isFinite(num)) return fallback;\n const floored = Math.floor(num);\n return floored >= 0 ? floored : fallback;\n}\n\nfunction clampScore(value: number, fallback: number): number {\n if (!Number.isFinite(value)) return fallback;\n return Math.max(0, Math.min(1, value));\n}\n\nfunction trimmedCron(value: unknown, fallback: string): string {\n return typeof value === 'string' && value.trim() ? value.trim() : fallback;\n}\n\n/** Fills in deep-phase defaults; mirrors `resolveDreamingConfig` deep object when only partial overrides are given. */\nexport function resolveDeepDefaults(overrides?: Partial<DreamingDeepConfig>): DreamingDeepConfig {\n return {\n enabled: overrides?.enabled !== false,\n cron: trimmedCron(overrides?.cron, DEFAULT_DEEP_CRON),\n minScore: clampScore(Number(overrides?.minScore), 0.8),\n minRecallCount: toPositiveInt(overrides?.minRecallCount, 3),\n minUniqueQueries: toPositiveInt(overrides?.minUniqueQueries, 3),\n limit: toNonNegInt(overrides?.limit, 10),\n recencyHalfLifeDays: toPositiveInt(overrides?.recencyHalfLifeDays, DEFAULT_RECENCY_HALF_LIFE_DAYS),\n maxAgeDays: toPositiveInt(overrides?.maxAgeDays, DEFAULT_MAX_AGE_DAYS),\n };\n}\n\n// ── Scoring (deep promotion) ─────────────────────────────────────────\n\nexport function computeCandidateScore(\n entry: DreamingStoreEntryScoringView,\n nowMs: number,\n recencyHalfLifeDays: number,\n): { avgScore: number; score: number; recencyDecay: number } {\n const recall = Math.max(0, entry.recallCount);\n const avgRaw = recall > 0 ? entry.totalScore / recall : 0;\n const avgScore = clamp01(avgRaw);\n\n let recencyDecay = 1;\n if (entry.lastRecalledAt) {\n const last = Date.parse(entry.lastRecalledAt);\n if (Number.isFinite(last)) {\n const days = Math.max(0, (nowMs - last) / MS_PER_DAY);\n const hl = Math.max(0.1, recencyHalfLifeDays);\n recencyDecay = 0.5 ** (days / hl);\n }\n }\n\n const logRecall = Math.log(1 + recall);\n const reinforcement = REINFORCEMENT_WEIGHT * Math.min(logRecall, 3);\n\n const qCount = entry.queryHashes?.length ?? 0;\n const uniqueQueryTerm = Math.min(1, qCount / 6) * DIVERSITY_WEIGHT;\n\n const dims = [entry.dailyCount, entry.groundedCount, entry.lightHits, entry.remHits].filter(\n (n) => n > 0,\n ).length;\n const signalDiversity = ((dims / DIVERSITY_DIMENSION_COUNT) * DIVERSITY_WEIGHT) / 2;\n\n const score = clamp01(avgScore * recencyDecay + reinforcement * 0.25 + uniqueQueryTerm + signalDiversity);\n\n return { avgScore, score, recencyDecay };\n}\n\nexport function compareCandidatesByScore(\n a: { score: number; key: string },\n b: { score: number; key: string },\n): number {\n if (b.score !== a.score) return b.score - a.score;\n return a.key.localeCompare(b.key);\n}\n\n// ── File slices + integrity ─────────────────────────────────────────\n\nexport async function readFileLines(fullPath: string): Promise<string[] | null> {\n try {\n const raw = await fs.readFile(fullPath, 'utf-8');\n return raw.split(/\\r?\\n/);\n } catch (err) {\n if ((err as NodeJS.ErrnoException | undefined)?.code === 'ENOENT') return null;\n throw err;\n }\n}\n\n/** 1-based inclusive line range. Joins with newlines. */\nexport function sliceRange(lines: string[], startLine: number, endLine: number): string {\n const a = Math.max(1, Math.floor(startLine));\n const b = Math.max(a, Math.floor(endLine));\n const out = lines.slice(a - 1, b);\n return out.join('\\n').trimEnd();\n}\n\nexport function isExpiredEntry(\n lastRecalledAt: string | undefined,\n nowMs: number,\n maxAgeDays: number,\n): boolean {\n if (!lastRecalledAt) return true;\n const t = Date.parse(lastRecalledAt);\n if (!Number.isFinite(t)) return true;\n return (nowMs - t) / MS_PER_DAY > maxAgeDays;\n}\n\n/** Reject empty, tiny, or absurdly long snippets; blocks obvious “tool error” text. */\nexport function isContaminatedSnippet(snippet: string): boolean {\n const s = snippet.trim();\n if (s.length < 3) return true;\n if (s.length > 12_000) return true;\n if (!/\\p{L}/u.test(s) && !/\\d/.test(s)) return true;\n return false;\n}\n\nexport function normalizeSnippetForHash(text: string): string {\n return text\n .toLowerCase()\n .replace(/\\s+/g, ' ')\n .replace(/['\"`]/g, '')\n .trim();\n}\n\nexport function snippetHash(text: string): string {\n const n = normalizeSnippetForHash(text);\n return createHash('sha1').update(n, 'utf8').digest('hex').slice(0, 20);\n}\n\nexport function extractPromotionMarkers(markdown: string): { keys: Set<string>; hashes: Set<string> } {\n const keys = new Set<string>();\n const hashes = new Set<string>();\n let m: RegExpExecArray | null;\n const re = /<!--\\s*xopc-memory-promotion\\s+key=\"([^\"]*)\"\\s+hash=\"([^\"]*)\"\\s*-->/g;\n while ((m = re.exec(markdown)) !== null) {\n if (m[1]) keys.add(m[1]);\n if (m[2]) hashes.add(m[2]);\n }\n return { keys, hashes };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","names":[],"sources":["../../../../../src/agent/memory/dreaming/utils.ts"],"sourcesContent":["import fs from 'node:fs/promises';\nimport path from 'node:path';\nimport { createHash } from 'node:crypto';\n\nimport type { DreamingDeepConfig } from './config.js';\nimport {\n DEFAULT_DEEP_CRON,\n DEFAULT_MAX_AGE_DAYS,\n DEFAULT_RECENCY_HALF_LIFE_DAYS,\n DIVERSITY_DIMENSION_COUNT,\n DIVERSITY_WEIGHT,\n MS_PER_DAY,\n REINFORCEMENT_WEIGHT,\n} from './constants.js';\n// `DreamingStoreEntry` lives in `./short-term-store.js`, which imports value\n// helpers from THIS file — so importing it back creates a circular cycle.\n// `computeCandidateScore` only reads three numeric/string fields; declaring a\n// narrow structural type keeps the helper decoupled.\ntype DreamingStoreEntryScoringView = {\n recallCount: number;\n totalScore: number;\n lastRecalledAt?: string;\n queryHashes?: readonly string[];\n sourceCount: number;\n groundedCount: number;\n lightHits: number;\n remHits: number;\n};\n\n// ── Path + keying ─────────────────────────────────────────────────────\n\n/** Normalize a workspace-relative memory path: forward slashes, no odd ../ escapes at start. */\nexport function normalizeMemoryPath(rel: string): string {\n if (typeof rel !== 'string' || !rel.trim()) return '';\n const s = rel.trim().replace(/\\\\/g, '/');\n return path.posix\n .normalize(s)\n .replace(/^(\\.\\/)+/, '')\n .replace(/\\/+/g, '/');\n}\n\n/**\n * Stable id for a short-term store entry (path + line range).\n * Format: `{normalizedPath}#{start}-{end}` (1-based, inclusive end).\n */\nexport function buildEntryKey(parts: { path: string; startLine: number; endLine: number }): string {\n const p = normalizeMemoryPath(parts.path);\n const a = Math.max(1, Math.floor(parts.startLine));\n const b = Math.max(a, Math.floor(parts.endLine));\n return `${p}#${a}-${b}`;\n}\n\n/** YYYY-MM-DD in local time. */\nexport function isoDay(d: Date): string {\n const y = d.getFullYear();\n const m = String(d.getMonth() + 1).padStart(2, '0');\n const day = String(d.getDate()).padStart(2, '0');\n return `${y}-${m}-${day}`;\n}\n\n// ── Numbers + deep config defaults ────────────────────────────────────\n\nexport function clamp01(value: number): number {\n if (!Number.isFinite(value)) return 0;\n return Math.max(0, Math.min(1, value));\n}\n\nfunction toPositiveInt(value: unknown, fallback: number): number {\n const num = typeof value === 'string' ? Number(value) : Number(value);\n if (!Number.isFinite(num)) return fallback;\n const floored = Math.floor(num);\n return floored > 0 ? floored : fallback;\n}\n\nfunction toNonNegInt(value: unknown, fallback: number): number {\n const num = typeof value === 'string' ? Number(value) : Number(value);\n if (!Number.isFinite(num)) return fallback;\n const floored = Math.floor(num);\n return floored >= 0 ? floored : fallback;\n}\n\nfunction clampScore(value: number, fallback: number): number {\n if (!Number.isFinite(value)) return fallback;\n return Math.max(0, Math.min(1, value));\n}\n\nfunction trimmedCron(value: unknown, fallback: string): string {\n return typeof value === 'string' && value.trim() ? value.trim() : fallback;\n}\n\n/** Fills in deep-phase defaults; mirrors `resolveDreamingConfig` deep object when only partial overrides are given. */\nexport function resolveDeepDefaults(overrides?: Partial<DreamingDeepConfig>): DreamingDeepConfig {\n return {\n enabled: overrides?.enabled !== false,\n cron: trimmedCron(overrides?.cron, DEFAULT_DEEP_CRON),\n minScore: clampScore(Number(overrides?.minScore), 0.8),\n minRecallCount: toPositiveInt(overrides?.minRecallCount, 3),\n minUniqueQueries: toPositiveInt(overrides?.minUniqueQueries, 3),\n limit: toNonNegInt(overrides?.limit, 10),\n recencyHalfLifeDays: toPositiveInt(overrides?.recencyHalfLifeDays, DEFAULT_RECENCY_HALF_LIFE_DAYS),\n maxAgeDays: toPositiveInt(overrides?.maxAgeDays, DEFAULT_MAX_AGE_DAYS),\n };\n}\n\n// ── Scoring (deep promotion) ─────────────────────────────────────────\n\nexport function computeCandidateScore(\n entry: DreamingStoreEntryScoringView,\n nowMs: number,\n recencyHalfLifeDays: number,\n): { avgScore: number; score: number; recencyDecay: number } {\n const recall = Math.max(0, entry.recallCount);\n const avgRaw = recall > 0 ? entry.totalScore / recall : 0;\n const avgScore = clamp01(avgRaw);\n\n let recencyDecay = 1;\n if (entry.lastRecalledAt) {\n const last = Date.parse(entry.lastRecalledAt);\n if (Number.isFinite(last)) {\n const days = Math.max(0, (nowMs - last) / MS_PER_DAY);\n const hl = Math.max(0.1, recencyHalfLifeDays);\n recencyDecay = 0.5 ** (days / hl);\n }\n }\n\n const logRecall = Math.log(1 + recall);\n const reinforcement = REINFORCEMENT_WEIGHT * Math.min(logRecall, 3);\n\n const qCount = entry.queryHashes?.length ?? 0;\n const uniqueQueryTerm = Math.min(1, qCount / 6) * DIVERSITY_WEIGHT;\n\n const dims = [entry.sourceCount, entry.groundedCount, entry.lightHits, entry.remHits].filter(\n (n) => n > 0,\n ).length;\n const signalDiversity = ((dims / DIVERSITY_DIMENSION_COUNT) * DIVERSITY_WEIGHT) / 2;\n\n const score = clamp01(avgScore * recencyDecay + reinforcement * 0.25 + uniqueQueryTerm + signalDiversity);\n\n return { avgScore, score, recencyDecay };\n}\n\nexport function compareCandidatesByScore(\n a: { score: number; key: string },\n b: { score: number; key: string },\n): number {\n if (b.score !== a.score) return b.score - a.score;\n return a.key.localeCompare(b.key);\n}\n\n// ── File slices + integrity ─────────────────────────────────────────\n\nexport async function readFileLines(fullPath: string): Promise<string[] | null> {\n try {\n const raw = await fs.readFile(fullPath, 'utf-8');\n return raw.split(/\\r?\\n/);\n } catch (err) {\n if ((err as NodeJS.ErrnoException | undefined)?.code === 'ENOENT') return null;\n throw err;\n }\n}\n\n/** 1-based inclusive line range. Joins with newlines. */\nexport function sliceRange(lines: string[], startLine: number, endLine: number): string {\n const a = Math.max(1, Math.floor(startLine));\n const b = Math.max(a, Math.floor(endLine));\n const out = lines.slice(a - 1, b);\n return out.join('\\n').trimEnd();\n}\n\nexport function isExpiredEntry(\n lastRecalledAt: string | undefined,\n nowMs: number,\n maxAgeDays: number,\n): boolean {\n if (!lastRecalledAt) return true;\n const t = Date.parse(lastRecalledAt);\n if (!Number.isFinite(t)) return true;\n return (nowMs - t) / MS_PER_DAY > maxAgeDays;\n}\n\n/** Reject empty, tiny, or absurdly long snippets; blocks obvious “tool error” text. */\nexport function isContaminatedSnippet(snippet: string): boolean {\n const s = snippet.trim();\n if (s.length < 3) return true;\n if (s.length > 12_000) return true;\n if (!/\\p{L}/u.test(s) && !/\\d/.test(s)) return true;\n return false;\n}\n\nexport function normalizeSnippetForHash(text: string): string {\n return text\n .toLowerCase()\n .replace(/\\s+/g, ' ')\n .replace(/['\"`]/g, '')\n .trim();\n}\n\nexport function snippetHash(text: string): string {\n const n = normalizeSnippetForHash(text);\n return createHash('sha1').update(n, 'utf8').digest('hex').slice(0, 20);\n}\n\nexport function extractPromotionMarkers(markdown: string): { keys: Set<string>; hashes: Set<string> } {\n const keys = new Set<string>();\n const hashes = new Set<string>();\n let m: RegExpExecArray | null;\n const re = /<!--\\s*xopc-memory-promotion\\s+key=\"([^\"]*)\"\\s+hash=\"([^\"]*)\"\\s*-->/g;\n while ((m = re.exec(markdown)) !== null) {\n if (m[1]) keys.add(m[1]);\n if (m[2]) hashes.add(m[2]);\n }\n return { keys, hashes };\n}\n"],"mappings":"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"}
|
|
@@ -12,5 +12,3 @@ export type { MemoryProvider, MemoryProviderInitOptions } from './provider.js';
|
|
|
12
12
|
export { StubMemoryProvider } from './stub-memory-provider.js';
|
|
13
13
|
export type { MemorySnapshot, MemoryStoreConfig } from './types.js';
|
|
14
14
|
export { extractAgentUserPlainText } from './user-message-text.js';
|
|
15
|
-
export { MemoryFlushService } from './memory-flush.js';
|
|
16
|
-
export type { MemoryFlushConfig, MemoryFlushOptions, MemoryFlushResult } from './memory-flush.js';
|
|
@@ -8,6 +8,5 @@ import { BuiltinMemoryProvider } from "./builtin-provider.js";
|
|
|
8
8
|
import { MemoryManager } from "./manager.js";
|
|
9
9
|
import { discoverMemoryPlugins } from "./plugin-discovery.js";
|
|
10
10
|
import { createMemoryManagerFromConfig } from "./create-memory-manager.js";
|
|
11
|
-
import { MemoryFlushService } from "./memory-flush.js";
|
|
12
11
|
import { StubMemoryProvider } from "./stub-memory-provider.js";
|
|
13
|
-
export { BuiltinMemoryProvider, BuiltinMemoryStore, MEMORY_ENTRY_DELIMITER, MemoryContextAssembler,
|
|
12
|
+
export { BuiltinMemoryProvider, BuiltinMemoryStore, MEMORY_ENTRY_DELIMITER, MemoryContextAssembler, MemoryManager, StubMemoryProvider, buildMemoryContextBlock, createMemoryManagerFromConfig, discoverMemoryPlugins, extractAgentUserPlainText, injectPrefetchIntoUserMessage, isCuratedMemoryInPrompt, isMemorySubsystemEnabled, resolveBuiltinMemoryStoreConfig, sanitizeMemoryContextFenceEscapes, scanForThreats, shouldInjectMemoryPrefetchThisTurn, shouldRegisterCuratedMemoryTool };
|
|
@@ -5,10 +5,10 @@ function buildMemorySection(params) {
|
|
|
5
5
|
const citationsMode = params.citationsMode ?? "on";
|
|
6
6
|
const citationInstruction = citationsMode === "off" ? "Citations are disabled: do not mention file paths or line numbers in replies." : citationsMode === "source-only" ? "Citations: mention file path when it helps (e.g., Source: MEMORY.md)." : "Citations: include Source: <path#line> when it helps the user verify memory snippets.";
|
|
7
7
|
const toolLines = [];
|
|
8
|
-
if (params.availableTools.has("memory_search")) toolLines.push("1. Run `memory_search`
|
|
8
|
+
if (params.availableTools.has("memory_search")) toolLines.push("1. Run `memory_search` to search indexed profile and curated memory sources");
|
|
9
9
|
if (params.availableTools.has("session_search")) toolLines.push(`${toolLines.length + 1}. For **other chat sessions** / cross-session history, use \`session_search\` with keywords (or omit \`query\` to list recent sessions)`);
|
|
10
|
-
if (params.availableTools.has("memory_get")) toolLines.push(`${toolLines.length + 1}. Use \`memory_get\`
|
|
11
|
-
if (params.availableTools.has("curated_memory")) toolLines.push(`${toolLines.length + 1}. For
|
|
10
|
+
if (params.availableTools.has("memory_get")) toolLines.push(`${toolLines.length + 1}. Use \`memory_get\` only for paths returned by \`memory_search\``);
|
|
11
|
+
if (params.availableTools.has("curated_memory")) toolLines.push(`${toolLines.length + 1}. For durable facts, preferences, and user-approved notes, use \`curated_memory\``);
|
|
12
12
|
toolLines.push(`${toolLines.length + 1}. If low confidence after search, say you checked`);
|
|
13
13
|
return [
|
|
14
14
|
"## Memory Recall",
|
|
@@ -20,17 +20,18 @@ function buildMemorySection(params) {
|
|
|
20
20
|
"Before answering anything about prior work, decisions, dates, people, preferences, or todos:",
|
|
21
21
|
...toolLines,
|
|
22
22
|
"",
|
|
23
|
-
"### Memory
|
|
23
|
+
"### Memory Sources",
|
|
24
24
|
"",
|
|
25
|
-
"- **
|
|
26
|
-
"- **
|
|
27
|
-
"- **
|
|
25
|
+
"- **Session history:** use `session_search` when available for other chats and prior turns.",
|
|
26
|
+
"- **Profile memory:** injected `MEMORY.md` content may appear in Project Context when present.",
|
|
27
|
+
"- **Indexed memory:** cite only paths and line numbers returned by `memory_search` / `memory_get`.",
|
|
28
|
+
"- **Curated memory:** use `curated_memory` for live read/write of durable facts and preferences.",
|
|
28
29
|
"",
|
|
29
30
|
"### Writing to Memory",
|
|
30
31
|
"",
|
|
31
|
-
"- **Declarative vs procedural:** Save facts and preferences
|
|
32
|
-
"-
|
|
33
|
-
"-
|
|
32
|
+
"- **Declarative vs procedural:** Save facts and preferences with `curated_memory`. Save reusable task playbooks with `skill_manage` as skills.",
|
|
33
|
+
"- When someone says \"remember this\" → use `curated_memory` when available; otherwise explain that durable memory is not writable from this turn.",
|
|
34
|
+
"- Do not invent, cite, or promise memory file paths that were not returned by a memory tool.",
|
|
34
35
|
"- **Text > Brain**"
|
|
35
36
|
].join("\n");
|
|
36
37
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-skills.js","names":[],"sources":["../../../../../src/agent/prompt/sections/memory-skills.ts"],"sourcesContent":["import type { MemoryCitationsMode } from '../types.js';\n\nexport function buildMemorySection(params: {\n availableTools: Set<string>;\n citationsMode?: MemoryCitationsMode;\n hasProfileMemory?: boolean;\n includeMemorySection?: boolean;\n}): string {\n if (params.includeMemorySection === false) {\n return '';\n }\n const hasMemoryTools =\n params.availableTools.has('memory_search') ||\n params.availableTools.has('memory_get') ||\n params.availableTools.has('curated_memory');\n if (!hasMemoryTools && !params.hasProfileMemory) {\n return '';\n }\n\n const citationsMode = params.citationsMode ?? 'on';\n const citationInstruction =\n citationsMode === 'off'\n ? 'Citations are disabled: do not mention file paths or line numbers in replies.'\n : citationsMode === 'source-only'\n ? 'Citations: mention file path when it helps (e.g., Source: MEMORY.md).'\n : 'Citations: include Source: <path#line> when it helps the user verify memory snippets.';\n\n const toolLines: string[] = [];\n if (params.availableTools.has('memory_search')) {\n toolLines.push('1. Run `memory_search`
|
|
1
|
+
{"version":3,"file":"memory-skills.js","names":[],"sources":["../../../../../src/agent/prompt/sections/memory-skills.ts"],"sourcesContent":["import type { MemoryCitationsMode } from '../types.js';\n\nexport function buildMemorySection(params: {\n availableTools: Set<string>;\n citationsMode?: MemoryCitationsMode;\n hasProfileMemory?: boolean;\n includeMemorySection?: boolean;\n}): string {\n if (params.includeMemorySection === false) {\n return '';\n }\n const hasMemoryTools =\n params.availableTools.has('memory_search') ||\n params.availableTools.has('memory_get') ||\n params.availableTools.has('curated_memory');\n if (!hasMemoryTools && !params.hasProfileMemory) {\n return '';\n }\n\n const citationsMode = params.citationsMode ?? 'on';\n const citationInstruction =\n citationsMode === 'off'\n ? 'Citations are disabled: do not mention file paths or line numbers in replies.'\n : citationsMode === 'source-only'\n ? 'Citations: mention file path when it helps (e.g., Source: MEMORY.md).'\n : 'Citations: include Source: <path#line> when it helps the user verify memory snippets.';\n\n const toolLines: string[] = [];\n if (params.availableTools.has('memory_search')) {\n toolLines.push('1. Run `memory_search` to search indexed profile and curated memory sources');\n }\n if (params.availableTools.has('session_search')) {\n toolLines.push(\n `${toolLines.length + 1}. For **other chat sessions** / cross-session history, use \\`session_search\\` with keywords (or omit \\`query\\` to list recent sessions)`,\n );\n }\n if (params.availableTools.has('memory_get')) {\n toolLines.push(`${toolLines.length + 1}. Use \\`memory_get\\` only for paths returned by \\`memory_search\\``);\n }\n if (params.availableTools.has('curated_memory')) {\n toolLines.push(\n `${toolLines.length + 1}. For durable facts, preferences, and user-approved notes, use \\`curated_memory\\``,\n );\n }\n toolLines.push(`${toolLines.length + 1}. If low confidence after search, say you checked`);\n\n return [\n '## Memory Recall',\n '',\n citationInstruction,\n '',\n 'Startup profile files (SOUL, USER, MEMORY, etc.) are already in Project Context above when injected. Do not re-read them unless the user asks or you need lines beyond what was injected.',\n '',\n 'Before answering anything about prior work, decisions, dates, people, preferences, or todos:',\n ...toolLines,\n '',\n '### Memory Sources',\n '',\n '- **Session history:** use `session_search` when available for other chats and prior turns.',\n '- **Profile memory:** injected `MEMORY.md` content may appear in Project Context when present.',\n '- **Indexed memory:** cite only paths and line numbers returned by `memory_search` / `memory_get`.',\n '- **Curated memory:** use `curated_memory` for live read/write of durable facts and preferences.',\n '',\n '### Writing to Memory',\n '',\n '- **Declarative vs procedural:** Save facts and preferences with `curated_memory`. Save reusable task playbooks with `skill_manage` as skills.',\n '- When someone says \"remember this\" → use `curated_memory` when available; otherwise explain that durable memory is not writable from this turn.',\n '- Do not invent, cite, or promise memory file paths that were not returned by a memory tool.',\n '- **Text > Brain**',\n ].join('\\n');\n}\n\nexport function buildExternalMemorySection(text: string | undefined): string {\n const t = text?.trim();\n if (!t) {\n return '';\n }\n return `## External memory provider\\n\\n${t}`;\n}\n\nexport function buildSkillsSection(hasSkillTools: boolean): string {\n if (!hasSkillTools) {\n return '';\n }\n\n return [\n '## Skills (mandatory)',\n 'Before replying: scan <available_skills> <description> entries.',\n '- If exactly one skill clearly applies: use `skill_view(name)` to load its SKILL.md, then follow it.',\n '- If multiple could apply: choose the most specific one, then load/follow it.',\n '- If none clearly apply: do not load any SKILL.md.',\n 'Constraints: never load more than one skill up front; only load after selecting.',\n '- When a skill drives external API writes, assume rate limits: prefer fewer larger writes, avoid tight one-item loops, serialize bursts when possible, and respect 429/Retry-After.',\n '',\n '**Division of labor with memory:** Skills = **procedural** workflows; memory / `curated_memory` = **declarative** facts and preferences.',\n ].join('\\n');\n}\n"],"mappings":";AAEA,SAAgB,mBAAmB,QAKxB;AACT,KAAI,OAAO,yBAAyB,MAClC,QAAO;AAMT,KAAI,EAHF,OAAO,eAAe,IAAI,gBAAgB,IAC1C,OAAO,eAAe,IAAI,aAAa,IACvC,OAAO,eAAe,IAAI,iBAAiB,KACtB,CAAC,OAAO,iBAC7B,QAAO;CAGT,MAAM,gBAAgB,OAAO,iBAAiB;CAC9C,MAAM,sBACJ,kBAAkB,QACd,kFACA,kBAAkB,gBAChB,0EACA;CAER,MAAM,YAAsB,EAAE;AAC9B,KAAI,OAAO,eAAe,IAAI,gBAAgB,CAC5C,WAAU,KAAK,8EAA8E;AAE/F,KAAI,OAAO,eAAe,IAAI,iBAAiB,CAC7C,WAAU,KACR,GAAG,UAAU,SAAS,EAAE,yIACzB;AAEH,KAAI,OAAO,eAAe,IAAI,aAAa,CACzC,WAAU,KAAK,GAAG,UAAU,SAAS,EAAE,mEAAmE;AAE5G,KAAI,OAAO,eAAe,IAAI,iBAAiB,CAC7C,WAAU,KACR,GAAG,UAAU,SAAS,EAAE,mFACzB;AAEH,WAAU,KAAK,GAAG,UAAU,SAAS,EAAE,mDAAmD;AAE1F,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA,GAAG;EACH;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,KAAK,KAAK;;AAGd,SAAgB,2BAA2B,MAAkC;CAC3E,MAAM,IAAI,MAAM,MAAM;AACtB,KAAI,CAAC,EACH,QAAO;AAET,QAAO,kCAAkC;;AAG3C,SAAgB,mBAAmB,eAAgC;AACjE,KAAI,CAAC,cACH,QAAO;AAGT,QAAO;EACL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC,KAAK,KAAK"}
|
|
@@ -47,8 +47,8 @@ const CORE_TOOL_SUMMARIES = {
|
|
|
47
47
|
browser_use: "Control the configured browser",
|
|
48
48
|
send_message: "Send messages to the current channel",
|
|
49
49
|
send_media: "Send media attachments to the current channel",
|
|
50
|
-
memory_search: "Semantic search over
|
|
51
|
-
memory_get: "Read specific lines from memory
|
|
50
|
+
memory_search: "Semantic search over indexed memory sources",
|
|
51
|
+
memory_get: "Read specific lines from memory sources returned by search",
|
|
52
52
|
session_search: "Search other chat sessions or list recent sessions",
|
|
53
53
|
curated_memory: "Read/write structured notes under agent home memories/",
|
|
54
54
|
session_status: "Show session usage/time/model state",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tooling.js","names":[],"sources":["../../../../../src/agent/prompt/sections/tooling.ts"],"sourcesContent":["const CORE_TOOL_ORDER = [\n 'read_file',\n 'write_file',\n 'edit_file',\n 'grep',\n 'find',\n 'list_dir',\n 'shell',\n 'web_search',\n 'web_fetch',\n 'web_extract',\n 'browser_use',\n 'send_message',\n 'send_media',\n 'memory_search',\n 'memory_get',\n 'session_search',\n 'curated_memory',\n 'session_status',\n 'tool_manual',\n 'delegate_task',\n 'workflow',\n 'automation',\n 'skills_list',\n 'skill_view',\n 'skill_manage',\n 'todo',\n 'clarify',\n 'text_to_speech',\n 'image',\n 'image_generate',\n 'create_share',\n 'execute_code',\n] as const;\n\nconst CORE_TOOL_SUMMARIES: Record<string, string> = {\n read_file: 'Read file contents',\n write_file: 'Create or overwrite files',\n edit_file: 'Make precise edits to files',\n grep: 'Search file contents for patterns',\n find: 'Find files by glob pattern',\n list_dir: 'List directory contents',\n shell: 'Run shell commands (supports background via yieldMs/background)',\n web_search: 'Search the web',\n web_fetch: 'Fetch and extract readable content from a URL',\n web_extract: 'Extract structured content from a URL',\n browser_use: 'Control the configured browser',\n send_message: 'Send messages to the current channel',\n send_media: 'Send media attachments to the current channel',\n memory_search: 'Semantic search over
|
|
1
|
+
{"version":3,"file":"tooling.js","names":[],"sources":["../../../../../src/agent/prompt/sections/tooling.ts"],"sourcesContent":["const CORE_TOOL_ORDER = [\n 'read_file',\n 'write_file',\n 'edit_file',\n 'grep',\n 'find',\n 'list_dir',\n 'shell',\n 'web_search',\n 'web_fetch',\n 'web_extract',\n 'browser_use',\n 'send_message',\n 'send_media',\n 'memory_search',\n 'memory_get',\n 'session_search',\n 'curated_memory',\n 'session_status',\n 'tool_manual',\n 'delegate_task',\n 'workflow',\n 'automation',\n 'skills_list',\n 'skill_view',\n 'skill_manage',\n 'todo',\n 'clarify',\n 'text_to_speech',\n 'image',\n 'image_generate',\n 'create_share',\n 'execute_code',\n] as const;\n\nconst CORE_TOOL_SUMMARIES: Record<string, string> = {\n read_file: 'Read file contents',\n write_file: 'Create or overwrite files',\n edit_file: 'Make precise edits to files',\n grep: 'Search file contents for patterns',\n find: 'Find files by glob pattern',\n list_dir: 'List directory contents',\n shell: 'Run shell commands (supports background via yieldMs/background)',\n web_search: 'Search the web',\n web_fetch: 'Fetch and extract readable content from a URL',\n web_extract: 'Extract structured content from a URL',\n browser_use: 'Control the configured browser',\n send_message: 'Send messages to the current channel',\n send_media: 'Send media attachments to the current channel',\n memory_search: 'Semantic search over indexed memory sources',\n memory_get: 'Read specific lines from memory sources returned by search',\n session_search: 'Search other chat sessions or list recent sessions',\n curated_memory: 'Read/write structured notes under agent home memories/',\n session_status: 'Show session usage/time/model state',\n tool_manual: 'Load built-in usage manuals for complex tools',\n delegate_task: 'Spawn an isolated sub-agent for a delegated task',\n workflow: 'Start a persisted multi-phase workflow run',\n automation: 'Manage product automations',\n skills_list: 'List available skills',\n skill_view: 'Load a skill SKILL.md or sub-documents',\n skill_manage: 'Create or update skills on disk',\n todo: 'Manage in-session todo items',\n clarify: 'Ask the user a blocking clarification question',\n text_to_speech: 'Send a standalone voice message',\n image: 'Analyze an image with the configured vision model',\n image_generate: 'Generate images with the configured image model',\n create_share: 'Create a shareable link for workspace files',\n execute_code: 'Run code in a sandbox with a restricted tool subset',\n};\n\nexport function buildToolingSection(params: {\n toolNames?: string[];\n toolSummaries?: Record<string, string>;\n}): string {\n const rawToolNames = (params.toolNames ?? []).map((tool) => tool.trim()).filter(Boolean);\n const canonicalByNormalized = new Map<string, string>();\n for (const name of rawToolNames) {\n const normalized = name.toLowerCase();\n if (!canonicalByNormalized.has(normalized)) {\n canonicalByNormalized.set(normalized, name);\n }\n }\n const resolveToolName = (normalized: string) =>\n canonicalByNormalized.get(normalized) ?? normalized;\n\n const normalizedTools = rawToolNames.map((tool) => tool.toLowerCase());\n const availableTools = new Set(normalizedTools);\n\n const externalToolSummaries = new Map<string, string>();\n for (const [key, value] of Object.entries(params.toolSummaries ?? {})) {\n const normalized = key.trim().toLowerCase();\n if (!normalized || !value?.trim()) {\n continue;\n }\n externalToolSummaries.set(normalized, value.trim());\n }\n\n const extraTools = Array.from(\n new Set(normalizedTools.filter((tool) => !CORE_TOOL_ORDER.includes(tool as (typeof CORE_TOOL_ORDER)[number]))),\n );\n const enabledTools = CORE_TOOL_ORDER.filter((tool) => availableTools.has(tool));\n const toolLines = enabledTools.map((tool) => {\n const summary = CORE_TOOL_SUMMARIES[tool] ?? externalToolSummaries.get(tool);\n const name = resolveToolName(tool);\n return summary ? `- ${name}: ${summary}` : `- ${name}`;\n });\n for (const tool of extraTools.toSorted()) {\n const summary = CORE_TOOL_SUMMARIES[tool] ?? externalToolSummaries.get(tool);\n const name = resolveToolName(tool);\n toolLines.push(summary ? `- ${name}: ${summary}` : `- ${name}`);\n }\n\n const shellToolName = resolveToolName('shell');\n const hasDelegate = availableTools.has('delegate_task');\n const hasWorkflow = availableTools.has('workflow');\n const hasToolManual = availableTools.has('tool_manual');\n\n const orchestrationLines: string[] = [];\n if (hasDelegate) {\n orchestrationLines.push(\n '- Sub-agent delegation → use `delegate_task(goal, context?, toolset?)` for focused parallel work; results return to you automatically.',\n );\n }\n if (hasWorkflow) {\n orchestrationLines.push(\n '- Multi-phase workflows → use `workflow` to start persisted runs with subagents when a script defines phases.',\n );\n }\n\n return [\n '## Tooling',\n 'Tool availability (filtered by policy):',\n 'Tool names are case-sensitive. Call tools exactly as listed.',\n toolLines.length > 0\n ? toolLines.join('\\n')\n : '- No tools are registered for this session.',\n `For long waits, avoid rapid poll loops: use \\`${shellToolName}\\` with enough yieldMs or poll in reasonable intervals.`,\n hasToolManual\n ? 'Some complex tools have built-in manuals. Use `tool_manual(tool)` before non-trivial use.'\n : '',\n hasDelegate\n ? 'If a task is complex or long-running, delegate it. Completion is push-based: the sub-agent result returns when done.'\n : '',\n ...orchestrationLines,\n 'Do not poll delegate/workflow status in a tight loop; check on-demand or wait for completion.',\n ]\n .filter(Boolean)\n .join('\\n');\n}\n\nexport function hasSkillsTools(toolNames: string[]): boolean {\n const normalized = new Set(toolNames.map((t) => t.toLowerCase()));\n return normalized.has('skills_list') || normalized.has('skill_view');\n}\n"],"mappings":"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"}
|