@jack200714/mafw 4.5.1
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/.opencode/mafw/.backup-v61/parametric/base-skill-manifest.yaml +2 -0
- package/.opencode/mafw/STATUS.md +6 -0
- package/.opencode/mafw/ledger.md +20 -0
- package/.opencode/mafw/memory/.harmonic_index.json +28 -0
- package/.opencode/mafw/memory/.review_queue.json +1 -0
- package/.opencode/mafw/memory/tier3/parametric.json +13 -0
- package/.opencode/mafw/memory/tier3/test-goal.json +19 -0
- package/.opencode/mafw/memory-index.json +6 -0
- package/.opencode/mafw/parametric/base-skill-manifest.yaml +2 -0
- package/.opencode/mafw/waves.json +4 -0
- package/.opencode/opencode.json +3 -0
- package/.opencode/plugins/mafw-plugin.ts +8 -0
- package/.opencode/skills/banner-design/SKILL.md +196 -0
- package/.opencode/skills/banner-design/references/banner-sizes-and-styles.md +118 -0
- package/.opencode/skills/brand/SKILL.md +97 -0
- package/.opencode/skills/brand/references/approval-checklist.md +169 -0
- package/.opencode/skills/brand/references/asset-organization.md +157 -0
- package/.opencode/skills/brand/references/brand-guideline-template.md +140 -0
- package/.opencode/skills/brand/references/color-palette-management.md +186 -0
- package/.opencode/skills/brand/references/consistency-checklist.md +94 -0
- package/.opencode/skills/brand/references/logo-usage-rules.md +185 -0
- package/.opencode/skills/brand/references/messaging-framework.md +85 -0
- package/.opencode/skills/brand/references/typography-specifications.md +214 -0
- package/.opencode/skills/brand/references/update.md +118 -0
- package/.opencode/skills/brand/references/visual-identity.md +96 -0
- package/.opencode/skills/brand/references/voice-framework.md +88 -0
- package/.opencode/skills/brand/scripts/extract-colors.cjs +341 -0
- package/.opencode/skills/brand/scripts/inject-brand-context.cjs +349 -0
- package/.opencode/skills/brand/scripts/sync-brand-to-tokens.cjs +248 -0
- package/.opencode/skills/brand/scripts/tests/test_sync_brand_to_tokens.py +52 -0
- package/.opencode/skills/brand/scripts/validate-asset.cjs +387 -0
- package/.opencode/skills/brand/templates/brand-guidelines-starter.md +275 -0
- package/.opencode/skills/design/SKILL.md +313 -0
- package/.opencode/skills/design/data/cip/deliverables.csv +51 -0
- package/.opencode/skills/design/data/cip/industries.csv +21 -0
- package/.opencode/skills/design/data/cip/mockup-contexts.csv +21 -0
- package/.opencode/skills/design/data/cip/styles.csv +21 -0
- package/.opencode/skills/design/data/icon/styles.csv +16 -0
- package/.opencode/skills/design/data/logo/colors.csv +56 -0
- package/.opencode/skills/design/data/logo/industries.csv +56 -0
- package/.opencode/skills/design/data/logo/styles.csv +56 -0
- package/.opencode/skills/design/references/banner-sizes-and-styles.md +118 -0
- package/.opencode/skills/design/references/cip-deliverable-guide.md +95 -0
- package/.opencode/skills/design/references/cip-design.md +121 -0
- package/.opencode/skills/design/references/cip-prompt-engineering.md +84 -0
- package/.opencode/skills/design/references/cip-style-guide.md +68 -0
- package/.opencode/skills/design/references/design-routing.md +207 -0
- package/.opencode/skills/design/references/icon-design.md +122 -0
- package/.opencode/skills/design/references/logo-color-psychology.md +101 -0
- package/.opencode/skills/design/references/logo-design.md +92 -0
- package/.opencode/skills/design/references/logo-prompt-engineering.md +158 -0
- package/.opencode/skills/design/references/logo-style-guide.md +109 -0
- package/.opencode/skills/design/references/slides-copywriting-formulas.md +84 -0
- package/.opencode/skills/design/references/slides-create.md +4 -0
- package/.opencode/skills/design/references/slides-html-template.md +295 -0
- package/.opencode/skills/design/references/slides-layout-patterns.md +137 -0
- package/.opencode/skills/design/references/slides-strategies.md +94 -0
- package/.opencode/skills/design/references/slides.md +42 -0
- package/.opencode/skills/design/references/social-photos-design.md +329 -0
- package/.opencode/skills/design/scripts/cip/core.py +215 -0
- package/.opencode/skills/design/scripts/cip/generate.py +484 -0
- package/.opencode/skills/design/scripts/cip/render-html.py +424 -0
- package/.opencode/skills/design/scripts/cip/search.py +127 -0
- package/.opencode/skills/design/scripts/icon/generate.py +487 -0
- package/.opencode/skills/design/scripts/logo/core.py +175 -0
- package/.opencode/skills/design/scripts/logo/generate.py +362 -0
- package/.opencode/skills/design/scripts/logo/search.py +114 -0
- package/.opencode/skills/design-system/SKILL.md +244 -0
- package/.opencode/skills/design-system/data/slide-backgrounds.csv +11 -0
- package/.opencode/skills/design-system/data/slide-charts.csv +26 -0
- package/.opencode/skills/design-system/data/slide-color-logic.csv +14 -0
- package/.opencode/skills/design-system/data/slide-copy.csv +26 -0
- package/.opencode/skills/design-system/data/slide-layout-logic.csv +16 -0
- package/.opencode/skills/design-system/data/slide-layouts.csv +26 -0
- package/.opencode/skills/design-system/data/slide-strategies.csv +16 -0
- package/.opencode/skills/design-system/data/slide-typography.csv +15 -0
- package/.opencode/skills/design-system/references/component-specs.md +236 -0
- package/.opencode/skills/design-system/references/component-tokens.md +214 -0
- package/.opencode/skills/design-system/references/primitive-tokens.md +203 -0
- package/.opencode/skills/design-system/references/semantic-tokens.md +215 -0
- package/.opencode/skills/design-system/references/states-and-variants.md +241 -0
- package/.opencode/skills/design-system/references/tailwind-integration.md +251 -0
- package/.opencode/skills/design-system/references/token-architecture.md +224 -0
- package/.opencode/skills/design-system/scripts/embed-tokens.cjs +99 -0
- package/.opencode/skills/design-system/scripts/fetch-background.py +317 -0
- package/.opencode/skills/design-system/scripts/generate-slide.py +770 -0
- package/.opencode/skills/design-system/scripts/generate-tokens.cjs +205 -0
- package/.opencode/skills/design-system/scripts/html-token-validator.py +327 -0
- package/.opencode/skills/design-system/scripts/search-slides.py +218 -0
- package/.opencode/skills/design-system/scripts/slide-token-validator.py +35 -0
- package/.opencode/skills/design-system/scripts/slide_search_core.py +453 -0
- package/.opencode/skills/design-system/scripts/tests/test_validate_tokens.py +48 -0
- package/.opencode/skills/design-system/scripts/validate-tokens.cjs +246 -0
- package/.opencode/skills/design-system/templates/design-tokens-starter.json +143 -0
- package/.opencode/skills/frontend-design/LICENSE.txt +21 -0
- package/.opencode/skills/frontend-design/SKILL.md +115 -0
- package/.opencode/skills/mafw-automation-interview/SKILL.md +43 -0
- package/.opencode/skills/mafw-cli-scanner/SKILL.md +37 -0
- package/.opencode/skills/mafw-compression-verifier/SKILL.md +52 -0
- package/.opencode/skills/mafw-desktop-inspector/SKILL.md +170 -0
- package/.opencode/skills/mafw-execute/SKILL.md +62 -0
- package/.opencode/skills/mafw-gateway-restart/SKILL.md +87 -0
- package/.opencode/skills/mafw-github-scanner/SKILL.md +38 -0
- package/.opencode/skills/mafw-goal/SKILL.md +76 -0
- package/.opencode/skills/mafw-interview/SKILL.md +68 -0
- package/.opencode/skills/mafw-memory-extractor/SKILL.md +65 -0
- package/.opencode/skills/mafw-plan/SKILL.md +58 -0
- package/.opencode/skills/mafw-review/SKILL.md +85 -0
- package/.opencode/skills/runtime-plugin-authoring/SKILL.md +791 -0
- package/.opencode/skills/slides/SKILL.md +40 -0
- package/.opencode/skills/slides/references/copywriting-formulas.md +84 -0
- package/.opencode/skills/slides/references/create.md +4 -0
- package/.opencode/skills/slides/references/html-template.md +295 -0
- package/.opencode/skills/slides/references/layout-patterns.md +137 -0
- package/.opencode/skills/slides/references/slide-strategies.md +94 -0
- package/.opencode/skills/ui-styling/LICENSE.txt +202 -0
- package/.opencode/skills/ui-styling/SKILL.md +324 -0
- package/.opencode/skills/ui-styling/references/canvas-design-system.md +320 -0
- package/.opencode/skills/ui-styling/references/shadcn-accessibility.md +471 -0
- package/.opencode/skills/ui-styling/references/shadcn-components.md +424 -0
- package/.opencode/skills/ui-styling/references/shadcn-theming.md +373 -0
- package/.opencode/skills/ui-styling/references/tailwind-customization.md +483 -0
- package/.opencode/skills/ui-styling/references/tailwind-responsive.md +382 -0
- package/.opencode/skills/ui-styling/references/tailwind-utilities.md +455 -0
- package/.opencode/skills/ui-styling/scripts/requirements.txt +17 -0
- package/.opencode/skills/ui-styling/scripts/shadcn_add.py +308 -0
- package/.opencode/skills/ui-styling/scripts/tailwind_config_gen.py +473 -0
- package/.opencode/skills/ui-styling/scripts/tests/coverage-ui.json +1 -0
- package/.opencode/skills/ui-styling/scripts/tests/requirements.txt +3 -0
- package/.opencode/skills/ui-styling/scripts/tests/test_shadcn_add.py +266 -0
- package/.opencode/skills/ui-styling/scripts/tests/test_tailwind_config_gen.py +394 -0
- package/.opencode/skills/ui-ux-pro-max/SKILL.md +388 -0
- package/.opencode/skills/ui-ux-pro-max/data/app-interface.csv +31 -0
- package/.opencode/skills/ui-ux-pro-max/data/charts.csv +26 -0
- package/.opencode/skills/ui-ux-pro-max/data/colors.csv +193 -0
- package/.opencode/skills/ui-ux-pro-max/data/google-fonts.csv +1924 -0
- package/.opencode/skills/ui-ux-pro-max/data/icons.csv +106 -0
- package/.opencode/skills/ui-ux-pro-max/data/landing.csv +35 -0
- package/.opencode/skills/ui-ux-pro-max/data/motion.csv +17 -0
- package/.opencode/skills/ui-ux-pro-max/data/products.csv +193 -0
- package/.opencode/skills/ui-ux-pro-max/data/react-performance.csv +45 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/angular.csv +51 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/astro.csv +54 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/avalonia.csv +57 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/flutter.csv +53 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/html-tailwind.csv +56 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/javafx.csv +76 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/jetpack-compose.csv +53 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/laravel.csv +51 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/nextjs.csv +53 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxt-ui.csv +71 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/nuxtjs.csv +59 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/react-native.csv +52 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/react.csv +54 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/shadcn.csv +61 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/svelte.csv +54 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/swiftui.csv +51 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/threejs.csv +54 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/uno.csv +60 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/uwp.csv +56 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/vue.csv +50 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/winui.csv +60 -0
- package/.opencode/skills/ui-ux-pro-max/data/stacks/wpf.csv +57 -0
- package/.opencode/skills/ui-ux-pro-max/data/styles.csv +85 -0
- package/.opencode/skills/ui-ux-pro-max/data/typography.csv +75 -0
- package/.opencode/skills/ui-ux-pro-max/data/ui-reasoning.csv +162 -0
- package/.opencode/skills/ui-ux-pro-max/data/ux-guidelines.csv +100 -0
- package/.opencode/skills/ui-ux-pro-max/scripts/core.py +464 -0
- package/.opencode/skills/ui-ux-pro-max/scripts/design_system.py +1479 -0
- package/.opencode/skills/ui-ux-pro-max/scripts/search.py +162 -0
- package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_core.py +134 -0
- package/.opencode/skills/ui-ux-pro-max/scripts/tests/test_design_system_mode.py +159 -0
- package/.opencode/skills/ui-ux-pro-max/scripts/validate_data.py +114 -0
- package/LICENSE +21 -0
- package/README.md +199 -0
- package/bin/mafw.js +436 -0
- package/dist/hooks/bash-python-guide.d.ts +4 -0
- package/dist/hooks/bash-python-guide.d.ts.map +1 -0
- package/dist/hooks/bash-python-guide.js +67 -0
- package/dist/hooks/bash-python-guide.js.map +1 -0
- package/dist/hooks/handoff.d.ts +9 -0
- package/dist/hooks/handoff.d.ts.map +1 -0
- package/dist/hooks/handoff.js +8 -0
- package/dist/hooks/handoff.js.map +1 -0
- package/dist/hooks/hook-manager.d.ts +32 -0
- package/dist/hooks/hook-manager.d.ts.map +1 -0
- package/dist/hooks/hook-manager.js +91 -0
- package/dist/hooks/hook-manager.js.map +1 -0
- package/dist/hooks/llm-after.d.ts +8 -0
- package/dist/hooks/llm-after.d.ts.map +1 -0
- package/dist/hooks/llm-after.js +8 -0
- package/dist/hooks/llm-after.js.map +1 -0
- package/dist/hooks/media-ingest.d.ts +43 -0
- package/dist/hooks/media-ingest.d.ts.map +1 -0
- package/dist/hooks/media-ingest.js +269 -0
- package/dist/hooks/media-ingest.js.map +1 -0
- package/dist/hooks/memory-guide.d.ts +3 -0
- package/dist/hooks/memory-guide.d.ts.map +1 -0
- package/dist/hooks/memory-guide.js +47 -0
- package/dist/hooks/memory-guide.js.map +1 -0
- package/dist/hooks/observation-capture.d.ts +17 -0
- package/dist/hooks/observation-capture.d.ts.map +1 -0
- package/dist/hooks/observation-capture.js +20 -0
- package/dist/hooks/observation-capture.js.map +1 -0
- package/dist/hooks/session-compacting.d.ts +6 -0
- package/dist/hooks/session-compacting.d.ts.map +1 -0
- package/dist/hooks/session-compacting.js +6 -0
- package/dist/hooks/session-compacting.js.map +1 -0
- package/dist/hooks/session-ending.d.ts +20 -0
- package/dist/hooks/session-ending.d.ts.map +1 -0
- package/dist/hooks/session-ending.js +113 -0
- package/dist/hooks/session-ending.js.map +1 -0
- package/dist/hooks/session-recall.d.ts +2 -0
- package/dist/hooks/session-recall.d.ts.map +1 -0
- package/dist/hooks/session-recall.js +103 -0
- package/dist/hooks/session-recall.js.map +1 -0
- package/dist/hooks/session-start.d.ts +9 -0
- package/dist/hooks/session-start.d.ts.map +1 -0
- package/dist/hooks/session-start.js +42 -0
- package/dist/hooks/session-start.js.map +1 -0
- package/dist/hooks/tool-before.d.ts +10 -0
- package/dist/hooks/tool-before.d.ts.map +1 -0
- package/dist/hooks/tool-before.js +6 -0
- package/dist/hooks/tool-before.js.map +1 -0
- package/dist/hooks/tool-executed.d.ts +19 -0
- package/dist/hooks/tool-executed.d.ts.map +1 -0
- package/dist/hooks/tool-executed.js +12 -0
- package/dist/hooks/tool-executed.js.map +1 -0
- package/dist/hooks/user-profile.d.ts +2 -0
- package/dist/hooks/user-profile.d.ts.map +1 -0
- package/dist/hooks/user-profile.js +30 -0
- package/dist/hooks/user-profile.js.map +1 -0
- package/dist/hooks/user-prompt.d.ts +9 -0
- package/dist/hooks/user-prompt.d.ts.map +1 -0
- package/dist/hooks/user-prompt.js +8 -0
- package/dist/hooks/user-prompt.js.map +1 -0
- package/dist/hooks/voice-guide.d.ts +8 -0
- package/dist/hooks/voice-guide.d.ts.map +1 -0
- package/dist/hooks/voice-guide.js +101 -0
- package/dist/hooks/voice-guide.js.map +1 -0
- package/dist/plugin.d.ts +55 -0
- package/dist/plugin.d.ts.map +1 -0
- package/dist/plugin.js +342 -0
- package/dist/plugin.js.map +1 -0
- package/dist/tools/add-memory.d.ts +17 -0
- package/dist/tools/add-memory.d.ts.map +1 -0
- package/dist/tools/add-memory.js +60 -0
- package/dist/tools/add-memory.js.map +1 -0
- package/dist/tools/media-ask.d.ts +14 -0
- package/dist/tools/media-ask.d.ts.map +1 -0
- package/dist/tools/media-ask.js +224 -0
- package/dist/tools/media-ask.js.map +1 -0
- package/dist/tools/media-speak.d.ts +16 -0
- package/dist/tools/media-speak.d.ts.map +1 -0
- package/dist/tools/media-speak.js +78 -0
- package/dist/tools/media-speak.js.map +1 -0
- package/dist/tools/media-upload.d.ts +14 -0
- package/dist/tools/media-upload.d.ts.map +1 -0
- package/dist/tools/media-upload.js +155 -0
- package/dist/tools/media-upload.js.map +1 -0
- package/dist/tools/python-exec.d.ts +13 -0
- package/dist/tools/python-exec.d.ts.map +1 -0
- package/dist/tools/python-exec.js +99 -0
- package/dist/tools/python-exec.js.map +1 -0
- package/dist/tools/python-restart.d.ts +11 -0
- package/dist/tools/python-restart.d.ts.map +1 -0
- package/dist/tools/python-restart.js +33 -0
- package/dist/tools/python-restart.js.map +1 -0
- package/dist/utils/circuit-breaker.d.ts +23 -0
- package/dist/utils/circuit-breaker.d.ts.map +1 -0
- package/dist/utils/circuit-breaker.js +75 -0
- package/dist/utils/circuit-breaker.js.map +1 -0
- package/dist/utils/config-loader.d.ts +20 -0
- package/dist/utils/config-loader.d.ts.map +1 -0
- package/dist/utils/config-loader.js +194 -0
- package/dist/utils/config-loader.js.map +1 -0
- package/dist/utils/fallback.d.ts +7 -0
- package/dist/utils/fallback.d.ts.map +1 -0
- package/dist/utils/fallback.js +20 -0
- package/dist/utils/fallback.js.map +1 -0
- package/dist/utils/git.d.ts +24 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +71 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/github.d.ts +37 -0
- package/dist/utils/github.d.ts.map +1 -0
- package/dist/utils/github.js +53 -0
- package/dist/utils/github.js.map +1 -0
- package/dist/utils/global-path.d.ts +2 -0
- package/dist/utils/global-path.d.ts.map +1 -0
- package/dist/utils/global-path.js +32 -0
- package/dist/utils/global-path.js.map +1 -0
- package/dist/utils/logger.d.ts +6 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +69 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/obs-capture.d.ts +7 -0
- package/dist/utils/obs-capture.d.ts.map +1 -0
- package/dist/utils/obs-capture.js +49 -0
- package/dist/utils/obs-capture.js.map +1 -0
- package/dist/utils/retry.d.ts +11 -0
- package/dist/utils/retry.d.ts.map +1 -0
- package/dist/utils/retry.js +45 -0
- package/dist/utils/retry.js.map +1 -0
- package/dist/utils/self-wiring.d.ts +13 -0
- package/dist/utils/self-wiring.d.ts.map +1 -0
- package/dist/utils/self-wiring.js +86 -0
- package/dist/utils/self-wiring.js.map +1 -0
- package/dist/utils/state.d.ts +106 -0
- package/dist/utils/state.d.ts.map +1 -0
- package/dist/utils/state.js +166 -0
- package/dist/utils/state.js.map +1 -0
- package/dist/utils/status.d.ts +47 -0
- package/dist/utils/status.d.ts.map +1 -0
- package/dist/utils/status.js +147 -0
- package/dist/utils/status.js.map +1 -0
- package/dist/utils/ttl-map.d.ts +17 -0
- package/dist/utils/ttl-map.d.ts.map +1 -0
- package/dist/utils/ttl-map.js +67 -0
- package/dist/utils/ttl-map.js.map +1 -0
- package/gateway/dist/automation-engine.js +729 -0
- package/gateway/dist/chat/chat-sessions.js +95 -0
- package/gateway/dist/chat/graph-runner.js +76 -0
- package/gateway/dist/chat/index.js +7 -0
- package/gateway/dist/chat/intent-classifier.js +23 -0
- package/gateway/dist/config.js +416 -0
- package/gateway/dist/core/auth.js +96 -0
- package/gateway/dist/core/budget-guard.js +52 -0
- package/gateway/dist/core/compression/bm25-index.js +176 -0
- package/gateway/dist/core/compression/compression-pipeline.js +157 -0
- package/gateway/dist/core/compression/compression-strategy-selector.js +32 -0
- package/gateway/dist/core/compression/compression-verifier.js +79 -0
- package/gateway/dist/core/compression/diff-compressor.js +69 -0
- package/gateway/dist/core/compression/hybrid-compressor.js +104 -0
- package/gateway/dist/core/compression/lesson-compactor.js +114 -0
- package/gateway/dist/core/compression/memory-index.js +190 -0
- package/gateway/dist/core/compression/observation-classifier.js +41 -0
- package/gateway/dist/core/compression/observation-deduplicator.js +43 -0
- package/gateway/dist/core/compression/rrf-fusion.js +63 -0
- package/gateway/dist/core/compression/session-pruner.js +137 -0
- package/gateway/dist/core/compression/token-budget-allocator.js +141 -0
- package/gateway/dist/core/compression/vector-index.js +117 -0
- package/gateway/dist/core/compression/zero-token-compressor.js +80 -0
- package/gateway/dist/core/cost/cognitive-router.js +41 -0
- package/gateway/dist/core/cost/cost-estimator.js +63 -0
- package/gateway/dist/core/cost/types.js +19 -0
- package/gateway/dist/core/engine/degradation.js +159 -0
- package/gateway/dist/core/engine/goal-worktree-manager.js +131 -0
- package/gateway/dist/core/engine/lesson-manager.js +222 -0
- package/gateway/dist/core/engine/optimistic-sync.js +100 -0
- package/gateway/dist/core/engine/phase-orchestrator.js +153 -0
- package/gateway/dist/core/engine/report-generator.js +131 -0
- package/gateway/dist/core/engine/state-lock.js +91 -0
- package/gateway/dist/core/engine/task-branch-manager.js +102 -0
- package/gateway/dist/core/engine/wave-dependency.js +67 -0
- package/gateway/dist/core/engine/wave-executor.js +112 -0
- package/gateway/dist/core/goal-budget.js +63 -0
- package/gateway/dist/core/graph/entity-extractor.js +53 -0
- package/gateway/dist/core/graph/graph-searcher.js +120 -0
- package/gateway/dist/core/graph/knowledge-graph-manager.js +138 -0
- package/gateway/dist/core/graph/relation-inferencer.js +67 -0
- package/gateway/dist/core/graph/types.js +2 -0
- package/gateway/dist/core/hooks/handoff.js +7 -0
- package/gateway/dist/core/hooks/hook-manager.js +91 -0
- package/gateway/dist/core/hooks/llm-after.js +7 -0
- package/gateway/dist/core/hooks/observation-capture.js +19 -0
- package/gateway/dist/core/hooks/session-compacting.js +5 -0
- package/gateway/dist/core/hooks/session-ending.js +123 -0
- package/gateway/dist/core/hooks/session-start.js +51 -0
- package/gateway/dist/core/hooks/tool-before.js +5 -0
- package/gateway/dist/core/hooks/tool-executed.js +11 -0
- package/gateway/dist/core/hooks/user-prompt.js +7 -0
- package/gateway/dist/core/index.js +119 -0
- package/gateway/dist/core/langchain/agent-adapter.js +39 -0
- package/gateway/dist/core/langchain/index.js +12 -0
- package/gateway/dist/core/langchain/memory.js +35 -0
- package/gateway/dist/core/langchain/node-runner.js +115 -0
- package/gateway/dist/core/langchain/retriever.js +28 -0
- package/gateway/dist/core/langgraph/checkpointer.js +200 -0
- package/gateway/dist/core/langgraph/graph.js +57 -0
- package/gateway/dist/core/langgraph/index.js +25 -0
- package/gateway/dist/core/langgraph/loop-state.js +71 -0
- package/gateway/dist/core/langgraph/nodes/archive.node.js +27 -0
- package/gateway/dist/core/langgraph/nodes/execute.node.js +5 -0
- package/gateway/dist/core/langgraph/nodes/plan.node.js +101 -0
- package/gateway/dist/core/langgraph/nodes/review.node.js +89 -0
- package/gateway/dist/core/langgraph/nodes/sync.node.js +70 -0
- package/gateway/dist/core/langgraph/review-parser.js +35 -0
- package/gateway/dist/core/langgraph/signature-detector.js +23 -0
- package/gateway/dist/core/manager/goal-snapshot.js +73 -0
- package/gateway/dist/core/manager/milestone-push.js +81 -0
- package/gateway/dist/core/manager/question-ledger.js +121 -0
- package/gateway/dist/core/manager/system-rule-templates.js +76 -0
- package/gateway/dist/core/mcp/tools.js +562 -0
- package/gateway/dist/core/mcp/validator.js +165 -0
- package/gateway/dist/core/mcp-server.js +86 -0
- package/gateway/dist/core/memory/abstraction-distiller.js +44 -0
- package/gateway/dist/core/memory/cognitive-graph.js +103 -0
- package/gateway/dist/core/memory/energy-system.js +69 -0
- package/gateway/dist/core/memory/extractor.js +164 -0
- package/gateway/dist/core/memory/harmonic-index.js +530 -0
- package/gateway/dist/core/memory/harmonic-types.js +6 -0
- package/gateway/dist/core/memory/injector.js +111 -0
- package/gateway/dist/core/memory/l5-store.js +141 -0
- package/gateway/dist/core/memory/matcher.js +66 -0
- package/gateway/dist/core/memory/merger.js +95 -0
- package/gateway/dist/core/memory/migrate-v6.1.js +133 -0
- package/gateway/dist/core/memory/minhash-merger.js +195 -0
- package/gateway/dist/core/memory/observation-service.js +87 -0
- package/gateway/dist/core/memory/privacy-filter.js +163 -0
- package/gateway/dist/core/memory/reranker.js +208 -0
- package/gateway/dist/core/memory/review-scheduler.js +100 -0
- package/gateway/dist/core/memory/salience-perceptor.js +24 -0
- package/gateway/dist/core/memory/store.js +284 -0
- package/gateway/dist/core/memory/t1-store.js +183 -0
- package/gateway/dist/core/memory/t1-to-t2-compressor.js +119 -0
- package/gateway/dist/core/memory/validator.js +66 -0
- package/gateway/dist/core/plugin.js +902 -0
- package/gateway/dist/core/skills/mafw-execute/entry.js +226 -0
- package/gateway/dist/core/skills/mafw-goal/entry.js +203 -0
- package/gateway/dist/core/skills/mafw-plan/entry.js +184 -0
- package/gateway/dist/core/skills/mafw-review/entry.js +200 -0
- package/gateway/dist/core/storage/file-storage.js +120 -0
- package/gateway/dist/core/storage/sqlite-storage.js +205 -0
- package/gateway/dist/core/storage/types.js +2 -0
- package/gateway/dist/core/tools/archive-worktree.js +208 -0
- package/gateway/dist/core/tools/remote-cli.js +106 -0
- package/gateway/dist/core/tools/run-ask-user.js +79 -0
- package/gateway/dist/core/tools/run-execute-wave.js +32 -0
- package/gateway/dist/core/tools/run-plan.js +42 -0
- package/gateway/dist/core/tools/run-record-feedback.js +55 -0
- package/gateway/dist/core/tools/run-review.js +46 -0
- package/gateway/dist/core/tools/write-lesson.js +97 -0
- package/gateway/dist/core/utils/circuit-breaker.js +74 -0
- package/gateway/dist/core/utils/config-loader.js +203 -0
- package/gateway/dist/core/utils/fallback.js +19 -0
- package/gateway/dist/core/utils/git.js +70 -0
- package/gateway/dist/core/utils/github.js +52 -0
- package/gateway/dist/core/utils/global-path.js +41 -0
- package/gateway/dist/core/utils/logger.js +71 -0
- package/gateway/dist/core/utils/retry.js +44 -0
- package/gateway/dist/core/utils/state.js +175 -0
- package/gateway/dist/core/utils/status.js +156 -0
- package/gateway/dist/cost/service.js +17 -0
- package/gateway/dist/dashboard/api.js +1156 -0
- package/gateway/dist/dashboard/public/assets/index-DOXm39dm.css +1 -0
- package/gateway/dist/dashboard/public/index.html +13 -0
- package/gateway/dist/dashboard/server.js +140 -0
- package/gateway/dist/dashboard/types.js +2 -0
- package/gateway/dist/desktop-client.js +124 -0
- package/gateway/dist/eval-endpoint.js +219 -0
- package/gateway/dist/event-bus.js +7 -0
- package/gateway/dist/feedback/anchor-promotion.js +99 -0
- package/gateway/dist/feedback/deferred-memory.js +13 -0
- package/gateway/dist/feedback/miss-attribution.js +20 -0
- package/gateway/dist/graph/anchor-graph-store.js +113 -0
- package/gateway/dist/graph/anchor-graph.js +48 -0
- package/gateway/dist/health.js +53 -0
- package/gateway/dist/heartbeat.js +109 -0
- package/gateway/dist/index.js +6377 -0
- package/gateway/dist/interceptors/memory-injector.js +79 -0
- package/gateway/dist/ledger.js +121 -0
- package/gateway/dist/loop-monitor.js +74 -0
- package/gateway/dist/mcp/handlers/add-memory.js +113 -0
- package/gateway/dist/mcp/handlers/ask-user.js +63 -0
- package/gateway/dist/mcp/handlers/commit-heuristic.js +19 -0
- package/gateway/dist/mcp/handlers/create-goal.js +83 -0
- package/gateway/dist/mcp/handlers/desktop-click.js +18 -0
- package/gateway/dist/mcp/handlers/desktop-get-state.js +15 -0
- package/gateway/dist/mcp/handlers/desktop-navigate.js +18 -0
- package/gateway/dist/mcp/handlers/desktop-screenshot.js +16 -0
- package/gateway/dist/mcp/handlers/desktop-scroll.js +19 -0
- package/gateway/dist/mcp/handlers/desktop-type.js +19 -0
- package/gateway/dist/mcp/handlers/draft-automation-rule.js +45 -0
- package/gateway/dist/mcp/handlers/get-automation-history.js +15 -0
- package/gateway/dist/mcp/handlers/get-automation-rule.js +26 -0
- package/gateway/dist/mcp/handlers/get-axioms.js +14 -0
- package/gateway/dist/mcp/handlers/get-deltas.js +77 -0
- package/gateway/dist/mcp/handlers/get-memory.js +67 -0
- package/gateway/dist/mcp/handlers/get-model-route.js +16 -0
- package/gateway/dist/mcp/handlers/get-triage-item.js +17 -0
- package/gateway/dist/mcp/handlers/list-automation-rules.js +24 -0
- package/gateway/dist/mcp/handlers/list-triage-items.js +14 -0
- package/gateway/dist/mcp/handlers/load-state.js +54 -0
- package/gateway/dist/mcp/handlers/manager-answer-question.js +67 -0
- package/gateway/dist/mcp/handlers/manager-cancel-goal.js +58 -0
- package/gateway/dist/mcp/handlers/manager-get-evidence.js +57 -0
- package/gateway/dist/mcp/handlers/manager-get-goal-status.js +58 -0
- package/gateway/dist/mcp/handlers/manager-list-goals.js +60 -0
- package/gateway/dist/mcp/handlers/manager-list-pending-questions.js +67 -0
- package/gateway/dist/mcp/handlers/manager-set-goal.js +78 -0
- package/gateway/dist/mcp/handlers/merge-memory.js +184 -0
- package/gateway/dist/mcp/handlers/pin-memory.js +53 -0
- package/gateway/dist/mcp/handlers/propose-triage-decision.js +38 -0
- package/gateway/dist/mcp/handlers/record-feedback.js +59 -0
- package/gateway/dist/mcp/handlers/resolve-merge.js +92 -0
- package/gateway/dist/mcp/handlers/restart-agent.js +22 -0
- package/gateway/dist/mcp/handlers/run-automation.js +28 -0
- package/gateway/dist/mcp/handlers/search-hybrid.js +181 -0
- package/gateway/dist/mcp/handlers/supersede-memory.js +47 -0
- package/gateway/dist/mcp/handlers/update-state.js +59 -0
- package/gateway/dist/mcp/handlers/validate-rule.js +39 -0
- package/gateway/dist/mcp/sse-transport.js +97 -0
- package/gateway/dist/mcp/streamable-endpoint.js +98 -0
- package/gateway/dist/mcp/tool-registry.js +630 -0
- package/gateway/dist/media/media-agent.js +755 -0
- package/gateway/dist/media/media-plugin-loader.js +254 -0
- package/gateway/dist/media/media-runtime-executor.js +227 -0
- package/gateway/dist/media/media-service.js +199 -0
- package/gateway/dist/media/pi-adapter.js +159 -0
- package/gateway/dist/media/plugin-context.js +14 -0
- package/gateway/dist/media/tts-service.js +173 -0
- package/gateway/dist/memory/abstraction-matcher.js +28 -0
- package/gateway/dist/memory/consolidation-service.js +209 -0
- package/gateway/dist/memory/derived-terms.js +50 -0
- package/gateway/dist/memory/embedding-provider.js +330 -0
- package/gateway/dist/memory/embedding-runtime.js +174 -0
- package/gateway/dist/memory/event-log.js +84 -0
- package/gateway/dist/memory/gateway-db.js +465 -0
- package/gateway/dist/memory/harmonic-file-store.js +338 -0
- package/gateway/dist/memory/llamacpp-provider.js +370 -0
- package/gateway/dist/memory/okf-index-renderer.js +61 -0
- package/gateway/dist/memory/okf-migrator.js +71 -0
- package/gateway/dist/memory/okf-parser.js +57 -0
- package/gateway/dist/memory/okf-writer.js +107 -0
- package/gateway/dist/memory/service.js +81 -0
- package/gateway/dist/memory/t1-db.js +139 -0
- package/gateway/dist/memory/unmerge-cleanup.js +82 -0
- package/gateway/dist/memory/vector-store.js +246 -0
- package/gateway/dist/memory/write-queue.js +31 -0
- package/gateway/dist/metrics/collector.js +28 -0
- package/gateway/dist/mobile/device-store.js +123 -0
- package/gateway/dist/mobile/mdns-advertiser.js +55 -0
- package/gateway/dist/mobile/media-proxy.js +292 -0
- package/gateway/dist/mobile/pairing.js +139 -0
- package/gateway/dist/mobile/push-gateway.js +126 -0
- package/gateway/dist/mobile/tts-artifact.js +43 -0
- package/gateway/dist/opencode-adapter.js +241 -0
- package/gateway/dist/orchestration/outcome-recorder.js +218 -0
- package/gateway/dist/orchestration/policy.js +57 -0
- package/gateway/dist/orchestration/registry.js +47 -0
- package/gateway/dist/plugins/hub.js +188 -0
- package/gateway/dist/poll.js +110 -0
- package/gateway/dist/python/kernel-service.js +762 -0
- package/gateway/dist/recall/constraints-migrate.js +108 -0
- package/gateway/dist/recall/data-dir-migrate.js +120 -0
- package/gateway/dist/recall/gateway-db-migrate.js +134 -0
- package/gateway/dist/recall/index-scan.js +437 -0
- package/gateway/dist/recall/inject-format.js +222 -0
- package/gateway/dist/recall/legacy-t1-archive.js +86 -0
- package/gateway/dist/recall/memory-worker.js +128 -0
- package/gateway/dist/recall/pipeline-rules.js +76 -0
- package/gateway/dist/recall/recall-context.js +297 -0
- package/gateway/dist/recall/reflect-cursor.js +50 -0
- package/gateway/dist/recall/reflection.js +318 -0
- package/gateway/dist/recall/session-worker-pool.js +100 -0
- package/gateway/dist/recall/stale-verify.js +72 -0
- package/gateway/dist/recall/step-inject.js +173 -0
- package/gateway/dist/recall/time-anchor.js +143 -0
- package/gateway/dist/recall/ttl-map.js +72 -0
- package/gateway/dist/recall/turn-completion.js +44 -0
- package/gateway/dist/recall/turn-pipeline.js +150 -0
- package/gateway/dist/recovery.js +158 -0
- package/gateway/dist/resources/sdk-session.js +202 -0
- package/gateway/dist/retrieval/guided-retriever.js +131 -0
- package/gateway/dist/retrieval/idf-stats.js +31 -0
- package/gateway/dist/routes/embedding-config.js +100 -0
- package/gateway/dist/routes/goal-sessions.js +45 -0
- package/gateway/dist/routes/manager-rotate.js +55 -0
- package/gateway/dist/routes/media-switch.js +78 -0
- package/gateway/dist/routes/model-config.js +132 -0
- package/gateway/dist/routes/note-board.js +51 -0
- package/gateway/dist/routes/permission.js +43 -0
- package/gateway/dist/routes/pinned-recall.js +36 -0
- package/gateway/dist/routes/plugins.js +77 -0
- package/gateway/dist/routes/restart-agent.js +28 -0
- package/gateway/dist/routes/runtime-switch.js +80 -0
- package/gateway/dist/routes/session-branch.js +63 -0
- package/gateway/dist/routes/session-mutations.js +58 -0
- package/gateway/dist/routes/session-summarize.js +49 -0
- package/gateway/dist/routes/usage-plugins.js +251 -0
- package/gateway/dist/runtime/agent-definition.js +2 -0
- package/gateway/dist/runtime/auth.js +80 -0
- package/gateway/dist/runtime/completion-http.js +97 -0
- package/gateway/dist/runtime/contract.js +37 -0
- package/gateway/dist/runtime/loader.js +240 -0
- package/gateway/dist/runtime/normalize.js +59 -0
- package/gateway/dist/runtime/opencode-runtime.js +336 -0
- package/gateway/dist/runtime/pi/pi-agent-config.js +294 -0
- package/gateway/dist/runtime/pi/pi-approval-bridge.js +44 -0
- package/gateway/dist/runtime/pi/pi-approval-extension.js +62 -0
- package/gateway/dist/runtime/pi/pi-events.js +122 -0
- package/gateway/dist/runtime/pi/pi-messages.js +20 -0
- package/gateway/dist/runtime/pi/pi-provider.js +19 -0
- package/gateway/dist/runtime/pi/pi-session-storage.js +44 -0
- package/gateway/dist/runtime/pi/pi-session.js +328 -0
- package/gateway/dist/runtime/plugins/pi-runtime.js +285 -0
- package/gateway/dist/runtime/serve-for-runtime.js +22 -0
- package/gateway/dist/runtime/serve-sidecar.js +164 -0
- package/gateway/dist/runtime/serve-supervisor.js +80 -0
- package/gateway/dist/self-update.js +309 -0
- package/gateway/dist/skills/manager-agent-config.js +37 -0
- package/gateway/dist/skills/manager-identity.js +11 -0
- package/gateway/dist/skills/memory-curator-agent.js +55 -0
- package/gateway/dist/trajectory/api.js +154 -0
- package/gateway/dist/trajectory/backfill-provider.js +41 -0
- package/gateway/dist/trajectory/collector.js +337 -0
- package/gateway/dist/trajectory/trajectory-store.js +336 -0
- package/gateway/dist/trajectory/types.js +17 -0
- package/gateway/dist/tray/index.js +131 -0
- package/gateway/dist/tray/tray.ps1 +96 -0
- package/gateway/dist/types.js +2 -0
- package/gateway/dist/usage/builtin-plugins/commandcode.js +71 -0
- package/gateway/dist/usage/builtin-plugins/deepseek.js +56 -0
- package/gateway/dist/usage/builtin-plugins/gateway.js +127 -0
- package/gateway/dist/usage/builtin-plugins/kimi-for-coding.js +93 -0
- package/gateway/dist/usage/builtin-plugins/kimi.js +48 -0
- package/gateway/dist/usage/builtin-plugins/opencode-go.js +54 -0
- package/gateway/dist/usage/builtin-plugins/openrouter.js +48 -0
- package/gateway/dist/usage/builtin-plugins/siliconflow-cn.js +49 -0
- package/gateway/dist/usage/builtin-plugins/zhipuai-coding-plan.js +44 -0
- package/gateway/dist/usage/model-prices.js +39 -0
- package/gateway/dist/usage/model-stats.js +21 -0
- package/gateway/dist/usage/plugin-context.js +79 -0
- package/gateway/dist/usage/plugin-loader.js +280 -0
- package/gateway/dist/usage/templates.js +227 -0
- package/gateway/dist/usage/types.js +2 -0
- package/gateway/dist/usage/usage-poller.js +216 -0
- package/gateway/package.json +37 -0
- package/package.json +78 -0
- package/packages/tui/dist/cli.js +4664 -0
|
@@ -0,0 +1,437 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Index scan: a cheap worker-model call reads the full harmonic index
|
|
3
|
+
// (formatted as compact text) and picks relevant memory IDs for a given query.
|
|
4
|
+
// This catches the semantic gap that BM25 misses — preference queries,
|
|
5
|
+
// paraphrase, cross-session aggregation.
|
|
6
|
+
//
|
|
7
|
+
// Transport: direct OpenAI-compatible HTTP chat completion (NOT an opencode
|
|
8
|
+
// session). The scan is a stateless classification call — a session adds
|
|
9
|
+
// create/prompt/delete churn and a per-session prompt-cache key for zero
|
|
10
|
+
// benefit, and one-shot sessions flooded the session registry.
|
|
11
|
+
//
|
|
12
|
+
// Cache-first design (the dominant cost lever is provider prompt-cache hits):
|
|
13
|
+
// - The index text is a cacheable prefix: static header (no entry count),
|
|
14
|
+
// entries sorted oldest-first (append-only), no volatile fields (energy
|
|
15
|
+
// omitted), and an explicit `cache_control: ephemeral` marker on the
|
|
16
|
+
// index block → deterministic DashScope explicit-cache hits (10% input
|
|
17
|
+
// billing, 5-min TTL reset on hit) instead of best-effort implicit cache.
|
|
18
|
+
// - The query is always the final suffix — dynamic content last.
|
|
19
|
+
//
|
|
20
|
+
// The index text is ~25-30k tokens for 690 entries — well within the model's
|
|
21
|
+
// context window. Scan latency with a warm cache is a few seconds; results
|
|
22
|
+
// are consumed via async prefetch snapshots, never on the sync recall path
|
|
23
|
+
// (the plugin client aborts recall requests after 100ms).
|
|
24
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
+
exports.IndexScanService = void 0;
|
|
26
|
+
exports.resolveScanBaseUrl = resolveScanBaseUrl;
|
|
27
|
+
exports.hashText = hashText;
|
|
28
|
+
exports.formatEntryForIndex = formatEntryForIndex;
|
|
29
|
+
exports.formatIndexForScan = formatIndexForScan;
|
|
30
|
+
exports.formatIndexForScanSlim = formatIndexForScanSlim;
|
|
31
|
+
exports.parseScanResponse = parseScanResponse;
|
|
32
|
+
exports.resolveShortIds = resolveShortIds;
|
|
33
|
+
const logger_1 = require("../core/utils/logger");
|
|
34
|
+
const memory_curator_agent_1 = require("../skills/memory-curator-agent");
|
|
35
|
+
const completion_http_1 = require("../runtime/completion-http");
|
|
36
|
+
/**
|
|
37
|
+
* Map a provider id to its OpenAI-compatible chat-completions endpoint.
|
|
38
|
+
* `endpoints` (config `recall.scanEndpoints`) wins over the hardcoded table;
|
|
39
|
+
* unknown providers resolve to undefined — the caller may still inject an
|
|
40
|
+
* endpoint via ScanHttpDeps.resolveEndpoint (e.g. derived from the opencode
|
|
41
|
+
* provider config) or fall back to BM25-only recall.
|
|
42
|
+
*/
|
|
43
|
+
function resolveScanBaseUrl(providerID, endpoints) {
|
|
44
|
+
if (!providerID)
|
|
45
|
+
return undefined;
|
|
46
|
+
const mapped = endpoints?.[providerID];
|
|
47
|
+
if (mapped)
|
|
48
|
+
return mapped;
|
|
49
|
+
const p = providerID.toLowerCase();
|
|
50
|
+
if (p.includes('alibaba') || p.includes('dashscope') || p.includes('qwen')) {
|
|
51
|
+
return 'https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions';
|
|
52
|
+
}
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
/** FNV-1a (32-bit) — cheap byte-stability fingerprint for the index text. */
|
|
56
|
+
function hashText(s) {
|
|
57
|
+
let h = 0x811c9dc5;
|
|
58
|
+
for (let i = 0; i < s.length; i++) {
|
|
59
|
+
h ^= s.charCodeAt(i);
|
|
60
|
+
h = Math.imul(h, 0x01000193);
|
|
61
|
+
}
|
|
62
|
+
return (h >>> 0).toString(16).padStart(8, '0');
|
|
63
|
+
}
|
|
64
|
+
const SCAN_SYSTEM = `You are a memory retrieval system. Given a memory index and a user query, identify the most relevant memory entries.
|
|
65
|
+
|
|
66
|
+
The index lists memories in this format:
|
|
67
|
+
- [id:<short_id>] (<date>) <type> | <summary> | anchors: <keywords>
|
|
68
|
+
|
|
69
|
+
Return ONLY valid JSON (no markdown):
|
|
70
|
+
{"relevant_ids": ["<short_id>", ...], "reasoning": "<one sentence>", "confidence": <0.0-1.0>}
|
|
71
|
+
|
|
72
|
+
Rules:
|
|
73
|
+
- You MUST return 5-8 entry IDs. If the index has entries at all, return at least 5.
|
|
74
|
+
- Return MORE entries rather than fewer — false negatives are worse than false positives
|
|
75
|
+
- Rank them by relevance (most relevant first)
|
|
76
|
+
- Consider semantic relevance, not just keyword matching — look for topic overlap, related concepts, paraphrases
|
|
77
|
+
- For preference queries (what does the user like/dislike), prioritize entries with "preference" type or "pref:" anchors
|
|
78
|
+
- For temporal queries (when/what happened), prioritize entries with matching dates
|
|
79
|
+
- For multi-session queries (what did we discuss about X), look for entries sharing topic anchors
|
|
80
|
+
- If answering the question requires combining information from multiple memories, include ALL relevant entries
|
|
81
|
+
- confidence = how sure you are that the selected entries answer the query (0.0 = guess, 1.0 = certain)
|
|
82
|
+
- If nothing is relevant, return {"relevant_ids": [], "reasoning": "no relevant memories", "confidence": 0.0}
|
|
83
|
+
${memory_curator_agent_1.HARD_BOUNDARIES}`;
|
|
84
|
+
/**
|
|
85
|
+
* Format a single harmonic index entry into a compact index line.
|
|
86
|
+
* Only immutable fields are included (date, type, summary, anchors) —
|
|
87
|
+
* volatile fields like energy would invalidate the provider prompt cache
|
|
88
|
+
* on every decay pass.
|
|
89
|
+
*
|
|
90
|
+
* Truncation opts (Step 4 slimming): absCap/anchorCap shorten the line for
|
|
91
|
+
* scan-prompt size; caps are caller-fixed constants so the rendered text
|
|
92
|
+
* stays byte-stable across refreshes (cache-safe). Truncation slices at
|
|
93
|
+
* UTF-16 code-unit boundaries — fine for CJK BMP text, only cosmetic for
|
|
94
|
+
* the rare astral char.
|
|
95
|
+
*/
|
|
96
|
+
function formatEntryForIndex(entry, opts) {
|
|
97
|
+
// Tail-10 short id: harmonic ids are mem_<millis>_<rand>, so a head prefix
|
|
98
|
+
// collides for same-millisecond writes (batch ingests share one prefix —
|
|
99
|
+
// "unique 12-char prefixes: 1/53" in the LongMemEval harness). The tail
|
|
100
|
+
// (rand + ts suffix) is unique per entry; resolveShortIds matches endsWith.
|
|
101
|
+
const id = (entry.id || '?').slice(-10);
|
|
102
|
+
const date = formatDateOnly(entry.created_at);
|
|
103
|
+
const type = entry.type || 'unknown';
|
|
104
|
+
// Don't truncate abstraction — let the model see full content for better relevance judgment
|
|
105
|
+
let summary = (entry.primary_abstraction || '').replace(/\n/g, ' ');
|
|
106
|
+
if (opts?.absCap && summary.length > opts.absCap)
|
|
107
|
+
summary = summary.slice(0, opts.absCap) + '…';
|
|
108
|
+
const anchors = (entry.cue_anchors || []).slice(0, opts?.anchorCap ?? 5).join(', ');
|
|
109
|
+
return `- [id:${id}] (${date}) ${type} | ${summary} | anchors: ${anchors}`;
|
|
110
|
+
}
|
|
111
|
+
function formatDateOnly(iso) {
|
|
112
|
+
if (!iso)
|
|
113
|
+
return 'unknown';
|
|
114
|
+
try {
|
|
115
|
+
const d = new Date(iso);
|
|
116
|
+
if (Number.isNaN(d.getTime()))
|
|
117
|
+
return 'unknown';
|
|
118
|
+
const yyyy = d.getFullYear();
|
|
119
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
120
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
121
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
122
|
+
}
|
|
123
|
+
catch {
|
|
124
|
+
return 'unknown';
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Format the full harmonic index into a compact text block for scanning.
|
|
129
|
+
* Excludes superseded entries. Sorted by created_at ASCENDING (oldest first)
|
|
130
|
+
* so the text is append-only: after new writes the refreshed text keeps the
|
|
131
|
+
* entire previous prefix byte-identical, preserving provider prompt-cache
|
|
132
|
+
* hits across hourly refreshes. Header is static (no entry count) for the
|
|
133
|
+
* same reason.
|
|
134
|
+
*/
|
|
135
|
+
function formatIndexForScan(index) {
|
|
136
|
+
const entries = index.getIndex().entries
|
|
137
|
+
.filter((e) => !e.superseded_by)
|
|
138
|
+
.sort((a, b) => {
|
|
139
|
+
const ta = a.created_at ? new Date(a.created_at).getTime() : 0;
|
|
140
|
+
const tb = b.created_at ? new Date(b.created_at).getTime() : 0;
|
|
141
|
+
return ta - tb; // oldest first — append-only order
|
|
142
|
+
});
|
|
143
|
+
const lines = entries.map((e) => formatEntryForIndex(e));
|
|
144
|
+
return `# Memory Index\n\n${lines.join('\n')}`;
|
|
145
|
+
}
|
|
146
|
+
/** Slimming variant — same ordering/filtering, truncated lines (Step 4 A/B). */
|
|
147
|
+
function formatIndexForScanSlim(index, opts) {
|
|
148
|
+
const entries = index.getIndex().entries
|
|
149
|
+
.filter((e) => !e.superseded_by)
|
|
150
|
+
.sort((a, b) => {
|
|
151
|
+
const ta = a.created_at ? new Date(a.created_at).getTime() : 0;
|
|
152
|
+
const tb = b.created_at ? new Date(b.created_at).getTime() : 0;
|
|
153
|
+
return ta - tb;
|
|
154
|
+
});
|
|
155
|
+
const lines = entries.map((e) => formatEntryForIndex(e, opts));
|
|
156
|
+
return `# Memory Index\n\n${lines.join('\n')}`;
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* Parse the scan response into structured result.
|
|
160
|
+
* Tolerant of markdown fences and extra text around JSON.
|
|
161
|
+
*/
|
|
162
|
+
function parseScanResponse(text) {
|
|
163
|
+
if (!text?.trim())
|
|
164
|
+
return null;
|
|
165
|
+
try {
|
|
166
|
+
// Strip markdown fences if present
|
|
167
|
+
const cleaned = text.replace(/^```(?:json)?\s*/i, '').replace(/```\s*$/, '').trim();
|
|
168
|
+
const parsed = JSON.parse(cleaned);
|
|
169
|
+
if (!Array.isArray(parsed?.relevant_ids))
|
|
170
|
+
return null;
|
|
171
|
+
return {
|
|
172
|
+
relevantIds: parsed.relevant_ids.filter((id) => typeof id === 'string').slice(0, 10),
|
|
173
|
+
confidence: typeof parsed.confidence === 'number' ? parsed.confidence : 0.5,
|
|
174
|
+
reasoning: typeof parsed.reasoning === 'string' ? parsed.reasoning : undefined,
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
catch {
|
|
178
|
+
return null;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Resolve short IDs from scan response to full harmonic index entry IDs.
|
|
183
|
+
* The index shows tail-10 short ids (see formatEntryForIndex); this maps
|
|
184
|
+
* them back with endsWith. Ambiguous tails resolve to the first match —
|
|
185
|
+
* tail collisions are ~0 for 10 chars over ~1k entries.
|
|
186
|
+
*/
|
|
187
|
+
function resolveShortIds(shortIds, index) {
|
|
188
|
+
const entries = index.getIndex().entries;
|
|
189
|
+
const resolved = [];
|
|
190
|
+
for (const shortId of shortIds) {
|
|
191
|
+
const match = entries.find((e) => e.id.endsWith(shortId));
|
|
192
|
+
if (match)
|
|
193
|
+
resolved.push(match.id);
|
|
194
|
+
}
|
|
195
|
+
return resolved;
|
|
196
|
+
}
|
|
197
|
+
/**
|
|
198
|
+
* IndexScanService runs scan queries over direct HTTP and caches the index
|
|
199
|
+
* text. Created once by the gateway, reused across all scan calls.
|
|
200
|
+
*/
|
|
201
|
+
class IndexScanService {
|
|
202
|
+
index;
|
|
203
|
+
workerModel;
|
|
204
|
+
deps;
|
|
205
|
+
cachedIndexText = null;
|
|
206
|
+
cachedAt = 0;
|
|
207
|
+
lastIndexHash = null;
|
|
208
|
+
inFlight = null;
|
|
209
|
+
// Failure cooldown state: a slow or broken worker model must not burn a
|
|
210
|
+
// request on every recall call. Exponential backoff (1/2/4 min, capped);
|
|
211
|
+
// a healthy scan resets the streak. Low-confidence results are healthy
|
|
212
|
+
// "no match" answers and do NOT arm the cooldown.
|
|
213
|
+
lastFailAt = 0;
|
|
214
|
+
consecutiveFails = 0;
|
|
215
|
+
lastAttemptFailed = false;
|
|
216
|
+
// Warn-once flag for unresolvable endpoint/key so a misconfiguration
|
|
217
|
+
// doesn't spam the log on every scan attempt.
|
|
218
|
+
warnedUnresolvable = false;
|
|
219
|
+
// Async prefetch snapshots: scan results precomputed when a user message
|
|
220
|
+
// arrives, consumed later by the sync recall path (never awaited there).
|
|
221
|
+
snapshots = new Map();
|
|
222
|
+
lastPrefetchAt = new Map();
|
|
223
|
+
static SNAPSHOT_TTL_MS = 10 * 60_000;
|
|
224
|
+
static SNAPSHOT_CAP = 64;
|
|
225
|
+
static PREFETCH_THROTTLE_MS = 60_000;
|
|
226
|
+
constructor(index, workerModel, deps = {}) {
|
|
227
|
+
this.index = index;
|
|
228
|
+
this.workerModel = workerModel;
|
|
229
|
+
this.deps = deps;
|
|
230
|
+
}
|
|
231
|
+
/** Refresh the cached index text. Called hourly by turn-compress cron. */
|
|
232
|
+
refreshCache() {
|
|
233
|
+
const text = formatIndexForScan(this.index);
|
|
234
|
+
const hash = hashText(text);
|
|
235
|
+
const changed = this.lastIndexHash !== null && this.lastIndexHash !== hash;
|
|
236
|
+
logger_1.log.info(`[IndexScan] cache refreshed: ${text.split('\n').length - 2} entries, ${text.length} chars, hash=${hash}${this.lastIndexHash !== null ? `, changed=${changed}` : ''}`);
|
|
237
|
+
this.cachedIndexText = text;
|
|
238
|
+
this.lastIndexHash = hash;
|
|
239
|
+
this.cachedAt = Date.now();
|
|
240
|
+
}
|
|
241
|
+
/** Get the cached index text, refreshing if needed. */
|
|
242
|
+
getIndexText() {
|
|
243
|
+
if (!this.cachedIndexText) {
|
|
244
|
+
this.refreshCache();
|
|
245
|
+
}
|
|
246
|
+
return this.cachedIndexText;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Run a scan: send cached index + query to the worker model over direct
|
|
250
|
+
* HTTP, parse the response. Returns null on failure (fail-open: callers
|
|
251
|
+
* fall back to BM25 only). Deduplicates concurrent calls — if a scan is
|
|
252
|
+
* already in-flight, returns the same promise.
|
|
253
|
+
*/
|
|
254
|
+
async scan(query, options = {}) {
|
|
255
|
+
const timeoutMs = options.timeoutMs ?? this.deps.timeoutMs ?? 30_000;
|
|
256
|
+
const minConfidence = options.minConfidence ?? 0.3;
|
|
257
|
+
// Failure cooldown (exponential backoff: 1/2/4 min, capped)
|
|
258
|
+
if (this.consecutiveFails > 0) {
|
|
259
|
+
const cooldown = Math.min(60_000 * 2 ** (this.consecutiveFails - 1), 240_000);
|
|
260
|
+
if (Date.now() - this.lastFailAt < cooldown)
|
|
261
|
+
return null;
|
|
262
|
+
}
|
|
263
|
+
// Deduplicate concurrent scans
|
|
264
|
+
if (this.inFlight)
|
|
265
|
+
return this.inFlight;
|
|
266
|
+
this.inFlight = this._doScan(query, timeoutMs, minConfidence);
|
|
267
|
+
try {
|
|
268
|
+
const result = await this.inFlight;
|
|
269
|
+
if (this.lastAttemptFailed) {
|
|
270
|
+
this.consecutiveFails++;
|
|
271
|
+
this.lastFailAt = Date.now();
|
|
272
|
+
}
|
|
273
|
+
else {
|
|
274
|
+
this.consecutiveFails = 0;
|
|
275
|
+
}
|
|
276
|
+
return result;
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
this.inFlight = null;
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async _doScan(query, timeoutMs, minConfidence) {
|
|
283
|
+
const providerID = this.workerModel?.providerID;
|
|
284
|
+
const modelID = this.workerModel?.modelID;
|
|
285
|
+
const startedAt = Date.now();
|
|
286
|
+
try {
|
|
287
|
+
const indexText = this.getIndexText();
|
|
288
|
+
const req = {
|
|
289
|
+
model: { providerID: providerID ?? '', modelID: modelID ?? '' },
|
|
290
|
+
system: [
|
|
291
|
+
{ text: SCAN_SYSTEM },
|
|
292
|
+
// Cacheable-prefix hint: DashScope caches the block from the start
|
|
293
|
+
// of the messages up to here (SCAN_SYSTEM + index) for 5 minutes,
|
|
294
|
+
// reset on every hit → deterministic ~10% input cost. Mapped to
|
|
295
|
+
// `cache_control: ephemeral` by the direct-HTTP transport; other
|
|
296
|
+
// runtimes may ignore it (fail-open).
|
|
297
|
+
{ text: indexText, cacheable: true },
|
|
298
|
+
],
|
|
299
|
+
user: [{ type: 'text', text: `User query: ${query}\n\nSelect the most relevant memory entries from the index above.` }],
|
|
300
|
+
temperature: 0,
|
|
301
|
+
// Reasoning models (glm-5.3-flash always thinks, ~300-1500 reasoning
|
|
302
|
+
// tokens) need headroom or the JSON gets truncated mid-stream
|
|
303
|
+
// (finish_reason=length → unparsable → cooldown). The JSON itself is
|
|
304
|
+
// ~100 tokens; 4096 covers the reasoning budget with margin.
|
|
305
|
+
maxTokens: 4096,
|
|
306
|
+
timeoutMs,
|
|
307
|
+
};
|
|
308
|
+
let content;
|
|
309
|
+
let usage;
|
|
310
|
+
const channel = this.deps.completion?.();
|
|
311
|
+
try {
|
|
312
|
+
if (!channel)
|
|
313
|
+
throw new Error('no completion channel');
|
|
314
|
+
const res = await channel.complete(req);
|
|
315
|
+
content = res.text;
|
|
316
|
+
usage = res.usage;
|
|
317
|
+
}
|
|
318
|
+
catch (contractErr) {
|
|
319
|
+
if (channel) {
|
|
320
|
+
logger_1.log.warn(`[IndexScan] completion channel failed (${contractErr.message}), falling back to direct HTTP`);
|
|
321
|
+
}
|
|
322
|
+
let res;
|
|
323
|
+
try {
|
|
324
|
+
res = await (0, completion_http_1.httpComplete)(req, this.deps);
|
|
325
|
+
}
|
|
326
|
+
catch (httpErr) {
|
|
327
|
+
if (/no endpoint or API key/.test(httpErr.message)) {
|
|
328
|
+
if (!this.warnedUnresolvable) {
|
|
329
|
+
logger_1.log.warn(`[IndexScan] no endpoint or API key for provider "${providerID ?? '?'}" — scan disabled`);
|
|
330
|
+
this.warnedUnresolvable = true;
|
|
331
|
+
}
|
|
332
|
+
this.lastAttemptFailed = true;
|
|
333
|
+
return null;
|
|
334
|
+
}
|
|
335
|
+
logger_1.log.warn(`[IndexScan] scan failed: ${httpErr.message}`);
|
|
336
|
+
this.lastAttemptFailed = true;
|
|
337
|
+
return null;
|
|
338
|
+
}
|
|
339
|
+
content = res.text;
|
|
340
|
+
usage = res.usage;
|
|
341
|
+
}
|
|
342
|
+
const result = parseScanResponse(content);
|
|
343
|
+
// Self-record usage: direct-HTTP scans bypass opencode, so without this
|
|
344
|
+
// sink the memory-system token stats silently lose the scan's share.
|
|
345
|
+
if (this.deps.recordUsage && usage) {
|
|
346
|
+
try {
|
|
347
|
+
this.deps.recordUsage({
|
|
348
|
+
providerID,
|
|
349
|
+
modelID,
|
|
350
|
+
input: usage.input,
|
|
351
|
+
cached: usage.cached,
|
|
352
|
+
output: usage.output,
|
|
353
|
+
latencyMs: Date.now() - startedAt,
|
|
354
|
+
ok: result != null,
|
|
355
|
+
indexHash: hashText(this.getIndexText()),
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
catch { /* never block the scan path on recorder errors */ }
|
|
359
|
+
}
|
|
360
|
+
if (!result) {
|
|
361
|
+
this.lastAttemptFailed = true; // unparsable output — model likely broken
|
|
362
|
+
logger_1.log.warn(`[IndexScan] scan failed: unparsable response (${Date.now() - startedAt}ms)`);
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
// Resolve short IDs to full IDs
|
|
366
|
+
result.relevantIds = resolveShortIds(result.relevantIds, this.index);
|
|
367
|
+
const elapsed = Date.now() - startedAt;
|
|
368
|
+
if (result.confidence < minConfidence) {
|
|
369
|
+
logger_1.log.info(`[IndexScan] ok: low confidence ${result.confidence} < ${minConfidence}, discarding (${elapsed}ms${usage?.cached != null ? `, cachedTokens=${usage.cached}` : ''})`);
|
|
370
|
+
this.lastAttemptFailed = false; // healthy "no match"
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
this.lastAttemptFailed = false;
|
|
374
|
+
logger_1.log.info(`[IndexScan] ok: ids=${result.relevantIds.length} confidence=${result.confidence} (${elapsed}ms${usage?.cached != null ? `, cachedTokens=${usage.cached}` : ''})`);
|
|
375
|
+
return result;
|
|
376
|
+
}
|
|
377
|
+
catch (err) {
|
|
378
|
+
logger_1.log.warn(`[IndexScan] scan failed: ${err.message}`);
|
|
379
|
+
this.lastAttemptFailed = true;
|
|
380
|
+
return null;
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Fire-and-forget prefetch: scan the query now and store the result as a
|
|
385
|
+
* per-session snapshot for later sync-path consumption. Throttled per
|
|
386
|
+
* session (60s); scan-internal dedup/cooldown still apply.
|
|
387
|
+
*/
|
|
388
|
+
prefetch(sessionID, query) {
|
|
389
|
+
if (!sessionID || !query.trim())
|
|
390
|
+
return;
|
|
391
|
+
const now = Date.now();
|
|
392
|
+
const last = this.lastPrefetchAt.get(sessionID) ?? 0;
|
|
393
|
+
if (now - last < IndexScanService.PREFETCH_THROTTLE_MS)
|
|
394
|
+
return;
|
|
395
|
+
this.lastPrefetchAt.set(sessionID, now);
|
|
396
|
+
void this.scan(query)
|
|
397
|
+
.then((result) => {
|
|
398
|
+
if (result)
|
|
399
|
+
this.setSnapshot(sessionID, result);
|
|
400
|
+
})
|
|
401
|
+
.catch(() => { });
|
|
402
|
+
}
|
|
403
|
+
setSnapshot(sessionID, result) {
|
|
404
|
+
if (this.snapshots.size >= IndexScanService.SNAPSHOT_CAP && !this.snapshots.has(sessionID)) {
|
|
405
|
+
// Evict the oldest snapshot when at capacity.
|
|
406
|
+
let oldestKey = '';
|
|
407
|
+
let oldestAt = Infinity;
|
|
408
|
+
for (const [k, v] of this.snapshots) {
|
|
409
|
+
if (v.at < oldestAt) {
|
|
410
|
+
oldestAt = v.at;
|
|
411
|
+
oldestKey = k;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
if (oldestKey)
|
|
415
|
+
this.snapshots.delete(oldestKey);
|
|
416
|
+
}
|
|
417
|
+
this.snapshots.set(sessionID, { result, at: Date.now() });
|
|
418
|
+
}
|
|
419
|
+
/**
|
|
420
|
+
* Latest precomputed scan snapshot for a session. The sync recall path
|
|
421
|
+
* merges this in-memory and never awaits a scan. Snapshots expire after
|
|
422
|
+
* 10 minutes.
|
|
423
|
+
*/
|
|
424
|
+
getSnapshot(sessionID) {
|
|
425
|
+
const snap = this.snapshots.get(sessionID);
|
|
426
|
+
if (!snap)
|
|
427
|
+
return null;
|
|
428
|
+
if (Date.now() - snap.at > IndexScanService.SNAPSHOT_TTL_MS) {
|
|
429
|
+
this.snapshots.delete(sessionID);
|
|
430
|
+
return null;
|
|
431
|
+
}
|
|
432
|
+
return snap.result;
|
|
433
|
+
}
|
|
434
|
+
/** No long-lived resources to dispose (direct HTTP, no sessions). */
|
|
435
|
+
async dispose() { }
|
|
436
|
+
}
|
|
437
|
+
exports.IndexScanService = IndexScanService;
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.NOTE_BOARD_BUDGET = exports.PINNED_BUDGET = void 0;
|
|
4
|
+
exports.renderMemoryBlocks = renderMemoryBlocks;
|
|
5
|
+
exports.formatRecallContext = formatRecallContext;
|
|
6
|
+
exports.formatPinnedProfile = formatPinnedProfile;
|
|
7
|
+
exports.formatNoteBoard = formatNoteBoard;
|
|
8
|
+
function formatDate(iso) {
|
|
9
|
+
if (!iso)
|
|
10
|
+
return '';
|
|
11
|
+
try {
|
|
12
|
+
const d = new Date(iso);
|
|
13
|
+
if (Number.isNaN(d.getTime()))
|
|
14
|
+
return '';
|
|
15
|
+
const yyyy = d.getFullYear();
|
|
16
|
+
const mm = String(d.getMonth() + 1).padStart(2, '0');
|
|
17
|
+
const dd = String(d.getDate()).padStart(2, '0');
|
|
18
|
+
return `${yyyy}-${mm}-${dd}`;
|
|
19
|
+
}
|
|
20
|
+
catch {
|
|
21
|
+
return '';
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
function pointerLine(m) {
|
|
25
|
+
const tags = [];
|
|
26
|
+
const date = formatDate(m.created_at);
|
|
27
|
+
if (date)
|
|
28
|
+
tags.push(date);
|
|
29
|
+
if (m.type)
|
|
30
|
+
tags.push(m.type);
|
|
31
|
+
const tagStr = tags.length > 0 ? `[${tags.join(' ')}] ` : '';
|
|
32
|
+
const text = (m.primary_abstraction || m.memory_value || '?').replace(/\n/g, ' ');
|
|
33
|
+
const energy = typeof m.energy === 'number' ? m.energy.toFixed(1) : '?';
|
|
34
|
+
// ids are `mem_<timestamp>_<rand>` — the FIRST 6 chars are constant across
|
|
35
|
+
// every memory; use the tail so pointers stay unique and verifiable.
|
|
36
|
+
return `- #mem-${(m.id || '?').slice(-6)} ${tagStr}"${text}" (E:${energy})`;
|
|
37
|
+
}
|
|
38
|
+
// Maps an entry to its block bucket. Order matters: parametric/delta entries go
|
|
39
|
+
// to <deltas>, procedural patterns to <patterns>, episodic/history to
|
|
40
|
+
// <history>, everything else to <facts>. Mirrors the classification used by
|
|
41
|
+
// the gateway first-turn transform.
|
|
42
|
+
function bucketFor(entry) {
|
|
43
|
+
if (entry.source === 'parametric' || entry.type === 'delta' || entry.type === 'constraint' || entry.type === 'prompt')
|
|
44
|
+
return 'deltas';
|
|
45
|
+
if (entry.source === 'procedural' || entry.type === 'pattern')
|
|
46
|
+
return 'patterns';
|
|
47
|
+
if (entry.source === 'episodic' || entry.type === 'history' || entry.type === 'review' || entry.verdict)
|
|
48
|
+
return 'history';
|
|
49
|
+
return 'facts';
|
|
50
|
+
}
|
|
51
|
+
function lineFor(entry) {
|
|
52
|
+
switch (bucketFor(entry)) {
|
|
53
|
+
case 'deltas':
|
|
54
|
+
return `[Δ ${entry.type || 'constraint'}] ${entry.content || entry.rule || entry.prompt_delta || entry.pattern_template || ''}`;
|
|
55
|
+
case 'patterns':
|
|
56
|
+
return `[${((entry.successRate || 0) * 100).toFixed(0)}%] ${entry.pattern || entry.content || ''}`;
|
|
57
|
+
case 'history':
|
|
58
|
+
return `Loop ${entry.loopNum || '?'}: ${entry.verdict || '?'} ☑ ${entry.summary || entry.content || ''}`;
|
|
59
|
+
case 'facts': {
|
|
60
|
+
const content = (entry.facts && entry.facts.length ? entry.facts.join('; ') : entry.content) || '';
|
|
61
|
+
return content ? `☑ ${content}` : '';
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Renders a list of memory entries into XML block strings, one per bucket:
|
|
67
|
+
* `<deltas>`, `<patterns>`, `<facts>`, `<history>`. Empty buckets are omitted.
|
|
68
|
+
* Callers join with "\n\n".
|
|
69
|
+
*/
|
|
70
|
+
function renderMemoryBlocks(entries) {
|
|
71
|
+
const buckets = { deltas: [], patterns: [], facts: [], history: [] };
|
|
72
|
+
for (const entry of entries) {
|
|
73
|
+
const line = lineFor(entry);
|
|
74
|
+
if (line.trim())
|
|
75
|
+
buckets[bucketFor(entry)].push(line);
|
|
76
|
+
}
|
|
77
|
+
const chunks = [];
|
|
78
|
+
for (const tag of ['deltas', 'patterns', 'facts', 'history']) {
|
|
79
|
+
if (buckets[tag].length > 0)
|
|
80
|
+
chunks.push(`<${tag}>\n${buckets[tag].join('\n')}\n</${tag}>`);
|
|
81
|
+
}
|
|
82
|
+
return chunks;
|
|
83
|
+
}
|
|
84
|
+
// ---- Pointer rendering (boundary recall) ----
|
|
85
|
+
const POINTER_HEADER = `[联想线索 · 依据前请验证:取全文 → mafw_get_memory("#mem-后6位");检索更多 → mafw_search_hybrid]`;
|
|
86
|
+
const TAG = `<recall>`;
|
|
87
|
+
const END_TAG = `</recall>`;
|
|
88
|
+
/**
|
|
89
|
+
* Group memories by type for better cross-session visibility.
|
|
90
|
+
* Multi-session queries benefit from seeing memories organized by topic.
|
|
91
|
+
*/
|
|
92
|
+
function groupMemoriesByType(memories) {
|
|
93
|
+
const groups = new Map();
|
|
94
|
+
for (const m of memories) {
|
|
95
|
+
const type = m.type || 'other';
|
|
96
|
+
if (!groups.has(type))
|
|
97
|
+
groups.set(type, []);
|
|
98
|
+
groups.get(type).push(m);
|
|
99
|
+
}
|
|
100
|
+
return groups;
|
|
101
|
+
}
|
|
102
|
+
function formatRecallContext(memories) {
|
|
103
|
+
if (memories.length === 0)
|
|
104
|
+
return { pointers: null };
|
|
105
|
+
// Episodic grouping: memories sharing a source session render under one
|
|
106
|
+
// session header with a date range (Memora-style narrative recovery —
|
|
107
|
+
// multi-session questions need to see that two hits came from one episode).
|
|
108
|
+
const sessionGroups = new Map();
|
|
109
|
+
for (const m of memories) {
|
|
110
|
+
if (!m.source_session_id)
|
|
111
|
+
continue;
|
|
112
|
+
if (!sessionGroups.has(m.source_session_id))
|
|
113
|
+
sessionGroups.set(m.source_session_id, []);
|
|
114
|
+
sessionGroups.get(m.source_session_id).push(m);
|
|
115
|
+
}
|
|
116
|
+
const groupedIds = new Set();
|
|
117
|
+
for (const mems of sessionGroups.values()) {
|
|
118
|
+
if (mems.length < 2)
|
|
119
|
+
continue;
|
|
120
|
+
for (const m of mems)
|
|
121
|
+
groupedIds.add(m.id);
|
|
122
|
+
}
|
|
123
|
+
const groups = groupMemoriesByType(memories);
|
|
124
|
+
const lines = [];
|
|
125
|
+
let sessionHeaders = 0;
|
|
126
|
+
for (const [sessionId, mems] of sessionGroups) {
|
|
127
|
+
if (mems.length < 2)
|
|
128
|
+
continue;
|
|
129
|
+
const dates = mems.map(m => formatDate(m.created_at)).filter(Boolean).sort();
|
|
130
|
+
const range = dates.length >= 2 && dates[0] !== dates[dates.length - 1]
|
|
131
|
+
? `${dates[0]}~${dates[dates.length - 1]}`
|
|
132
|
+
: dates[0] ?? '';
|
|
133
|
+
lines.push(`[session ${sessionId.slice(0, 8)}${range ? ' · ' + range : ''}]`);
|
|
134
|
+
for (const m of mems) {
|
|
135
|
+
lines.push(` ${pointerLine(m)}`);
|
|
136
|
+
}
|
|
137
|
+
sessionHeaders++;
|
|
138
|
+
}
|
|
139
|
+
// Everything else (ungrouped or singletons) renders as before.
|
|
140
|
+
const rest = memories.filter(m => !groupedIds.has(m.id));
|
|
141
|
+
const restGroups = groupMemoriesByType(rest);
|
|
142
|
+
if (restGroups.size <= 1 && sessionHeaders === 0) {
|
|
143
|
+
lines.push(...rest.slice(0, 3).map(pointerLine));
|
|
144
|
+
}
|
|
145
|
+
else if (rest.length > 0) {
|
|
146
|
+
for (const [type, mems] of restGroups) {
|
|
147
|
+
if (mems.length === 0)
|
|
148
|
+
continue;
|
|
149
|
+
lines.push(`[${type}]`);
|
|
150
|
+
for (const m of mems.slice(0, 2)) {
|
|
151
|
+
lines.push(` ${pointerLine(m)}`);
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
const pointers = `${TAG}\n${POINTER_HEADER}\n${lines.slice(0, 5).join('\n')}\n${END_TAG}`;
|
|
156
|
+
return { pointers };
|
|
157
|
+
}
|
|
158
|
+
// ---- Pinned disclosure profile rendering (GET /api/recall/pinned) ----
|
|
159
|
+
exports.PINNED_BUDGET = { max: 20, maxChars: 2000 };
|
|
160
|
+
const PROFILE_TAG = '<user-profile>';
|
|
161
|
+
const PROFILE_END_TAG = '</user-profile>';
|
|
162
|
+
/**
|
|
163
|
+
* Renders pinned memories into the <user-profile> disclosure block.
|
|
164
|
+
* Enforces the hard budget (max entries / max chars); entries beyond the
|
|
165
|
+
* budget are dropped (caller logs the overflow count). Returns null profile
|
|
166
|
+
* for an empty set — callers must not inject an empty block.
|
|
167
|
+
*/
|
|
168
|
+
function formatPinnedProfile(entries) {
|
|
169
|
+
const lines = [];
|
|
170
|
+
let chars = 0;
|
|
171
|
+
for (const m of entries.slice(0, exports.PINNED_BUDGET.max)) {
|
|
172
|
+
const text = (m.memory_value || m.primary_abstraction || '?').replace(/\n/g, ' ');
|
|
173
|
+
const line = `- ${text}`;
|
|
174
|
+
if (chars + line.length > exports.PINNED_BUDGET.maxChars)
|
|
175
|
+
break;
|
|
176
|
+
lines.push(line);
|
|
177
|
+
chars += line.length;
|
|
178
|
+
}
|
|
179
|
+
if (lines.length === 0)
|
|
180
|
+
return { profile: null, used: 0 };
|
|
181
|
+
return { profile: `${PROFILE_TAG}\n${lines.join('\n')}\n${PROFILE_END_TAG}`, used: lines.length };
|
|
182
|
+
}
|
|
183
|
+
// ---- Sticky note board rendering (appended to /api/recall/context) ----
|
|
184
|
+
// maxChars 1600 ≈ 800-1200 tok/turn(中文 ≈1 字/token),低于 pinned 的 2000
|
|
185
|
+
// 保持"pinned=长期身份 > sticky=近期提醒"的层级。perNoteCap 是防饿死关键:
|
|
186
|
+
// 无上限时一条超长便签吃光总预算,后续条目全部被 break 丢弃(实证 bug)。
|
|
187
|
+
exports.NOTE_BOARD_BUDGET = { max: 10, maxChars: 1600, perNoteCap: 240 };
|
|
188
|
+
const BOARD_TAG = '<note-board>';
|
|
189
|
+
const BOARD_END_TAG = '</note-board>';
|
|
190
|
+
/**
|
|
191
|
+
* Renders sticky notes into the <note-board> block: guaranteed per-turn
|
|
192
|
+
* visibility for user-commissioned memories until their sticky_until passes.
|
|
193
|
+
* Board-level expiry only — the memory itself is never deleted or hidden
|
|
194
|
+
* from search. Enforces a hard budget; returns null board for an empty set.
|
|
195
|
+
* `now` is injectable for deterministic tests.
|
|
196
|
+
*/
|
|
197
|
+
function formatNoteBoard(entries, now = new Date()) {
|
|
198
|
+
const lines = [];
|
|
199
|
+
let chars = 0;
|
|
200
|
+
for (const m of entries.slice(0, exports.NOTE_BOARD_BUDGET.max)) {
|
|
201
|
+
let text = (m.memory_value || m.primary_abstraction || '?').replace(/\n/g, ' ');
|
|
202
|
+
if (text.length > exports.NOTE_BOARD_BUDGET.perNoteCap) {
|
|
203
|
+
// 单条截断防饿死:板上只留前 240 字;完整内容 NotesDock/get_memory 可看
|
|
204
|
+
text = `${text.slice(0, exports.NOTE_BOARD_BUDGET.perNoteCap)}…`;
|
|
205
|
+
}
|
|
206
|
+
const until = m.sticky_until ? new Date(m.sticky_until) : null;
|
|
207
|
+
const valid = until && !Number.isNaN(until.getTime());
|
|
208
|
+
const daysLeft = valid ? Math.max(0, Math.ceil((until.getTime() - now.getTime()) / 86400e3)) : null;
|
|
209
|
+
const date = valid ? formatDate(m.sticky_until) : '';
|
|
210
|
+
const line = `- ${date ? `[${date.slice(5)}] ` : ''}${text}${daysLeft !== null ? `(剩 ${daysLeft} 天)` : ''}`;
|
|
211
|
+
if (chars + line.length > exports.NOTE_BOARD_BUDGET.maxChars)
|
|
212
|
+
break;
|
|
213
|
+
lines.push(line);
|
|
214
|
+
chars += line.length;
|
|
215
|
+
}
|
|
216
|
+
if (lines.length === 0)
|
|
217
|
+
return { board: null, used: 0 };
|
|
218
|
+
return {
|
|
219
|
+
board: `${BOARD_TAG}\n[用户叮嘱 · 到期自动下架,记忆本体保留]\n${lines.join('\n')}\n${BOARD_END_TAG}`,
|
|
220
|
+
used: lines.length,
|
|
221
|
+
};
|
|
222
|
+
}
|