abmind 0.0.1 → 0.1.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/CHANGELOG.md +34 -0
- package/LICENSE +190 -0
- package/README.md +191 -0
- package/dist/cli/abmind-backfill.d.ts +3 -0
- package/dist/cli/abmind-backfill.d.ts.map +1 -0
- package/dist/cli/abmind-backfill.js +64 -0
- package/dist/cli/abmind-backfill.js.map +1 -0
- package/dist/cli/abmind-backup.d.ts +5 -0
- package/dist/cli/abmind-backup.d.ts.map +1 -0
- package/dist/cli/abmind-backup.js +44 -0
- package/dist/cli/abmind-backup.js.map +1 -0
- package/dist/cli/abmind-bundle.d.ts +7 -0
- package/dist/cli/abmind-bundle.d.ts.map +1 -0
- package/dist/cli/abmind-bundle.js +17 -0
- package/dist/cli/abmind-bundle.js.map +1 -0
- package/dist/cli/abmind-edit.d.ts +10 -0
- package/dist/cli/abmind-edit.d.ts.map +1 -0
- package/dist/cli/abmind-edit.js +160 -0
- package/dist/cli/abmind-edit.js.map +1 -0
- package/dist/cli/abmind-embed.d.ts +3 -0
- package/dist/cli/abmind-embed.d.ts.map +1 -0
- package/dist/cli/abmind-embed.js +83 -0
- package/dist/cli/abmind-embed.js.map +1 -0
- package/dist/cli/abmind-expand.d.ts +6 -0
- package/dist/cli/abmind-expand.d.ts.map +1 -0
- package/dist/cli/abmind-expand.js +100 -0
- package/dist/cli/abmind-expand.js.map +1 -0
- package/dist/cli/abmind-hook-doctor.d.ts +8 -0
- package/dist/cli/abmind-hook-doctor.d.ts.map +1 -0
- package/dist/cli/abmind-hook-doctor.js +73 -0
- package/dist/cli/abmind-hook-doctor.js.map +1 -0
- package/dist/cli/abmind-hook-recall.d.ts +16 -0
- package/dist/cli/abmind-hook-recall.d.ts.map +1 -0
- package/dist/cli/abmind-hook-recall.js +123 -0
- package/dist/cli/abmind-hook-recall.js.map +1 -0
- package/dist/cli/abmind-hook-store.d.ts +17 -0
- package/dist/cli/abmind-hook-store.d.ts.map +1 -0
- package/dist/cli/abmind-hook-store.js +115 -0
- package/dist/cli/abmind-hook-store.js.map +1 -0
- package/dist/cli/abmind-hook-wakeup.d.ts +12 -0
- package/dist/cli/abmind-hook-wakeup.d.ts.map +1 -0
- package/dist/cli/abmind-hook-wakeup.js +143 -0
- package/dist/cli/abmind-hook-wakeup.js.map +1 -0
- package/dist/cli/abmind-ingest.d.ts +7 -0
- package/dist/cli/abmind-ingest.d.ts.map +1 -0
- package/dist/cli/abmind-ingest.js +96 -0
- package/dist/cli/abmind-ingest.js.map +1 -0
- package/dist/cli/abmind-install.d.ts +10 -0
- package/dist/cli/abmind-install.d.ts.map +1 -0
- package/dist/cli/abmind-install.js +240 -0
- package/dist/cli/abmind-install.js.map +1 -0
- package/dist/cli/abmind-mcp.d.ts +3 -0
- package/dist/cli/abmind-mcp.d.ts.map +1 -0
- package/dist/cli/abmind-mcp.js +26 -0
- package/dist/cli/abmind-mcp.js.map +1 -0
- package/dist/cli/abmind-memory-stats.d.ts +3 -0
- package/dist/cli/abmind-memory-stats.d.ts.map +1 -0
- package/dist/cli/abmind-memory-stats.js +45 -0
- package/dist/cli/abmind-memory-stats.js.map +1 -0
- package/dist/cli/abmind-migrate-openclaw.d.ts +12 -0
- package/dist/cli/abmind-migrate-openclaw.d.ts.map +1 -0
- package/dist/cli/abmind-migrate-openclaw.js +247 -0
- package/dist/cli/abmind-migrate-openclaw.js.map +1 -0
- package/dist/cli/abmind-recall.d.ts +8 -0
- package/dist/cli/abmind-recall.d.ts.map +1 -0
- package/dist/cli/abmind-recall.js +94 -0
- package/dist/cli/abmind-recall.js.map +1 -0
- package/dist/cli/abmind-reset.d.ts +11 -0
- package/dist/cli/abmind-reset.d.ts.map +1 -0
- package/dist/cli/abmind-reset.js +143 -0
- package/dist/cli/abmind-reset.js.map +1 -0
- package/dist/cli/abmind-restore.d.ts +5 -0
- package/dist/cli/abmind-restore.d.ts.map +1 -0
- package/dist/cli/abmind-restore.js +64 -0
- package/dist/cli/abmind-restore.js.map +1 -0
- package/dist/cli/abmind-retro-extract.d.ts +15 -0
- package/dist/cli/abmind-retro-extract.d.ts.map +1 -0
- package/dist/cli/abmind-retro-extract.js +113 -0
- package/dist/cli/abmind-retro-extract.js.map +1 -0
- package/dist/cli/abmind-rollback.d.ts +8 -0
- package/dist/cli/abmind-rollback.d.ts.map +1 -0
- package/dist/cli/abmind-rollback.js +76 -0
- package/dist/cli/abmind-rollback.js.map +1 -0
- package/dist/cli/abmind-secrets.d.ts +12 -0
- package/dist/cli/abmind-secrets.d.ts.map +1 -0
- package/dist/cli/abmind-secrets.js +83 -0
- package/dist/cli/abmind-secrets.js.map +1 -0
- package/dist/cli/abmind-sleep-apply.d.ts +3 -0
- package/dist/cli/abmind-sleep-apply.d.ts.map +1 -0
- package/dist/cli/abmind-sleep-apply.js +50 -0
- package/dist/cli/abmind-sleep-apply.js.map +1 -0
- package/dist/cli/abmind-sleep-report.d.ts +3 -0
- package/dist/cli/abmind-sleep-report.d.ts.map +1 -0
- package/dist/cli/abmind-sleep-report.js +40 -0
- package/dist/cli/abmind-sleep-report.js.map +1 -0
- package/dist/cli/abmind-sleep-state.d.ts +3 -0
- package/dist/cli/abmind-sleep-state.d.ts.map +1 -0
- package/dist/cli/abmind-sleep-state.js +33 -0
- package/dist/cli/abmind-sleep-state.js.map +1 -0
- package/dist/cli/abmind-sleep.d.ts +14 -0
- package/dist/cli/abmind-sleep.d.ts.map +1 -0
- package/dist/cli/abmind-sleep.js +186 -0
- package/dist/cli/abmind-sleep.js.map +1 -0
- package/dist/cli/abmind-status-runtime.d.ts +9 -0
- package/dist/cli/abmind-status-runtime.d.ts.map +1 -0
- package/dist/cli/abmind-status-runtime.js +132 -0
- package/dist/cli/abmind-status-runtime.js.map +1 -0
- package/dist/cli/abmind-status.d.ts +3 -0
- package/dist/cli/abmind-status.d.ts.map +1 -0
- package/dist/cli/abmind-status.js +45 -0
- package/dist/cli/abmind-status.js.map +1 -0
- package/dist/cli/abmind-store.d.ts +54 -0
- package/dist/cli/abmind-store.d.ts.map +1 -0
- package/dist/cli/abmind-store.js +251 -0
- package/dist/cli/abmind-store.js.map +1 -0
- package/dist/cli/abmind-update.d.ts +11 -0
- package/dist/cli/abmind-update.d.ts.map +1 -0
- package/dist/cli/abmind-update.js +160 -0
- package/dist/cli/abmind-update.js.map +1 -0
- package/dist/cli/abmind-wakeup.d.ts +3 -0
- package/dist/cli/abmind-wakeup.d.ts.map +1 -0
- package/dist/cli/abmind-wakeup.js +36 -0
- package/dist/cli/abmind-wakeup.js.map +1 -0
- package/dist/cli/abmind.d.ts +10 -0
- package/dist/cli/abmind.d.ts.map +1 -0
- package/dist/cli/abmind.js +100 -0
- package/dist/cli/abmind.js.map +1 -0
- package/dist/src/abm-v2-vocab.d.ts +10 -0
- package/dist/src/abm-v2-vocab.d.ts.map +1 -0
- package/dist/src/abm-v2-vocab.js +66 -0
- package/dist/src/abm-v2-vocab.js.map +1 -0
- package/dist/src/adapters/openclaw.d.ts +66 -0
- package/dist/src/adapters/openclaw.d.ts.map +1 -0
- package/dist/src/adapters/openclaw.js +97 -0
- package/dist/src/adapters/openclaw.js.map +1 -0
- package/dist/src/backend-factory.d.ts +9 -0
- package/dist/src/backend-factory.d.ts.map +1 -0
- package/dist/src/backend-factory.js +23 -0
- package/dist/src/backend-factory.js.map +1 -0
- package/dist/src/backup.d.ts +19 -0
- package/dist/src/backup.d.ts.map +1 -0
- package/dist/src/backup.js +191 -0
- package/dist/src/backup.js.map +1 -0
- package/dist/src/brain-patterns.d.ts +20 -0
- package/dist/src/brain-patterns.d.ts.map +1 -0
- package/dist/src/brain-patterns.js +72 -0
- package/dist/src/brain-patterns.js.map +1 -0
- package/dist/src/cli-entry.d.ts +10 -0
- package/dist/src/cli-entry.d.ts.map +1 -0
- package/dist/src/cli-entry.js +29 -0
- package/dist/src/cli-entry.js.map +1 -0
- package/dist/src/cli-flags.d.ts +29 -0
- package/dist/src/cli-flags.d.ts.map +1 -0
- package/dist/src/cli-flags.js +72 -0
- package/dist/src/cli-flags.js.map +1 -0
- package/dist/src/cli-runner-raw.d.ts +21 -0
- package/dist/src/cli-runner-raw.d.ts.map +1 -0
- package/dist/src/cli-runner-raw.js +41 -0
- package/dist/src/cli-runner-raw.js.map +1 -0
- package/dist/src/cli-runner.d.ts +26 -0
- package/dist/src/cli-runner.d.ts.map +1 -0
- package/dist/src/cli-runner.js +51 -0
- package/dist/src/cli-runner.js.map +1 -0
- package/dist/src/consolidation-search.d.ts +17 -0
- package/dist/src/consolidation-search.d.ts.map +1 -0
- package/dist/src/consolidation-search.js +83 -0
- package/dist/src/consolidation-search.js.map +1 -0
- package/dist/src/context-engine.d.ts +89 -0
- package/dist/src/context-engine.d.ts.map +1 -0
- package/dist/src/context-engine.js +216 -0
- package/dist/src/context-engine.js.map +1 -0
- package/dist/src/context-tier-renderer.d.ts +79 -0
- package/dist/src/context-tier-renderer.d.ts.map +1 -0
- package/dist/src/context-tier-renderer.js +223 -0
- package/dist/src/context-tier-renderer.js.map +1 -0
- package/dist/src/contradiction-checker.d.ts +16 -0
- package/dist/src/contradiction-checker.d.ts.map +1 -0
- package/dist/src/contradiction-checker.js +40 -0
- package/dist/src/contradiction-checker.js.map +1 -0
- package/dist/src/crypto.d.ts +15 -0
- package/dist/src/crypto.d.ts.map +1 -0
- package/dist/src/crypto.js +86 -0
- package/dist/src/crypto.js.map +1 -0
- package/dist/src/deploy-lib/cleanup.d.ts +38 -0
- package/dist/src/deploy-lib/cleanup.d.ts.map +1 -0
- package/dist/src/deploy-lib/cleanup.js +71 -0
- package/dist/src/deploy-lib/cleanup.js.map +1 -0
- package/dist/src/deploy-lib/index.d.ts +20 -0
- package/dist/src/deploy-lib/index.d.ts.map +1 -0
- package/dist/src/deploy-lib/index.js +20 -0
- package/dist/src/deploy-lib/index.js.map +1 -0
- package/dist/src/deploy-lib/lock.d.ts +37 -0
- package/dist/src/deploy-lib/lock.d.ts.map +1 -0
- package/dist/src/deploy-lib/lock.js +107 -0
- package/dist/src/deploy-lib/lock.js.map +1 -0
- package/dist/src/deploy-lib/manifest.d.ts +42 -0
- package/dist/src/deploy-lib/manifest.d.ts.map +1 -0
- package/dist/src/deploy-lib/manifest.js +38 -0
- package/dist/src/deploy-lib/manifest.js.map +1 -0
- package/dist/src/deploy-lib/paths.d.ts +29 -0
- package/dist/src/deploy-lib/paths.d.ts.map +1 -0
- package/dist/src/deploy-lib/paths.js +40 -0
- package/dist/src/deploy-lib/paths.js.map +1 -0
- package/dist/src/deploy-lib/releases.d.ts +52 -0
- package/dist/src/deploy-lib/releases.d.ts.map +1 -0
- package/dist/src/deploy-lib/releases.js +119 -0
- package/dist/src/deploy-lib/releases.js.map +1 -0
- package/dist/src/deploy-lib/safe-copy.d.ts +16 -0
- package/dist/src/deploy-lib/safe-copy.d.ts.map +1 -0
- package/dist/src/deploy-lib/safe-copy.js +32 -0
- package/dist/src/deploy-lib/safe-copy.js.map +1 -0
- package/dist/src/embedding-health.d.ts +12 -0
- package/dist/src/embedding-health.d.ts.map +1 -0
- package/dist/src/embedding-health.js +18 -0
- package/dist/src/embedding-health.js.map +1 -0
- package/dist/src/embedding-provider.d.ts +48 -0
- package/dist/src/embedding-provider.d.ts.map +1 -0
- package/dist/src/embedding-provider.js +145 -0
- package/dist/src/embedding-provider.js.map +1 -0
- package/dist/src/embedding-quantize.d.ts +11 -0
- package/dist/src/embedding-quantize.d.ts.map +1 -0
- package/dist/src/embedding-quantize.js +38 -0
- package/dist/src/embedding-quantize.js.map +1 -0
- package/dist/src/emotion-arc.d.ts +17 -0
- package/dist/src/emotion-arc.d.ts.map +1 -0
- package/dist/src/emotion-arc.js +62 -0
- package/dist/src/emotion-arc.js.map +1 -0
- package/dist/src/emotion-tagger.d.ts +8 -0
- package/dist/src/emotion-tagger.d.ts.map +1 -0
- package/dist/src/emotion-tagger.js +45 -0
- package/dist/src/emotion-tagger.js.map +1 -0
- package/dist/src/emotion-utils.d.ts +13 -0
- package/dist/src/emotion-utils.d.ts.map +1 -0
- package/dist/src/emotion-utils.js +76 -0
- package/dist/src/emotion-utils.js.map +1 -0
- package/dist/src/entity-graph.d.ts +33 -0
- package/dist/src/entity-graph.d.ts.map +1 -0
- package/dist/src/entity-graph.js +42 -0
- package/dist/src/entity-graph.js.map +1 -0
- package/dist/src/env-schema.d.ts +46 -0
- package/dist/src/env-schema.d.ts.map +1 -0
- package/dist/src/env-schema.js +93 -0
- package/dist/src/env-schema.js.map +1 -0
- package/dist/src/hook-helpers.d.ts +16 -0
- package/dist/src/hook-helpers.d.ts.map +1 -0
- package/dist/src/hook-helpers.js +64 -0
- package/dist/src/hook-helpers.js.map +1 -0
- package/dist/src/imemory-system.d.ts +138 -0
- package/dist/src/imemory-system.d.ts.map +1 -0
- package/dist/src/imemory-system.js +40 -0
- package/dist/src/imemory-system.js.map +1 -0
- package/dist/src/importance-flagger.d.ts +8 -0
- package/dist/src/importance-flagger.d.ts.map +1 -0
- package/dist/src/importance-flagger.js +24 -0
- package/dist/src/importance-flagger.js.map +1 -0
- package/dist/src/index.d.ts +79 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +79 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/ingest-pipeline.d.ts +32 -0
- package/dist/src/ingest-pipeline.d.ts.map +1 -0
- package/dist/src/ingest-pipeline.js +58 -0
- package/dist/src/ingest-pipeline.js.map +1 -0
- package/dist/src/injection-scanner.d.ts +16 -0
- package/dist/src/injection-scanner.d.ts.map +1 -0
- package/dist/src/injection-scanner.js +182 -0
- package/dist/src/injection-scanner.js.map +1 -0
- package/dist/src/local-time.d.ts +12 -0
- package/dist/src/local-time.d.ts.map +1 -0
- package/dist/src/local-time.js +23 -0
- package/dist/src/local-time.js.map +1 -0
- package/dist/src/maintenance-service.d.ts +46 -0
- package/dist/src/maintenance-service.d.ts.map +1 -0
- package/dist/src/maintenance-service.js +280 -0
- package/dist/src/maintenance-service.js.map +1 -0
- package/dist/src/mcp-server.d.ts +6 -0
- package/dist/src/mcp-server.d.ts.map +1 -0
- package/dist/src/mcp-server.js +50 -0
- package/dist/src/mcp-server.js.map +1 -0
- package/dist/src/media-sanitizer.d.ts +11 -0
- package/dist/src/media-sanitizer.d.ts.map +1 -0
- package/dist/src/media-sanitizer.js +56 -0
- package/dist/src/media-sanitizer.js.map +1 -0
- package/dist/src/mem-config-env.d.ts +18 -0
- package/dist/src/mem-config-env.d.ts.map +1 -0
- package/dist/src/mem-config-env.js +43 -0
- package/dist/src/mem-config-env.js.map +1 -0
- package/dist/src/mem-env.d.ts +5 -0
- package/dist/src/mem-env.d.ts.map +1 -0
- package/dist/src/mem-env.js +19 -0
- package/dist/src/mem-env.js.map +1 -0
- package/dist/src/mem-logger.d.ts +20 -0
- package/dist/src/mem-logger.d.ts.map +1 -0
- package/dist/src/mem-logger.js +24 -0
- package/dist/src/mem-logger.js.map +1 -0
- package/dist/src/mem-paths.d.ts +20 -0
- package/dist/src/mem-paths.d.ts.map +1 -0
- package/dist/src/mem-paths.js +43 -0
- package/dist/src/mem-paths.js.map +1 -0
- package/dist/src/mem-types.d.ts +157 -0
- package/dist/src/mem-types.d.ts.map +1 -0
- package/dist/src/mem-types.js +2 -0
- package/dist/src/mem-types.js.map +1 -0
- package/dist/src/memory-backend.d.ts +65 -0
- package/dist/src/memory-backend.d.ts.map +1 -0
- package/dist/src/memory-backend.js +43 -0
- package/dist/src/memory-backend.js.map +1 -0
- package/dist/src/memory-compressor.d.ts +17 -0
- package/dist/src/memory-compressor.d.ts.map +1 -0
- package/dist/src/memory-compressor.js +147 -0
- package/dist/src/memory-compressor.js.map +1 -0
- package/dist/src/memory-config.d.ts +30 -0
- package/dist/src/memory-config.d.ts.map +1 -0
- package/dist/src/memory-config.js +62 -0
- package/dist/src/memory-config.js.map +1 -0
- package/dist/src/memory-db.d.ts +7 -0
- package/dist/src/memory-db.d.ts.map +1 -0
- package/dist/src/memory-db.js +173 -0
- package/dist/src/memory-db.js.map +1 -0
- package/dist/src/memory-editor.d.ts +32 -0
- package/dist/src/memory-editor.d.ts.map +1 -0
- package/dist/src/memory-editor.js +339 -0
- package/dist/src/memory-editor.js.map +1 -0
- package/dist/src/memory-index.d.ts +91 -0
- package/dist/src/memory-index.d.ts.map +1 -0
- package/dist/src/memory-index.js +310 -0
- package/dist/src/memory-index.js.map +1 -0
- package/dist/src/memory-ipc-client.d.ts +22 -0
- package/dist/src/memory-ipc-client.d.ts.map +1 -0
- package/dist/src/memory-ipc-client.js +75 -0
- package/dist/src/memory-ipc-client.js.map +1 -0
- package/dist/src/memory-ipc-server.d.ts +17 -0
- package/dist/src/memory-ipc-server.d.ts.map +1 -0
- package/dist/src/memory-ipc-server.js +86 -0
- package/dist/src/memory-ipc-server.js.map +1 -0
- package/dist/src/memory-manager.d.ts +155 -0
- package/dist/src/memory-manager.d.ts.map +1 -0
- package/dist/src/memory-manager.js +463 -0
- package/dist/src/memory-manager.js.map +1 -0
- package/dist/src/memory-renderer.d.ts +15 -0
- package/dist/src/memory-renderer.d.ts.map +1 -0
- package/dist/src/memory-renderer.js +126 -0
- package/dist/src/memory-renderer.js.map +1 -0
- package/dist/src/message-store.d.ts +42 -0
- package/dist/src/message-store.d.ts.map +1 -0
- package/dist/src/message-store.js +145 -0
- package/dist/src/message-store.js.map +1 -0
- package/dist/src/mmr.d.ts +17 -0
- package/dist/src/mmr.d.ts.map +1 -0
- package/dist/src/mmr.js +48 -0
- package/dist/src/mmr.js.map +1 -0
- package/dist/src/ollama-embed.d.ts +46 -0
- package/dist/src/ollama-embed.d.ts.map +1 -0
- package/dist/src/ollama-embed.js +139 -0
- package/dist/src/ollama-embed.js.map +1 -0
- package/dist/src/openclaw-plugin/engine.d.ts +87 -0
- package/dist/src/openclaw-plugin/engine.d.ts.map +1 -0
- package/dist/src/openclaw-plugin/engine.js +121 -0
- package/dist/src/openclaw-plugin/engine.js.map +1 -0
- package/dist/src/openclaw-plugin/hooks.d.ts +41 -0
- package/dist/src/openclaw-plugin/hooks.d.ts.map +1 -0
- package/dist/src/openclaw-plugin/hooks.js +155 -0
- package/dist/src/openclaw-plugin/hooks.js.map +1 -0
- package/dist/src/openclaw-plugin/index.d.ts +20 -0
- package/dist/src/openclaw-plugin/index.d.ts.map +1 -0
- package/dist/src/openclaw-plugin/index.js +151 -0
- package/dist/src/openclaw-plugin/index.js.map +1 -0
- package/dist/src/openclaw-plugin/message-adapter.d.ts +13 -0
- package/dist/src/openclaw-plugin/message-adapter.d.ts.map +1 -0
- package/dist/src/openclaw-plugin/message-adapter.js +49 -0
- package/dist/src/openclaw-plugin/message-adapter.js.map +1 -0
- package/dist/src/openclaw-plugin/prompt-builder.d.ts +17 -0
- package/dist/src/openclaw-plugin/prompt-builder.d.ts.map +1 -0
- package/dist/src/openclaw-plugin/prompt-builder.js +22 -0
- package/dist/src/openclaw-plugin/prompt-builder.js.map +1 -0
- package/dist/src/openclaw-plugin/public-artifacts.d.ts +20 -0
- package/dist/src/openclaw-plugin/public-artifacts.d.ts.map +1 -0
- package/dist/src/openclaw-plugin/public-artifacts.js +59 -0
- package/dist/src/openclaw-plugin/public-artifacts.js.map +1 -0
- package/dist/src/openclaw-plugin/runtime-adapter.d.ts +26 -0
- package/dist/src/openclaw-plugin/runtime-adapter.d.ts.map +1 -0
- package/dist/src/openclaw-plugin/runtime-adapter.js +161 -0
- package/dist/src/openclaw-plugin/runtime-adapter.js.map +1 -0
- package/dist/src/openclaw-plugin/session-mapper.d.ts +11 -0
- package/dist/src/openclaw-plugin/session-mapper.d.ts.map +1 -0
- package/dist/src/openclaw-plugin/session-mapper.js +17 -0
- package/dist/src/openclaw-plugin/session-mapper.js.map +1 -0
- package/dist/src/openclaw-plugin/tools.d.ts +32 -0
- package/dist/src/openclaw-plugin/tools.d.ts.map +1 -0
- package/dist/src/openclaw-plugin/tools.js +131 -0
- package/dist/src/openclaw-plugin/tools.js.map +1 -0
- package/dist/src/openclaw-plugin/types.d.ts +35 -0
- package/dist/src/openclaw-plugin/types.d.ts.map +1 -0
- package/dist/src/openclaw-plugin/types.js +2 -0
- package/dist/src/openclaw-plugin/types.js.map +1 -0
- package/dist/src/query-tokenizer.d.ts +33 -0
- package/dist/src/query-tokenizer.d.ts.map +1 -0
- package/dist/src/query-tokenizer.js +84 -0
- package/dist/src/query-tokenizer.js.map +1 -0
- package/dist/src/recall-benchmark.d.ts +13 -0
- package/dist/src/recall-benchmark.d.ts.map +1 -0
- package/dist/src/recall-benchmark.js +256 -0
- package/dist/src/recall-benchmark.js.map +1 -0
- package/dist/src/recall-engine.d.ts +81 -0
- package/dist/src/recall-engine.d.ts.map +1 -0
- package/dist/src/recall-engine.js +387 -0
- package/dist/src/recall-engine.js.map +1 -0
- package/dist/src/redact-secrets.d.ts +20 -0
- package/dist/src/redact-secrets.d.ts.map +1 -0
- package/dist/src/redact-secrets.js +42 -0
- package/dist/src/redact-secrets.js.map +1 -0
- package/dist/src/runtime-store.d.ts +47 -0
- package/dist/src/runtime-store.d.ts.map +1 -0
- package/dist/src/runtime-store.js +78 -0
- package/dist/src/runtime-store.js.map +1 -0
- package/dist/src/session-context.d.ts +10 -0
- package/dist/src/session-context.d.ts.map +1 -0
- package/dist/src/session-context.js +83 -0
- package/dist/src/session-context.js.map +1 -0
- package/dist/src/session-memory.d.ts +8 -0
- package/dist/src/session-memory.d.ts.map +1 -0
- package/dist/src/session-memory.js +45 -0
- package/dist/src/session-memory.js.map +1 -0
- package/dist/src/signature-generator.d.ts +15 -0
- package/dist/src/signature-generator.d.ts.map +1 -0
- package/dist/src/signature-generator.js +78 -0
- package/dist/src/signature-generator.js.map +1 -0
- package/dist/src/sleep/basic.d.ts +48 -0
- package/dist/src/sleep/basic.d.ts.map +1 -0
- package/dist/src/sleep/basic.js +147 -0
- package/dist/src/sleep/basic.js.map +1 -0
- package/dist/src/sleep/levels.d.ts +20 -0
- package/dist/src/sleep/levels.d.ts.map +1 -0
- package/dist/src/sleep/levels.js +11 -0
- package/dist/src/sleep/levels.js.map +1 -0
- package/dist/src/sleep/native.d.ts +41 -0
- package/dist/src/sleep/native.d.ts.map +1 -0
- package/dist/src/sleep/native.js +112 -0
- package/dist/src/sleep/native.js.map +1 -0
- package/dist/src/sleep/orchestrator.d.ts +64 -0
- package/dist/src/sleep/orchestrator.d.ts.map +1 -0
- package/dist/src/sleep/orchestrator.js +960 -0
- package/dist/src/sleep/orchestrator.js.map +1 -0
- package/dist/src/sleep/runtime.d.ts +15 -0
- package/dist/src/sleep/runtime.d.ts.map +1 -0
- package/dist/src/sleep/runtime.js +2 -0
- package/dist/src/sleep/runtime.js.map +1 -0
- package/dist/src/sleep/sleep-daily-summary.d.ts +64 -0
- package/dist/src/sleep/sleep-daily-summary.d.ts.map +1 -0
- package/dist/src/sleep/sleep-daily-summary.js +273 -0
- package/dist/src/sleep/sleep-daily-summary.js.map +1 -0
- package/dist/src/sleep/sleep-extract-daily.d.ts +12 -0
- package/dist/src/sleep/sleep-extract-daily.d.ts.map +1 -0
- package/dist/src/sleep/sleep-extract-daily.js +55 -0
- package/dist/src/sleep/sleep-extract-daily.js.map +1 -0
- package/dist/src/sleep/sleep-prompt-loader.d.ts +27 -0
- package/dist/src/sleep/sleep-prompt-loader.d.ts.map +1 -0
- package/dist/src/sleep/sleep-prompt-loader.js +116 -0
- package/dist/src/sleep/sleep-prompt-loader.js.map +1 -0
- package/dist/src/sleep/test-harness.d.ts +67 -0
- package/dist/src/sleep/test-harness.d.ts.map +1 -0
- package/dist/src/sleep/test-harness.js +121 -0
- package/dist/src/sleep/test-harness.js.map +1 -0
- package/dist/src/sleep/trigger.d.ts +3 -0
- package/dist/src/sleep/trigger.d.ts.map +1 -0
- package/dist/src/sleep/trigger.js +66 -0
- package/dist/src/sleep/trigger.js.map +1 -0
- package/dist/src/sleep-data-access.d.ts +54 -0
- package/dist/src/sleep-data-access.d.ts.map +1 -0
- package/dist/src/sleep-data-access.js +176 -0
- package/dist/src/sleep-data-access.js.map +1 -0
- package/dist/src/sleep-pipeline.d.ts +9 -0
- package/dist/src/sleep-pipeline.d.ts.map +1 -0
- package/dist/src/sleep-pipeline.js +8 -0
- package/dist/src/sleep-pipeline.js.map +1 -0
- package/dist/src/sleep-state-gatherer.d.ts +74 -0
- package/dist/src/sleep-state-gatherer.d.ts.map +1 -0
- package/dist/src/sleep-state-gatherer.js +275 -0
- package/dist/src/sleep-state-gatherer.js.map +1 -0
- package/dist/src/sqlite-backend.d.ts +23 -0
- package/dist/src/sqlite-backend.d.ts.map +1 -0
- package/dist/src/sqlite-backend.js +48 -0
- package/dist/src/sqlite-backend.js.map +1 -0
- package/dist/src/test-helpers.d.ts +3 -0
- package/dist/src/test-helpers.d.ts.map +1 -0
- package/dist/src/test-helpers.js +5 -0
- package/dist/src/test-helpers.js.map +1 -0
- package/dist/src/tier-llm-refinement.d.ts +35 -0
- package/dist/src/tier-llm-refinement.d.ts.map +1 -0
- package/dist/src/tier-llm-refinement.js +131 -0
- package/dist/src/tier-llm-refinement.js.map +1 -0
- package/dist/src/timeline-builder.d.ts +45 -0
- package/dist/src/timeline-builder.d.ts.map +1 -0
- package/dist/src/timeline-builder.js +147 -0
- package/dist/src/timeline-builder.js.map +1 -0
- package/dist/src/trigram-search.d.ts +28 -0
- package/dist/src/trigram-search.d.ts.map +1 -0
- package/dist/src/trigram-search.js +192 -0
- package/dist/src/trigram-search.js.map +1 -0
- package/dist/src/turn-classifier.d.ts +29 -0
- package/dist/src/turn-classifier.d.ts.map +1 -0
- package/dist/src/turn-classifier.js +186 -0
- package/dist/src/turn-classifier.js.map +1 -0
- package/dist/src/user-utils.d.ts +6 -0
- package/dist/src/user-utils.d.ts.map +1 -0
- package/dist/src/user-utils.js +27 -0
- package/dist/src/user-utils.js.map +1 -0
- package/dist/src/wake-up-builder.d.ts +12 -0
- package/dist/src/wake-up-builder.d.ts.map +1 -0
- package/dist/src/wake-up-builder.js +45 -0
- package/dist/src/wake-up-builder.js.map +1 -0
- package/dist/src/wake-up-renderer.d.ts +29 -0
- package/dist/src/wake-up-renderer.d.ts.map +1 -0
- package/dist/src/wake-up-renderer.js +182 -0
- package/dist/src/wake-up-renderer.js.map +1 -0
- package/openclaw.plugin.json +72 -0
- package/package.json +92 -5
- package/prompts/sleep/01-gc-noise.md +28 -0
- package/prompts/sleep/02-daily-summary.md +3 -0
- package/prompts/sleep/03-extract-from-daily.md +3 -0
- package/prompts/sleep/04-retrospective.md +30 -0
- package/prompts/sleep/06-feedback.md +17 -0
- package/prompts/sleep/07-topic-assignment.md +23 -0
- package/prompts/sleep/08-core-promotion.md +26 -0
- package/prompts/sleep/09-merge.md +19 -0
- package/prompts/sleep/10-translation.md +17 -0
- package/prompts/sleep/11-skill-review.md +12 -0
- package/prompts/sleep/12-core-knowledge.md +14 -0
- package/prompts/sleep/13-consolidation.md +11 -0
- package/prompts/sleep/14-emotion-context.md +17 -0
- package/prompts/sleep/15-contradiction-and-graph.md +48 -0
- package/prompts/sleep/16-rem-synthesis.md +31 -0
- package/prompts/sleep/basic.md +12 -0
|
@@ -0,0 +1,960 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* abmind sleep orchestrator — library function for overnight memory maintenance.
|
|
4
|
+
*
|
|
5
|
+
* Called via runSleepCycle({ runtime, level, ... }). Gathers system state,
|
|
6
|
+
* runs through a pipeline of prompt-driven steps (gc-noise, daily-summary,
|
|
7
|
+
* extract-from-daily, retrospective, etc.), persists audit log, returns result.
|
|
8
|
+
*
|
|
9
|
+
* Library-only — no CLI entry point here. Standalone entry lives in
|
|
10
|
+
* cli/abmind-sleep.ts.
|
|
11
|
+
*
|
|
12
|
+
* Flags (passed via RunOpts.flags):
|
|
13
|
+
* --dry-run Gather state + build prompts, print to stdout, skip LLM calls
|
|
14
|
+
* --verbose Detailed logging at each orchestration step
|
|
15
|
+
* --force Run housekeeping even if no messages since last sleep
|
|
16
|
+
*/
|
|
17
|
+
import { localISO } from "../local-time.js";
|
|
18
|
+
import { getAbmindEnv } from "../env-schema.js";
|
|
19
|
+
import { join, basename } from "node:path";
|
|
20
|
+
import { appendFileSync, mkdirSync, existsSync, readdirSync, readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
21
|
+
import { MemoryManager } from "../memory-manager.js";
|
|
22
|
+
import { loadMemoryConfig } from "../memory-config.js";
|
|
23
|
+
import { SleepStateGatherer } from "../sleep-state-gatherer.js";
|
|
24
|
+
import { loadSleepSteps, buildSleepVars, substituteVars } from "../sleep-pipeline.js";
|
|
25
|
+
import { buildDailySummary, writeDailyFile, LLMUnavailableError } from "../sleep-pipeline.js";
|
|
26
|
+
import { extractFromDaily } from "../sleep-pipeline.js";
|
|
27
|
+
import { logInfo, logWarn, logError } from "../mem-logger.js";
|
|
28
|
+
import { redactSecrets } from "../redact-secrets.js";
|
|
29
|
+
import { localDate } from "../local-time.js";
|
|
30
|
+
import { parseLevel, DEFAULT_LEVEL } from "./levels.js";
|
|
31
|
+
const TAG = "abmind-sleep";
|
|
32
|
+
/** Format a timestamp as YYYYMMDD (for lock file names). */
|
|
33
|
+
function toDateStr(ts) {
|
|
34
|
+
const d = new Date(ts);
|
|
35
|
+
return `${d.getFullYear()}${String(d.getMonth() + 1).padStart(2, "0")}${String(d.getDate()).padStart(2, "0")}`;
|
|
36
|
+
}
|
|
37
|
+
/** Format a timestamp as YYYY-MM-DD (for daily file paths). */
|
|
38
|
+
function toIsoDate(ts) {
|
|
39
|
+
const d = new Date(ts);
|
|
40
|
+
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
41
|
+
}
|
|
42
|
+
/** Steps whose failure blocks watermark advance. Public so tests can derive reject targets. */
|
|
43
|
+
export const ESSENTIAL_STEPS = new Set(["daily-summary", "extract-from-daily", "retrospective"]);
|
|
44
|
+
const CATCHUP_MAX_AGE_DAYS = 3;
|
|
45
|
+
/** Thrown by runSleepCycle when memory layer fails to initialize. */
|
|
46
|
+
export class SleepInitError extends Error {
|
|
47
|
+
constructor(message) { super(message); this.name = "SleepInitError"; }
|
|
48
|
+
}
|
|
49
|
+
/** Thrown by runSleepCycle when the wall-clock timeout expires before completion. */
|
|
50
|
+
export class SleepTimeoutError extends Error {
|
|
51
|
+
constructor(message) { super(message); this.name = "SleepTimeoutError"; }
|
|
52
|
+
}
|
|
53
|
+
export function parseArgs(argv) {
|
|
54
|
+
const args = argv.slice(2);
|
|
55
|
+
const parsed = { dryRun: false, verbose: false, force: false };
|
|
56
|
+
for (const arg of args) {
|
|
57
|
+
switch (arg) {
|
|
58
|
+
case "--dry-run":
|
|
59
|
+
parsed.dryRun = true;
|
|
60
|
+
break;
|
|
61
|
+
case "--verbose":
|
|
62
|
+
parsed.verbose = true;
|
|
63
|
+
break;
|
|
64
|
+
case "--force":
|
|
65
|
+
parsed.force = true;
|
|
66
|
+
break;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return parsed;
|
|
70
|
+
}
|
|
71
|
+
// getPrimaryUserId moved to SleepDataAccess in memory package
|
|
72
|
+
function readStateFile(path) {
|
|
73
|
+
try {
|
|
74
|
+
if (!existsSync(path))
|
|
75
|
+
return null;
|
|
76
|
+
const raw = JSON.parse(readFileSync(path, "utf-8"));
|
|
77
|
+
if (typeof raw !== "object" || raw === null || !raw.steps)
|
|
78
|
+
return null;
|
|
79
|
+
// Backfill defaults for legacy lock files
|
|
80
|
+
if (!raw.status)
|
|
81
|
+
raw.status = "ongoing";
|
|
82
|
+
if (raw.llmCalls == null)
|
|
83
|
+
raw.llmCalls = 0;
|
|
84
|
+
return raw;
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
function writeStateFile(path, state) {
|
|
91
|
+
writeFileSync(path, JSON.stringify(state, null, 2));
|
|
92
|
+
}
|
|
93
|
+
// ── Wired pre-tasks (delegated to abmind MaintenanceService) ────────────────
|
|
94
|
+
async function runWiredPreTasks(sleepData, memoryDir, memory) {
|
|
95
|
+
const r = await memory.maintenance.runPreSleepTasks(memory, sleepData);
|
|
96
|
+
// Bridge-side: log rotation (not memory's concern)
|
|
97
|
+
let logsDeleted = 0;
|
|
98
|
+
try {
|
|
99
|
+
const logsDir = join(memoryDir, "..", "logs");
|
|
100
|
+
if (existsSync(logsDir)) {
|
|
101
|
+
const cutoff = Date.now() - 7 * 86400000;
|
|
102
|
+
for (const f of readdirSync(logsDir)) {
|
|
103
|
+
if (!f.startsWith("bridge-") || !f.endsWith(".log"))
|
|
104
|
+
continue;
|
|
105
|
+
const match = f.match(/bridge-(\d{4}-\d{2}-\d{2})\.log/);
|
|
106
|
+
if (match && new Date(match[1]).getTime() < cutoff) {
|
|
107
|
+
unlinkSync(join(logsDir, f));
|
|
108
|
+
logsDeleted++;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
catch (err) {
|
|
114
|
+
logWarn(TAG, `[WIRED] log rotation: ${err instanceof Error ? err.message : String(err)}`);
|
|
115
|
+
}
|
|
116
|
+
return { purged: r.purged, deduped: r.deduped, embedded: r.embedded, anomaliesFixed: r.anomaliesFixed, walOk: r.walOk, ftsOk: r.ftsOk, logsDeleted };
|
|
117
|
+
}
|
|
118
|
+
function formatWiredResults(r) {
|
|
119
|
+
const parts = [];
|
|
120
|
+
if (r.purged > 0)
|
|
121
|
+
parts.push(`${r.purged} garbage purged`);
|
|
122
|
+
if (r.deduped > 0)
|
|
123
|
+
parts.push(`${r.deduped} dupes deleted`);
|
|
124
|
+
parts.push(`WAL ${r.walOk ? "ok" : "FAILED"}`);
|
|
125
|
+
parts.push(`FTS ${r.ftsOk ? "ok" : "FAILED"}`);
|
|
126
|
+
if (r.embedded > 0)
|
|
127
|
+
parts.push(`${r.embedded} embedded`);
|
|
128
|
+
if (r.anomaliesFixed > 0)
|
|
129
|
+
parts.push(`${r.anomaliesFixed} anomalies fixed`);
|
|
130
|
+
if (r.logsDeleted > 0)
|
|
131
|
+
parts.push(`${r.logsDeleted} old logs deleted`);
|
|
132
|
+
return parts.length > 0 ? parts.join(", ") : "nothing to do";
|
|
133
|
+
}
|
|
134
|
+
// ── Transport ───────────────────────────────────────────────────────────────
|
|
135
|
+
// (SleepRuntime is imported at the top of the file.)
|
|
136
|
+
const MAX_RETRIES = 3;
|
|
137
|
+
/** Budget tracker — shared across all sendWithRetry calls in a sleep cycle. */
|
|
138
|
+
class LlmBudget {
|
|
139
|
+
state;
|
|
140
|
+
statePath;
|
|
141
|
+
exhausted = false;
|
|
142
|
+
constructor(state, statePath) {
|
|
143
|
+
this.state = state;
|
|
144
|
+
this.statePath = statePath;
|
|
145
|
+
}
|
|
146
|
+
/** Increment counter, return false if budget exhausted. */
|
|
147
|
+
consume() {
|
|
148
|
+
this.state.llmCalls = (this.state.llmCalls ?? 0) + 1;
|
|
149
|
+
writeStateFile(this.statePath, this.state);
|
|
150
|
+
if (this.state.llmCalls > getAbmindEnv().sleepMaxLlmCalls) {
|
|
151
|
+
this.exhausted = true;
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
return true;
|
|
155
|
+
}
|
|
156
|
+
get calls() { return this.state.llmCalls ?? 0; }
|
|
157
|
+
}
|
|
158
|
+
async function sendWithRetry(runtime, prompt, stepName, _verbose, budget) {
|
|
159
|
+
for (let attempt = 1; attempt <= MAX_RETRIES; attempt++) {
|
|
160
|
+
if (budget && !budget.consume()) {
|
|
161
|
+
logWarn(TAG, `[BUDGET] LLM call limit (${getAbmindEnv().sleepMaxLlmCalls}) reached at step ${stepName} — suspending`);
|
|
162
|
+
return null;
|
|
163
|
+
}
|
|
164
|
+
try {
|
|
165
|
+
return await runtime.complete(prompt);
|
|
166
|
+
}
|
|
167
|
+
catch (err) {
|
|
168
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
169
|
+
logWarn(TAG, `Step ${stepName} attempt ${attempt}/${MAX_RETRIES} failed: ${msg}`);
|
|
170
|
+
if (attempt === MAX_RETRIES) {
|
|
171
|
+
logError(TAG, `Step ${stepName} failed after ${MAX_RETRIES} attempts, skipping`);
|
|
172
|
+
return null;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
// ── Audit trail helpers ─────────────────────────────────────────────────────
|
|
179
|
+
function buildSnapshotSummary(snapshot) {
|
|
180
|
+
return [
|
|
181
|
+
`Working dirs: ${snapshot.workingDirs.length}`,
|
|
182
|
+
`Messages: ${snapshot.dbStats.messageCount}`,
|
|
183
|
+
`Embeddings: ${snapshot.dbStats.embeddingCount}`,
|
|
184
|
+
`Extracted memories: ${snapshot.dbStats.extractedMemoryCount}`,
|
|
185
|
+
`Disk: ${(snapshot.diskUsageBytes / 1024 / 1024).toFixed(1)} MB / ${(snapshot.diskBudgetBytes / 1024 / 1024).toFixed(0)} MB`,
|
|
186
|
+
`Topics: ${snapshot.topicFiles.length}`,
|
|
187
|
+
`FTS5: messages=${snapshot.fts5Health.messages_fts}, extracted=${snapshot.fts5Health.extracted_memories_fts}, original=${snapshot.fts5Health.extracted_memories_original_fts}`,
|
|
188
|
+
].join(", ");
|
|
189
|
+
}
|
|
190
|
+
/**
|
|
191
|
+
* Parse outcome counts from the subagent's free-form text response.
|
|
192
|
+
*
|
|
193
|
+
* The subagent response is unstructured text, so we use best-effort regex
|
|
194
|
+
* matching for common patterns like "consolidated 3 files", "pruned 42
|
|
195
|
+
* messages", etc. Returns 0 for any count that can't be parsed.
|
|
196
|
+
*/
|
|
197
|
+
function parseOutcomesFromResponse(response) {
|
|
198
|
+
const defaults = {
|
|
199
|
+
filesConsolidated: 0,
|
|
200
|
+
messagesPruned: 0,
|
|
201
|
+
embeddingsRemoved: 0,
|
|
202
|
+
sessionsCleaned: 0,
|
|
203
|
+
topicsMerged: 0,
|
|
204
|
+
topicsDeleted: 0,
|
|
205
|
+
};
|
|
206
|
+
if (!response)
|
|
207
|
+
return defaults;
|
|
208
|
+
const text = response.toLowerCase();
|
|
209
|
+
// Each pattern array contains regexes to try in order for a given outcome.
|
|
210
|
+
// We take the first match found. Patterns cover both "verb N noun" and
|
|
211
|
+
// "N noun verb" orderings that an LLM might produce.
|
|
212
|
+
const patterns = [
|
|
213
|
+
{
|
|
214
|
+
key: "filesConsolidated",
|
|
215
|
+
regexes: [
|
|
216
|
+
/consolidat\w*\s+(\d+)\s+(?:file|dir|working)/i,
|
|
217
|
+
/(\d+)\s+(?:file|dir|working\s*dir)\w*\s+consolidat/i,
|
|
218
|
+
/files?\s+consolidated\s*:\s*(\d+)/i,
|
|
219
|
+
],
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
key: "messagesPruned",
|
|
223
|
+
regexes: [
|
|
224
|
+
/prun\w*\s+(\d+)\s+message/i,
|
|
225
|
+
/(\d+)\s+message\w*\s+prun/i,
|
|
226
|
+
/(?:delet|remov)\w*\s+(\d+)\s+message/i,
|
|
227
|
+
/(\d+)\s+message\w*\s+(?:delet|remov)/i,
|
|
228
|
+
/messages?\s+pruned\s*:\s*(\d+)/i,
|
|
229
|
+
],
|
|
230
|
+
},
|
|
231
|
+
{
|
|
232
|
+
key: "embeddingsRemoved",
|
|
233
|
+
regexes: [
|
|
234
|
+
/(?:remov|delet|clean)\w*\s+(\d+)\s+embedding/i,
|
|
235
|
+
/(\d+)\s+embedding\w*\s+(?:remov|delet|clean)/i,
|
|
236
|
+
/embeddings?\s+removed\s*:\s*(\d+)/i,
|
|
237
|
+
],
|
|
238
|
+
},
|
|
239
|
+
{
|
|
240
|
+
key: "sessionsCleaned",
|
|
241
|
+
regexes: [
|
|
242
|
+
/(?:clean|delet|remov)\w*\s+(\d+)\s+session/i,
|
|
243
|
+
/(\d+)\s+session\w*\s+(?:clean|delet|remov)/i,
|
|
244
|
+
/sessions?\s+cleaned\s*:\s*(\d+)/i,
|
|
245
|
+
],
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
key: "topicsMerged",
|
|
249
|
+
regexes: [
|
|
250
|
+
/merg\w*\s+(\d+)\s+topic/i,
|
|
251
|
+
/(\d+)\s+topic\w*\s+merg/i,
|
|
252
|
+
/topics?\s+merged\s*:\s*(\d+)/i,
|
|
253
|
+
],
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
key: "topicsDeleted",
|
|
257
|
+
regexes: [
|
|
258
|
+
/delet\w*\s+(\d+)\s+topic/i,
|
|
259
|
+
/(\d+)\s+topic\w*\s+delet/i,
|
|
260
|
+
/topics?\s+deleted\s*:\s*(\d+)/i,
|
|
261
|
+
],
|
|
262
|
+
},
|
|
263
|
+
];
|
|
264
|
+
for (const { key, regexes } of patterns) {
|
|
265
|
+
for (const regex of regexes) {
|
|
266
|
+
const match = text.match(regex);
|
|
267
|
+
if (match?.[1]) {
|
|
268
|
+
const parsed = parseInt(match[1], 10);
|
|
269
|
+
if (!isNaN(parsed) && parsed >= 0) {
|
|
270
|
+
defaults[key] = parsed;
|
|
271
|
+
break;
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return defaults;
|
|
277
|
+
}
|
|
278
|
+
function writeAuditLog(memoryDir, entry) {
|
|
279
|
+
const sleepDir = join(memoryDir, "sleep");
|
|
280
|
+
mkdirSync(sleepDir, { recursive: true });
|
|
281
|
+
const suffix = [
|
|
282
|
+
``,
|
|
283
|
+
`---`,
|
|
284
|
+
``,
|
|
285
|
+
`## CLI Wrapper`,
|
|
286
|
+
``,
|
|
287
|
+
`**Timestamp:** ${entry.timestamp}`,
|
|
288
|
+
`**Model:** ${entry.model}`,
|
|
289
|
+
``,
|
|
290
|
+
`### State Snapshot`,
|
|
291
|
+
`${entry.stateSnapshotSummary}`,
|
|
292
|
+
``,
|
|
293
|
+
`### Outcomes`,
|
|
294
|
+
`- Files consolidated: ${entry.outcomes.filesConsolidated}`,
|
|
295
|
+
`- Messages pruned: ${entry.outcomes.messagesPruned}`,
|
|
296
|
+
`- Embeddings removed: ${entry.outcomes.embeddingsRemoved}`,
|
|
297
|
+
`- Sessions cleaned: ${entry.outcomes.sessionsCleaned}`,
|
|
298
|
+
`- Topics merged: ${entry.outcomes.topicsMerged}`,
|
|
299
|
+
`- Topics deleted: ${entry.outcomes.topicsDeleted}`,
|
|
300
|
+
entry.error ? `\n### Error\n${entry.error}` : "",
|
|
301
|
+
].join("\n");
|
|
302
|
+
// Find the subagent's audit file and append to it
|
|
303
|
+
const today = localDate().replace(/-/g, "");
|
|
304
|
+
try {
|
|
305
|
+
const files = readdirSync(sleepDir)
|
|
306
|
+
.filter(f => f.startsWith(`sleep_${today}`) && f.endsWith(".md"))
|
|
307
|
+
.sort();
|
|
308
|
+
if (files.length > 0) {
|
|
309
|
+
const target = join(sleepDir, files[files.length - 1]);
|
|
310
|
+
const existingLines = readFileSync(target, "utf-8").split("\n").length;
|
|
311
|
+
if (existingLines < 50) {
|
|
312
|
+
logWarn(TAG, `Sleep audit suspiciously short (${existingLines} lines) — subagent may have truncated`);
|
|
313
|
+
}
|
|
314
|
+
appendFileSync(target, redactSecrets(suffix), "utf-8");
|
|
315
|
+
return;
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
catch { /* fall through to standalone */ }
|
|
319
|
+
// Fallback: no subagent file found — write standalone
|
|
320
|
+
const now = new Date();
|
|
321
|
+
const dateStr = localDate().replace(/-/g, "");
|
|
322
|
+
const timeStr = now.toTimeString().slice(0, 5).replace(/:/g, "");
|
|
323
|
+
const filename = `sleep_${dateStr}_${timeStr}.md`;
|
|
324
|
+
writeFileSync(join(sleepDir, filename), redactSecrets(`# Sleep Audit Log${suffix}`), "utf-8");
|
|
325
|
+
}
|
|
326
|
+
function scanPreviousLocks(sleepDir, todayStr) {
|
|
327
|
+
if (!existsSync(sleepDir))
|
|
328
|
+
return [];
|
|
329
|
+
const locks = [];
|
|
330
|
+
const todayMs = dateStrToMs(todayStr);
|
|
331
|
+
for (const f of readdirSync(sleepDir)) {
|
|
332
|
+
const m = f.match(/^sleep_(\d{8})\.lock$/);
|
|
333
|
+
if (!m || m[1] === todayStr)
|
|
334
|
+
continue;
|
|
335
|
+
const state = readStateFile(join(sleepDir, f));
|
|
336
|
+
if (!state)
|
|
337
|
+
continue;
|
|
338
|
+
const ageDays = Math.round((todayMs - dateStrToMs(m[1])) / 86400000);
|
|
339
|
+
if (ageDays > 0)
|
|
340
|
+
locks.push({ path: join(sleepDir, f), dateStr: m[1], state, ageDays });
|
|
341
|
+
}
|
|
342
|
+
return locks.sort((a, b) => b.dateStr.localeCompare(a.dateStr)); // newest first
|
|
343
|
+
}
|
|
344
|
+
function dateStrToMs(ds) {
|
|
345
|
+
return new Date(`${ds.slice(0, 4)}-${ds.slice(4, 6)}-${ds.slice(6, 8)}T00:00:00`).getTime();
|
|
346
|
+
}
|
|
347
|
+
function dateStrToFormatted(ds) {
|
|
348
|
+
return `${ds.slice(0, 4)}-${ds.slice(4, 6)}-${ds.slice(6, 8)}`;
|
|
349
|
+
}
|
|
350
|
+
function failedEssentials(state) {
|
|
351
|
+
const failed = [];
|
|
352
|
+
for (const name of ESSENTIAL_STEPS) {
|
|
353
|
+
const s = state.steps[name];
|
|
354
|
+
if (!s || s.status === "failed" || s.status === "timeout" || s.status === "pending") {
|
|
355
|
+
failed.push(name);
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
return failed;
|
|
359
|
+
}
|
|
360
|
+
async function runCatchUp(locks, sleepData, memoryConfig, steps, flags, runtime, budget) {
|
|
361
|
+
for (const lock of locks) {
|
|
362
|
+
if (lock.ageDays > CATCHUP_MAX_AGE_DAYS) {
|
|
363
|
+
logError(TAG, `[CATCH-UP] Abandoning stale lock ${basename(lock.path)} — ${lock.ageDays} days old, data unrecoverable`);
|
|
364
|
+
unlinkSync(lock.path);
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
const needed = failedEssentials(lock.state);
|
|
368
|
+
if (needed.length === 0) {
|
|
369
|
+
logInfo(TAG, `[CATCH-UP] Cleaning up completed lock ${basename(lock.path)}`);
|
|
370
|
+
unlinkSync(lock.path);
|
|
371
|
+
continue;
|
|
372
|
+
}
|
|
373
|
+
logInfo(TAG, `[CATCH-UP] ${basename(lock.path)} — recovering: ${needed.join(", ")}`);
|
|
374
|
+
// 04a — daily summary with date-range
|
|
375
|
+
if (needed.includes("daily-summary")) {
|
|
376
|
+
const start = Date.now();
|
|
377
|
+
try {
|
|
378
|
+
const ctxWindow = getAbmindEnv().sleepCtxWindow;
|
|
379
|
+
const userId = sleepData.getPrimaryUserId();
|
|
380
|
+
const dayStart = dateStrToMs(lock.dateStr);
|
|
381
|
+
const dayEnd = dayStart + 86400000;
|
|
382
|
+
const summary = await buildDailySummary(sleepData.getDb(), (p) => sendWithRetry(runtime, p, "catch-up-04a", flags.verbose, budget).then(r => { if (r === null)
|
|
383
|
+
throw new LLMUnavailableError(); return r; }), {
|
|
384
|
+
ctxWindow, memoryDir: memoryConfig.memoryDir, userId, watermarkTs: 0,
|
|
385
|
+
dateRange: { startTs: dayStart, endTs: dayEnd },
|
|
386
|
+
});
|
|
387
|
+
if (summary) {
|
|
388
|
+
writeDailyFile(memoryConfig.memoryDir, dateStrToFormatted(lock.dateStr), summary);
|
|
389
|
+
lock.state.steps["daily-summary"] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10 };
|
|
390
|
+
}
|
|
391
|
+
else {
|
|
392
|
+
lock.state.steps["daily-summary"] = { status: "skipped" };
|
|
393
|
+
}
|
|
394
|
+
logInfo(TAG, `[CATCH-UP] ✓ 04a-daily-summary for ${lock.dateStr} (${((Date.now() - start) / 1000).toFixed(1)}s)`);
|
|
395
|
+
}
|
|
396
|
+
catch (err) {
|
|
397
|
+
logWarn(TAG, `[CATCH-UP] ✗ 04a-daily-summary for ${lock.dateStr}: ${err instanceof Error ? err.message : String(err)}`);
|
|
398
|
+
lock.state.steps["daily-summary"] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
|
|
399
|
+
}
|
|
400
|
+
writeStateFile(lock.path, lock.state);
|
|
401
|
+
}
|
|
402
|
+
// 04b — extract from daily (needs daily file to exist)
|
|
403
|
+
if (needed.includes("extract-from-daily")) {
|
|
404
|
+
const dailyPath = join(memoryConfig.memoryDir, "daily", `daily_${dateStrToFormatted(lock.dateStr)}.md`);
|
|
405
|
+
if (!existsSync(dailyPath)) {
|
|
406
|
+
logInfo(TAG, `[CATCH-UP] ⏭ 04b — no daily file for ${lock.dateStr}`);
|
|
407
|
+
lock.state.steps["extract-from-daily"] = { status: "skipped" };
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
const start = Date.now();
|
|
411
|
+
try {
|
|
412
|
+
const userId = sleepData.getPrimaryUserId();
|
|
413
|
+
const result = await extractFromDaily(dailyPath, userId, (p) => sendWithRetry(runtime, p, "catch-up-04b", flags.verbose, budget).then(r => { if (r === null)
|
|
414
|
+
throw new LLMUnavailableError(); return r; }));
|
|
415
|
+
lock.state.steps["extract-from-daily"] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10 };
|
|
416
|
+
logInfo(TAG, `[CATCH-UP] ✓ 04b-extract-from-daily for ${lock.dateStr} (${((Date.now() - start) / 1000).toFixed(1)}s) — ${result.slice(0, 80)}`);
|
|
417
|
+
}
|
|
418
|
+
catch (err) {
|
|
419
|
+
logWarn(TAG, `[CATCH-UP] ✗ 04b for ${lock.dateStr}: ${err instanceof Error ? err.message : String(err)}`);
|
|
420
|
+
lock.state.steps["extract-from-daily"] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
|
|
421
|
+
}
|
|
422
|
+
}
|
|
423
|
+
writeStateFile(lock.path, lock.state);
|
|
424
|
+
}
|
|
425
|
+
// Prompt-driven essentials (retrospective — now includes extraction)
|
|
426
|
+
for (const stepName of ["retrospective"]) {
|
|
427
|
+
if (!needed.includes(stepName))
|
|
428
|
+
continue;
|
|
429
|
+
const step = steps.find(s => s.name === stepName);
|
|
430
|
+
if (!step) {
|
|
431
|
+
logWarn(TAG, `[CATCH-UP] Step file not found: ${stepName}`);
|
|
432
|
+
continue;
|
|
433
|
+
}
|
|
434
|
+
const start = Date.now();
|
|
435
|
+
const response = await sendWithRetry(runtime, step.rawPrompt, `catch-up-${stepName}`, flags.verbose, budget);
|
|
436
|
+
if (response) {
|
|
437
|
+
lock.state.steps[stepName] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10 };
|
|
438
|
+
logInfo(TAG, `[CATCH-UP] ✓ ${stepName} (${((Date.now() - start) / 1000).toFixed(1)}s)`);
|
|
439
|
+
}
|
|
440
|
+
else {
|
|
441
|
+
lock.state.steps[stepName] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
|
|
442
|
+
logWarn(TAG, `[CATCH-UP] ✗ ${stepName}`);
|
|
443
|
+
}
|
|
444
|
+
writeStateFile(lock.path, lock.state);
|
|
445
|
+
}
|
|
446
|
+
// Final check: all essentials recovered?
|
|
447
|
+
const stillFailing = failedEssentials(lock.state);
|
|
448
|
+
if (stillFailing.length === 0) {
|
|
449
|
+
logInfo(TAG, `[CATCH-UP] ✅ ${basename(lock.path)} — all essentials recovered, lock deleted`);
|
|
450
|
+
unlinkSync(lock.path);
|
|
451
|
+
}
|
|
452
|
+
else {
|
|
453
|
+
logWarn(TAG, `[CATCH-UP] ${basename(lock.path)} — still failing: ${stillFailing.join(", ")} (failing ${lock.ageDays} day(s))`);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
// ── Main orchestration ──────────────────────────────────────────────────────
|
|
458
|
+
/**
|
|
459
|
+
* Run the full sleep cycle. Extracted from main() for testability (#175).
|
|
460
|
+
* - Deterministic time injection via opts.now for decision sites
|
|
461
|
+
* - Throws SleepInitError / SleepTimeoutError instead of process.exit
|
|
462
|
+
* - Returns { ok, failCount } for observable outcomes
|
|
463
|
+
*
|
|
464
|
+
* Default args preserve current main() behavior exactly.
|
|
465
|
+
*/
|
|
466
|
+
export async function runSleepCycle(opts) {
|
|
467
|
+
const flags = opts.flags ?? parseArgs(process.argv);
|
|
468
|
+
const now = opts.now ?? Date.now;
|
|
469
|
+
const timeoutMs = opts.timeoutMs ?? getAbmindEnv().sleepTimeoutMin * 60 * 1000;
|
|
470
|
+
const backoffMs = opts.backoffMs ?? ((n) => [10, 30, 60][Math.min(n, 2)] * 1000);
|
|
471
|
+
const runtime = opts.runtime;
|
|
472
|
+
if (flags.verbose) {
|
|
473
|
+
logInfo(TAG, "Verbose mode enabled");
|
|
474
|
+
}
|
|
475
|
+
const memoryConfig = { ...loadMemoryConfig(), ...opts.memoryConfigOverride };
|
|
476
|
+
const memory = new MemoryManager(memoryConfig);
|
|
477
|
+
try {
|
|
478
|
+
await memory.initialize();
|
|
479
|
+
}
|
|
480
|
+
catch (err) {
|
|
481
|
+
throw new SleepInitError(`Failed to initialize MemoryManager: ${err instanceof Error ? err.message : String(err)}`);
|
|
482
|
+
}
|
|
483
|
+
try {
|
|
484
|
+
const sleepData = memory.getSleepData();
|
|
485
|
+
// State file path — use opts.now for deterministic today derivation
|
|
486
|
+
const dateStr = toDateStr(now());
|
|
487
|
+
const statePath = join(memoryConfig.memoryDir, "sleep", `sleep_${dateStr}.lock`);
|
|
488
|
+
const existingState = readStateFile(statePath);
|
|
489
|
+
const isResume = existingState !== null && Object.values(existingState.steps).some(s => s.status === "ok");
|
|
490
|
+
// Gather state
|
|
491
|
+
// cron integration is caller-owned (bridge-only). Abmind library runs without cron awareness.
|
|
492
|
+
const cronFn = undefined;
|
|
493
|
+
const gatherer = new SleepStateGatherer(memory, memoryConfig, cronFn);
|
|
494
|
+
const snapshot = await gatherer.gather();
|
|
495
|
+
if (flags.verbose)
|
|
496
|
+
logInfo(TAG, `State gathered: ${buildSnapshotSummary(snapshot)}`);
|
|
497
|
+
// Guardrail: skip if no messages since last sleep (unless --force or resuming)
|
|
498
|
+
const msgCount = snapshot.dbStats.messagesSinceLastSleep;
|
|
499
|
+
if (msgCount === 0 && !flags.force && !isResume) {
|
|
500
|
+
logInfo(TAG, `[SLEEP] No messages since last sleep — nothing to process. Use --force to run housekeeping anyway.`);
|
|
501
|
+
return { ok: true, failCount: 0 };
|
|
502
|
+
}
|
|
503
|
+
// Wired pre-tasks (always run — fast, idempotent)
|
|
504
|
+
logInfo(TAG, `[SLEEP] Running wired pre-tasks${isResume ? " (resume)" : ""}...`);
|
|
505
|
+
const wiredResults = await runWiredPreTasks(sleepData, memoryConfig.memoryDir, memory);
|
|
506
|
+
logInfo(TAG, `[SLEEP] Wired: ${formatWiredResults(wiredResults)}`);
|
|
507
|
+
// Build candidate lists for conditional prompts
|
|
508
|
+
const candidates = sleepData.buildSleepCandidates();
|
|
509
|
+
logInfo(TAG, `[SLEEP] Candidates: topics=${candidates.untaggedMemories ? "yes" : "none"}, promote=${candidates.promotionCandidates ? "yes" : "none"}, contradict=${candidates.contradictions ? "yes" : "none"}, merge=${candidates.mergeCandidates ? "yes" : "none"}, translate=${candidates.translationIssues ? "yes" : "none"}, emotion-ctx=${candidates.emotionContextGaps ? "yes" : "none"}, feedback=${candidates.recallFeedback ? "yes" : "none"}`);
|
|
510
|
+
// Load step files + build vars
|
|
511
|
+
const vars = buildSleepVars(snapshot);
|
|
512
|
+
vars.WIRED_RESULTS = formatWiredResults(wiredResults);
|
|
513
|
+
// Inject candidate lists as template variables
|
|
514
|
+
vars.UNTAGGED_MEMORIES = candidates.untaggedMemories || "No untagged memories found.";
|
|
515
|
+
vars.PROMOTION_CANDIDATES = candidates.promotionCandidates || "No promotion candidates found.";
|
|
516
|
+
vars.CONTRADICTION_WARNINGS = candidates.contradictions || "";
|
|
517
|
+
vars.MERGE_CANDIDATES = candidates.mergeCandidates || "No merge candidates found.";
|
|
518
|
+
vars.TRANSLATION_ISSUES = candidates.translationIssues || "No translation issues found.";
|
|
519
|
+
vars.EMOTION_CONTEXT_GAPS = candidates.emotionContextGaps || "No emotion context gaps found.";
|
|
520
|
+
vars.RECALL_FEEDBACK = candidates.recallFeedback || "No recalls happened today.";
|
|
521
|
+
vars.WIRED_RESULTS = formatWiredResults(wiredResults);
|
|
522
|
+
vars.RESUME_CONTEXT = isResume
|
|
523
|
+
? `This is a RESUMED sleep cycle. Steps already completed: ${Object.entries(existingState.steps).filter(([, s]) => s.status === "ok" || s.status === "skipped").map(([k]) => k).join(", ")}. Only pending/failed steps will run.`
|
|
524
|
+
: "Fresh sleep cycle — all steps will run.";
|
|
525
|
+
// Pre-query messages for retro (watermark-scoped, noise-stripped)
|
|
526
|
+
const lastSleepTs = snapshot.lastSleepTimestamp ?? 0;
|
|
527
|
+
try {
|
|
528
|
+
const garbagePath = join(memoryConfig.memoryDir, "garbage.json");
|
|
529
|
+
const garbageIds = new Set();
|
|
530
|
+
try {
|
|
531
|
+
const raw = JSON.parse(readFileSync(garbagePath, "utf-8"));
|
|
532
|
+
const entries = Array.isArray(raw) ? raw : (raw?.messages ?? []);
|
|
533
|
+
for (const e of entries) {
|
|
534
|
+
if (e?.messageId)
|
|
535
|
+
garbageIds.add(e.messageId);
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
catch { /* no garbage file */ }
|
|
539
|
+
const msgs = sleepData.getMessagesAfter(lastSleepTs);
|
|
540
|
+
const lines = msgs
|
|
541
|
+
.filter(m => !garbageIds.has(m.id) && !m.content.startsWith("[SYSTEM"))
|
|
542
|
+
.map(m => `[${m.role}]${m.emotion_score ? ` (emotion:${m.emotion_score})` : ""} ${m.content.slice(0, 500)}`);
|
|
543
|
+
vars.CLEAN_MESSAGES = lines.length > 0
|
|
544
|
+
? `${lines.length} messages since last sleep:\n\n${lines.join("\n")}`
|
|
545
|
+
: "No messages since last sleep.";
|
|
546
|
+
logInfo(TAG, `[SLEEP] Pre-queried ${lines.length} messages for retro (${msgs.length} total, ${garbageIds.size} garbage filtered)`);
|
|
547
|
+
}
|
|
548
|
+
catch {
|
|
549
|
+
vars.CLEAN_MESSAGES = "Error loading messages — use abmind recall to search.";
|
|
550
|
+
}
|
|
551
|
+
// Set remaining missing vars
|
|
552
|
+
vars.MESSAGES_SINCE_WATERMARK = vars.CLEAN_MESSAGES; // same data, different name for gc-noise
|
|
553
|
+
vars.RETRO_PATH = join(memoryConfig.memoryDir, "daily", `daily_${toIsoDate(now())}.md`);
|
|
554
|
+
try {
|
|
555
|
+
const { getLatestConsolidationFile } = await import("../consolidation-search.js");
|
|
556
|
+
const latest = getLatestConsolidationFile(memoryConfig.memoryDir, "weekly");
|
|
557
|
+
vars.CONSOLIDATION_PATH = latest?.filePath ?? "No consolidation files yet.";
|
|
558
|
+
}
|
|
559
|
+
catch {
|
|
560
|
+
vars.CONSOLIDATION_PATH = "No consolidation files yet.";
|
|
561
|
+
}
|
|
562
|
+
const steps = loadSleepSteps();
|
|
563
|
+
// Merge snapshot vars + bridge vars into one map for JIT substitution
|
|
564
|
+
const snapshotVars = buildSleepVars(snapshot);
|
|
565
|
+
for (const [k, v] of Object.entries(snapshotVars))
|
|
566
|
+
vars[k] = vars[k] ?? v;
|
|
567
|
+
// Progress protocol — emit PROGRESS:<pct>:<label> on stdout
|
|
568
|
+
const totalSteps = steps.length;
|
|
569
|
+
let stepIndex = 0;
|
|
570
|
+
const emitProgress = (label) => {
|
|
571
|
+
const pct = Math.round((stepIndex / totalSteps) * 100);
|
|
572
|
+
process.stdout.write(`PROGRESS:${pct}:${label}\n`);
|
|
573
|
+
};
|
|
574
|
+
if (flags.dryRun) {
|
|
575
|
+
for (const step of steps)
|
|
576
|
+
process.stdout.write(`\n--- ${step.filename} ---\n${substituteVars(step.rawPrompt, vars)}\n`);
|
|
577
|
+
return { ok: true, failCount: 0 };
|
|
578
|
+
}
|
|
579
|
+
// Skip logic — candidate-driven (empty = skip)
|
|
580
|
+
const skipSet = new Set();
|
|
581
|
+
// Level tiering — controls which prompts are eligible.
|
|
582
|
+
// Precedence: opts.level > SLEEP_QUALITY env (legacy bridge path) > DEFAULT_LEVEL.
|
|
583
|
+
const quality = opts.level ?? (getAbmindEnv().sleepQuality ? parseLevel(getAbmindEnv().sleepQuality) : DEFAULT_LEVEL);
|
|
584
|
+
const curationDay = getAbmindEnv().sleepCurationDay;
|
|
585
|
+
const today = new Date(now()).toLocaleDateString("en", { weekday: "long" }).toLowerCase();
|
|
586
|
+
const isCurationDay = today === curationDay;
|
|
587
|
+
const BUDGET_ONLY = new Set(["gc-noise", "daily-summary", "extract-from-daily"]);
|
|
588
|
+
const WEEKLY_ONLY = new Set(["skill-review", "core-knowledge", "consolidation"]);
|
|
589
|
+
const ULTIMATE_ONLY = new Set(["rem-synthesis"]);
|
|
590
|
+
if (quality === "budget") {
|
|
591
|
+
for (const step of steps) {
|
|
592
|
+
if (!BUDGET_ONLY.has(step.name))
|
|
593
|
+
skipSet.add(step.name);
|
|
594
|
+
}
|
|
595
|
+
logInfo(TAG, `[SLEEP] Quality=budget — only essential extraction`);
|
|
596
|
+
}
|
|
597
|
+
else if (quality === "normal" && !isCurationDay) {
|
|
598
|
+
for (const name of WEEKLY_ONLY)
|
|
599
|
+
skipSet.add(name);
|
|
600
|
+
for (const name of ULTIMATE_ONLY)
|
|
601
|
+
skipSet.add(name);
|
|
602
|
+
logInfo(TAG, `[SLEEP] Quality=normal — weekly + ultimate prompts skipped (curation day: ${curationDay})`);
|
|
603
|
+
}
|
|
604
|
+
else if (quality === "normal" && isCurationDay) {
|
|
605
|
+
for (const name of ULTIMATE_ONLY)
|
|
606
|
+
skipSet.add(name);
|
|
607
|
+
logInfo(TAG, `[SLEEP] Quality=normal (curation day) — ultimate prompts skipped`);
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
// ultimate: only skip REM on non-curation days
|
|
611
|
+
if (!isCurationDay)
|
|
612
|
+
skipSet.add("rem-synthesis");
|
|
613
|
+
logInfo(TAG, `[SLEEP] Quality=${quality}${isCurationDay ? " (curation day)" : ""} — all eligible`);
|
|
614
|
+
}
|
|
615
|
+
// Candidate-driven skips (empty = nothing to do)
|
|
616
|
+
if (!candidates.recallFeedback)
|
|
617
|
+
skipSet.add("feedback");
|
|
618
|
+
if (!candidates.untaggedMemories)
|
|
619
|
+
skipSet.add("topic-assignment");
|
|
620
|
+
if (!candidates.promotionCandidates)
|
|
621
|
+
skipSet.add("core-promotion");
|
|
622
|
+
if (!candidates.mergeCandidates)
|
|
623
|
+
skipSet.add("merge");
|
|
624
|
+
if (!candidates.translationIssues)
|
|
625
|
+
skipSet.add("translation-check");
|
|
626
|
+
if (!candidates.translationIssues)
|
|
627
|
+
skipSet.add("translation");
|
|
628
|
+
if (!candidates.emotionContextGaps)
|
|
629
|
+
skipSet.add("emotion-context");
|
|
630
|
+
if (!candidates.emotionContextGaps)
|
|
631
|
+
skipSet.add("emotion-context-backfill");
|
|
632
|
+
// Legacy skip names (old prompt files)
|
|
633
|
+
if (snapshot.topicFiles.length === 0)
|
|
634
|
+
skipSet.add("topic-reorg");
|
|
635
|
+
if (snapshot.dbStats.extractedMemoryCount < 10) {
|
|
636
|
+
skipSet.add("merge");
|
|
637
|
+
skipSet.add("darwinism");
|
|
638
|
+
}
|
|
639
|
+
if (snapshot.dbStats.extractedMemoryCount < 20)
|
|
640
|
+
skipSet.add("rem-synthesis");
|
|
641
|
+
try {
|
|
642
|
+
if (!existsSync(join(memoryConfig.memoryDir, "..", "received")))
|
|
643
|
+
skipSet.add("media-cleanup");
|
|
644
|
+
}
|
|
645
|
+
catch { /* */ }
|
|
646
|
+
try {
|
|
647
|
+
const shortCount = sleepData.getShortMessageCount();
|
|
648
|
+
if (shortCount === 0)
|
|
649
|
+
skipSet.add("gc-noise");
|
|
650
|
+
}
|
|
651
|
+
catch { /* */ }
|
|
652
|
+
// Initialize state file
|
|
653
|
+
const state = existingState ?? {
|
|
654
|
+
status: "ongoing",
|
|
655
|
+
pid: process.pid,
|
|
656
|
+
startedAt: now(),
|
|
657
|
+
llmCalls: 0,
|
|
658
|
+
wiredResults,
|
|
659
|
+
steps: {},
|
|
660
|
+
};
|
|
661
|
+
state.status = "ongoing";
|
|
662
|
+
state.pid = process.pid;
|
|
663
|
+
state.wiredResults = wiredResults;
|
|
664
|
+
// 20-min wall-clock timeout
|
|
665
|
+
const timeoutHandle = setTimeout(() => {
|
|
666
|
+
logError(TAG, `[SLEEP] ⏰ ${Math.round(timeoutMs / 60000)}-minute timeout reached — aborting`);
|
|
667
|
+
throw new SleepTimeoutError(`Sleep cycle timeout after ${Math.round(timeoutMs / 60000)} minutes`);
|
|
668
|
+
}, timeoutMs);
|
|
669
|
+
// Resolve model name for logging. Bridge wraps and passes via env; library default is "unknown".
|
|
670
|
+
const modelUsed = getAbmindEnv().sleepModelName;
|
|
671
|
+
let dreamySucceeded = true;
|
|
672
|
+
let dailySummaryPath = null;
|
|
673
|
+
try {
|
|
674
|
+
// ── LLM call budget (hard safety limit) ──
|
|
675
|
+
const budget = new LlmBudget(state, statePath);
|
|
676
|
+
// ── Catch-up: recover failed essentials from previous days ──
|
|
677
|
+
const sleepDir = join(memoryConfig.memoryDir, "sleep");
|
|
678
|
+
const previousLocks = scanPreviousLocks(sleepDir, dateStr);
|
|
679
|
+
if (previousLocks.length > 0) {
|
|
680
|
+
logInfo(TAG, `[CATCH-UP] Found ${previousLocks.length} previous lock(s)`);
|
|
681
|
+
await runCatchUp(previousLocks, sleepData, memoryConfig, steps, flags, runtime, budget);
|
|
682
|
+
}
|
|
683
|
+
emitProgress("starting");
|
|
684
|
+
let consecutiveFailures = 0;
|
|
685
|
+
// Create day directory for per-step logs
|
|
686
|
+
const stepLogDir = join(sleepDir, dateStr);
|
|
687
|
+
mkdirSync(stepLogDir, { recursive: true });
|
|
688
|
+
for (const step of steps) {
|
|
689
|
+
// Hard safety: LLM call budget exhausted → suspend
|
|
690
|
+
if (budget.exhausted) {
|
|
691
|
+
logWarn(TAG, `[BUDGET] Suspending sleep — ${budget.calls}/${getAbmindEnv().sleepMaxLlmCalls} LLM calls used`);
|
|
692
|
+
state.status = "suspended";
|
|
693
|
+
writeStateFile(statePath, state);
|
|
694
|
+
break;
|
|
695
|
+
}
|
|
696
|
+
emitProgress(step.name);
|
|
697
|
+
stepIndex++;
|
|
698
|
+
// Resume: skip already completed steps
|
|
699
|
+
if (isResume && existingState?.steps[step.name]?.status === "ok") {
|
|
700
|
+
logInfo(TAG, `[SLEEP] ⏭ ${step.name} — already done (resume)`);
|
|
701
|
+
continue;
|
|
702
|
+
}
|
|
703
|
+
if (isResume && existingState?.steps[step.name]?.status === "skipped") {
|
|
704
|
+
logInfo(TAG, `[SLEEP] ⏭ ${step.name} — skipped (resume)`);
|
|
705
|
+
continue;
|
|
706
|
+
}
|
|
707
|
+
// Skip logic (identity and report always run)
|
|
708
|
+
if (step.skippable && skipSet.has(step.name)) {
|
|
709
|
+
logInfo(TAG, `[SLEEP] ⏭ ${step.name} — skipped`);
|
|
710
|
+
state.steps[step.name] = { status: "skipped" };
|
|
711
|
+
writeStateFile(statePath, state);
|
|
712
|
+
continue;
|
|
713
|
+
}
|
|
714
|
+
const start = Date.now();
|
|
715
|
+
logInfo(TAG, `[SLEEP] → ${step.name}`);
|
|
716
|
+
state.steps[step.name] = { status: "pending" };
|
|
717
|
+
writeStateFile(statePath, state);
|
|
718
|
+
// Code-driven steps
|
|
719
|
+
if (step.name === "daily-summary") {
|
|
720
|
+
try {
|
|
721
|
+
const ctxWindow = getAbmindEnv().sleepCtxWindow;
|
|
722
|
+
const userId = sleepData.getPrimaryUserId();
|
|
723
|
+
const watermarkTs = sleepData.getExtractionWatermark(userId);
|
|
724
|
+
// Determine target date from first unprocessed message
|
|
725
|
+
const firstMsgTs = sleepData.getFirstMessageAfter(userId, watermarkTs);
|
|
726
|
+
const firstMsgDate = firstMsgTs ? new Date(firstMsgTs) : new Date(now());
|
|
727
|
+
const targetDate = `${firstMsgDate.getFullYear()}-${String(firstMsgDate.getMonth() + 1).padStart(2, "0")}-${String(firstMsgDate.getDate()).padStart(2, "0")}`;
|
|
728
|
+
const summary = await buildDailySummary(sleepData.getDb(), (p) => sendWithRetry(runtime, p, "daily-summary", flags.verbose, budget).then(r => { if (r === null)
|
|
729
|
+
throw new LLMUnavailableError(); return r; }), {
|
|
730
|
+
ctxWindow, memoryDir: memoryConfig.memoryDir, userId, watermarkTs,
|
|
731
|
+
});
|
|
732
|
+
if (summary) {
|
|
733
|
+
dailySummaryPath = writeDailyFile(memoryConfig.memoryDir, targetDate, summary);
|
|
734
|
+
state.steps[step.name] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10, path: dailySummaryPath };
|
|
735
|
+
writeFileSync(join(stepLogDir, `${String(stepIndex).padStart(2, "0")}-${step.name}.md`), redactSecrets(summary), "utf-8");
|
|
736
|
+
}
|
|
737
|
+
else {
|
|
738
|
+
state.steps[step.name] = { status: "skipped" };
|
|
739
|
+
}
|
|
740
|
+
}
|
|
741
|
+
catch (err) {
|
|
742
|
+
logWarn(TAG, `[SLEEP] 04a failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
743
|
+
state.steps[step.name] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
|
|
744
|
+
dreamySucceeded = false;
|
|
745
|
+
}
|
|
746
|
+
writeStateFile(statePath, state);
|
|
747
|
+
logInfo(TAG, `[SLEEP] ${state.steps[step.name]?.status === "ok" ? "✓" : "✗"} ${step.name} (${((Date.now() - start) / 1000).toFixed(1)}s)`);
|
|
748
|
+
continue;
|
|
749
|
+
}
|
|
750
|
+
if (step.name === "extract-from-daily") {
|
|
751
|
+
// Resume path: if daily-summary already completed in a prior run, the
|
|
752
|
+
// in-memory dailySummaryPath is null. Recover it from the lock's
|
|
753
|
+
// recorded path so extract-from-daily can still run. #181.
|
|
754
|
+
if (!dailySummaryPath) {
|
|
755
|
+
const priorPath = state.steps["daily-summary"]?.path;
|
|
756
|
+
if (priorPath && existsSync(priorPath)) {
|
|
757
|
+
dailySummaryPath = priorPath;
|
|
758
|
+
logInfo(TAG, `[SLEEP] ${step.name} — recovered daily path from lock (${priorPath})`);
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
if (!dailySummaryPath) {
|
|
762
|
+
state.steps[step.name] = { status: "skipped" };
|
|
763
|
+
writeStateFile(statePath, state);
|
|
764
|
+
logInfo(TAG, `[SLEEP] ⏭ ${step.name} — no daily summary`);
|
|
765
|
+
continue;
|
|
766
|
+
}
|
|
767
|
+
try {
|
|
768
|
+
const userId = sleepData.getPrimaryUserId();
|
|
769
|
+
const result = await extractFromDaily(dailySummaryPath, userId, (p) => sendWithRetry(runtime, p, "04b-extract", flags.verbose, budget).then(r => { if (r === null)
|
|
770
|
+
throw new LLMUnavailableError(); return r; }));
|
|
771
|
+
state.steps[step.name] = { status: "ok", duration: Math.round((Date.now() - start) / 100) / 10 };
|
|
772
|
+
writeFileSync(join(stepLogDir, `${String(stepIndex).padStart(2, "0")}-${step.name}.md`), redactSecrets(result), "utf-8");
|
|
773
|
+
logInfo(TAG, `[SLEEP] ✓ ${step.name} (${((Date.now() - start) / 1000).toFixed(1)}s) — ${result.slice(0, 80)}`);
|
|
774
|
+
}
|
|
775
|
+
catch (err) {
|
|
776
|
+
logWarn(TAG, `[SLEEP] 04b failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
777
|
+
state.steps[step.name] = { status: "failed", duration: Math.round((Date.now() - start) / 100) / 10 };
|
|
778
|
+
dreamySucceeded = false;
|
|
779
|
+
}
|
|
780
|
+
writeStateFile(statePath, state);
|
|
781
|
+
continue;
|
|
782
|
+
}
|
|
783
|
+
// Standard prompt-driven step — JIT substitution
|
|
784
|
+
// Populate contradiction+graph vars JIT (needs today's extractions to exist)
|
|
785
|
+
if (step.name === "contradiction-and-graph") {
|
|
786
|
+
try {
|
|
787
|
+
const todayStart = new Date(now());
|
|
788
|
+
todayStart.setHours(0, 0, 0, 0);
|
|
789
|
+
const memDb = memory.getDatabase();
|
|
790
|
+
const newRows = (memDb?.prepare(`SELECT id, content_en, memory_type, topic, trust FROM extracted_memories WHERE created_at >= ? AND memory_type != 'observation' ORDER BY created_at DESC LIMIT 30`).all(todayStart.getTime()) ?? []);
|
|
791
|
+
if (newRows.length === 0) {
|
|
792
|
+
state.steps[step.name] = { status: "skipped" };
|
|
793
|
+
writeStateFile(statePath, state);
|
|
794
|
+
logInfo(TAG, `[SLEEP] ⏭ ${step.name} — no new extractions today`);
|
|
795
|
+
continue;
|
|
796
|
+
}
|
|
797
|
+
vars.NEW_EXTRACTIONS = newRows.map(r => `[id=${r.id}] (${r.memory_type}, trust=${r.trust}) ${r.content_en}`).join("\n");
|
|
798
|
+
// Find candidates: FTS5 top-5 per new memory, capped at 20 total
|
|
799
|
+
const candidateIds = new Set();
|
|
800
|
+
const candidateRows = [];
|
|
801
|
+
for (const nr of newRows.slice(0, 5)) {
|
|
802
|
+
const keywords = nr.content_en.split(/\s+/).filter(w => w.length > 3).slice(0, 3).join(" OR ");
|
|
803
|
+
if (!keywords)
|
|
804
|
+
continue;
|
|
805
|
+
try {
|
|
806
|
+
const matches = memDb.prepare(`SELECT em.id, em.content_en, em.memory_type, em.trust, em.credibility FROM extracted_memories em JOIN extracted_memories_fts fts ON em.id = fts.rowid WHERE extracted_memories_fts MATCH ? AND em.id != ? AND em.trust >= ? AND em.memory_type != 'observation' AND em.valid_to IS NULL LIMIT 5`).all(keywords, nr.id, nr.trust);
|
|
807
|
+
for (const m of matches) {
|
|
808
|
+
if (!candidateIds.has(m.id) && candidateIds.size < 20) {
|
|
809
|
+
candidateIds.add(m.id);
|
|
810
|
+
candidateRows.push(m);
|
|
811
|
+
}
|
|
812
|
+
}
|
|
813
|
+
}
|
|
814
|
+
catch { /* FTS query might fail on special chars — skip */ }
|
|
815
|
+
}
|
|
816
|
+
vars.CONTRADICTION_CANDIDATES = candidateRows.length > 0
|
|
817
|
+
? candidateRows.map(r => `[id=${r.id}] (${r.memory_type}, trust=${r.trust}, cred=${r.credibility}) ${r.content_en}`).join("\n")
|
|
818
|
+
: "No existing memories with overlapping content found.";
|
|
819
|
+
}
|
|
820
|
+
catch (err) {
|
|
821
|
+
logWarn(TAG, `[SLEEP] contradiction-and-graph var prep failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
822
|
+
state.steps[step.name] = { status: "skipped" };
|
|
823
|
+
writeStateFile(statePath, state);
|
|
824
|
+
continue;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
// Populate REM sample vars JIT
|
|
828
|
+
if (step.name === "rem-synthesis") {
|
|
829
|
+
try {
|
|
830
|
+
const memDb = memory.getDatabase();
|
|
831
|
+
const sample = memDb?.prepare(`SELECT id, content_en, memory_type, created_at FROM extracted_memories WHERE trust >= 2 AND memory_type != 'observation' AND valid_to IS NULL ORDER BY RANDOM() LIMIT 10`).all() ?? [];
|
|
832
|
+
if (sample.length < 5) {
|
|
833
|
+
state.steps[step.name] = { status: "skipped" };
|
|
834
|
+
writeStateFile(statePath, state);
|
|
835
|
+
logInfo(TAG, `[SLEEP] ⏭ ${step.name} — not enough memories for REM`);
|
|
836
|
+
continue;
|
|
837
|
+
}
|
|
838
|
+
vars.REM_SAMPLE = sample.map(r => `[${r.memory_type}, ${new Date(r.created_at).toISOString().slice(0, 10)}] ${r.content_en}`).join("\n");
|
|
839
|
+
}
|
|
840
|
+
catch {
|
|
841
|
+
state.steps[step.name] = { status: "skipped" };
|
|
842
|
+
writeStateFile(statePath, state);
|
|
843
|
+
continue;
|
|
844
|
+
}
|
|
845
|
+
}
|
|
846
|
+
const prompt = substituteVars(step.rawPrompt, vars);
|
|
847
|
+
const ctxBefore = -1;
|
|
848
|
+
const response = await sendWithRetry(runtime, prompt, step.name, flags.verbose, budget);
|
|
849
|
+
const ctxAfter = -1;
|
|
850
|
+
const duration = Date.now() - start;
|
|
851
|
+
if (response) {
|
|
852
|
+
state.steps[step.name] = { status: "ok", duration: Math.round(duration / 100) / 10, ctxBefore, ctxAfter };
|
|
853
|
+
writeFileSync(join(stepLogDir, `${String(stepIndex).padStart(2, "0")}-${step.name}.md`), redactSecrets(response), "utf-8");
|
|
854
|
+
// Generic output chaining + explicit aliases
|
|
855
|
+
vars[step.name.toUpperCase().replace(/-/g, "_") + "_OUTPUT"] = response;
|
|
856
|
+
if (step.name === "retrospective")
|
|
857
|
+
vars.RETRO_CONTENT = response;
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
state.steps[step.name] = { status: "failed", duration: Math.round(duration / 100) / 10, attempts: MAX_RETRIES, ctxBefore, ctxAfter };
|
|
861
|
+
dreamySucceeded = false;
|
|
862
|
+
}
|
|
863
|
+
writeStateFile(statePath, state);
|
|
864
|
+
logInfo(TAG, `[SLEEP] ${response ? "✓" : "✗"} ${step.name} (${(duration / 1000).toFixed(1)}s, ${response?.length ?? 0} chars)`);
|
|
865
|
+
// Backoff between steps: 10s → 30s → 60s on consecutive failures, reset on success
|
|
866
|
+
if (response) {
|
|
867
|
+
consecutiveFailures = 0;
|
|
868
|
+
}
|
|
869
|
+
else {
|
|
870
|
+
consecutiveFailures++;
|
|
871
|
+
}
|
|
872
|
+
const isEssential = step.name.startsWith("04") || step.name === "00-identity";
|
|
873
|
+
if (!isEssential) {
|
|
874
|
+
const delayMs = backoffMs(consecutiveFailures);
|
|
875
|
+
if (delayMs > 0) {
|
|
876
|
+
logInfo(TAG, `[SLEEP] Waiting ${Math.round(delayMs / 1000)}s before next step`);
|
|
877
|
+
await new Promise(r => setTimeout(r, delayMs));
|
|
878
|
+
}
|
|
879
|
+
}
|
|
880
|
+
}
|
|
881
|
+
}
|
|
882
|
+
finally {
|
|
883
|
+
clearTimeout(timeoutHandle);
|
|
884
|
+
// Runtime lifecycle is caller-owned — no shutdown call from the library.
|
|
885
|
+
}
|
|
886
|
+
// Set final status
|
|
887
|
+
if (state.status === "ongoing") {
|
|
888
|
+
state.status = dreamySucceeded ? "completed" : "failed";
|
|
889
|
+
writeStateFile(statePath, state);
|
|
890
|
+
}
|
|
891
|
+
// Advance extraction watermark — only when all steps succeeded
|
|
892
|
+
if (dreamySucceeded) {
|
|
893
|
+
try {
|
|
894
|
+
const count = sleepData.advanceExtractionWatermarks();
|
|
895
|
+
logInfo(TAG, `[SLEEP] Extraction watermark advanced for ${count} chat(s)`);
|
|
896
|
+
}
|
|
897
|
+
catch { /* non-fatal */ }
|
|
898
|
+
}
|
|
899
|
+
else {
|
|
900
|
+
logWarn(TAG, "[SLEEP] Watermark NOT advanced — essential steps failed, messages preserved for catch-up");
|
|
901
|
+
}
|
|
902
|
+
// Write audit
|
|
903
|
+
const stepEntries = Object.entries(state.steps);
|
|
904
|
+
const okCount = stepEntries.filter(([, s]) => s.status === "ok").length;
|
|
905
|
+
const failCount = stepEntries.filter(([, s]) => s.status === "failed" || s.status === "timeout").length;
|
|
906
|
+
const skipCount = stepEntries.filter(([, s]) => s.status === "skipped").length;
|
|
907
|
+
const totalDuration = (Date.now() - state.startedAt) / 1000;
|
|
908
|
+
const allResponses = stepEntries.map(([k, v]) => `[${k}] ${v.status}${v.duration ? ` (${v.duration}s)` : ""}`).join("\n");
|
|
909
|
+
try {
|
|
910
|
+
writeAuditLog(memoryConfig.memoryDir, {
|
|
911
|
+
timestamp: localISO(),
|
|
912
|
+
model: modelUsed,
|
|
913
|
+
stateSnapshotSummary: buildSnapshotSummary(snapshot),
|
|
914
|
+
subagentResponse: `Wired: ${formatWiredResults(wiredResults)}\n${allResponses}${vars.RETRO_CONTENT ? "\n\n--- Retrospective ---\n" + vars.RETRO_CONTENT : ""}`,
|
|
915
|
+
outcomes: { filesConsolidated: 0, messagesPruned: wiredResults.purged + wiredResults.deduped, embeddingsRemoved: 0, sessionsCleaned: 0, topicsMerged: 0, topicsDeleted: 0 },
|
|
916
|
+
});
|
|
917
|
+
}
|
|
918
|
+
catch (err) {
|
|
919
|
+
process.stderr.write(`Warning: Failed to write audit — ${err instanceof Error ? err.message : String(err)}\n`);
|
|
920
|
+
}
|
|
921
|
+
// Wired post-task: flush old messages (keep max 500, age out >7 days, garbage 12h)
|
|
922
|
+
if (dreamySucceeded) {
|
|
923
|
+
try {
|
|
924
|
+
// Flush garbage-marked messages
|
|
925
|
+
const garbagePath = join(memoryConfig.memoryDir, "garbage.json");
|
|
926
|
+
if (existsSync(garbagePath)) {
|
|
927
|
+
const raw = JSON.parse(readFileSync(garbagePath, "utf-8"));
|
|
928
|
+
const garbage = Array.isArray(raw) ? raw : (Array.isArray(raw?.messages) ? raw.messages : []);
|
|
929
|
+
if (garbage.length > 0) {
|
|
930
|
+
const ids = garbage.map(g => g.msg_id).filter((id) => typeof id === "number");
|
|
931
|
+
if (ids.length > 0) {
|
|
932
|
+
sleepData.deleteMessagesByIds(ids);
|
|
933
|
+
logInfo(TAG, `[SLEEP] Flushed ${ids.length} garbage messages`);
|
|
934
|
+
}
|
|
935
|
+
writeFileSync(garbagePath, "[]");
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
// Age out + cap
|
|
939
|
+
const { agedOut, capped } = sleepData.flushOldMessages({ maxAgeDays: 7, maxCount: 500 });
|
|
940
|
+
if (agedOut > 0)
|
|
941
|
+
logInfo(TAG, `[SLEEP] Flushed ${agedOut} messages >7d`);
|
|
942
|
+
if (capped > 0)
|
|
943
|
+
logInfo(TAG, `[SLEEP] Flushed ${capped} messages (cap 500)`);
|
|
944
|
+
}
|
|
945
|
+
catch (err) {
|
|
946
|
+
logWarn(TAG, `[WIRED] flush failed: ${err instanceof Error ? err.message : String(err)}`);
|
|
947
|
+
}
|
|
948
|
+
}
|
|
949
|
+
emitProgress("done");
|
|
950
|
+
logInfo(TAG, `[SLEEP] 🏁 ${okCount} ok, ${failCount} failed, ${skipCount} skipped | wired: ${formatWiredResults(wiredResults)} | ${totalDuration.toFixed(0)}s total`);
|
|
951
|
+
return { ok: failCount === 0, failCount };
|
|
952
|
+
}
|
|
953
|
+
finally {
|
|
954
|
+
memory.close();
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
// CLI entry + isDirectRun removed — the standalone entry point now lives in
|
|
958
|
+
// cli/abmind.ts as the `abmind sleep` subcommand. Library consumers call
|
|
959
|
+
// runSleepCycle(opts) directly with their own SleepRuntime.
|
|
960
|
+
//# sourceMappingURL=orchestrator.js.map
|