openclaw-hybrid-memory 2026.5.310 → 2026.6.10
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/api/plugin-runtime.ts +2 -0
- package/backends/facts-db/contradictions.ts +1 -1
- package/cli/cmd-extract-directives.ts +225 -11
- package/cli/cmd-extract-proposals.ts +5 -6
- package/cli/cmd-extract-reinforcement.ts +71 -0
- package/cli/cmd-feedback.ts +15 -9
- package/cli/commands/manage/register-reflection-pipeline.ts +247 -13
- package/cli/commands/manage/register-storage-maintenance.ts +224 -15
- package/cli/commands/manage/storage-stats-helpers.ts +13 -2
- package/cli/context.ts +9 -19
- package/cli/distill.ts +31 -1
- package/cli/register.ts +28 -38
- package/dist/api/plugin-runtime.js.map +1 -1
- package/dist/backends/agent-health-store.js.map +1 -1
- package/dist/backends/apitap-store.js.map +1 -1
- package/dist/backends/audit-store.js.map +1 -1
- package/dist/backends/base-sqlite-store.js.map +1 -1
- package/dist/backends/cost-tracker.js.map +1 -1
- package/dist/backends/credentials-db.js +2 -3
- package/dist/backends/credentials-db.js.map +1 -1
- package/dist/backends/crystallization-store.js.map +1 -1
- package/dist/backends/edict-store.js.map +1 -1
- package/dist/backends/event-bus.js.map +1 -1
- package/dist/backends/event-log.js.map +1 -1
- package/dist/backends/facts-db/cache-manager.js.map +1 -1
- package/dist/backends/facts-db/clusters.js.map +1 -1
- package/dist/backends/facts-db/contradictions.js +1 -1
- package/dist/backends/facts-db/contradictions.js.map +1 -1
- package/dist/backends/facts-db/crud.js.map +1 -1
- package/dist/backends/facts-db/db-connection.js.map +1 -1
- package/dist/backends/facts-db/entity-autolink.js.map +1 -1
- package/dist/backends/facts-db/entity-layer.js.map +1 -1
- package/dist/backends/facts-db/episodes.js.map +1 -1
- package/dist/backends/facts-db/fact-queries.js.map +1 -1
- package/dist/backends/facts-db/fact-read-queries.js.map +1 -1
- package/dist/backends/facts-db/facts-db-layer1.js.map +1 -1
- package/dist/backends/facts-db/facts-db-layer2.js.map +1 -1
- package/dist/backends/facts-db/facts-db-layer3.js.map +1 -1
- package/dist/backends/facts-db/fts-text.js.map +1 -1
- package/dist/backends/facts-db/generated-skills/policy.js.map +1 -1
- package/dist/backends/facts-db/generated-skills.js.map +1 -1
- package/dist/backends/facts-db/housekeeping.js.map +1 -1
- package/dist/backends/facts-db/links.js.map +1 -1
- package/dist/backends/facts-db/maintenance.js.map +1 -1
- package/dist/backends/facts-db/procedures/crud.js.map +1 -1
- package/dist/backends/facts-db/procedures/internal.js.map +1 -1
- package/dist/backends/facts-db/procedures/promotion.js.map +1 -1
- package/dist/backends/facts-db/procedures/search.js.map +1 -1
- package/dist/backends/facts-db/procedures/stats.js.map +1 -1
- package/dist/backends/facts-db/reinforcement.js.map +1 -1
- package/dist/backends/facts-db/row-mapper.js.map +1 -1
- package/dist/backends/facts-db/scan-cursors.js.map +1 -1
- package/dist/backends/facts-db/schema-bootstrap.js.map +1 -1
- package/dist/backends/facts-db/scope-sql.js.map +1 -1
- package/dist/backends/facts-db/search.js.map +1 -1
- package/dist/backends/facts-db/stats.js.map +1 -1
- package/dist/backends/facts-db/types.js.map +1 -1
- package/dist/backends/facts-db/variants.js.map +1 -1
- package/dist/backends/identity-reflection-store.js.map +1 -1
- package/dist/backends/issue-store.js.map +1 -1
- package/dist/backends/learnings-db.js.map +1 -1
- package/dist/backends/migrations/facts-migrations.js.map +1 -1
- package/dist/backends/migrations/procedures.js.map +1 -1
- package/dist/backends/narratives-db.js.map +1 -1
- package/dist/backends/persona-state-store.js.map +1 -1
- package/dist/backends/proposals-db.js.map +1 -1
- package/dist/backends/scope-filter-sql.js.map +1 -1
- package/dist/backends/sqlite-schema-meta.js.map +1 -1
- package/dist/backends/tool-proposal-store.js.map +1 -1
- package/dist/backends/vector-db/constants.js.map +1 -1
- package/dist/backends/vector-db/path-utils.js.map +1 -1
- package/dist/backends/vector-db/runtime-locks.js.map +1 -1
- package/dist/backends/vector-db/vector-db-class.js.map +1 -1
- package/dist/backends/wal.js.map +1 -1
- package/dist/backends/workflow-store.js.map +1 -1
- package/dist/benchmark/shadow-eval.js.map +1 -1
- package/dist/cli/active-tasks.js.map +1 -1
- package/dist/cli/backup.js.map +1 -1
- package/dist/cli/benchmark.js.map +1 -1
- package/dist/cli/cmd-backfill.js.map +1 -1
- package/dist/cli/cmd-config.js.map +1 -1
- package/dist/cli/cmd-credentials.js.map +1 -1
- package/dist/cli/cmd-demo.js.map +1 -1
- package/dist/cli/cmd-distill.js.map +1 -1
- package/dist/cli/cmd-doctor.js.map +1 -1
- package/dist/cli/cmd-examples.js.map +1 -1
- package/dist/cli/cmd-extract-daily.js.map +1 -1
- package/dist/cli/cmd-extract-directives.js +141 -10
- package/dist/cli/cmd-extract-directives.js.map +1 -1
- package/dist/cli/cmd-extract-procedures.js.map +1 -1
- package/dist/cli/cmd-extract-proposals.js +3 -2
- package/dist/cli/cmd-extract-proposals.js.map +1 -1
- package/dist/cli/cmd-extract-reinforcement.js +39 -0
- package/dist/cli/cmd-extract-reinforcement.js.map +1 -1
- package/dist/cli/cmd-extract-sessions.js.map +1 -1
- package/dist/cli/cmd-feedback.js +9 -4
- package/dist/cli/cmd-feedback.js.map +1 -1
- package/dist/cli/cmd-health.js.map +1 -1
- package/dist/cli/cmd-providers.js.map +1 -1
- package/dist/cli/cmd-selfcorrection.js.map +1 -1
- package/dist/cli/cmd-setup.js.map +1 -1
- package/dist/cli/cmd-status.js.map +1 -1
- package/dist/cli/cmd-store.js.map +1 -1
- package/dist/cli/cmd-user-friendly.js.map +1 -1
- package/dist/cli/cmd-verify.js.map +1 -1
- package/dist/cli/commands/manage/bindings.js.map +1 -1
- package/dist/cli/commands/manage/dream-cycle-followup.js.map +1 -1
- package/dist/cli/commands/manage/maintenance-heartbeat.js.map +1 -1
- package/dist/cli/commands/manage/register-agents-audit-runall.js.map +1 -1
- package/dist/cli/commands/manage/register-analyze-maintenance-logs.js.map +1 -1
- package/dist/cli/commands/manage/register-budget-proposals.js.map +1 -1
- package/dist/cli/commands/manage/register-config-cli.js.map +1 -1
- package/dist/cli/commands/manage/register-corrections-and-pipeline.js.map +1 -1
- package/dist/cli/commands/manage/register-corrections.js.map +1 -1
- package/dist/cli/commands/manage/register-council.js.map +1 -1
- package/dist/cli/commands/manage/register-credentials-scope.js.map +1 -1
- package/dist/cli/commands/manage/register-digest.js.map +1 -1
- package/dist/cli/commands/manage/register-lifecycle.js.map +1 -1
- package/dist/cli/commands/manage/register-procedure-lifecycle.js.map +1 -1
- package/dist/cli/commands/manage/register-reconcile-cron-ledgers.js.map +1 -1
- package/dist/cli/commands/manage/register-reflection-pipeline.js +144 -7
- package/dist/cli/commands/manage/register-reflection-pipeline.js.map +1 -1
- package/dist/cli/commands/manage/register-self-correction-feedback.js.map +1 -1
- package/dist/cli/commands/manage/register-storage-and-stats.js.map +1 -1
- package/dist/cli/commands/manage/register-storage-entities-decay.js.map +1 -1
- package/dist/cli/commands/manage/register-storage-graph-audit.js.map +1 -1
- package/dist/cli/commands/manage/register-storage-maintenance.js +152 -9
- package/dist/cli/commands/manage/register-storage-maintenance.js.map +1 -1
- package/dist/cli/commands/manage/register-validate-cron-exit.js.map +1 -1
- package/dist/cli/commands/manage/storage-stats-helpers.js +10 -3
- package/dist/cli/commands/manage/storage-stats-helpers.js.map +1 -1
- package/dist/cli/commands/register-manage-commands.js.map +1 -1
- package/dist/cli/config-feature-summaries.js.map +1 -1
- package/dist/cli/config-output-sink.js.map +1 -1
- package/dist/cli/distill-session-jsonl.js.map +1 -1
- package/dist/cli/distill.js +10 -1
- package/dist/cli/distill.js.map +1 -1
- package/dist/cli/global-verbose.js.map +1 -1
- package/dist/cli/goals.js.map +1 -1
- package/dist/cli/hybrid-mem-commander-utils.js.map +1 -1
- package/dist/cli/install/config-merge.js.map +1 -1
- package/dist/cli/install/cron-jobs.js.map +1 -1
- package/dist/cli/install/embedding-detect.js.map +1 -1
- package/dist/cli/install/run-install.js.map +1 -1
- package/dist/cli/install/workspace.js.map +1 -1
- package/dist/cli/proposals.js.map +1 -1
- package/dist/cli/register.js.map +1 -1
- package/dist/cli/shared.js.map +1 -1
- package/dist/cli/skills.js.map +1 -1
- package/dist/cli/task-queue-status.js.map +1 -1
- package/dist/cli/verified.js.map +1 -1
- package/dist/cli/verify/fact-count.js.map +1 -1
- package/dist/cli/verify/openclaw-config.js.map +1 -1
- package/dist/cli/verify/plugin-config-credentials.js.map +1 -1
- package/dist/cli/verify/sections/config-cron.js.map +1 -1
- package/dist/cli/verify/sections/embeddings.js.map +1 -1
- package/dist/cli/verify/sections/infrastructure.js.map +1 -1
- package/dist/cli/verify/sections/llm-models.js.map +1 -1
- package/dist/cli/verify/sections/reconcile.js.map +1 -1
- package/dist/cli/verify/verify-run-state.js.map +1 -1
- package/dist/cli/verify-llm-azure-auth.js.map +1 -1
- package/dist/cli/verify.js.map +1 -1
- package/dist/config/hybrid-schema.js.map +1 -1
- package/dist/config/index.js.map +1 -1
- package/dist/config/maintenance-fallback-policy.js.map +1 -1
- package/dist/config/parsers/capture.js.map +1 -1
- package/dist/config/parsers/core.js.map +1 -1
- package/dist/config/parsers/features.js.map +1 -1
- package/dist/config/parsers/index.js.map +1 -1
- package/dist/config/parsers/maintenance.js.map +1 -1
- package/dist/config/parsers/retrieval.js.map +1 -1
- package/dist/config/parsers/sensors.js.map +1 -1
- package/dist/config/skill-sections.js.map +1 -1
- package/dist/config/skill-size-limits.js.map +1 -1
- package/dist/config/types/agents.js.map +1 -1
- package/dist/config/types/bootstrap.js.map +1 -1
- package/dist/config/types/core.js.map +1 -1
- package/dist/config/types/index.js.map +1 -1
- package/dist/config/utils.js.map +1 -1
- package/dist/index-help.js.map +1 -1
- package/dist/index-testing-exports.js.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/lifecycle/hook-resolution-api.js.map +1 -1
- package/dist/lifecycle/hooks.js +0 -1
- package/dist/lifecycle/hooks.js.map +1 -1
- package/dist/lifecycle/resolve-agent-id.js.map +1 -1
- package/dist/lifecycle/session-state.js.map +1 -1
- package/dist/lifecycle/stage-active-task.js.map +1 -1
- package/dist/lifecycle/stage-auth-failure.js.map +1 -1
- package/dist/lifecycle/stage-capture/run-capture.js.map +1 -1
- package/dist/lifecycle/stage-capture.js.map +1 -1
- package/dist/lifecycle/stage-cleanup.js.map +1 -1
- package/dist/lifecycle/stage-credential-hint.js.map +1 -1
- package/dist/lifecycle/stage-frustration.js.map +1 -1
- package/dist/lifecycle/stage-goal-stewardship.js.map +1 -1
- package/dist/lifecycle/stage-goal-subagent.js.map +1 -1
- package/dist/lifecycle/stage-injection.js +1 -1
- package/dist/lifecycle/stage-injection.js.map +1 -1
- package/dist/lifecycle/stage-recall/run-recall.js.map +1 -1
- package/dist/lifecycle/stage-recall.js.map +1 -1
- package/dist/lifecycle/stage-setup.js.map +1 -1
- package/dist/routes/dashboard/collectors.js.map +1 -1
- package/dist/routes/dashboard/html.js.map +1 -1
- package/dist/routes/dashboard/server.js.map +1 -1
- package/dist/routes/dashboard-graph.js.map +1 -1
- package/dist/routes/graphql-resolvers.js.map +1 -1
- package/dist/routes/graphql-server.js.map +1 -1
- package/dist/services/active-task-checkpoint.js.map +1 -1
- package/dist/services/active-task-injection.js.map +1 -1
- package/dist/services/active-task.js.map +1 -1
- package/dist/services/adaptive-catch-up-pacing.js +25 -0
- package/dist/services/adaptive-catch-up-pacing.js.map +1 -0
- package/dist/services/adaptive-maintenance-llm.js.map +1 -1
- package/dist/services/adaptive-model-limits.js.map +1 -1
- package/dist/services/ambient-retrieval.js.map +1 -1
- package/dist/services/apitap-service.js.map +1 -1
- package/dist/services/audit-health-exit-info.js.map +1 -1
- package/dist/services/audit-health-json.js.map +1 -1
- package/dist/services/auth-failure-detect.js.map +1 -1
- package/dist/services/auto-capture.js.map +1 -1
- package/dist/services/auto-classifier.js.map +1 -1
- package/dist/services/auto-skills-audit.js.map +1 -1
- package/dist/services/bootstrap-optional.js.map +1 -1
- package/dist/services/bootstrap-priority.js.map +1 -1
- package/dist/services/bootstrap.js.map +1 -1
- package/dist/services/capture-provenance.js.map +1 -1
- package/dist/services/capture-utils.js.map +1 -1
- package/dist/services/chat.js +22 -3
- package/dist/services/chat.js.map +1 -1
- package/dist/services/classification-scope.js.map +1 -1
- package/dist/services/classification.js.map +1 -1
- package/dist/services/cli-sql-dump.js.map +1 -1
- package/dist/services/consolidation.js.map +1 -1
- package/dist/services/context-audit.js +1 -1
- package/dist/services/context-audit.js.map +1 -1
- package/dist/services/context-budget.js.map +1 -1
- package/dist/services/context-engine.js.map +1 -1
- package/dist/services/contextual-variants.js.map +1 -1
- package/dist/services/continuous-verifier.js.map +1 -1
- package/dist/services/contradiction-adjudicator.js.map +1 -1
- package/dist/services/cost-context.js.map +1 -1
- package/dist/services/cost-feature-labels.js.map +1 -1
- package/dist/services/credential-migration.js.map +1 -1
- package/dist/services/credential-scanner.js.map +1 -1
- package/dist/services/credential-validation.js.map +1 -1
- package/dist/services/cron-exit-validator.js.map +1 -1
- package/dist/services/cron-guard.js.map +1 -1
- package/dist/services/cron-job-bash-harness.js +52 -5
- package/dist/services/cron-job-bash-harness.js.map +1 -1
- package/dist/services/cron-maintenance-reconciler.js +1 -3
- package/dist/services/cron-maintenance-reconciler.js.map +1 -1
- package/dist/services/cross-agent-learning.js.map +1 -1
- package/dist/services/crystallization-proposer.js.map +1 -1
- package/dist/services/dedupe-policy.js.map +1 -1
- package/dist/services/deprecated-cron-commands.js.map +1 -1
- package/dist/services/directive-extract.js.map +1 -1
- package/dist/services/document-chunker.js.map +1 -1
- package/dist/services/document-grader.js.map +1 -1
- package/dist/services/dream-cycle.js.map +1 -1
- package/dist/services/embedding-migration.js.map +1 -1
- package/dist/services/embedding-registry.js.map +1 -1
- package/dist/services/embeddings/chain-provider.js.map +1 -1
- package/dist/services/embeddings/factory.js.map +1 -1
- package/dist/services/embeddings/fallback-provider.js.map +1 -1
- package/dist/services/embeddings/ollama-provider.js.map +1 -1
- package/dist/services/embeddings/onnx-provider.js.map +1 -1
- package/dist/services/embeddings/openai-provider.js.map +1 -1
- package/dist/services/embeddings/shared.js +3 -3
- package/dist/services/embeddings/shared.js.map +1 -1
- package/dist/services/embeddings/types.js.map +1 -1
- package/dist/services/entity-enrichment-adaptive.js +128 -0
- package/dist/services/entity-enrichment-adaptive.js.map +1 -0
- package/dist/services/entity-enrichment-cli.js +389 -42
- package/dist/services/entity-enrichment-cli.js.map +1 -1
- package/dist/services/entity-enrichment.js +31 -5
- package/dist/services/entity-enrichment.js.map +1 -1
- package/dist/services/error-reporter/noisy-errors.js.map +1 -1
- package/dist/services/error-reporter/sanitize.js.map +1 -1
- package/dist/services/error-reporter.js.map +1 -1
- package/dist/services/event-hub-repair.js.map +1 -1
- package/dist/services/export-memory.js.map +1 -1
- package/dist/services/fact-extraction.js.map +1 -1
- package/dist/services/feedback-effectiveness.js.map +1 -1
- package/dist/services/find-duplicates.js.map +1 -1
- package/dist/services/frustration-detector.js.map +1 -1
- package/dist/services/fts-search.js.map +1 -1
- package/dist/services/gap-detector.js.map +1 -1
- package/dist/services/generated-skill-lifecycle.js.map +1 -1
- package/dist/services/generated-skill-validation.js.map +1 -1
- package/dist/services/goal-active-task-mirror.js.map +1 -1
- package/dist/services/goal-circuit-breaker.js.map +1 -1
- package/dist/services/goal-health.js.map +1 -1
- package/dist/services/goal-registry.js.map +1 -1
- package/dist/services/goal-stewardship-heartbeat.js.map +1 -1
- package/dist/services/goal-stewardship-llm-triage.js.map +1 -1
- package/dist/services/goal-stewardship-verify-cron.js.map +1 -1
- package/dist/services/goal-stewardship.js.map +1 -1
- package/dist/services/goal-subagent.js.map +1 -1
- package/dist/services/graph-retrieval.js.map +1 -1
- package/dist/services/humanizer-score.js.map +1 -1
- package/dist/services/hybrid-mem-cron-default-job-steps.js.map +1 -1
- package/dist/services/hyde-helper.js.map +1 -1
- package/dist/services/identity-reflection.js.map +1 -1
- package/dist/services/implicit-feedback-extract.js.map +1 -1
- package/dist/services/index.js.map +1 -1
- package/dist/services/ingest-utils.js.map +1 -1
- package/dist/services/intent-template.js.map +1 -1
- package/dist/services/json-array-parser.js.map +1 -1
- package/dist/services/knowledge-gaps.js.map +1 -1
- package/dist/services/language-keywords-build.js.map +1 -1
- package/dist/services/lifecycle/github-adapter.js.map +1 -1
- package/dist/services/llm-rate-limit-headers.js +1 -2
- package/dist/services/llm-rate-limit-headers.js.map +1 -1
- package/dist/services/maintenance-auto-fix.js.map +1 -1
- package/dist/services/maintenance-log-analyzer.js +7 -1
- package/dist/services/maintenance-log-analyzer.js.map +1 -1
- package/dist/services/maintenance-timestamp.js.map +1 -1
- package/dist/services/memory-diagnostics.js.map +1 -1
- package/dist/services/memory-index.js.map +1 -1
- package/dist/services/merge-results.js.map +1 -1
- package/dist/services/model-capabilities.js.map +1 -1
- package/dist/services/model-pricing.js.map +1 -1
- package/dist/services/narrative-recall.js.map +1 -1
- package/dist/services/openclaw-session-artifact.js.map +1 -1
- package/dist/services/passive-observer.js.map +1 -1
- package/dist/services/pattern-detector-hash.js.map +1 -1
- package/dist/services/pattern-detector.js.map +1 -1
- package/dist/services/pending-autopilot/foundation.js.map +1 -1
- package/dist/services/pending-autopilot/redaction.js.map +1 -1
- package/dist/services/pending-autopilot/store.js.map +1 -1
- package/dist/services/pending-autopilot/types.js.map +1 -1
- package/dist/services/pending-digest-autopilot-cron.js.map +1 -1
- package/dist/services/pending-digest-autopilot.js.map +1 -1
- package/dist/services/pending-review-digest.js.map +1 -1
- package/dist/services/persona-proposal-triage.js.map +1 -1
- package/dist/services/persona-state-promotion.js.map +1 -1
- package/dist/services/post-compaction-recall.js.map +1 -1
- package/dist/services/pre-consolidation-flush.js.map +1 -1
- package/dist/services/pre-finalization-guard.js.map +1 -1
- package/dist/services/procedure-cluster.js.map +1 -1
- package/dist/services/procedure-extractor.js.map +1 -1
- package/dist/services/procedure-promotion/duplicate-skill-cache.js.map +1 -1
- package/dist/services/procedure-promotion-policy.js.map +1 -1
- package/dist/services/procedure-selection-metrics.js.map +1 -1
- package/dist/services/procedure-skill-eval.js.map +1 -1
- package/dist/services/procedure-skill-generator.js.map +1 -1
- package/dist/services/procedure-skill-recipe.js.map +1 -1
- package/dist/services/procedure-skill-shrink.js.map +1 -1
- package/dist/services/procedure-skill-workflow.js.map +1 -1
- package/dist/services/provenance.js.map +1 -1
- package/dist/services/public-export-bundle.js.map +1 -1
- package/dist/services/python-bridge.js.map +1 -1
- package/dist/services/query-expander.js.map +1 -1
- package/dist/services/query-validator.js.map +1 -1
- package/dist/services/recall-pipeline.js.map +1 -1
- package/dist/services/recall-timing.js.map +1 -1
- package/dist/services/recent-http-attempts.js.map +1 -1
- package/dist/services/reflection/shared.js.map +1 -1
- package/dist/services/reflection.js.map +1 -1
- package/dist/services/reinforcement-extract.js.map +1 -1
- package/dist/services/reranker.js.map +1 -1
- package/dist/services/responses-adapter.js.map +1 -1
- package/dist/services/retrieval-aliases.js.map +1 -1
- package/dist/services/retrieval-mode-policy.js.map +1 -1
- package/dist/services/retrieval-orchestrator/packing.js.map +1 -1
- package/dist/services/retrieval-orchestrator.d.ts +2 -3
- package/dist/services/retrieval-orchestrator.js.map +1 -1
- package/dist/services/rrf-fusion.js.map +1 -1
- package/dist/services/self-correction-extract.js.map +1 -1
- package/dist/services/session-observability.js.map +1 -1
- package/dist/services/session-pre-filter.js.map +1 -1
- package/dist/services/shortest-path.js.map +1 -1
- package/dist/services/skill-allowed-tools.js.map +1 -1
- package/dist/services/skill-creator-validator.js.map +1 -1
- package/dist/services/skill-crystallizer-helpers.js.map +1 -1
- package/dist/services/skill-crystallizer.js.map +1 -1
- package/dist/services/skill-description-builder.js.map +1 -1
- package/dist/services/skill-eval-synthesizer.js.map +1 -1
- package/dist/services/skill-examples-builder.js.map +1 -1
- package/dist/services/skill-frontmatter.js.map +1 -1
- package/dist/services/skill-name-validator.js.map +1 -1
- package/dist/services/skill-prompt-injection.js.map +1 -1
- package/dist/services/skill-reference-sidecar.js.map +1 -1
- package/dist/services/skill-script-bundler.js.map +1 -1
- package/dist/services/skill-validator.js.map +1 -1
- package/dist/services/startup-memory-attribution.js.map +1 -1
- package/dist/services/task-hygiene.js.map +1 -1
- package/dist/services/task-ledger/canonical.js.map +1 -1
- package/dist/services/task-ledger-facts.js.map +1 -1
- package/dist/services/task-queue-leases.js.map +1 -1
- package/dist/services/task-queue-watchdog.js.map +1 -1
- package/dist/services/tool-effectiveness.js.map +1 -1
- package/dist/services/tool-proposer.js.map +1 -1
- package/dist/services/tools-md-section.js.map +1 -1
- package/dist/services/topic-clusters.js.map +1 -1
- package/dist/services/trajectory-tracker.js.map +1 -1
- package/dist/services/vector-backend-observability.js.map +1 -1
- package/dist/services/vector-lifecycle-audit.js.map +1 -1
- package/dist/services/vector-maintenance.js.map +1 -1
- package/dist/services/vector-search.js.map +1 -1
- package/dist/services/verification-store.js.map +1 -1
- package/dist/services/verified-fact-triage.js.map +1 -1
- package/dist/services/wal-helpers.js.map +1 -1
- package/dist/services/workflow-tracker.js.map +1 -1
- package/dist/setup/bootstrap-databases.js.map +1 -1
- package/dist/setup/cli-context/cli-services.js.map +1 -1
- package/dist/setup/cli-context/help-text.js.map +1 -1
- package/dist/setup/cli-context/metadata.js.map +1 -1
- package/dist/setup/cli-context/register-cli-with-help.js.map +1 -1
- package/dist/setup/cli-context/register-full.js.map +1 -1
- package/dist/setup/cli-context/register-help.js.map +1 -1
- package/dist/setup/cost-instrumentation.js.map +1 -1
- package/dist/setup/hybrid-memory-generation-state.js.map +1 -1
- package/dist/setup/hybrid-memory-reload-coordinator.js +13 -13
- package/dist/setup/hybrid-memory-reload-coordinator.js.map +1 -1
- package/dist/setup/plugin-service.js.map +1 -1
- package/dist/setup/provider-router.js.map +1 -1
- package/dist/setup/register-context-engine.js.map +1 -1
- package/dist/setup/register-hooks.js.map +1 -1
- package/dist/setup/register-plugin.js +25 -21
- package/dist/setup/register-plugin.js.map +1 -1
- package/dist/setup/register-tools.js.map +1 -1
- package/dist/setup/reregister-policy.js +2 -2
- package/dist/setup/reregister-policy.js.map +1 -1
- package/dist/setup/tool-installers.js.map +1 -1
- package/dist/setup/workspace-bootstrap.js.map +1 -1
- package/dist/src/worker/narratives.js.map +1 -1
- package/dist/tools/apitap-tools.js.map +1 -1
- package/dist/tools/credential-tools.js.map +1 -1
- package/dist/tools/crystallization-tools.js.map +1 -1
- package/dist/tools/dashboard-routes.js.map +1 -1
- package/dist/tools/document-tools.js.map +1 -1
- package/dist/tools/goal-tools.js.map +1 -1
- package/dist/tools/graph-tools.js.map +1 -1
- package/dist/tools/issue-tools.js.map +1 -1
- package/dist/tools/memory/build-runtime.js.map +1 -1
- package/dist/tools/memory/helpers.js.map +1 -1
- package/dist/tools/memory/register-checkpoint-tools.js.map +1 -1
- package/dist/tools/memory/register-directory-tools.js.map +1 -1
- package/dist/tools/memory/register-edict-tools.js.map +1 -1
- package/dist/tools/memory/register-episode-tools.js.map +1 -1
- package/dist/tools/memory/register-recall-tools.js.map +1 -1
- package/dist/tools/memory/register-store-tools.js.map +1 -1
- package/dist/tools/memory-tools.js.map +1 -1
- package/dist/tools/persona-tools.js.map +1 -1
- package/dist/tools/provenance-tools.js.map +1 -1
- package/dist/tools/public-api-routes.js.map +1 -1
- package/dist/tools/safe-register-http-route.js.map +1 -1
- package/dist/tools/self-extension-tools.js.map +1 -1
- package/dist/tools/task-hygiene-tools.js.map +1 -1
- package/dist/tools/utility-tools.js.map +1 -1
- package/dist/tools/verification-tools.js.map +1 -1
- package/dist/tools/workflow-tools.js.map +1 -1
- package/dist/types/issue-types.js.map +1 -1
- package/dist/types/learnings-types.js.map +1 -1
- package/dist/types/memory.js.map +1 -1
- package/dist/utils/apim-gateway-fetch.js.map +1 -1
- package/dist/utils/atomic-write.js.map +1 -1
- package/dist/utils/auth-failover.js.map +1 -1
- package/dist/utils/auth.js.map +1 -1
- package/dist/utils/compaction-model-watchdog.js.map +1 -1
- package/dist/utils/consolidation-controls.js.map +1 -1
- package/dist/utils/constants.js.map +1 -1
- package/dist/utils/date-detector.js.map +1 -1
- package/dist/utils/dates.js.map +1 -1
- package/dist/utils/decay.js.map +1 -1
- package/dist/utils/duration.js.map +1 -1
- package/dist/utils/embed-call.js.map +1 -1
- package/dist/utils/entity-lookup-resolve.js.map +1 -1
- package/dist/utils/entity-mention-quality.js.map +1 -1
- package/dist/utils/entity-stopwords.js.map +1 -1
- package/dist/utils/env-manager.js.map +1 -1
- package/dist/utils/error-tracking.js.map +1 -1
- package/dist/utils/event-loop-yield.js.map +1 -1
- package/dist/utils/extract-last-user-message.js.map +1 -1
- package/dist/utils/extraction-from-template.js.map +1 -1
- package/dist/utils/fact-embeddings.js.map +1 -1
- package/dist/utils/file-snapshot.js.map +1 -1
- package/dist/utils/format.js.map +1 -1
- package/dist/utils/fs.js.map +1 -1
- package/dist/utils/gh-repo-arg.js.map +1 -1
- package/dist/utils/hybrid-mem-json-cli.js.map +1 -1
- package/dist/utils/language-keywords.js.map +1 -1
- package/dist/utils/lifecycle-generation.js.map +1 -1
- package/dist/utils/llm-json-array.js.map +1 -1
- package/dist/utils/llm-selection.js.map +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/utils/model-provider-family.js.map +1 -1
- package/dist/utils/model-tier.js.map +1 -1
- package/dist/utils/openclaw-agent-defaults.js.map +1 -1
- package/dist/utils/path.js.map +1 -1
- package/dist/utils/plugin-root.js.map +1 -1
- package/dist/utils/plugin-update-check.js.map +1 -1
- package/dist/utils/procedure-risk.js.map +1 -1
- package/dist/utils/progress-indicators.js.map +1 -1
- package/dist/utils/prompt-loader.js.map +1 -1
- package/dist/utils/provenance.js.map +1 -1
- package/dist/utils/provider-detection.js.map +1 -1
- package/dist/utils/registration-superseded.js.map +1 -1
- package/dist/utils/salience.js.map +1 -1
- package/dist/utils/sanitize-messages.js.map +1 -1
- package/dist/utils/scope-filter.js.map +1 -1
- package/dist/utils/skill-discovery.js.map +1 -1
- package/dist/utils/sqlite-file-perms.js.map +1 -1
- package/dist/utils/sqlite-outcome-compat.js.map +1 -1
- package/dist/utils/sqlite-transaction.js.map +1 -1
- package/dist/utils/stable-stringify.js.map +1 -1
- package/dist/utils/subagent-ended-utils.js.map +1 -1
- package/dist/utils/tags.js.map +1 -1
- package/dist/utils/text.js.map +1 -1
- package/dist/utils/timeout.js.map +1 -1
- package/dist/utils/typebox.js.map +1 -1
- package/dist/utils/version-check.js.map +1 -1
- package/dist/utils/wal-replay.js.map +1 -1
- package/dist/versionInfo.js.map +1 -1
- package/index.ts +2 -2
- package/lifecycle/hooks.ts +0 -1
- package/npm-shrinkwrap.json +487 -186
- package/openclaw.plugin.json +1 -1
- package/package.json +2 -2
- package/services/adaptive-catch-up-pacing.ts +28 -0
- package/services/chat.ts +34 -1
- package/services/cron-job-bash-harness.ts +52 -5
- package/services/embeddings/shared.ts +5 -2
- package/services/entity-enrichment-adaptive.ts +245 -0
- package/services/entity-enrichment-cli.ts +553 -47
- package/services/entity-enrichment.ts +43 -2
- package/services/llm-rate-limit-headers.ts +1 -4
- package/services/maintenance-log-analyzer.ts +13 -9
- package/services/reinforcement-extract.ts +19 -0
- package/setup/hybrid-memory-reload-coordinator.ts +26 -0
- package/setup/register-plugin.ts +62 -32
- package/setup/reregister-policy.ts +10 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"public-api-routes.js","names":[],"sources":["../../tools/public-api-routes.ts"],"sourcesContent":["import type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { AuditStore } from \"../backends/audit-store.js\";\nimport type { EventLog } from \"../backends/event-log.js\";\nimport type { FactsDB } from \"../backends/facts-db.js\";\nimport type { NarrativesDB } from \"../backends/narratives-db.js\";\nimport type { ActiveTaskProjectionConfig } from \"../config.js\";\nimport { buildPublicExportBundle } from \"../services/public-export-bundle.js\";\nimport {\n getActiveTaskProjectionStatus,\n readActiveTaskRowsFromFacts,\n refreshActiveTaskProjectionBestEffort,\n} from \"../services/task-ledger-facts.js\";\nimport type { ScopeFilter } from \"../types/memory.js\";\nimport { parseDuration } from \"../utils/duration.js\";\nimport { resolveWorkspacePath } from \"../utils/path.js\";\nimport { versionInfo } from \"../versionInfo.js\";\nimport type { HttpRequestHandler, HttpRouteOptions } from \"./http-route-types.js\";\nimport { type SafeRouteLogger, createSafeRegisterHttpRoute } from \"./safe-register-http-route.js\";\n\ntype ActiveTaskConfigSubset = {\n enabled: boolean;\n ledger: \"markdown\" | \"facts\";\n filePath: string;\n staleThreshold: string;\n projection: ActiveTaskProjectionConfig;\n};\n\ntype PublicApiConfig = {\n health: {\n enabled: boolean;\n authenticated: boolean;\n };\n activeTask?: Partial<ActiveTaskConfigSubset>;\n};\n\nexport interface PublicApiRoutesContext {\n cfg: PublicApiConfig;\n factsDb: FactsDB;\n narrativesDb: NarrativesDB | null;\n auditStore?: AuditStore | null;\n eventLog?: EventLog | null;\n resolvedSqlitePath?: string;\n}\n\nexport const PUBLIC_API_PREFIX = \"/plugins/memory-public\";\n\nexport const PUBLIC_API_PATHS = {\n health: \"/health\",\n search: \"/search\",\n timeline: \"/timeline\",\n stats: \"/stats\",\n export: \"/export\",\n fact: \"/fact\",\n session: \"/session\",\n activeTasks: \"/active-tasks\",\n} as const;\n\nconst JSON_HEADERS = { \"Content-Type\": \"application/json\" } as const;\nconst DEFAULT_PUBLIC_SCOPE_SENTINEL = \"__public_api_unscoped__\";\nconst DEFAULT_ACTIVE_TASK_PROJECTION: ActiveTaskProjectionConfig = {\n mode: \"readable\",\n excludeGenericTitle: true,\n titleMinChars: 0,\n dedupeBy: \"none\",\n sectioned: true,\n};\n\nfunction toJson(status: number, body: unknown): { status: number; headers: Record<string, string>; body: string } {\n return { status, headers: { ...JSON_HEADERS }, body: JSON.stringify(body) };\n}\n\nfunction parseReqUrl(url: string): URL {\n return new URL(url, \"http://localhost\");\n}\n\nfunction parseLimitParam(raw: string | null, fallback = 20, max = 200): number {\n if (raw == null || raw.trim() === \"\") return fallback;\n const parsed = Number.parseInt(raw, 10);\n if (!Number.isFinite(parsed) || parsed < 1) return fallback;\n return Math.min(parsed, max);\n}\n\nfunction parseBooleanParam(raw: string | null): boolean {\n if (!raw) return false;\n const normalized = raw.trim().toLowerCase();\n return normalized === \"1\" || normalized === \"true\" || normalized === \"yes\" || normalized === \"on\";\n}\n\nfunction resolveActiveTaskConfig(cfg: PublicApiConfig): ActiveTaskConfigSubset {\n const raw = cfg.activeTask ?? {};\n return {\n enabled: raw.enabled !== false,\n ledger: raw.ledger === \"facts\" ? \"facts\" : \"markdown\",\n filePath:\n typeof raw.filePath === \"string\" && raw.filePath.trim().length > 0 ? raw.filePath.trim() : \"ACTIVE-TASKS.md\",\n staleThreshold:\n typeof raw.staleThreshold === \"string\" && raw.staleThreshold.trim().length > 0\n ? raw.staleThreshold.trim()\n : \"24h\",\n projection:\n raw.projection && typeof raw.projection === \"object\"\n ? { ...DEFAULT_ACTIVE_TASK_PROJECTION, ...raw.projection }\n : { ...DEFAULT_ACTIVE_TASK_PROJECTION },\n };\n}\n\nfunction extractFactId(url: URL): string | null {\n const byQuery = url.searchParams.get(\"id\")?.trim();\n if (byQuery) return byQuery;\n\n const routePrefix = `${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.fact}/`;\n if (url.pathname.startsWith(routePrefix)) {\n try {\n const maybe = decodeURIComponent(url.pathname.slice(routePrefix.length)).trim();\n return maybe || null;\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\nfunction getHeader(req: { headers?: Record<string, string> }, key: string): string | null {\n if (!req.headers) return null;\n const target = key.toLowerCase();\n for (const existing of Object.keys(req.headers)) {\n if (existing.toLowerCase() === target) {\n const raw = req.headers[existing];\n if (typeof raw !== \"string\") return null;\n const trimmed = raw.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n }\n return null;\n}\n\n/**\n * SECURITY: identity headers must be populated by trusted gateway middleware.\n * Missing identity defaults to global-only visibility.\n */\nfunction resolveScopeFilter(req: { headers?: Record<string, string> }): ScopeFilter {\n const userId = getHeader(req, \"x-openclaw-user-id\");\n const agentId = getHeader(req, \"x-openclaw-agent-id\");\n const sessionId = getHeader(req, \"x-openclaw-session-id\");\n\n if (!userId && !agentId && !sessionId) {\n return { agentId: DEFAULT_PUBLIC_SCOPE_SENTINEL };\n }\n\n return { userId: userId ?? undefined, agentId: agentId ?? undefined, sessionId: sessionId ?? undefined };\n}\n\nexport function registerPublicApiRoutes(ctx: PublicApiRoutesContext, api: ClawdbotPluginApi): void {\n if (!ctx.cfg.health.enabled) return;\n if (typeof api.registerHttpRoute !== \"function\") return;\n\n const routeOpts: HttpRouteOptions = {\n authenticated: ctx.cfg.health.authenticated,\n };\n\n // Route paths flow through the safe wrapper so the OpenClaw 2026.5.4+\n // gateway no longer emits \"http route registration missing path\" warnings\n // (issue #1173): empty/whitespace paths are rejected with a clear error,\n // and trailing slashes are normalized.\n const logger = (api.logger ?? { warn: () => {} }) as SafeRouteLogger;\n const register = createSafeRegisterHttpRoute(api, logger, \"memory-public\");\n const makeRoute = (path: string, handler: HttpRequestHandler) => register(path, handler, routeOpts);\n const activeTaskCfg = resolveActiveTaskConfig(ctx.cfg);\n const activeTaskFilePath = resolveWorkspacePath(activeTaskCfg.filePath);\n const staleMinutes = (() => {\n try {\n return parseDuration(activeTaskCfg.staleThreshold);\n } catch {\n return parseDuration(\"24h\");\n }\n })();\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.health}`, async () =>\n toJson(200, {\n status: \"ok\",\n plugin: \"openclaw-hybrid-memory\",\n version: {\n pluginVersion: versionInfo.pluginVersion,\n schemaVersion: versionInfo.schemaVersion,\n },\n endpoints: Object.values(PUBLIC_API_PATHS).map((path) => `${PUBLIC_API_PREFIX}${path}`),\n }),\n );\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.search}`, async (req) => {\n const url = parseReqUrl(req.url);\n const query = (url.searchParams.get(\"q\") ?? \"\").trim();\n const limit = parseLimitParam(url.searchParams.get(\"limit\"), 10, 100);\n const scopeFilter = resolveScopeFilter(req);\n\n if (!query) {\n return toJson(400, {\n error: 'Missing required query parameter \"q\"',\n });\n }\n\n const results = ctx.factsDb.search(query, limit, { tierFilter: \"all\", scopeFilter });\n return toJson(200, {\n query,\n limit,\n count: results.length,\n results,\n });\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.timeline}`, async (req) => {\n const url = parseReqUrl(req.url);\n const limit = parseLimitParam(url.searchParams.get(\"limit\"), 20, 200);\n const scopeFilter = resolveScopeFilter(req);\n const facts = ctx.factsDb.getAll({ scopeFilter }).slice(0, limit);\n\n return toJson(200, {\n limit,\n count: facts.length,\n timeline: facts,\n });\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.stats}`, async (req) => {\n const scopeFilter = resolveScopeFilter(req);\n const scopedFacts = ctx.factsDb.getAll({ scopeFilter });\n\n const bySource: Record<string, number> = {};\n const byCategory: Record<string, number> = {};\n const byTier: Record<string, number> = { hot: 0, warm: 0, cold: 0, structural: 0 };\n let estimatedTokens = 0;\n\n for (const fact of scopedFacts) {\n bySource[fact.source] = (bySource[fact.source] || 0) + 1;\n byCategory[fact.category] = (byCategory[fact.category] || 0) + 1;\n const tier = fact.tier || \"warm\";\n byTier[tier] = (byTier[tier] || 0) + 1;\n const text = fact.summary || fact.text;\n estimatedTokens += Math.ceil(text.length / 4);\n }\n\n return toJson(200, {\n generatedAt: new Date().toISOString(),\n facts: {\n active: scopedFacts.length,\n expired: 0,\n bySource,\n byCategory,\n byTier,\n estimatedTokens,\n },\n episodes: {\n total: 0,\n },\n procedures: {\n total: 0,\n validated: 0,\n },\n provenance: {\n links: 0,\n },\n narratives: {\n recent: 0,\n },\n });\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.export}`, async (req) => {\n const url = parseReqUrl(req.url);\n const limit = parseLimitParam(url.searchParams.get(\"limit\"), 100, 1000);\n const narrativeLimit = parseLimitParam(url.searchParams.get(\"narrativeLimit\"), 20, 500);\n const scopeFilter = resolveScopeFilter(req);\n\n const bundle = buildPublicExportBundle(ctx.factsDb, ctx.narrativesDb, {\n factsLimit: limit,\n episodesLimit: limit,\n proceduresLimit: limit,\n narrativesLimit: narrativeLimit,\n linksLimit: limit,\n scopeFilter,\n });\n\n return toJson(200, bundle);\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.fact}`, async (req) => {\n const url = parseReqUrl(req.url);\n const factId = extractFactId(url);\n const scopeFilter = resolveScopeFilter(req);\n if (!factId) {\n return toJson(400, {\n error: \"Missing fact id. Use /fact?id=<uuid> (or /fact/<uuid> when supported by your gateway).\",\n });\n }\n\n let resolvedId = factId;\n let fact = ctx.factsDb.getById(resolvedId, { scopeFilter });\n\n if (!fact && resolvedId.length >= 4) {\n const prefixMatches = ctx.factsDb.findByIdPrefixScoped(resolvedId, scopeFilter);\n\n if (prefixMatches && \"ambiguous\" in prefixMatches && prefixMatches.ambiguous) {\n return toJson(409, {\n error: `Fact id prefix is ambiguous (${prefixMatches.count} matches). Use a longer id.`,\n });\n }\n if (prefixMatches && \"id\" in prefixMatches && prefixMatches.id) {\n resolvedId = prefixMatches.id;\n fact = ctx.factsDb.getById(resolvedId, { scopeFilter });\n }\n }\n\n if (!fact) {\n return toJson(404, {\n error: `Fact not found: ${factId}`,\n });\n }\n\n const outgoingLinks = ctx.factsDb.getLinksFrom(resolvedId);\n const incomingLinks = ctx.factsDb.getLinksTo(resolvedId);\n\n const linkedFactIds = [\n ...outgoingLinks.map((link) => link.targetFactId),\n ...incomingLinks.map((link) => link.sourceFactId),\n ];\n const scopedLinkedFacts = ctx.factsDb.getByIds(linkedFactIds, { scopeFilter });\n\n const filteredOutgoingLinks = outgoingLinks.filter((link) => scopedLinkedFacts.has(link.targetFactId));\n const filteredIncomingLinks = incomingLinks.filter((link) => scopedLinkedFacts.has(link.sourceFactId));\n\n return toJson(200, {\n id: resolvedId,\n fact,\n links: {\n outgoing: filteredOutgoingLinks,\n incoming: filteredIncomingLinks,\n },\n });\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.activeTasks}`, async (req) => {\n const url = parseReqUrl(req.url);\n const renderRequested = parseBooleanParam(url.searchParams.get(\"render\"));\n const includeCompleted = parseBooleanParam(url.searchParams.get(\"includeCompleted\"));\n const scopeFilter = resolveScopeFilter(req);\n\n if (!activeTaskCfg.enabled) {\n return toJson(404, {\n error: \"active_tasks_disabled\",\n });\n }\n\n let renderApplied = false;\n let renderError: string | null = null;\n if (renderRequested) {\n if (activeTaskCfg.ledger !== \"facts\") {\n renderError = \"render_requested_but_activeTask_ledger_is_not_facts\";\n } else {\n const result = await refreshActiveTaskProjectionBestEffort({\n factsDb: ctx.factsDb,\n staleMinutes,\n filePath: activeTaskFilePath,\n projection: activeTaskCfg.projection,\n reason: \"public_api_active_tasks_render\",\n source: \"public_api\",\n logger: api.logger,\n });\n renderApplied = result.rendered;\n renderError = result.error ?? null;\n }\n }\n\n const rows = readActiveTaskRowsFromFacts(ctx.factsDb, staleMinutes, scopeFilter);\n const projection = await getActiveTaskProjectionStatus(ctx.factsDb, activeTaskFilePath, {\n scopeFilter,\n latestProjectFactSec: rows.latestProjectFactSec,\n });\n const warnings: string[] = [];\n if (activeTaskCfg.ledger !== \"facts\") {\n warnings.push(\"activeTask.ledger is markdown; DB snapshot may diverge from markdown ledger.\");\n }\n if (projection.stale) {\n warnings.push(`ACTIVE-TASKS.md projection is stale (${projection.staleReasons.join(\", \")}).`);\n }\n\n return toJson(200, {\n generatedAt: new Date().toISOString(),\n source: \"category:project\",\n staleThresholdMinutes: staleMinutes,\n ledger: activeTaskCfg.ledger,\n active: rows.active,\n activeCount: rows.active.length,\n staleCount: rows.active.filter((row) => row.stale).length,\n ...(includeCompleted ? { completed: rows.completed, completedCount: rows.completed.length } : {}),\n projection,\n render: {\n requested: renderRequested,\n applied: renderApplied,\n error: renderError,\n },\n warnings,\n });\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.session}`, async (req) => {\n const url = parseReqUrl(req.url);\n const sessionId = url.searchParams.get(\"sessionId\")?.trim() ?? null;\n const agentId = url.searchParams.get(\"agentId\")?.trim() ?? null;\n const limit = parseLimitParam(url.searchParams.get(\"limit\"), 50, 200);\n\n const { buildSessionObservabilityReport } = await import(\"../services/session-observability.js\");\n\n const report = await buildSessionObservabilityReport({\n factsDb: ctx.factsDb,\n eventLog: ctx.eventLog ?? null,\n narrativesDb: ctx.narrativesDb ?? null,\n auditStore: ctx.auditStore ?? null,\n sessionId,\n agentId,\n limit,\n });\n\n return toJson(200, report);\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"public-api-routes.js","names":[],"sources":["../../tools/public-api-routes.ts"],"sourcesContent":["import type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { AuditStore } from \"../backends/audit-store.js\";\nimport type { EventLog } from \"../backends/event-log.js\";\nimport type { FactsDB } from \"../backends/facts-db.js\";\nimport type { NarrativesDB } from \"../backends/narratives-db.js\";\nimport type { ActiveTaskProjectionConfig } from \"../config.js\";\nimport { buildPublicExportBundle } from \"../services/public-export-bundle.js\";\nimport {\n getActiveTaskProjectionStatus,\n readActiveTaskRowsFromFacts,\n refreshActiveTaskProjectionBestEffort,\n} from \"../services/task-ledger-facts.js\";\nimport type { ScopeFilter } from \"../types/memory.js\";\nimport { parseDuration } from \"../utils/duration.js\";\nimport { resolveWorkspacePath } from \"../utils/path.js\";\nimport { versionInfo } from \"../versionInfo.js\";\nimport type { HttpRequestHandler, HttpRouteOptions } from \"./http-route-types.js\";\nimport { type SafeRouteLogger, createSafeRegisterHttpRoute } from \"./safe-register-http-route.js\";\n\ntype ActiveTaskConfigSubset = {\n enabled: boolean;\n ledger: \"markdown\" | \"facts\";\n filePath: string;\n staleThreshold: string;\n projection: ActiveTaskProjectionConfig;\n};\n\ntype PublicApiConfig = {\n health: {\n enabled: boolean;\n authenticated: boolean;\n };\n activeTask?: Partial<ActiveTaskConfigSubset>;\n};\n\nexport interface PublicApiRoutesContext {\n cfg: PublicApiConfig;\n factsDb: FactsDB;\n narrativesDb: NarrativesDB | null;\n auditStore?: AuditStore | null;\n eventLog?: EventLog | null;\n resolvedSqlitePath?: string;\n}\n\nexport const PUBLIC_API_PREFIX = \"/plugins/memory-public\";\n\nexport const PUBLIC_API_PATHS = {\n health: \"/health\",\n search: \"/search\",\n timeline: \"/timeline\",\n stats: \"/stats\",\n export: \"/export\",\n fact: \"/fact\",\n session: \"/session\",\n activeTasks: \"/active-tasks\",\n} as const;\n\nconst JSON_HEADERS = { \"Content-Type\": \"application/json\" } as const;\nconst DEFAULT_PUBLIC_SCOPE_SENTINEL = \"__public_api_unscoped__\";\nconst DEFAULT_ACTIVE_TASK_PROJECTION: ActiveTaskProjectionConfig = {\n mode: \"readable\",\n excludeGenericTitle: true,\n titleMinChars: 0,\n dedupeBy: \"none\",\n sectioned: true,\n};\n\nfunction toJson(status: number, body: unknown): { status: number; headers: Record<string, string>; body: string } {\n return { status, headers: { ...JSON_HEADERS }, body: JSON.stringify(body) };\n}\n\nfunction parseReqUrl(url: string): URL {\n return new URL(url, \"http://localhost\");\n}\n\nfunction parseLimitParam(raw: string | null, fallback = 20, max = 200): number {\n if (raw == null || raw.trim() === \"\") return fallback;\n const parsed = Number.parseInt(raw, 10);\n if (!Number.isFinite(parsed) || parsed < 1) return fallback;\n return Math.min(parsed, max);\n}\n\nfunction parseBooleanParam(raw: string | null): boolean {\n if (!raw) return false;\n const normalized = raw.trim().toLowerCase();\n return normalized === \"1\" || normalized === \"true\" || normalized === \"yes\" || normalized === \"on\";\n}\n\nfunction resolveActiveTaskConfig(cfg: PublicApiConfig): ActiveTaskConfigSubset {\n const raw = cfg.activeTask ?? {};\n return {\n enabled: raw.enabled !== false,\n ledger: raw.ledger === \"facts\" ? \"facts\" : \"markdown\",\n filePath:\n typeof raw.filePath === \"string\" && raw.filePath.trim().length > 0 ? raw.filePath.trim() : \"ACTIVE-TASKS.md\",\n staleThreshold:\n typeof raw.staleThreshold === \"string\" && raw.staleThreshold.trim().length > 0\n ? raw.staleThreshold.trim()\n : \"24h\",\n projection:\n raw.projection && typeof raw.projection === \"object\"\n ? { ...DEFAULT_ACTIVE_TASK_PROJECTION, ...raw.projection }\n : { ...DEFAULT_ACTIVE_TASK_PROJECTION },\n };\n}\n\nfunction extractFactId(url: URL): string | null {\n const byQuery = url.searchParams.get(\"id\")?.trim();\n if (byQuery) return byQuery;\n\n const routePrefix = `${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.fact}/`;\n if (url.pathname.startsWith(routePrefix)) {\n try {\n const maybe = decodeURIComponent(url.pathname.slice(routePrefix.length)).trim();\n return maybe || null;\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\nfunction getHeader(req: { headers?: Record<string, string> }, key: string): string | null {\n if (!req.headers) return null;\n const target = key.toLowerCase();\n for (const existing of Object.keys(req.headers)) {\n if (existing.toLowerCase() === target) {\n const raw = req.headers[existing];\n if (typeof raw !== \"string\") return null;\n const trimmed = raw.trim();\n return trimmed.length > 0 ? trimmed : null;\n }\n }\n return null;\n}\n\n/**\n * SECURITY: identity headers must be populated by trusted gateway middleware.\n * Missing identity defaults to global-only visibility.\n */\nfunction resolveScopeFilter(req: { headers?: Record<string, string> }): ScopeFilter {\n const userId = getHeader(req, \"x-openclaw-user-id\");\n const agentId = getHeader(req, \"x-openclaw-agent-id\");\n const sessionId = getHeader(req, \"x-openclaw-session-id\");\n\n if (!userId && !agentId && !sessionId) {\n return { agentId: DEFAULT_PUBLIC_SCOPE_SENTINEL };\n }\n\n return { userId: userId ?? undefined, agentId: agentId ?? undefined, sessionId: sessionId ?? undefined };\n}\n\nexport function registerPublicApiRoutes(ctx: PublicApiRoutesContext, api: ClawdbotPluginApi): void {\n if (!ctx.cfg.health.enabled) return;\n if (typeof api.registerHttpRoute !== \"function\") return;\n\n const routeOpts: HttpRouteOptions = {\n authenticated: ctx.cfg.health.authenticated,\n };\n\n // Route paths flow through the safe wrapper so the OpenClaw 2026.5.4+\n // gateway no longer emits \"http route registration missing path\" warnings\n // (issue #1173): empty/whitespace paths are rejected with a clear error,\n // and trailing slashes are normalized.\n const logger = (api.logger ?? { warn: () => {} }) as SafeRouteLogger;\n const register = createSafeRegisterHttpRoute(api, logger, \"memory-public\");\n const makeRoute = (path: string, handler: HttpRequestHandler) => register(path, handler, routeOpts);\n const activeTaskCfg = resolveActiveTaskConfig(ctx.cfg);\n const activeTaskFilePath = resolveWorkspacePath(activeTaskCfg.filePath);\n const staleMinutes = (() => {\n try {\n return parseDuration(activeTaskCfg.staleThreshold);\n } catch {\n return parseDuration(\"24h\");\n }\n })();\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.health}`, async () =>\n toJson(200, {\n status: \"ok\",\n plugin: \"openclaw-hybrid-memory\",\n version: {\n pluginVersion: versionInfo.pluginVersion,\n schemaVersion: versionInfo.schemaVersion,\n },\n endpoints: Object.values(PUBLIC_API_PATHS).map((path) => `${PUBLIC_API_PREFIX}${path}`),\n }),\n );\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.search}`, async (req) => {\n const url = parseReqUrl(req.url);\n const query = (url.searchParams.get(\"q\") ?? \"\").trim();\n const limit = parseLimitParam(url.searchParams.get(\"limit\"), 10, 100);\n const scopeFilter = resolveScopeFilter(req);\n\n if (!query) {\n return toJson(400, {\n error: 'Missing required query parameter \"q\"',\n });\n }\n\n const results = ctx.factsDb.search(query, limit, { tierFilter: \"all\", scopeFilter });\n return toJson(200, {\n query,\n limit,\n count: results.length,\n results,\n });\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.timeline}`, async (req) => {\n const url = parseReqUrl(req.url);\n const limit = parseLimitParam(url.searchParams.get(\"limit\"), 20, 200);\n const scopeFilter = resolveScopeFilter(req);\n const facts = ctx.factsDb.getAll({ scopeFilter }).slice(0, limit);\n\n return toJson(200, {\n limit,\n count: facts.length,\n timeline: facts,\n });\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.stats}`, async (req) => {\n const scopeFilter = resolveScopeFilter(req);\n const scopedFacts = ctx.factsDb.getAll({ scopeFilter });\n\n const bySource: Record<string, number> = {};\n const byCategory: Record<string, number> = {};\n const byTier: Record<string, number> = { hot: 0, warm: 0, cold: 0, structural: 0 };\n let estimatedTokens = 0;\n\n for (const fact of scopedFacts) {\n bySource[fact.source] = (bySource[fact.source] || 0) + 1;\n byCategory[fact.category] = (byCategory[fact.category] || 0) + 1;\n const tier = fact.tier || \"warm\";\n byTier[tier] = (byTier[tier] || 0) + 1;\n const text = fact.summary || fact.text;\n estimatedTokens += Math.ceil(text.length / 4);\n }\n\n return toJson(200, {\n generatedAt: new Date().toISOString(),\n facts: {\n active: scopedFacts.length,\n expired: 0,\n bySource,\n byCategory,\n byTier,\n estimatedTokens,\n },\n episodes: {\n total: 0,\n },\n procedures: {\n total: 0,\n validated: 0,\n },\n provenance: {\n links: 0,\n },\n narratives: {\n recent: 0,\n },\n });\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.export}`, async (req) => {\n const url = parseReqUrl(req.url);\n const limit = parseLimitParam(url.searchParams.get(\"limit\"), 100, 1000);\n const narrativeLimit = parseLimitParam(url.searchParams.get(\"narrativeLimit\"), 20, 500);\n const scopeFilter = resolveScopeFilter(req);\n\n const bundle = buildPublicExportBundle(ctx.factsDb, ctx.narrativesDb, {\n factsLimit: limit,\n episodesLimit: limit,\n proceduresLimit: limit,\n narrativesLimit: narrativeLimit,\n linksLimit: limit,\n scopeFilter,\n });\n\n return toJson(200, bundle);\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.fact}`, async (req) => {\n const url = parseReqUrl(req.url);\n const factId = extractFactId(url);\n const scopeFilter = resolveScopeFilter(req);\n if (!factId) {\n return toJson(400, {\n error: \"Missing fact id. Use /fact?id=<uuid> (or /fact/<uuid> when supported by your gateway).\",\n });\n }\n\n let resolvedId = factId;\n let fact = ctx.factsDb.getById(resolvedId, { scopeFilter });\n\n if (!fact && resolvedId.length >= 4) {\n const prefixMatches = ctx.factsDb.findByIdPrefixScoped(resolvedId, scopeFilter);\n\n if (prefixMatches && \"ambiguous\" in prefixMatches && prefixMatches.ambiguous) {\n return toJson(409, {\n error: `Fact id prefix is ambiguous (${prefixMatches.count} matches). Use a longer id.`,\n });\n }\n if (prefixMatches && \"id\" in prefixMatches && prefixMatches.id) {\n resolvedId = prefixMatches.id;\n fact = ctx.factsDb.getById(resolvedId, { scopeFilter });\n }\n }\n\n if (!fact) {\n return toJson(404, {\n error: `Fact not found: ${factId}`,\n });\n }\n\n const outgoingLinks = ctx.factsDb.getLinksFrom(resolvedId);\n const incomingLinks = ctx.factsDb.getLinksTo(resolvedId);\n\n const linkedFactIds = [\n ...outgoingLinks.map((link) => link.targetFactId),\n ...incomingLinks.map((link) => link.sourceFactId),\n ];\n const scopedLinkedFacts = ctx.factsDb.getByIds(linkedFactIds, { scopeFilter });\n\n const filteredOutgoingLinks = outgoingLinks.filter((link) => scopedLinkedFacts.has(link.targetFactId));\n const filteredIncomingLinks = incomingLinks.filter((link) => scopedLinkedFacts.has(link.sourceFactId));\n\n return toJson(200, {\n id: resolvedId,\n fact,\n links: {\n outgoing: filteredOutgoingLinks,\n incoming: filteredIncomingLinks,\n },\n });\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.activeTasks}`, async (req) => {\n const url = parseReqUrl(req.url);\n const renderRequested = parseBooleanParam(url.searchParams.get(\"render\"));\n const includeCompleted = parseBooleanParam(url.searchParams.get(\"includeCompleted\"));\n const scopeFilter = resolveScopeFilter(req);\n\n if (!activeTaskCfg.enabled) {\n return toJson(404, {\n error: \"active_tasks_disabled\",\n });\n }\n\n let renderApplied = false;\n let renderError: string | null = null;\n if (renderRequested) {\n if (activeTaskCfg.ledger !== \"facts\") {\n renderError = \"render_requested_but_activeTask_ledger_is_not_facts\";\n } else {\n const result = await refreshActiveTaskProjectionBestEffort({\n factsDb: ctx.factsDb,\n staleMinutes,\n filePath: activeTaskFilePath,\n projection: activeTaskCfg.projection,\n reason: \"public_api_active_tasks_render\",\n source: \"public_api\",\n logger: api.logger,\n });\n renderApplied = result.rendered;\n renderError = result.error ?? null;\n }\n }\n\n const rows = readActiveTaskRowsFromFacts(ctx.factsDb, staleMinutes, scopeFilter);\n const projection = await getActiveTaskProjectionStatus(ctx.factsDb, activeTaskFilePath, {\n scopeFilter,\n latestProjectFactSec: rows.latestProjectFactSec,\n });\n const warnings: string[] = [];\n if (activeTaskCfg.ledger !== \"facts\") {\n warnings.push(\"activeTask.ledger is markdown; DB snapshot may diverge from markdown ledger.\");\n }\n if (projection.stale) {\n warnings.push(`ACTIVE-TASKS.md projection is stale (${projection.staleReasons.join(\", \")}).`);\n }\n\n return toJson(200, {\n generatedAt: new Date().toISOString(),\n source: \"category:project\",\n staleThresholdMinutes: staleMinutes,\n ledger: activeTaskCfg.ledger,\n active: rows.active,\n activeCount: rows.active.length,\n staleCount: rows.active.filter((row) => row.stale).length,\n ...(includeCompleted ? { completed: rows.completed, completedCount: rows.completed.length } : {}),\n projection,\n render: {\n requested: renderRequested,\n applied: renderApplied,\n error: renderError,\n },\n warnings,\n });\n });\n\n makeRoute(`${PUBLIC_API_PREFIX}${PUBLIC_API_PATHS.session}`, async (req) => {\n const url = parseReqUrl(req.url);\n const sessionId = url.searchParams.get(\"sessionId\")?.trim() ?? null;\n const agentId = url.searchParams.get(\"agentId\")?.trim() ?? null;\n const limit = parseLimitParam(url.searchParams.get(\"limit\"), 50, 200);\n\n const { buildSessionObservabilityReport } = await import(\"../services/session-observability.js\");\n\n const report = await buildSessionObservabilityReport({\n factsDb: ctx.factsDb,\n eventLog: ctx.eventLog ?? null,\n narrativesDb: ctx.narrativesDb ?? null,\n auditStore: ctx.auditStore ?? null,\n sessionId,\n agentId,\n limit,\n });\n\n return toJson(200, report);\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"safe-register-http-route.js","names":[],"sources":["../../tools/safe-register-http-route.ts"],"sourcesContent":["/**\n * Safe wrapper around `api.registerHttpRoute` (issue #1173).\n *\n * The OpenClaw 2026.5.4+ gateway emits the warning\n * `[plugins] http route registration missing path`\n * when a plugin attempts to register an HTTP route with a missing or\n * empty-after-normalization path string. This wrapper:\n *\n * 1. Validates the path is a non-empty string starting with `/`.\n * 2. Normalizes trailing slashes (so `/plugins/memory-dashboard/` becomes\n * `/plugins/memory-dashboard` — gateway treats the trailing slash as a\n * separate, empty sub-path).\n * 3. De-duplicates registrations that collapse to the same normalized path.\n * 4. Surfaces a clear, actionable plugin-side error instead of silently\n * letting the gateway log a generic warning.\n *\n * OpenClaw 2026.5+ expects a single params object and a Node `(req, res)` handler.\n * This wrapper keeps the legacy `(path, legacyHandler, opts)` call shape used\n * by dashboard and public API modules and adapts handlers before registration.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\nimport type {\n HttpRequestHandler,\n HttpRouteOptions,\n RegisterHttpRouteGatewayAuth,\n RegisterHttpRouteGatewayHandler,\n RegisterHttpRouteGatewayParams,\n} from \"./http-route-types.js\";\n\nexport type RegisterHttpRouteFn = (path: string, handler: HttpRequestHandler, opts: HttpRouteOptions) => void;\n\ntype GatewayRegisterHttpRoute = (params: RegisterHttpRouteGatewayParams) => void;\n\nfunction incomingHeadersToRecord(headers: IncomingMessage[\"headers\"]): Record<string, string> {\n const out: Record<string, string> = {};\n if (!headers || typeof headers !== \"object\") return out;\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n out[key] = Array.isArray(value) ? value.join(\", \") : String(value);\n }\n return out;\n}\n\nfunction adaptLegacyHandlerToNode(\n legacy: HttpRequestHandler,\n logger: SafeRouteLogger,\n source: string,\n): RegisterHttpRouteGatewayHandler {\n return async (req: IncomingMessage, res: ServerResponse) => {\n try {\n const legacyReq = {\n method: req.method ?? \"GET\",\n url: req.url ?? \"/\",\n headers: incomingHeadersToRecord(req.headers),\n };\n const result = await legacy(legacyReq);\n res.writeHead(result.status, result.headers ?? {});\n res.end(result.body);\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n capturePluginError(e, {\n operation: \"http-legacy-route-handler\",\n subsystem: \"memory-hybrid-http\",\n metadata: { source },\n });\n (logger.error ?? logger.warn).call(logger, `${source}: HTTP route handler error: ${e.message}`);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n }\n res.end(\"Internal Server Error\");\n }\n };\n}\n\n/** Lightweight logger surface compatible with the OpenClaw plugin logger. */\nexport interface SafeRouteLogger {\n warn: (msg: string) => void;\n error?: (msg: string) => void;\n}\n\n/**\n * Normalize a route path:\n * - trims whitespace\n * - collapses repeated leading slashes to one\n * - strips trailing slashes (keeps the literal `/` for root)\n *\n * Returns the normalized string, or `null` if normalization would produce an\n * empty/invalid path (which is the case the gateway warns about).\n */\nexport function normalizeRoutePath(rawPath: unknown): string | null {\n if (typeof rawPath !== \"string\") return null;\n const trimmed = rawPath.trim();\n if (trimmed.length === 0) return null;\n if (!trimmed.startsWith(\"/\")) return null;\n\n // Collapse repeated leading slashes (e.g. \"//foo\" -> \"/foo\").\n let normalized = trimmed.replace(/^\\/+/, \"/\");\n // Strip trailing slashes except the literal root path.\n if (normalized.length > 1) {\n normalized = normalized.replace(/\\/+$/, \"\");\n }\n if (normalized.length === 0) return null;\n return normalized;\n}\n\n/**\n * Build a safe `registerHttpRoute` callable bound to the given plugin API and\n * logger. The returned function silently no-ops (with a clear warn log) if\n * the underlying gateway does not implement `registerHttpRoute`.\n */\nexport function createSafeRegisterHttpRoute(\n api: ClawdbotPluginApi,\n logger: SafeRouteLogger,\n source: string,\n): RegisterHttpRouteFn {\n const seen = new Set<string>();\n const register = api.registerHttpRoute as GatewayRegisterHttpRoute | undefined;\n\n return (rawPath, handler, opts) => {\n const normalized = normalizeRoutePath(rawPath);\n if (normalized === null) {\n const message = `${source}: refusing to register HTTP route with invalid path (received ${JSON.stringify(rawPath)}). Path must be a non-empty string starting with '/'.`;\n (logger.error ?? logger.warn).call(logger, message);\n return;\n }\n\n if (typeof register !== \"function\") {\n logger.warn(\n `${source}: api.registerHttpRoute is not available on this gateway; ` + `skipping route ${normalized}.`,\n );\n return;\n }\n\n if (seen.has(normalized)) {\n logger.warn(`${source}: duplicate HTTP route registration for ${normalized} ignored.`);\n return;\n }\n seen.add(normalized);\n\n const auth: RegisterHttpRouteGatewayAuth = opts.authenticated ? \"gateway\" : \"plugin\";\n register({\n path: normalized,\n handler: adaptLegacyHandlerToNode(handler, logger, source),\n auth,\n match: \"exact\",\n });\n };\n}\n"],"mappings":";;AAoCA,SAAS,wBAAwB,SAA6D;CAC5F,MAAM,MAA8B,
|
|
1
|
+
{"version":3,"file":"safe-register-http-route.js","names":[],"sources":["../../tools/safe-register-http-route.ts"],"sourcesContent":["/**\n * Safe wrapper around `api.registerHttpRoute` (issue #1173).\n *\n * The OpenClaw 2026.5.4+ gateway emits the warning\n * `[plugins] http route registration missing path`\n * when a plugin attempts to register an HTTP route with a missing or\n * empty-after-normalization path string. This wrapper:\n *\n * 1. Validates the path is a non-empty string starting with `/`.\n * 2. Normalizes trailing slashes (so `/plugins/memory-dashboard/` becomes\n * `/plugins/memory-dashboard` — gateway treats the trailing slash as a\n * separate, empty sub-path).\n * 3. De-duplicates registrations that collapse to the same normalized path.\n * 4. Surfaces a clear, actionable plugin-side error instead of silently\n * letting the gateway log a generic warning.\n *\n * OpenClaw 2026.5+ expects a single params object and a Node `(req, res)` handler.\n * This wrapper keeps the legacy `(path, legacyHandler, opts)` call shape used\n * by dashboard and public API modules and adapts handlers before registration.\n */\n\nimport type { IncomingMessage, ServerResponse } from \"node:http\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\nimport type {\n HttpRequestHandler,\n HttpRouteOptions,\n RegisterHttpRouteGatewayAuth,\n RegisterHttpRouteGatewayHandler,\n RegisterHttpRouteGatewayParams,\n} from \"./http-route-types.js\";\n\nexport type RegisterHttpRouteFn = (path: string, handler: HttpRequestHandler, opts: HttpRouteOptions) => void;\n\ntype GatewayRegisterHttpRoute = (params: RegisterHttpRouteGatewayParams) => void;\n\nfunction incomingHeadersToRecord(headers: IncomingMessage[\"headers\"]): Record<string, string> {\n const out: Record<string, string> = {};\n if (!headers || typeof headers !== \"object\") return out;\n for (const [key, value] of Object.entries(headers)) {\n if (value === undefined) continue;\n out[key] = Array.isArray(value) ? value.join(\", \") : String(value);\n }\n return out;\n}\n\nfunction adaptLegacyHandlerToNode(\n legacy: HttpRequestHandler,\n logger: SafeRouteLogger,\n source: string,\n): RegisterHttpRouteGatewayHandler {\n return async (req: IncomingMessage, res: ServerResponse) => {\n try {\n const legacyReq = {\n method: req.method ?? \"GET\",\n url: req.url ?? \"/\",\n headers: incomingHeadersToRecord(req.headers),\n };\n const result = await legacy(legacyReq);\n res.writeHead(result.status, result.headers ?? {});\n res.end(result.body);\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n capturePluginError(e, {\n operation: \"http-legacy-route-handler\",\n subsystem: \"memory-hybrid-http\",\n metadata: { source },\n });\n (logger.error ?? logger.warn).call(logger, `${source}: HTTP route handler error: ${e.message}`);\n if (!res.headersSent) {\n res.writeHead(500, { \"Content-Type\": \"text/plain; charset=utf-8\" });\n }\n res.end(\"Internal Server Error\");\n }\n };\n}\n\n/** Lightweight logger surface compatible with the OpenClaw plugin logger. */\nexport interface SafeRouteLogger {\n warn: (msg: string) => void;\n error?: (msg: string) => void;\n}\n\n/**\n * Normalize a route path:\n * - trims whitespace\n * - collapses repeated leading slashes to one\n * - strips trailing slashes (keeps the literal `/` for root)\n *\n * Returns the normalized string, or `null` if normalization would produce an\n * empty/invalid path (which is the case the gateway warns about).\n */\nexport function normalizeRoutePath(rawPath: unknown): string | null {\n if (typeof rawPath !== \"string\") return null;\n const trimmed = rawPath.trim();\n if (trimmed.length === 0) return null;\n if (!trimmed.startsWith(\"/\")) return null;\n\n // Collapse repeated leading slashes (e.g. \"//foo\" -> \"/foo\").\n let normalized = trimmed.replace(/^\\/+/, \"/\");\n // Strip trailing slashes except the literal root path.\n if (normalized.length > 1) {\n normalized = normalized.replace(/\\/+$/, \"\");\n }\n if (normalized.length === 0) return null;\n return normalized;\n}\n\n/**\n * Build a safe `registerHttpRoute` callable bound to the given plugin API and\n * logger. The returned function silently no-ops (with a clear warn log) if\n * the underlying gateway does not implement `registerHttpRoute`.\n */\nexport function createSafeRegisterHttpRoute(\n api: ClawdbotPluginApi,\n logger: SafeRouteLogger,\n source: string,\n): RegisterHttpRouteFn {\n const seen = new Set<string>();\n const register = api.registerHttpRoute as GatewayRegisterHttpRoute | undefined;\n\n return (rawPath, handler, opts) => {\n const normalized = normalizeRoutePath(rawPath);\n if (normalized === null) {\n const message = `${source}: refusing to register HTTP route with invalid path (received ${JSON.stringify(rawPath)}). Path must be a non-empty string starting with '/'.`;\n (logger.error ?? logger.warn).call(logger, message);\n return;\n }\n\n if (typeof register !== \"function\") {\n logger.warn(\n `${source}: api.registerHttpRoute is not available on this gateway; ` + `skipping route ${normalized}.`,\n );\n return;\n }\n\n if (seen.has(normalized)) {\n logger.warn(`${source}: duplicate HTTP route registration for ${normalized} ignored.`);\n return;\n }\n seen.add(normalized);\n\n const auth: RegisterHttpRouteGatewayAuth = opts.authenticated ? \"gateway\" : \"plugin\";\n register({\n path: normalized,\n handler: adaptLegacyHandlerToNode(handler, logger, source),\n auth,\n match: \"exact\",\n });\n };\n}\n"],"mappings":";;AAoCA,SAAS,wBAAwB,SAA6D;CAC5F,MAAM,MAA8B,CAAC;CACrC,IAAI,CAAC,WAAW,OAAO,YAAY,UAAU,OAAO;CACpD,KAAK,MAAM,CAAC,KAAK,UAAU,OAAO,QAAQ,OAAO,GAAG;EAClD,IAAI,UAAU,KAAA,GAAW;EACzB,IAAI,OAAO,MAAM,QAAQ,KAAK,IAAI,MAAM,KAAK,IAAI,IAAI,OAAO,KAAK;CACnE;CACA,OAAO;AACT;AAEA,SAAS,yBACP,QACA,QACA,QACiC;CACjC,OAAO,OAAO,KAAsB,QAAwB;EAC1D,IAAI;GAMF,MAAM,SAAS,MAAM,OAAO;IAJ1B,QAAQ,IAAI,UAAU;IACtB,KAAK,IAAI,OAAO;IAChB,SAAS,wBAAwB,IAAI,OAAO;GAEV,CAAC;GACrC,IAAI,UAAU,OAAO,QAAQ,OAAO,WAAW,CAAC,CAAC;GACjD,IAAI,IAAI,OAAO,IAAI;EACrB,SAAS,KAAK;GACZ,MAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;GAC5D,mBAAmB,GAAG;IACpB,WAAW;IACX,WAAW;IACX,UAAU,EAAE,OAAO;GACrB,CAAC;GACD,CAAC,OAAO,SAAS,OAAO,MAAM,KAAK,QAAQ,GAAG,OAAO,8BAA8B,EAAE,SAAS;GAC9F,IAAI,CAAC,IAAI,aACP,IAAI,UAAU,KAAK,EAAE,gBAAgB,4BAA4B,CAAC;GAEpE,IAAI,IAAI,uBAAuB;EACjC;CACF;AACF;;;;;;;;;;AAiBA,SAAgB,mBAAmB,SAAiC;CAClE,IAAI,OAAO,YAAY,UAAU,OAAO;CACxC,MAAM,UAAU,QAAQ,KAAK;CAC7B,IAAI,QAAQ,WAAW,GAAG,OAAO;CACjC,IAAI,CAAC,QAAQ,WAAW,GAAG,GAAG,OAAO;CAGrC,IAAI,aAAa,QAAQ,QAAQ,QAAQ,GAAG;CAE5C,IAAI,WAAW,SAAS,GACtB,aAAa,WAAW,QAAQ,QAAQ,EAAE;CAE5C,IAAI,WAAW,WAAW,GAAG,OAAO;CACpC,OAAO;AACT;;;;;;AAOA,SAAgB,4BACd,KACA,QACA,QACqB;CACrB,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,WAAW,IAAI;CAErB,QAAQ,SAAS,SAAS,SAAS;EACjC,MAAM,aAAa,mBAAmB,OAAO;EAC7C,IAAI,eAAe,MAAM;GACvB,MAAM,UAAU,GAAG,OAAO,gEAAgE,KAAK,UAAU,OAAO,EAAE;GAClH,CAAC,OAAO,SAAS,OAAO,MAAM,KAAK,QAAQ,OAAO;GAClD;EACF;EAEA,IAAI,OAAO,aAAa,YAAY;GAClC,OAAO,KACL,GAAG,OAAO,2EAAgF,WAAW,EACvG;GACA;EACF;EAEA,IAAI,KAAK,IAAI,UAAU,GAAG;GACxB,OAAO,KAAK,GAAG,OAAO,0CAA0C,WAAW,UAAU;GACrF;EACF;EACA,KAAK,IAAI,UAAU;EAEnB,MAAM,OAAqC,KAAK,gBAAgB,YAAY;EAC5E,SAAS;GACP,MAAM;GACN,SAAS,yBAAyB,SAAS,QAAQ,MAAM;GACzD;GACA,OAAO;EACT,CAAC;CACH;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"self-extension-tools.js","names":[],"sources":["../../tools/self-extension-tools.ts"],"sourcesContent":["/**\n * Self-Extension Tools — expose plugin self-extension to the agent (Issue #210).\n *\n * Tools:\n * - memory_propose_tool: manually trigger gap analysis and generate tool proposals\n * - memory_tool_proposals: list current proposals with status\n * - memory_tool_approve: approve a proposal (marks it for implementation)\n * - memory_tool_reject: reject a proposal\n */\n\nimport { Type } from \"@sinclair/typebox\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { ToolProposalStore } from \"../backends/tool-proposal-store.js\";\nimport type { WorkflowStore } from \"../backends/workflow-store.js\";\nimport type { HybridMemoryConfig } from \"../config.js\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\nimport { GapDetector } from \"../services/gap-detector.js\";\nimport { ToolProposer } from \"../services/tool-proposer.js\";\n\ninterface SelfExtensionToolsContext {\n toolProposalStore: ToolProposalStore;\n workflowStore: WorkflowStore;\n cfg: HybridMemoryConfig;\n}\n\nexport function registerSelfExtensionTools(ctx: SelfExtensionToolsContext, api: ClawdbotPluginApi): void {\n const { toolProposalStore, workflowStore, cfg } = ctx;\n\n // -------------------------------------------------------------------------\n // memory_propose_tool — trigger gap analysis and generate proposals\n // -------------------------------------------------------------------------\n api.registerTool({\n name: \"memory_propose_tool\",\n label: \"Propose New Tool from Usage Gaps\",\n description:\n \"Analyse workflow traces to detect recurring multi-step workarounds and generate tool proposals. Each proposal is a specification (not code) that a human or LLM can implement later. Requires selfExtension.enabled = true.\",\n parameters: Type.Object({\n minFrequency: Type.Optional(\n Type.Integer({\n minimum: 1,\n description: \"Override: minimum times a gap must be observed (default from config).\",\n }),\n ),\n minToolSavings: Type.Optional(\n Type.Integer({\n minimum: 1,\n description: \"Override: minimum tool calls saved per invocation (default from config).\",\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { minFrequency, minToolSavings } = params as {\n minFrequency?: number;\n minToolSavings?: number;\n };\n\n try {\n const gapDetector = new GapDetector(workflowStore);\n const proposer = new ToolProposer(gapDetector, toolProposalStore, cfg.selfExtension);\n const result = proposer.runCycle({\n ...(minFrequency !== undefined ? { minFrequency } : {}),\n ...(minToolSavings !== undefined ? { minToolSavings } : {}),\n });\n\n const lines: string[] = [];\n lines.push(\"Self-extension analysis complete.\");\n lines.push(` Proposed: ${result.proposed}`);\n lines.push(` Skipped: ${result.skipped}`);\n if (result.reasons.length > 0) {\n lines.push(\" Details:\");\n result.reasons.forEach((r) => {\n lines.push(` - ${r}`);\n });\n }\n if (result.proposed > 0) {\n lines.push(\"\");\n lines.push(\"Use memory_tool_proposals to review proposals.\");\n lines.push(\"Use memory_tool_approve <id> to mark a proposal as approved.\");\n }\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n details: result,\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"self-extension\",\n operation: \"memory-propose-tool\",\n phase: \"runtime\",\n });\n throw err;\n }\n },\n });\n\n // -------------------------------------------------------------------------\n // memory_tool_proposals — list proposals\n // -------------------------------------------------------------------------\n api.registerTool({\n name: \"memory_tool_proposals\",\n label: \"List Tool Proposals\",\n description:\n \"List tool proposals generated by the self-extension gap analyser. Filter by status (proposed/approved/rejected/implemented). Each proposal includes the tool name, rationale, and ID needed for approve/reject actions.\",\n parameters: Type.Object({\n status: Type.Optional(\n Type.Union(\n [Type.Literal(\"proposed\"), Type.Literal(\"approved\"), Type.Literal(\"rejected\"), Type.Literal(\"implemented\")],\n {\n description: \"Filter by proposal status. Omit to list all proposals.\",\n },\n ),\n ),\n limit: Type.Optional(\n Type.Integer({\n minimum: 1,\n maximum: 100,\n description: \"Maximum number of proposals to return. Default: 20.\",\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { status, limit } = params as {\n status?: \"proposed\" | \"approved\" | \"rejected\" | \"implemented\";\n limit?: number;\n };\n\n try {\n const proposals = toolProposalStore.list({\n status,\n limit: limit ?? 20,\n });\n\n if (proposals.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: status\n ? `No ${status} tool proposals found.`\n : \"No tool proposals found. Run memory_propose_tool to generate proposals.\",\n },\n ],\n details: [],\n };\n }\n\n const lines = proposals.map((p, i) => {\n let sourceInfo = \"\";\n try {\n const sources = JSON.parse(p.sourcePatterns) as string[];\n if (sources.length > 0) {\n sourceInfo = ` | from ${sources.length} pattern(s)`;\n }\n } catch {\n // ignore\n }\n return (\n `${i + 1}. [${p.status.toUpperCase()}] ${p.name}${sourceInfo}\\n` +\n ` ID: ${p.id}\\n` +\n ` ${p.description}\\n` +\n ` Created: ${p.createdAt}`\n );\n });\n\n const summary = `Found ${proposals.length} proposal(s)${status ? ` (${status})` : \"\"}:\\n\\n${lines.join(\"\\n\\n\")}`;\n\n return {\n content: [{ type: \"text\", text: summary }],\n details: proposals,\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"self-extension\",\n operation: \"memory-tool-proposals\",\n phase: \"runtime\",\n });\n throw err;\n }\n },\n });\n\n // -------------------------------------------------------------------------\n // memory_tool_approve — approve a pending proposal\n // -------------------------------------------------------------------------\n api.registerTool({\n name: \"memory_tool_approve\",\n label: \"Approve Tool Proposal\",\n description:\n \"Approve a pending tool proposal. Marks it as approved so a human or LLM can implement it. Requires the proposal ID from memory_tool_proposals.\",\n parameters: Type.Object({\n id: Type.String({\n description: \"The proposal ID to approve (from memory_tool_proposals).\",\n }),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { id } = params as { id: string };\n\n try {\n const gapDetector = new GapDetector(workflowStore);\n const proposer = new ToolProposer(gapDetector, toolProposalStore, cfg.selfExtension);\n const result = proposer.approveProposal(id);\n\n return {\n content: [\n {\n type: \"text\",\n text: result.success ? `✅ ${result.message}` : `❌ ${result.message}`,\n },\n ],\n details: result,\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"self-extension\",\n operation: \"memory-tool-approve\",\n phase: \"runtime\",\n });\n throw err;\n }\n },\n });\n\n // -------------------------------------------------------------------------\n // memory_tool_reject — reject a pending proposal\n // -------------------------------------------------------------------------\n api.registerTool({\n name: \"memory_tool_reject\",\n label: \"Reject Tool Proposal\",\n description:\n \"Reject a pending tool proposal. The gap will no longer be surfaced unless re-analysed. Requires the proposal ID from memory_tool_proposals.\",\n parameters: Type.Object({\n id: Type.String({\n description: \"The proposal ID to reject (from memory_tool_proposals).\",\n }),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { id } = params as { id: string };\n\n try {\n const gapDetector = new GapDetector(workflowStore);\n const proposer = new ToolProposer(gapDetector, toolProposalStore, cfg.selfExtension);\n const result = proposer.rejectProposal(id);\n\n return {\n content: [\n {\n type: \"text\",\n text: result.success ? `✅ ${result.message}` : `❌ ${result.message}`,\n },\n ],\n details: result,\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"self-extension\",\n operation: \"memory-tool-reject\",\n phase: \"runtime\",\n });\n throw err;\n }\n },\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"self-extension-tools.js","names":[],"sources":["../../tools/self-extension-tools.ts"],"sourcesContent":["/**\n * Self-Extension Tools — expose plugin self-extension to the agent (Issue #210).\n *\n * Tools:\n * - memory_propose_tool: manually trigger gap analysis and generate tool proposals\n * - memory_tool_proposals: list current proposals with status\n * - memory_tool_approve: approve a proposal (marks it for implementation)\n * - memory_tool_reject: reject a proposal\n */\n\nimport { Type } from \"@sinclair/typebox\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { ToolProposalStore } from \"../backends/tool-proposal-store.js\";\nimport type { WorkflowStore } from \"../backends/workflow-store.js\";\nimport type { HybridMemoryConfig } from \"../config.js\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\nimport { GapDetector } from \"../services/gap-detector.js\";\nimport { ToolProposer } from \"../services/tool-proposer.js\";\n\ninterface SelfExtensionToolsContext {\n toolProposalStore: ToolProposalStore;\n workflowStore: WorkflowStore;\n cfg: HybridMemoryConfig;\n}\n\nexport function registerSelfExtensionTools(ctx: SelfExtensionToolsContext, api: ClawdbotPluginApi): void {\n const { toolProposalStore, workflowStore, cfg } = ctx;\n\n // -------------------------------------------------------------------------\n // memory_propose_tool — trigger gap analysis and generate proposals\n // -------------------------------------------------------------------------\n api.registerTool({\n name: \"memory_propose_tool\",\n label: \"Propose New Tool from Usage Gaps\",\n description:\n \"Analyse workflow traces to detect recurring multi-step workarounds and generate tool proposals. Each proposal is a specification (not code) that a human or LLM can implement later. Requires selfExtension.enabled = true.\",\n parameters: Type.Object({\n minFrequency: Type.Optional(\n Type.Integer({\n minimum: 1,\n description: \"Override: minimum times a gap must be observed (default from config).\",\n }),\n ),\n minToolSavings: Type.Optional(\n Type.Integer({\n minimum: 1,\n description: \"Override: minimum tool calls saved per invocation (default from config).\",\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { minFrequency, minToolSavings } = params as {\n minFrequency?: number;\n minToolSavings?: number;\n };\n\n try {\n const gapDetector = new GapDetector(workflowStore);\n const proposer = new ToolProposer(gapDetector, toolProposalStore, cfg.selfExtension);\n const result = proposer.runCycle({\n ...(minFrequency !== undefined ? { minFrequency } : {}),\n ...(minToolSavings !== undefined ? { minToolSavings } : {}),\n });\n\n const lines: string[] = [];\n lines.push(\"Self-extension analysis complete.\");\n lines.push(` Proposed: ${result.proposed}`);\n lines.push(` Skipped: ${result.skipped}`);\n if (result.reasons.length > 0) {\n lines.push(\" Details:\");\n result.reasons.forEach((r) => {\n lines.push(` - ${r}`);\n });\n }\n if (result.proposed > 0) {\n lines.push(\"\");\n lines.push(\"Use memory_tool_proposals to review proposals.\");\n lines.push(\"Use memory_tool_approve <id> to mark a proposal as approved.\");\n }\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n details: result,\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"self-extension\",\n operation: \"memory-propose-tool\",\n phase: \"runtime\",\n });\n throw err;\n }\n },\n });\n\n // -------------------------------------------------------------------------\n // memory_tool_proposals — list proposals\n // -------------------------------------------------------------------------\n api.registerTool({\n name: \"memory_tool_proposals\",\n label: \"List Tool Proposals\",\n description:\n \"List tool proposals generated by the self-extension gap analyser. Filter by status (proposed/approved/rejected/implemented). Each proposal includes the tool name, rationale, and ID needed for approve/reject actions.\",\n parameters: Type.Object({\n status: Type.Optional(\n Type.Union(\n [Type.Literal(\"proposed\"), Type.Literal(\"approved\"), Type.Literal(\"rejected\"), Type.Literal(\"implemented\")],\n {\n description: \"Filter by proposal status. Omit to list all proposals.\",\n },\n ),\n ),\n limit: Type.Optional(\n Type.Integer({\n minimum: 1,\n maximum: 100,\n description: \"Maximum number of proposals to return. Default: 20.\",\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { status, limit } = params as {\n status?: \"proposed\" | \"approved\" | \"rejected\" | \"implemented\";\n limit?: number;\n };\n\n try {\n const proposals = toolProposalStore.list({\n status,\n limit: limit ?? 20,\n });\n\n if (proposals.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: status\n ? `No ${status} tool proposals found.`\n : \"No tool proposals found. Run memory_propose_tool to generate proposals.\",\n },\n ],\n details: [],\n };\n }\n\n const lines = proposals.map((p, i) => {\n let sourceInfo = \"\";\n try {\n const sources = JSON.parse(p.sourcePatterns) as string[];\n if (sources.length > 0) {\n sourceInfo = ` | from ${sources.length} pattern(s)`;\n }\n } catch {\n // ignore\n }\n return (\n `${i + 1}. [${p.status.toUpperCase()}] ${p.name}${sourceInfo}\\n` +\n ` ID: ${p.id}\\n` +\n ` ${p.description}\\n` +\n ` Created: ${p.createdAt}`\n );\n });\n\n const summary = `Found ${proposals.length} proposal(s)${status ? ` (${status})` : \"\"}:\\n\\n${lines.join(\"\\n\\n\")}`;\n\n return {\n content: [{ type: \"text\", text: summary }],\n details: proposals,\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"self-extension\",\n operation: \"memory-tool-proposals\",\n phase: \"runtime\",\n });\n throw err;\n }\n },\n });\n\n // -------------------------------------------------------------------------\n // memory_tool_approve — approve a pending proposal\n // -------------------------------------------------------------------------\n api.registerTool({\n name: \"memory_tool_approve\",\n label: \"Approve Tool Proposal\",\n description:\n \"Approve a pending tool proposal. Marks it as approved so a human or LLM can implement it. Requires the proposal ID from memory_tool_proposals.\",\n parameters: Type.Object({\n id: Type.String({\n description: \"The proposal ID to approve (from memory_tool_proposals).\",\n }),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { id } = params as { id: string };\n\n try {\n const gapDetector = new GapDetector(workflowStore);\n const proposer = new ToolProposer(gapDetector, toolProposalStore, cfg.selfExtension);\n const result = proposer.approveProposal(id);\n\n return {\n content: [\n {\n type: \"text\",\n text: result.success ? `✅ ${result.message}` : `❌ ${result.message}`,\n },\n ],\n details: result,\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"self-extension\",\n operation: \"memory-tool-approve\",\n phase: \"runtime\",\n });\n throw err;\n }\n },\n });\n\n // -------------------------------------------------------------------------\n // memory_tool_reject — reject a pending proposal\n // -------------------------------------------------------------------------\n api.registerTool({\n name: \"memory_tool_reject\",\n label: \"Reject Tool Proposal\",\n description:\n \"Reject a pending tool proposal. The gap will no longer be surfaced unless re-analysed. Requires the proposal ID from memory_tool_proposals.\",\n parameters: Type.Object({\n id: Type.String({\n description: \"The proposal ID to reject (from memory_tool_proposals).\",\n }),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { id } = params as { id: string };\n\n try {\n const gapDetector = new GapDetector(workflowStore);\n const proposer = new ToolProposer(gapDetector, toolProposalStore, cfg.selfExtension);\n const result = proposer.rejectProposal(id);\n\n return {\n content: [\n {\n type: \"text\",\n text: result.success ? `✅ ${result.message}` : `❌ ${result.message}`,\n },\n ],\n details: result,\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"self-extension\",\n operation: \"memory-tool-reject\",\n phase: \"runtime\",\n });\n throw err;\n }\n },\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"task-hygiene-tools.js","names":["pathJoin"],"sources":["../../tools/task-hygiene-tools.ts"],"sourcesContent":["import { isAbsolute, join as pathJoin } from \"node:path\";\n/**\n * Task hygiene tools — draft goal_register payloads from ACTIVE-TASKS.md rows.\n * @see docs/TASK-HYGIENE.md\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { HybridMemoryConfig } from \"../config.js\";\nimport { readActiveTaskFile } from \"../services/active-task.js\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\nimport { buildProposeGoalDraftFromTask } from \"../services/task-hygiene.js\";\nimport { parseDuration } from \"../utils/duration.js\";\nimport { stableStringify } from \"../utils/stable-stringify.js\";\n\nexport interface TaskHygieneToolsContext {\n cfg: HybridMemoryConfig;\n /** Absolute path to ACTIVE-TASKS.md */\n resolvedActiveTaskPath: string;\n workspaceRoot: string;\n}\n\nexport function resolveActiveTaskPathForTools(cfg: HybridMemoryConfig, workspaceRoot: string): string {\n return isAbsolute(cfg.activeTask.filePath)\n ? cfg.activeTask.filePath\n : pathJoin(workspaceRoot, cfg.activeTask.filePath);\n}\n\nexport function registerTaskHygieneTools(ctx: TaskHygieneToolsContext, api: ClawdbotPluginApi): void {\n const path = ctx.resolvedActiveTaskPath;\n const { cfg } = ctx;\n\n const disabled = () => ({\n content: [\n {\n type: \"text\" as const,\n text: \"Active tasks are disabled. Set activeTask.enabled: true and ensure ACTIVE-TASKS.md exists.\",\n },\n ],\n details: { error: \"active_task_disabled\" },\n });\n\n api.registerTool(\n {\n name: \"active_task_propose_goal\",\n label: \"Propose goal from active task\",\n description:\n \"Read ACTIVE-TASKS.md and return a draft goal_register payload for a task label (promote tactical work to stewardship goals).\",\n parameters: Type.Object({\n task_label: Type.String({ minLength: 1 }),\n }),\n async execute(_id: string, params: Record<string, unknown>) {\n if (!cfg.activeTask.enabled) return disabled();\n try {\n const staleMinutes = parseDuration(cfg.activeTask.staleThreshold);\n const file = await readActiveTaskFile(path, staleMinutes);\n if (!file || file.active.length === 0) {\n return {\n content: [{ type: \"text\", text: \"No active tasks in ACTIVE-TASKS.md (or file missing).\" }],\n details: { error: \"no_active_tasks\", path },\n };\n }\n const want = String((params as { task_label: string }).task_label).trim();\n const task =\n file.active.find((t) => t.label === want) ??\n file.active.find((t) => t.label.toLowerCase() === want.toLowerCase());\n if (!task) {\n return {\n content: [\n {\n type: \"text\",\n text: `No active task with label matching \"${want}\". Labels: ${file.active.map((t) => t.label).join(\", \")}`,\n },\n ],\n details: { error: \"task_not_found\", path },\n };\n }\n const draft = buildProposeGoalDraftFromTask(task);\n const goalRegisterExample = {\n label: draft.suggestedLabel,\n description: draft.suggestedDescription,\n acceptance_criteria: draft.suggestedCriteria,\n };\n const text = [\n \"**Draft for `goal_register` (refine with the user before calling when policy requires confirmation):**\",\n \"\",\n \"```json\",\n stableStringify(goalRegisterExample),\n \"```\",\n \"\",\n `**Notes:** ${draft.notes}`,\n ].join(\"\\n\");\n return {\n content: [{ type: \"text\", text }],\n details: { draft, goal_register_suggestion: goalRegisterExample, activeTaskPath: path },\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"task-hygiene-tools\",\n operation: \"active_task_propose_goal\",\n });\n return { content: [{ type: \"text\", text: String(err) }], details: { error: String(err) } };\n }\n },\n },\n { name: \"active_task_propose_goal\" },\n );\n}\n"],"mappings":";;;;;;;;;;;;AAqBA,SAAgB,8BAA8B,KAAyB,eAA+B;CACpG,OAAO,WAAW,IAAI,WAAW,
|
|
1
|
+
{"version":3,"file":"task-hygiene-tools.js","names":["pathJoin"],"sources":["../../tools/task-hygiene-tools.ts"],"sourcesContent":["import { isAbsolute, join as pathJoin } from \"node:path\";\n/**\n * Task hygiene tools — draft goal_register payloads from ACTIVE-TASKS.md rows.\n * @see docs/TASK-HYGIENE.md\n */\nimport { Type } from \"@sinclair/typebox\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { HybridMemoryConfig } from \"../config.js\";\nimport { readActiveTaskFile } from \"../services/active-task.js\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\nimport { buildProposeGoalDraftFromTask } from \"../services/task-hygiene.js\";\nimport { parseDuration } from \"../utils/duration.js\";\nimport { stableStringify } from \"../utils/stable-stringify.js\";\n\nexport interface TaskHygieneToolsContext {\n cfg: HybridMemoryConfig;\n /** Absolute path to ACTIVE-TASKS.md */\n resolvedActiveTaskPath: string;\n workspaceRoot: string;\n}\n\nexport function resolveActiveTaskPathForTools(cfg: HybridMemoryConfig, workspaceRoot: string): string {\n return isAbsolute(cfg.activeTask.filePath)\n ? cfg.activeTask.filePath\n : pathJoin(workspaceRoot, cfg.activeTask.filePath);\n}\n\nexport function registerTaskHygieneTools(ctx: TaskHygieneToolsContext, api: ClawdbotPluginApi): void {\n const path = ctx.resolvedActiveTaskPath;\n const { cfg } = ctx;\n\n const disabled = () => ({\n content: [\n {\n type: \"text\" as const,\n text: \"Active tasks are disabled. Set activeTask.enabled: true and ensure ACTIVE-TASKS.md exists.\",\n },\n ],\n details: { error: \"active_task_disabled\" },\n });\n\n api.registerTool(\n {\n name: \"active_task_propose_goal\",\n label: \"Propose goal from active task\",\n description:\n \"Read ACTIVE-TASKS.md and return a draft goal_register payload for a task label (promote tactical work to stewardship goals).\",\n parameters: Type.Object({\n task_label: Type.String({ minLength: 1 }),\n }),\n async execute(_id: string, params: Record<string, unknown>) {\n if (!cfg.activeTask.enabled) return disabled();\n try {\n const staleMinutes = parseDuration(cfg.activeTask.staleThreshold);\n const file = await readActiveTaskFile(path, staleMinutes);\n if (!file || file.active.length === 0) {\n return {\n content: [{ type: \"text\", text: \"No active tasks in ACTIVE-TASKS.md (or file missing).\" }],\n details: { error: \"no_active_tasks\", path },\n };\n }\n const want = String((params as { task_label: string }).task_label).trim();\n const task =\n file.active.find((t) => t.label === want) ??\n file.active.find((t) => t.label.toLowerCase() === want.toLowerCase());\n if (!task) {\n return {\n content: [\n {\n type: \"text\",\n text: `No active task with label matching \"${want}\". Labels: ${file.active.map((t) => t.label).join(\", \")}`,\n },\n ],\n details: { error: \"task_not_found\", path },\n };\n }\n const draft = buildProposeGoalDraftFromTask(task);\n const goalRegisterExample = {\n label: draft.suggestedLabel,\n description: draft.suggestedDescription,\n acceptance_criteria: draft.suggestedCriteria,\n };\n const text = [\n \"**Draft for `goal_register` (refine with the user before calling when policy requires confirmation):**\",\n \"\",\n \"```json\",\n stableStringify(goalRegisterExample),\n \"```\",\n \"\",\n `**Notes:** ${draft.notes}`,\n ].join(\"\\n\");\n return {\n content: [{ type: \"text\", text }],\n details: { draft, goal_register_suggestion: goalRegisterExample, activeTaskPath: path },\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"task-hygiene-tools\",\n operation: \"active_task_propose_goal\",\n });\n return { content: [{ type: \"text\", text: String(err) }], details: { error: String(err) } };\n }\n },\n },\n { name: \"active_task_propose_goal\" },\n );\n}\n"],"mappings":";;;;;;;;;;;;AAqBA,SAAgB,8BAA8B,KAAyB,eAA+B;CACpG,OAAO,WAAW,IAAI,WAAW,QAAQ,IACrC,IAAI,WAAW,WACfA,KAAS,eAAe,IAAI,WAAW,QAAQ;AACrD;AAEA,SAAgB,yBAAyB,KAA8B,KAA8B;CACnG,MAAM,OAAO,IAAI;CACjB,MAAM,EAAE,QAAQ;CAEhB,MAAM,kBAAkB;EACtB,SAAS,CACP;GACE,MAAM;GACN,MAAM;EACR,CACF;EACA,SAAS,EAAE,OAAO,uBAAuB;CAC3C;CAEA,IAAI,aACF;EACE,MAAM;EACN,OAAO;EACP,aACE;EACF,YAAY,KAAK,OAAO,EACtB,YAAY,KAAK,OAAO,EAAE,WAAW,EAAE,CAAC,EAC1C,CAAC;EACD,MAAM,QAAQ,KAAa,QAAiC;GAC1D,IAAI,CAAC,IAAI,WAAW,SAAS,OAAO,SAAS;GAC7C,IAAI;IAEF,MAAM,OAAO,MAAM,mBAAmB,MADjB,cAAc,IAAI,WAAW,cACK,CAAC;IACxD,IAAI,CAAC,QAAQ,KAAK,OAAO,WAAW,GAClC,OAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM;KAAwD,CAAC;KACzF,SAAS;MAAE,OAAO;MAAmB;KAAK;IAC5C;IAEF,MAAM,OAAO,OAAQ,OAAkC,UAAU,EAAE,KAAK;IACxE,MAAM,OACJ,KAAK,OAAO,MAAM,MAAM,EAAE,UAAU,IAAI,KACxC,KAAK,OAAO,MAAM,MAAM,EAAE,MAAM,YAAY,MAAM,KAAK,YAAY,CAAC;IACtE,IAAI,CAAC,MACH,OAAO;KACL,SAAS,CACP;MACE,MAAM;MACN,MAAM,uCAAuC,KAAK,aAAa,KAAK,OAAO,KAAK,MAAM,EAAE,KAAK,EAAE,KAAK,IAAI;KAC1G,CACF;KACA,SAAS;MAAE,OAAO;MAAkB;KAAK;IAC3C;IAEF,MAAM,QAAQ,8BAA8B,IAAI;IAChD,MAAM,sBAAsB;KAC1B,OAAO,MAAM;KACb,aAAa,MAAM;KACnB,qBAAqB,MAAM;IAC7B;IAUA,OAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAVf;OACX;OACA;OACA;OACA,gBAAgB,mBAAmB;OACnC;OACA;OACA,cAAc,MAAM;MACtB,EAAE,KAAK,IAEwB;KAAE,CAAC;KAChC,SAAS;MAAE;MAAO,0BAA0B;MAAqB,gBAAgB;KAAK;IACxF;GACF,SAAS,KAAK;IACZ,mBAAmB,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,GAAG;KACtE,WAAW;KACX,WAAW;IACb,CAAC;IACD,OAAO;KAAE,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,OAAO,GAAG;KAAE,CAAC;KAAG,SAAS,EAAE,OAAO,OAAO,GAAG,EAAE;IAAE;GAC3F;EACF;CACF,GACA,EAAE,MAAM,2BAA2B,CACrC;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utility-tools.js","names":[],"sources":["../../tools/utility-tools.ts"],"sourcesContent":["import { Type } from \"@sinclair/typebox\";\nimport type OpenAI from \"openai\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport { stringEnum } from \"../utils/typebox.js\";\n\nimport type { FactsDB } from \"../backends/facts-db.js\";\nimport type { VectorDB } from \"../backends/vector-db.js\";\nimport type { WriteAheadLog } from \"../backends/wal.js\";\nimport type { HybridMemoryConfig } from \"../config.js\";\nimport { isCompactVerbosity, resolveReflectionModelAndFallbacks } from \"../config.js\";\nimport type { EmbeddingProvider } from \"../services/embeddings.js\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\nimport { analyzeKnowledgeGaps } from \"../services/knowledge-gaps.js\";\nimport type { ProvenanceService } from \"../services/provenance.js\";\nimport { detectClusters } from \"../services/topic-clusters.js\";\nimport { deleteVectorsForFactIds } from \"../services/vector-maintenance.js\";\n\nexport interface PluginContext {\n factsDb: FactsDB;\n vectorDb: VectorDB;\n embeddings: EmbeddingProvider;\n openai: OpenAI;\n cfg: HybridMemoryConfig;\n wal: WriteAheadLog | null;\n resolvedSqlitePath: string;\n provenanceService?: ProvenanceService | null;\n}\n\n// Helper function types (exported for register-tools ToolsContext)\nexport type RunReflectionFn = (\n factsDb: FactsDB,\n vectorDb: VectorDB,\n embeddings: EmbeddingProvider,\n openai: OpenAI,\n config: { defaultWindow: number; minObservations: number; enabled?: boolean },\n opts: { window: number; dryRun: boolean; model: string; fallbackModels?: string[] },\n logger: { info: (msg: string) => void; warn: (msg: string) => void },\n provenanceService?: ProvenanceService | null,\n) => Promise<{ factsAnalyzed: number; patternsExtracted: number; patternsStored: number; window: number }>;\n\nexport type RunReflectionRulesFn = (\n factsDb: FactsDB,\n vectorDb: VectorDB,\n embeddings: EmbeddingProvider,\n openai: OpenAI,\n opts: { dryRun: boolean; model: string; fallbackModels?: string[] },\n logger: { info: (msg: string) => void; warn: (msg: string) => void },\n provenanceService?: ProvenanceService | null,\n) => Promise<{ rulesExtracted: number; rulesStored: number }>;\n\nexport type RunReflectionMetaFn = (\n factsDb: FactsDB,\n vectorDb: VectorDB,\n embeddings: EmbeddingProvider,\n openai: OpenAI,\n opts: { dryRun: boolean; model: string; fallbackModels?: string[] },\n logger: { info: (msg: string) => void; warn: (msg: string) => void },\n provenanceService?: ProvenanceService | null,\n) => Promise<{ metaExtracted: number; metaStored: number }>;\n\nexport function registerUtilityTools(\n ctx: PluginContext,\n api: ClawdbotPluginApi,\n runReflection: RunReflectionFn,\n runReflectionRules: RunReflectionRulesFn,\n runReflectionMeta: RunReflectionMetaFn,\n _walWrite: (operation: \"store\" | \"update\", data: Record<string, unknown>) => Promise<string>,\n _walRemove: (id: string) => Promise<void>,\n): void {\n const { factsDb, vectorDb, embeddings, openai, cfg, provenanceService } = ctx;\n\n // memory_checkpoint\n api.registerTool(\n {\n name: \"memory_checkpoint\",\n label: \"Memory Checkpoint\",\n description: \"Save or restore pre-flight checkpoints before risky/long operations. Auto-expires after 4 hours.\",\n parameters: Type.Object({\n action: stringEnum([\"save\", \"restore\"] as const),\n intent: Type.Optional(Type.String({ description: \"What you're about to do (for save)\" })),\n state: Type.Optional(Type.String({ description: \"Current state/context (for save)\" })),\n expectedOutcome: Type.Optional(Type.String({ description: \"What should happen if successful\" })),\n workingFiles: Type.Optional(\n Type.Array(Type.String(), {\n description: \"Files being modified\",\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { action, intent, state, expectedOutcome, workingFiles } = params as {\n action: \"save\" | \"restore\";\n intent?: string;\n state?: string;\n expectedOutcome?: string;\n workingFiles?: string[];\n };\n\n if (action === \"save\") {\n if (!intent || !state) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Checkpoint save requires 'intent' and 'state'.\",\n },\n ],\n details: { error: \"missing_param\" },\n };\n }\n const id = factsDb.saveCheckpoint({\n intent,\n state,\n expectedOutcome,\n workingFiles,\n });\n return {\n content: [\n {\n type: \"text\",\n text: `Checkpoint saved (id: ${id.slice(0, 8)}..., TTL: 4h). Intent: ${intent.slice(0, 80)}`,\n },\n ],\n details: { action: \"saved\", id },\n };\n }\n\n const checkpoint = factsDb.restoreCheckpoint();\n if (!checkpoint) {\n return {\n content: [\n {\n type: \"text\",\n text: \"No active checkpoint found (may have expired).\",\n },\n ],\n details: { action: \"not_found\" },\n };\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `Restored checkpoint (saved: ${checkpoint.savedAt}):\\n- Intent: ${checkpoint.intent}\\n- State: ${checkpoint.state}${checkpoint.expectedOutcome ? `\\n- Expected: ${checkpoint.expectedOutcome}` : \"\"}${checkpoint.workingFiles?.length ? `\\n- Files: ${checkpoint.workingFiles.join(\", \")}` : \"\"}`,\n },\n ],\n details: { action: \"restored\", checkpoint },\n };\n },\n },\n { name: \"memory_checkpoint\" },\n );\n\n // memory_prune\n api.registerTool(\n {\n name: \"memory_prune\",\n label: \"Memory Prune\",\n description: \"Prune expired memories and decay confidence of aging facts.\",\n parameters: Type.Object({\n mode: Type.Optional(stringEnum([\"hard\", \"soft\", \"both\"] as const)),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { mode = \"both\" } = params as { mode?: \"hard\" | \"soft\" | \"both\" };\n\n let hardPruned = 0;\n let softPruned = 0;\n let linksPruned = 0;\n\n if (mode === \"hard\" || mode === \"both\") {\n const pendingExpiredIds = factsDb.listExpiredFactIdsPendingPrune();\n hardPruned = factsDb.pruneExpired();\n await deleteVectorsForFactIds(vectorDb, pendingExpiredIds, {\n operation: \"tool-memory-prune-expired\",\n logger: api.logger,\n });\n }\n if (mode === \"soft\" || mode === \"both\") {\n const pendingLowConfidenceIds = factsDb.listLowConfidenceFactIdsPendingPrune();\n softPruned = factsDb.decayConfidence();\n await deleteVectorsForFactIds(vectorDb, pendingLowConfidenceIds, {\n operation: \"tool-memory-prune-decay\",\n logger: api.logger,\n });\n }\n\n // Always prune orphaned links (fast, no-op if none exist)\n linksPruned = factsDb.pruneOrphanedLinks();\n\n const breakdown = factsDb.statsBreakdown();\n const expired = factsDb.countExpired();\n\n const verbosity = cfg.verbosity ?? \"normal\";\n let text: string;\n if (isCompactVerbosity(verbosity)) {\n // Quiet: compact one-liner — statsBreakdown and countExpired are still computed above\n // and included in the `details` field for programmatic consumers; they're intentionally\n // omitted from the human-readable text to reduce noise in quiet sessions.\n const linksNote = linksPruned > 0 ? ` ${linksPruned} orphaned links.` : \"\";\n text = `Pruned: ${hardPruned + softPruned} (${hardPruned} expired, ${softPruned} low-confidence).${linksNote}`;\n } else {\n const linksNote = linksPruned > 0 ? `\\nOrphaned links removed: ${linksPruned}` : \"\";\n const baseText = `Pruned: ${hardPruned} expired + ${softPruned} low-confidence.${linksNote}\\nRemaining by class: ${JSON.stringify(breakdown)}\\nPending expired: ${expired}`;\n const verboseExtra = verbosity === \"verbose\" ? `\\nMode: ${mode}` : \"\";\n text = baseText + verboseExtra;\n }\n\n return {\n content: [{ type: \"text\", text }],\n details: { hardPruned, softPruned, linksPruned, breakdown, pendingExpired: expired },\n };\n },\n },\n { name: \"memory_prune\" },\n );\n\n // memory_reflect\n api.registerTool(\n {\n name: \"memory_reflect\",\n label: \"Memory Reflect\",\n description:\n \"Run reflection on recent facts to synthesize behavioral patterns. Analyzes facts from the last N days, sends to LLM to extract patterns, stores new patterns (permanent, high importance) for better agent alignment.\",\n parameters: Type.Object({\n window: Type.Optional(\n Type.Number({\n description: \"Time window in days (1–90, default from config)\",\n minimum: 1,\n maximum: 90,\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const reflectionCfg = cfg.reflection;\n if (!reflectionCfg.enabled) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Reflection is disabled. Enable reflection.enabled in plugin config to use memory_reflect.\",\n },\n ],\n details: { error: \"reflection_disabled\" },\n };\n }\n const window = Math.min(\n 90,\n Math.max(1, typeof params.window === \"number\" ? params.window : reflectionCfg.defaultWindow),\n );\n const { defaultModel, fallbackModels } = resolveReflectionModelAndFallbacks(cfg, \"default\");\n try {\n const result = await runReflection(\n factsDb,\n vectorDb,\n embeddings,\n openai,\n { defaultWindow: reflectionCfg.defaultWindow, minObservations: reflectionCfg.minObservations },\n { window, dryRun: false, model: defaultModel, fallbackModels },\n api.logger,\n provenanceService,\n );\n const verbosity = cfg.verbosity ?? \"normal\";\n let reflectText: string;\n if (isCompactVerbosity(verbosity)) {\n reflectText = `Reflected: ${result.patternsStored} patterns stored.`;\n } else if (verbosity === \"verbose\") {\n reflectText = `Reflection complete: ${result.factsAnalyzed} facts analyzed, ${result.patternsExtracted} patterns extracted, ${result.patternsStored} stored (window: ${result.window} days, model: ${defaultModel}).`;\n } else {\n reflectText = `Reflection complete: ${result.factsAnalyzed} facts analyzed, ${result.patternsExtracted} patterns extracted, ${result.patternsStored} stored (window: ${result.window} days).`;\n }\n return {\n content: [{ type: \"text\", text: reflectText }],\n details: {\n factsAnalyzed: result.factsAnalyzed,\n patternsExtracted: result.patternsExtracted,\n patternsStored: result.patternsStored,\n window: result.window,\n },\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"reflection\",\n operation: \"memory_reflect\",\n });\n throw err;\n }\n },\n },\n { name: \"memory_reflect\" },\n );\n\n // memory_reflect_rules\n api.registerTool(\n {\n name: \"memory_reflect_rules\",\n label: \"Memory Reflect Rules\",\n description:\n \"Synthesize existing behavioral patterns into actionable one-line rules (category rule). Run after memory_reflect when you have enough patterns.\",\n parameters: Type.Object({}),\n async execute() {\n const reflectionCfg = cfg.reflection;\n if (!reflectionCfg.enabled) {\n return {\n content: [{ type: \"text\", text: \"Reflection is disabled. Enable reflection.enabled in plugin config.\" }],\n details: { error: \"reflection_disabled\" },\n };\n }\n const { defaultModel, fallbackModels } = resolveReflectionModelAndFallbacks(cfg, \"default\");\n try {\n const result = await runReflectionRules(\n factsDb,\n vectorDb,\n embeddings,\n openai,\n { dryRun: false, model: defaultModel, fallbackModels },\n api.logger,\n provenanceService,\n );\n return {\n content: [\n {\n type: \"text\",\n text: `Rules synthesis: ${result.rulesExtracted} rules extracted, ${result.rulesStored} stored.`,\n },\n ],\n details: { rulesExtracted: result.rulesExtracted, rulesStored: result.rulesStored },\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"reflection\",\n operation: \"memory_reflect_rules\",\n });\n throw err;\n }\n },\n },\n { name: \"memory_reflect_rules\" },\n );\n\n // memory_reflect_meta\n api.registerTool(\n {\n name: \"memory_reflect_meta\",\n label: \"Memory Reflect Meta\",\n description:\n \"Synthesize existing patterns into 1-3 higher-level meta-patterns (working style, principles). Run after memory_reflect when you have enough patterns.\",\n parameters: Type.Object({}),\n async execute() {\n const reflectionCfg = cfg.reflection;\n if (!reflectionCfg.enabled) {\n return {\n content: [{ type: \"text\", text: \"Reflection is disabled. Enable reflection.enabled in plugin config.\" }],\n details: { error: \"reflection_disabled\" },\n };\n }\n const { defaultModel, fallbackModels } = resolveReflectionModelAndFallbacks(cfg, \"default\");\n try {\n const result = await runReflectionMeta(\n factsDb,\n vectorDb,\n embeddings,\n openai,\n { dryRun: false, model: defaultModel, fallbackModels },\n api.logger,\n provenanceService,\n );\n return {\n content: [\n {\n type: \"text\",\n text: `Meta-pattern synthesis: ${result.metaExtracted} extracted, ${result.metaStored} stored.`,\n },\n ],\n details: { metaExtracted: result.metaExtracted, metaStored: result.metaStored },\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"reflection\",\n operation: \"memory_reflect_meta\",\n });\n throw err;\n }\n },\n },\n { name: \"memory_reflect_meta\" },\n );\n\n // memory_clusters\n api.registerTool(\n {\n name: \"memory_clusters\",\n label: \"Memory Clusters\",\n description:\n \"Detect and return topic clusters — groups of densely interconnected facts forming natural knowledge domains. Runs BFS connected-component analysis on the memory graph. Returns cluster labels, sizes, and member fact IDs.\",\n parameters: Type.Object({\n minClusterSize: Type.Optional(\n Type.Number({\n description: \"Minimum facts to form a cluster (default from config, typically 3)\",\n minimum: 2,\n }),\n ),\n save: Type.Optional(\n Type.Boolean({\n description: \"Persist detected clusters to the database (default: true)\",\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const clustersCfg = cfg.clusters;\n if (!clustersCfg.enabled) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Topic cluster detection is disabled. Set clusters.enabled: true in plugin config.\",\n },\n ],\n details: { error: \"clusters_disabled\" },\n };\n }\n\n const minClusterSize =\n typeof params.minClusterSize === \"number\" && params.minClusterSize >= 2\n ? Math.floor(params.minClusterSize)\n : clustersCfg.minClusterSize;\n\n const shouldSave = params.save !== false;\n\n try {\n // Build existingClusterIds map for stable IDs across re-runs\n const existingClusters = factsDb.getClusters();\n const existingClusterIds = new Map<string, { id: string; createdAt: number }>();\n for (const cluster of existingClusters) {\n const members = factsDb.getClusterMembers(cluster.id);\n const componentKey = [...members].sort().join(\",\");\n existingClusterIds.set(componentKey, { id: cluster.id, createdAt: cluster.createdAt });\n }\n\n const result = detectClusters(factsDb, { minClusterSize, existingClusterIds });\n\n if (shouldSave) {\n factsDb.saveClusters(result.clusters);\n }\n\n const summary = result.clusters.map((c) => ` • ${c.label} (${c.factCount} facts)`).join(\"\\n\");\n\n const text =\n result.clusters.length === 0\n ? `No topic clusters found (need at least ${minClusterSize} interconnected facts per cluster). Total linked facts: ${result.totalLinkedFacts}.`\n : `Found ${result.clusters.length} topic cluster(s) from ${result.totalLinkedFacts} linked facts (${result.isolatedFacts} below threshold):\\n${summary}`;\n\n return {\n content: [{ type: \"text\", text }],\n details: {\n clusterCount: result.clusters.length,\n totalLinkedFacts: result.totalLinkedFacts,\n isolatedFacts: result.isolatedFacts,\n clusters: result.clusters.map((c) => ({\n id: c.id,\n label: c.label,\n factCount: c.factCount,\n factIds: c.factIds,\n })),\n },\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"clusters\",\n operation: \"memory_clusters\",\n });\n throw err;\n }\n },\n },\n { name: \"memory_clusters\" },\n );\n\n // memory_gaps\n api.registerTool(\n {\n name: \"memory_gaps\",\n label: \"Memory Gaps\",\n description:\n \"Analyze the memory graph to surface knowledge gaps (orphans, weakly linked facts, suggested links).\",\n parameters: Type.Object({\n mode: Type.Optional(\n Type.Union([Type.Literal(\"orphans\"), Type.Literal(\"weak\"), Type.Literal(\"all\")], {\n description: \"Which gap types to return (default: all).\",\n }),\n ),\n limit: Type.Optional(\n Type.Number({\n description: \"Max items per category (default: 20).\",\n minimum: 1,\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n if (!cfg.gaps.enabled) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Knowledge gap analysis is disabled. Set gaps.enabled: true in plugin config.\",\n },\n ],\n details: { error: \"gaps_disabled\" },\n };\n }\n\n const mode = params.mode === \"orphans\" || params.mode === \"weak\" || params.mode === \"all\" ? params.mode : \"all\";\n const limit =\n typeof params.limit === \"number\" && Number.isFinite(params.limit) && params.limit > 0\n ? Math.min(200, Math.floor(params.limit))\n : 20;\n\n try {\n const report = await analyzeKnowledgeGaps(\n factsDb,\n vectorDb,\n embeddings,\n mode,\n limit,\n cfg.gaps.similarityThreshold,\n );\n\n const lines: string[] = [\n `Knowledge gaps (${mode})`,\n ` Orphans: ${report.orphans.length}`,\n ` Weak links: ${report.weak.length}`,\n ` Suggested links: ${report.suggestedLinks.length}`,\n ];\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n details: report,\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"gaps\",\n operation: \"memory_gaps\",\n });\n throw err;\n }\n },\n },\n { name: \"memory_gaps\" },\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utility-tools.js","names":[],"sources":["../../tools/utility-tools.ts"],"sourcesContent":["import { Type } from \"@sinclair/typebox\";\nimport type OpenAI from \"openai\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport { stringEnum } from \"../utils/typebox.js\";\n\nimport type { FactsDB } from \"../backends/facts-db.js\";\nimport type { VectorDB } from \"../backends/vector-db.js\";\nimport type { WriteAheadLog } from \"../backends/wal.js\";\nimport type { HybridMemoryConfig } from \"../config.js\";\nimport { isCompactVerbosity, resolveReflectionModelAndFallbacks } from \"../config.js\";\nimport type { EmbeddingProvider } from \"../services/embeddings.js\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\nimport { analyzeKnowledgeGaps } from \"../services/knowledge-gaps.js\";\nimport type { ProvenanceService } from \"../services/provenance.js\";\nimport { detectClusters } from \"../services/topic-clusters.js\";\nimport { deleteVectorsForFactIds } from \"../services/vector-maintenance.js\";\n\nexport interface PluginContext {\n factsDb: FactsDB;\n vectorDb: VectorDB;\n embeddings: EmbeddingProvider;\n openai: OpenAI;\n cfg: HybridMemoryConfig;\n wal: WriteAheadLog | null;\n resolvedSqlitePath: string;\n provenanceService?: ProvenanceService | null;\n}\n\n// Helper function types (exported for register-tools ToolsContext)\nexport type RunReflectionFn = (\n factsDb: FactsDB,\n vectorDb: VectorDB,\n embeddings: EmbeddingProvider,\n openai: OpenAI,\n config: { defaultWindow: number; minObservations: number; enabled?: boolean },\n opts: { window: number; dryRun: boolean; model: string; fallbackModels?: string[] },\n logger: { info: (msg: string) => void; warn: (msg: string) => void },\n provenanceService?: ProvenanceService | null,\n) => Promise<{ factsAnalyzed: number; patternsExtracted: number; patternsStored: number; window: number }>;\n\nexport type RunReflectionRulesFn = (\n factsDb: FactsDB,\n vectorDb: VectorDB,\n embeddings: EmbeddingProvider,\n openai: OpenAI,\n opts: { dryRun: boolean; model: string; fallbackModels?: string[] },\n logger: { info: (msg: string) => void; warn: (msg: string) => void },\n provenanceService?: ProvenanceService | null,\n) => Promise<{ rulesExtracted: number; rulesStored: number }>;\n\nexport type RunReflectionMetaFn = (\n factsDb: FactsDB,\n vectorDb: VectorDB,\n embeddings: EmbeddingProvider,\n openai: OpenAI,\n opts: { dryRun: boolean; model: string; fallbackModels?: string[] },\n logger: { info: (msg: string) => void; warn: (msg: string) => void },\n provenanceService?: ProvenanceService | null,\n) => Promise<{ metaExtracted: number; metaStored: number }>;\n\nexport function registerUtilityTools(\n ctx: PluginContext,\n api: ClawdbotPluginApi,\n runReflection: RunReflectionFn,\n runReflectionRules: RunReflectionRulesFn,\n runReflectionMeta: RunReflectionMetaFn,\n _walWrite: (operation: \"store\" | \"update\", data: Record<string, unknown>) => Promise<string>,\n _walRemove: (id: string) => Promise<void>,\n): void {\n const { factsDb, vectorDb, embeddings, openai, cfg, provenanceService } = ctx;\n\n // memory_checkpoint\n api.registerTool(\n {\n name: \"memory_checkpoint\",\n label: \"Memory Checkpoint\",\n description: \"Save or restore pre-flight checkpoints before risky/long operations. Auto-expires after 4 hours.\",\n parameters: Type.Object({\n action: stringEnum([\"save\", \"restore\"] as const),\n intent: Type.Optional(Type.String({ description: \"What you're about to do (for save)\" })),\n state: Type.Optional(Type.String({ description: \"Current state/context (for save)\" })),\n expectedOutcome: Type.Optional(Type.String({ description: \"What should happen if successful\" })),\n workingFiles: Type.Optional(\n Type.Array(Type.String(), {\n description: \"Files being modified\",\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { action, intent, state, expectedOutcome, workingFiles } = params as {\n action: \"save\" | \"restore\";\n intent?: string;\n state?: string;\n expectedOutcome?: string;\n workingFiles?: string[];\n };\n\n if (action === \"save\") {\n if (!intent || !state) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Checkpoint save requires 'intent' and 'state'.\",\n },\n ],\n details: { error: \"missing_param\" },\n };\n }\n const id = factsDb.saveCheckpoint({\n intent,\n state,\n expectedOutcome,\n workingFiles,\n });\n return {\n content: [\n {\n type: \"text\",\n text: `Checkpoint saved (id: ${id.slice(0, 8)}..., TTL: 4h). Intent: ${intent.slice(0, 80)}`,\n },\n ],\n details: { action: \"saved\", id },\n };\n }\n\n const checkpoint = factsDb.restoreCheckpoint();\n if (!checkpoint) {\n return {\n content: [\n {\n type: \"text\",\n text: \"No active checkpoint found (may have expired).\",\n },\n ],\n details: { action: \"not_found\" },\n };\n }\n\n return {\n content: [\n {\n type: \"text\",\n text: `Restored checkpoint (saved: ${checkpoint.savedAt}):\\n- Intent: ${checkpoint.intent}\\n- State: ${checkpoint.state}${checkpoint.expectedOutcome ? `\\n- Expected: ${checkpoint.expectedOutcome}` : \"\"}${checkpoint.workingFiles?.length ? `\\n- Files: ${checkpoint.workingFiles.join(\", \")}` : \"\"}`,\n },\n ],\n details: { action: \"restored\", checkpoint },\n };\n },\n },\n { name: \"memory_checkpoint\" },\n );\n\n // memory_prune\n api.registerTool(\n {\n name: \"memory_prune\",\n label: \"Memory Prune\",\n description: \"Prune expired memories and decay confidence of aging facts.\",\n parameters: Type.Object({\n mode: Type.Optional(stringEnum([\"hard\", \"soft\", \"both\"] as const)),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { mode = \"both\" } = params as { mode?: \"hard\" | \"soft\" | \"both\" };\n\n let hardPruned = 0;\n let softPruned = 0;\n let linksPruned = 0;\n\n if (mode === \"hard\" || mode === \"both\") {\n const pendingExpiredIds = factsDb.listExpiredFactIdsPendingPrune();\n hardPruned = factsDb.pruneExpired();\n await deleteVectorsForFactIds(vectorDb, pendingExpiredIds, {\n operation: \"tool-memory-prune-expired\",\n logger: api.logger,\n });\n }\n if (mode === \"soft\" || mode === \"both\") {\n const pendingLowConfidenceIds = factsDb.listLowConfidenceFactIdsPendingPrune();\n softPruned = factsDb.decayConfidence();\n await deleteVectorsForFactIds(vectorDb, pendingLowConfidenceIds, {\n operation: \"tool-memory-prune-decay\",\n logger: api.logger,\n });\n }\n\n // Always prune orphaned links (fast, no-op if none exist)\n linksPruned = factsDb.pruneOrphanedLinks();\n\n const breakdown = factsDb.statsBreakdown();\n const expired = factsDb.countExpired();\n\n const verbosity = cfg.verbosity ?? \"normal\";\n let text: string;\n if (isCompactVerbosity(verbosity)) {\n // Quiet: compact one-liner — statsBreakdown and countExpired are still computed above\n // and included in the `details` field for programmatic consumers; they're intentionally\n // omitted from the human-readable text to reduce noise in quiet sessions.\n const linksNote = linksPruned > 0 ? ` ${linksPruned} orphaned links.` : \"\";\n text = `Pruned: ${hardPruned + softPruned} (${hardPruned} expired, ${softPruned} low-confidence).${linksNote}`;\n } else {\n const linksNote = linksPruned > 0 ? `\\nOrphaned links removed: ${linksPruned}` : \"\";\n const baseText = `Pruned: ${hardPruned} expired + ${softPruned} low-confidence.${linksNote}\\nRemaining by class: ${JSON.stringify(breakdown)}\\nPending expired: ${expired}`;\n const verboseExtra = verbosity === \"verbose\" ? `\\nMode: ${mode}` : \"\";\n text = baseText + verboseExtra;\n }\n\n return {\n content: [{ type: \"text\", text }],\n details: { hardPruned, softPruned, linksPruned, breakdown, pendingExpired: expired },\n };\n },\n },\n { name: \"memory_prune\" },\n );\n\n // memory_reflect\n api.registerTool(\n {\n name: \"memory_reflect\",\n label: \"Memory Reflect\",\n description:\n \"Run reflection on recent facts to synthesize behavioral patterns. Analyzes facts from the last N days, sends to LLM to extract patterns, stores new patterns (permanent, high importance) for better agent alignment.\",\n parameters: Type.Object({\n window: Type.Optional(\n Type.Number({\n description: \"Time window in days (1–90, default from config)\",\n minimum: 1,\n maximum: 90,\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const reflectionCfg = cfg.reflection;\n if (!reflectionCfg.enabled) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Reflection is disabled. Enable reflection.enabled in plugin config to use memory_reflect.\",\n },\n ],\n details: { error: \"reflection_disabled\" },\n };\n }\n const window = Math.min(\n 90,\n Math.max(1, typeof params.window === \"number\" ? params.window : reflectionCfg.defaultWindow),\n );\n const { defaultModel, fallbackModels } = resolveReflectionModelAndFallbacks(cfg, \"default\");\n try {\n const result = await runReflection(\n factsDb,\n vectorDb,\n embeddings,\n openai,\n { defaultWindow: reflectionCfg.defaultWindow, minObservations: reflectionCfg.minObservations },\n { window, dryRun: false, model: defaultModel, fallbackModels },\n api.logger,\n provenanceService,\n );\n const verbosity = cfg.verbosity ?? \"normal\";\n let reflectText: string;\n if (isCompactVerbosity(verbosity)) {\n reflectText = `Reflected: ${result.patternsStored} patterns stored.`;\n } else if (verbosity === \"verbose\") {\n reflectText = `Reflection complete: ${result.factsAnalyzed} facts analyzed, ${result.patternsExtracted} patterns extracted, ${result.patternsStored} stored (window: ${result.window} days, model: ${defaultModel}).`;\n } else {\n reflectText = `Reflection complete: ${result.factsAnalyzed} facts analyzed, ${result.patternsExtracted} patterns extracted, ${result.patternsStored} stored (window: ${result.window} days).`;\n }\n return {\n content: [{ type: \"text\", text: reflectText }],\n details: {\n factsAnalyzed: result.factsAnalyzed,\n patternsExtracted: result.patternsExtracted,\n patternsStored: result.patternsStored,\n window: result.window,\n },\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"reflection\",\n operation: \"memory_reflect\",\n });\n throw err;\n }\n },\n },\n { name: \"memory_reflect\" },\n );\n\n // memory_reflect_rules\n api.registerTool(\n {\n name: \"memory_reflect_rules\",\n label: \"Memory Reflect Rules\",\n description:\n \"Synthesize existing behavioral patterns into actionable one-line rules (category rule). Run after memory_reflect when you have enough patterns.\",\n parameters: Type.Object({}),\n async execute() {\n const reflectionCfg = cfg.reflection;\n if (!reflectionCfg.enabled) {\n return {\n content: [{ type: \"text\", text: \"Reflection is disabled. Enable reflection.enabled in plugin config.\" }],\n details: { error: \"reflection_disabled\" },\n };\n }\n const { defaultModel, fallbackModels } = resolveReflectionModelAndFallbacks(cfg, \"default\");\n try {\n const result = await runReflectionRules(\n factsDb,\n vectorDb,\n embeddings,\n openai,\n { dryRun: false, model: defaultModel, fallbackModels },\n api.logger,\n provenanceService,\n );\n return {\n content: [\n {\n type: \"text\",\n text: `Rules synthesis: ${result.rulesExtracted} rules extracted, ${result.rulesStored} stored.`,\n },\n ],\n details: { rulesExtracted: result.rulesExtracted, rulesStored: result.rulesStored },\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"reflection\",\n operation: \"memory_reflect_rules\",\n });\n throw err;\n }\n },\n },\n { name: \"memory_reflect_rules\" },\n );\n\n // memory_reflect_meta\n api.registerTool(\n {\n name: \"memory_reflect_meta\",\n label: \"Memory Reflect Meta\",\n description:\n \"Synthesize existing patterns into 1-3 higher-level meta-patterns (working style, principles). Run after memory_reflect when you have enough patterns.\",\n parameters: Type.Object({}),\n async execute() {\n const reflectionCfg = cfg.reflection;\n if (!reflectionCfg.enabled) {\n return {\n content: [{ type: \"text\", text: \"Reflection is disabled. Enable reflection.enabled in plugin config.\" }],\n details: { error: \"reflection_disabled\" },\n };\n }\n const { defaultModel, fallbackModels } = resolveReflectionModelAndFallbacks(cfg, \"default\");\n try {\n const result = await runReflectionMeta(\n factsDb,\n vectorDb,\n embeddings,\n openai,\n { dryRun: false, model: defaultModel, fallbackModels },\n api.logger,\n provenanceService,\n );\n return {\n content: [\n {\n type: \"text\",\n text: `Meta-pattern synthesis: ${result.metaExtracted} extracted, ${result.metaStored} stored.`,\n },\n ],\n details: { metaExtracted: result.metaExtracted, metaStored: result.metaStored },\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"reflection\",\n operation: \"memory_reflect_meta\",\n });\n throw err;\n }\n },\n },\n { name: \"memory_reflect_meta\" },\n );\n\n // memory_clusters\n api.registerTool(\n {\n name: \"memory_clusters\",\n label: \"Memory Clusters\",\n description:\n \"Detect and return topic clusters — groups of densely interconnected facts forming natural knowledge domains. Runs BFS connected-component analysis on the memory graph. Returns cluster labels, sizes, and member fact IDs.\",\n parameters: Type.Object({\n minClusterSize: Type.Optional(\n Type.Number({\n description: \"Minimum facts to form a cluster (default from config, typically 3)\",\n minimum: 2,\n }),\n ),\n save: Type.Optional(\n Type.Boolean({\n description: \"Persist detected clusters to the database (default: true)\",\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const clustersCfg = cfg.clusters;\n if (!clustersCfg.enabled) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Topic cluster detection is disabled. Set clusters.enabled: true in plugin config.\",\n },\n ],\n details: { error: \"clusters_disabled\" },\n };\n }\n\n const minClusterSize =\n typeof params.minClusterSize === \"number\" && params.minClusterSize >= 2\n ? Math.floor(params.minClusterSize)\n : clustersCfg.minClusterSize;\n\n const shouldSave = params.save !== false;\n\n try {\n // Build existingClusterIds map for stable IDs across re-runs\n const existingClusters = factsDb.getClusters();\n const existingClusterIds = new Map<string, { id: string; createdAt: number }>();\n for (const cluster of existingClusters) {\n const members = factsDb.getClusterMembers(cluster.id);\n const componentKey = [...members].sort().join(\",\");\n existingClusterIds.set(componentKey, { id: cluster.id, createdAt: cluster.createdAt });\n }\n\n const result = detectClusters(factsDb, { minClusterSize, existingClusterIds });\n\n if (shouldSave) {\n factsDb.saveClusters(result.clusters);\n }\n\n const summary = result.clusters.map((c) => ` • ${c.label} (${c.factCount} facts)`).join(\"\\n\");\n\n const text =\n result.clusters.length === 0\n ? `No topic clusters found (need at least ${minClusterSize} interconnected facts per cluster). Total linked facts: ${result.totalLinkedFacts}.`\n : `Found ${result.clusters.length} topic cluster(s) from ${result.totalLinkedFacts} linked facts (${result.isolatedFacts} below threshold):\\n${summary}`;\n\n return {\n content: [{ type: \"text\", text }],\n details: {\n clusterCount: result.clusters.length,\n totalLinkedFacts: result.totalLinkedFacts,\n isolatedFacts: result.isolatedFacts,\n clusters: result.clusters.map((c) => ({\n id: c.id,\n label: c.label,\n factCount: c.factCount,\n factIds: c.factIds,\n })),\n },\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"clusters\",\n operation: \"memory_clusters\",\n });\n throw err;\n }\n },\n },\n { name: \"memory_clusters\" },\n );\n\n // memory_gaps\n api.registerTool(\n {\n name: \"memory_gaps\",\n label: \"Memory Gaps\",\n description:\n \"Analyze the memory graph to surface knowledge gaps (orphans, weakly linked facts, suggested links).\",\n parameters: Type.Object({\n mode: Type.Optional(\n Type.Union([Type.Literal(\"orphans\"), Type.Literal(\"weak\"), Type.Literal(\"all\")], {\n description: \"Which gap types to return (default: all).\",\n }),\n ),\n limit: Type.Optional(\n Type.Number({\n description: \"Max items per category (default: 20).\",\n minimum: 1,\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n if (!cfg.gaps.enabled) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Knowledge gap analysis is disabled. Set gaps.enabled: true in plugin config.\",\n },\n ],\n details: { error: \"gaps_disabled\" },\n };\n }\n\n const mode = params.mode === \"orphans\" || params.mode === \"weak\" || params.mode === \"all\" ? params.mode : \"all\";\n const limit =\n typeof params.limit === \"number\" && Number.isFinite(params.limit) && params.limit > 0\n ? Math.min(200, Math.floor(params.limit))\n : 20;\n\n try {\n const report = await analyzeKnowledgeGaps(\n factsDb,\n vectorDb,\n embeddings,\n mode,\n limit,\n cfg.gaps.similarityThreshold,\n );\n\n const lines: string[] = [\n `Knowledge gaps (${mode})`,\n ` Orphans: ${report.orphans.length}`,\n ` Weak links: ${report.weak.length}`,\n ` Suggested links: ${report.suggestedLinks.length}`,\n ];\n\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n details: report,\n };\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), {\n subsystem: \"gaps\",\n operation: \"memory_gaps\",\n });\n throw err;\n }\n },\n },\n { name: \"memory_gaps\" },\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"verification-tools.js","names":[],"sources":["../../tools/verification-tools.ts"],"sourcesContent":["/**\n * Verification Tool Registrations\n *\n * Tool definitions for managing verified facts (Issue #162).\n */\n\nimport { Type } from \"@sinclair/typebox\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\n\nimport type { FactsDB } from \"../backends/facts-db.js\";\nimport type { VerificationStore } from \"../services/verification-store.js\";\nimport { VerificationError } from \"../services/verification-store.js\";\n\ninterface PluginContext {\n factsDb: FactsDB;\n verificationStore: VerificationStore;\n}\n\n/**\n * Register verification-related tools with the plugin API.\n *\n * This includes: memory_verify, memory_verified_list, memory_verification_status.\n */\nexport function registerVerificationTools(ctx: PluginContext, api: ClawdbotPluginApi): void {\n const { factsDb, verificationStore } = ctx;\n\n api.registerTool(\n {\n name: \"memory_verify\",\n label: \"Memory Verify\",\n description: \"Mark a fact as verified/critical and store it in the verification store.\",\n parameters: Type.Object({\n factId: Type.String({ description: \"ID of the fact to verify\" }),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { factId } = params as { factId: string };\n const fact = factsDb.getById(factId);\n if (!fact) {\n return {\n content: [{ type: \"text\", text: `Fact not found: ${factId}` }],\n details: { error: \"not_found\", id: factId },\n };\n }\n try {\n const existing = verificationStore.getVerified(factId);\n if (existing) {\n return {\n content: [{ type: \"text\", text: `Fact ${factId} is already verified (version ${existing.version}).` }],\n details: { status: \"already_verified\", id: factId, version: existing.version },\n };\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Verification status check failed for ${factId}: ${err}` }],\n details: { error: \"verification_status_failed\", id: factId },\n };\n }\n try {\n const verifiedId = verificationStore.verify(factId, fact.text, \"agent\");\n return {\n content: [{ type: \"text\", text: `Verified fact ${factId} (verification id: ${verifiedId}).` }],\n details: { status: \"verified\", id: factId, verificationId: verifiedId },\n };\n } catch (err) {\n if (err instanceof VerificationError) {\n return {\n content: [{ type: \"text\", text: `Verification failed for ${factId}: ${err.message}` }],\n details: { error: \"verification_failed\", id: factId },\n };\n }\n return {\n content: [{ type: \"text\", text: `Verification failed for ${factId}: ${err}` }],\n details: { error: \"verification_failed\", id: factId },\n };\n }\n },\n },\n { name: \"memory_verify\" },\n );\n\n api.registerTool(\n {\n name: \"memory_verified_list\",\n label: \"Verified Facts List\",\n description: \"List all verified facts with their latest verification metadata.\",\n parameters: Type.Object({}),\n async execute() {\n const verified = verificationStore.listLatestVerified();\n if (verified.length === 0) {\n return {\n content: [{ type: \"text\", text: \"No verified facts found.\" }],\n details: { count: 0 },\n };\n }\n const lines = verified.map((vf) => {\n const next = vf.nextVerification ? ` next=${vf.nextVerification}` : \"\";\n return `- ${vf.factId} (v${vf.version}) verified_at=${vf.verifiedAt}${next}: ${vf.canonicalText.slice(0, 120)}${vf.canonicalText.length > 120 ? \"…\" : \"\"}`;\n });\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n details: { count: verified.length },\n };\n },\n },\n { name: \"memory_verified_list\" },\n );\n\n api.registerTool(\n {\n name: \"memory_verification_status\",\n label: \"Verification Status\",\n description: \"Check whether a specific fact is verified and return its status.\",\n parameters: Type.Object({\n factId: Type.String({ description: \"ID of the fact to check\" }),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { factId } = params as { factId: string };\n try {\n const verified = verificationStore.getVerified(factId);\n if (!verified) {\n return {\n content: [{ type: \"text\", text: `Fact ${factId} is not verified.` }],\n details: { status: \"not_verified\", id: factId },\n };\n }\n return {\n content: [\n {\n type: \"text\",\n text: `Fact ${factId} is verified (v${verified.version}), verified_at=${verified.verifiedAt}.`,\n },\n ],\n details: {\n status: \"verified\",\n id: factId,\n verifiedAt: verified.verifiedAt,\n verifiedBy: verified.verifiedBy,\n nextVerification: verified.nextVerification,\n version: verified.version,\n },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Verification status check failed for ${factId}: ${err}` }],\n details: { error: \"verification_status_failed\", id: factId },\n };\n }\n },\n },\n { name: \"memory_verification_status\" },\n );\n}\n"],"mappings":";;;;;;;;;;;;;AAuBA,SAAgB,0BAA0B,KAAoB,KAA8B;CAC1F,MAAM,EAAE,SAAS,sBAAsB;CAEvC,IAAI,aACF;EACE,MAAM;EACN,OAAO;EACP,aAAa;EACb,YAAY,KAAK,OAAO,EACtB,QAAQ,KAAK,OAAO,EAAE,aAAa,
|
|
1
|
+
{"version":3,"file":"verification-tools.js","names":[],"sources":["../../tools/verification-tools.ts"],"sourcesContent":["/**\n * Verification Tool Registrations\n *\n * Tool definitions for managing verified facts (Issue #162).\n */\n\nimport { Type } from \"@sinclair/typebox\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\n\nimport type { FactsDB } from \"../backends/facts-db.js\";\nimport type { VerificationStore } from \"../services/verification-store.js\";\nimport { VerificationError } from \"../services/verification-store.js\";\n\ninterface PluginContext {\n factsDb: FactsDB;\n verificationStore: VerificationStore;\n}\n\n/**\n * Register verification-related tools with the plugin API.\n *\n * This includes: memory_verify, memory_verified_list, memory_verification_status.\n */\nexport function registerVerificationTools(ctx: PluginContext, api: ClawdbotPluginApi): void {\n const { factsDb, verificationStore } = ctx;\n\n api.registerTool(\n {\n name: \"memory_verify\",\n label: \"Memory Verify\",\n description: \"Mark a fact as verified/critical and store it in the verification store.\",\n parameters: Type.Object({\n factId: Type.String({ description: \"ID of the fact to verify\" }),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { factId } = params as { factId: string };\n const fact = factsDb.getById(factId);\n if (!fact) {\n return {\n content: [{ type: \"text\", text: `Fact not found: ${factId}` }],\n details: { error: \"not_found\", id: factId },\n };\n }\n try {\n const existing = verificationStore.getVerified(factId);\n if (existing) {\n return {\n content: [{ type: \"text\", text: `Fact ${factId} is already verified (version ${existing.version}).` }],\n details: { status: \"already_verified\", id: factId, version: existing.version },\n };\n }\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Verification status check failed for ${factId}: ${err}` }],\n details: { error: \"verification_status_failed\", id: factId },\n };\n }\n try {\n const verifiedId = verificationStore.verify(factId, fact.text, \"agent\");\n return {\n content: [{ type: \"text\", text: `Verified fact ${factId} (verification id: ${verifiedId}).` }],\n details: { status: \"verified\", id: factId, verificationId: verifiedId },\n };\n } catch (err) {\n if (err instanceof VerificationError) {\n return {\n content: [{ type: \"text\", text: `Verification failed for ${factId}: ${err.message}` }],\n details: { error: \"verification_failed\", id: factId },\n };\n }\n return {\n content: [{ type: \"text\", text: `Verification failed for ${factId}: ${err}` }],\n details: { error: \"verification_failed\", id: factId },\n };\n }\n },\n },\n { name: \"memory_verify\" },\n );\n\n api.registerTool(\n {\n name: \"memory_verified_list\",\n label: \"Verified Facts List\",\n description: \"List all verified facts with their latest verification metadata.\",\n parameters: Type.Object({}),\n async execute() {\n const verified = verificationStore.listLatestVerified();\n if (verified.length === 0) {\n return {\n content: [{ type: \"text\", text: \"No verified facts found.\" }],\n details: { count: 0 },\n };\n }\n const lines = verified.map((vf) => {\n const next = vf.nextVerification ? ` next=${vf.nextVerification}` : \"\";\n return `- ${vf.factId} (v${vf.version}) verified_at=${vf.verifiedAt}${next}: ${vf.canonicalText.slice(0, 120)}${vf.canonicalText.length > 120 ? \"…\" : \"\"}`;\n });\n return {\n content: [{ type: \"text\", text: lines.join(\"\\n\") }],\n details: { count: verified.length },\n };\n },\n },\n { name: \"memory_verified_list\" },\n );\n\n api.registerTool(\n {\n name: \"memory_verification_status\",\n label: \"Verification Status\",\n description: \"Check whether a specific fact is verified and return its status.\",\n parameters: Type.Object({\n factId: Type.String({ description: \"ID of the fact to check\" }),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { factId } = params as { factId: string };\n try {\n const verified = verificationStore.getVerified(factId);\n if (!verified) {\n return {\n content: [{ type: \"text\", text: `Fact ${factId} is not verified.` }],\n details: { status: \"not_verified\", id: factId },\n };\n }\n return {\n content: [\n {\n type: \"text\",\n text: `Fact ${factId} is verified (v${verified.version}), verified_at=${verified.verifiedAt}.`,\n },\n ],\n details: {\n status: \"verified\",\n id: factId,\n verifiedAt: verified.verifiedAt,\n verifiedBy: verified.verifiedBy,\n nextVerification: verified.nextVerification,\n version: verified.version,\n },\n };\n } catch (err) {\n return {\n content: [{ type: \"text\", text: `Verification status check failed for ${factId}: ${err}` }],\n details: { error: \"verification_status_failed\", id: factId },\n };\n }\n },\n },\n { name: \"memory_verification_status\" },\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-tools.js","names":[],"sources":["../../tools/workflow-tools.ts"],"sourcesContent":["/**\n * Workflow Tools — memory_workflows tool registration (Issue #209).\n *\n * Exposes workflow patterns (grouped by similar tool sequences) to the agent\n * so it can learn which tool sequences work best for given goals.\n */\n\nimport { Type } from \"@sinclair/typebox\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { WorkflowStore } from \"../backends/workflow-store.js\";\nimport { extractGoalKeywords } from \"../backends/workflow-store.js\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\n\ninterface WorkflowToolsContext {\n workflowStore: WorkflowStore;\n}\n\nexport function registerWorkflowTools(ctx: WorkflowToolsContext, api: ClawdbotPluginApi): void {\n const { workflowStore } = ctx;\n\n // -------------------------------------------------------------------------\n // memory_workflows — query recorded workflow patterns\n // -------------------------------------------------------------------------\n api.registerTool({\n name: \"memory_workflows\",\n label: \"Query Workflow Patterns\",\n description:\n \"Search recorded tool-sequence patterns to find which workflows succeed for a given goal. Returns patterns grouped by similar tool sequences with success rates and usage counts.\",\n parameters: Type.Object({\n goal: Type.Optional(\n Type.String({\n description:\n \"Optional natural-language goal to filter patterns by (keyword-matched). Omit to return all patterns.\",\n }),\n ),\n minSuccessRate: Type.Optional(\n Type.Number({\n minimum: 0,\n maximum: 1,\n description: \"Minimum success rate (0–1) to include a pattern. Default: 0 (all patterns).\",\n }),\n ),\n limit: Type.Optional(\n Type.Integer({\n minimum: 1,\n maximum: 100,\n description: \"Maximum number of patterns to return. Default: 20.\",\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { goal, minSuccessRate, limit } = params as {\n goal?: string;\n minSuccessRate?: number;\n limit?: number;\n };\n\n try {\n const hasGoalFilter = goal && goal.trim().length > 0;\n const patterns = workflowStore.getPatterns({\n minSuccessRate: minSuccessRate ?? 0,\n limit: hasGoalFilter ? undefined : (limit ?? 20),\n });\n\n // If goal supplied, further filter by keyword overlap\n let filtered = patterns;\n if (hasGoalFilter) {\n const keywords = new Set(extractGoalKeywords(goal));\n filtered = patterns.filter((p) =>\n p.exampleGoals.some((g) => extractGoalKeywords(g).some((k) => keywords.has(k))),\n );\n // Apply limit after goal filtering\n filtered = filtered.slice(0, limit ?? 20);\n }\n\n if (filtered.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: goal\n ? `No workflow patterns found matching goal: \"${goal}\"`\n : \"No workflow patterns recorded yet.\",\n },\n ],\n details: [],\n };\n }\n\n const lines = filtered.map((p, i) => {\n const rate = `${Math.round(p.successRate * 100)}%`;\n const seq = p.toolSequence.join(\" → \");\n const goals = p.exampleGoals.map((g) => `\"${g}\"`).join(\", \");\n return (\n `${i + 1}. [${rate} success, ${p.totalCount}× used, avg ${p.avgDurationMs}ms]\\n` +\n ` Tools: ${seq || \"(empty)\"}\\n` +\n ` Example goals: ${goals || \"(none)\"}`\n );\n });\n\n const summary = `Found ${filtered.length} workflow pattern(s)${goal ? ` matching \"${goal}\"` : \"\"}:\\n\\n${lines.join(\"\\n\\n\")}`;\n\n return {\n content: [{ type: \"text\", text: summary }],\n details: filtered,\n };\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n capturePluginError(e, {\n subsystem: \"workflows\",\n operation: \"memory-workflows\",\n phase: \"runtime\",\n });\n const msg = e.message;\n if (/not open|connection is not open|The database connection is not open/i.test(msg)) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Workflow patterns are temporarily unavailable (database not ready). Try again in a moment.\",\n },\n ],\n details: [],\n };\n }\n throw e;\n }\n },\n });\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,sBAAsB,KAA2B,KAA8B;CAC7F,MAAM,EAAE,kBAAkB;CAK1B,IAAI,aAAa;EACf,MAAM;EACN,OAAO;EACP,aACE;EACF,YAAY,KAAK,OAAO;GACtB,MAAM,KAAK,SACT,KAAK,OAAO,EACV,aACE,
|
|
1
|
+
{"version":3,"file":"workflow-tools.js","names":[],"sources":["../../tools/workflow-tools.ts"],"sourcesContent":["/**\n * Workflow Tools — memory_workflows tool registration (Issue #209).\n *\n * Exposes workflow patterns (grouped by similar tool sequences) to the agent\n * so it can learn which tool sequences work best for given goals.\n */\n\nimport { Type } from \"@sinclair/typebox\";\nimport type { ClawdbotPluginApi } from \"openclaw/plugin-sdk/core\";\nimport type { WorkflowStore } from \"../backends/workflow-store.js\";\nimport { extractGoalKeywords } from \"../backends/workflow-store.js\";\nimport { capturePluginError } from \"../services/error-reporter.js\";\n\ninterface WorkflowToolsContext {\n workflowStore: WorkflowStore;\n}\n\nexport function registerWorkflowTools(ctx: WorkflowToolsContext, api: ClawdbotPluginApi): void {\n const { workflowStore } = ctx;\n\n // -------------------------------------------------------------------------\n // memory_workflows — query recorded workflow patterns\n // -------------------------------------------------------------------------\n api.registerTool({\n name: \"memory_workflows\",\n label: \"Query Workflow Patterns\",\n description:\n \"Search recorded tool-sequence patterns to find which workflows succeed for a given goal. Returns patterns grouped by similar tool sequences with success rates and usage counts.\",\n parameters: Type.Object({\n goal: Type.Optional(\n Type.String({\n description:\n \"Optional natural-language goal to filter patterns by (keyword-matched). Omit to return all patterns.\",\n }),\n ),\n minSuccessRate: Type.Optional(\n Type.Number({\n minimum: 0,\n maximum: 1,\n description: \"Minimum success rate (0–1) to include a pattern. Default: 0 (all patterns).\",\n }),\n ),\n limit: Type.Optional(\n Type.Integer({\n minimum: 1,\n maximum: 100,\n description: \"Maximum number of patterns to return. Default: 20.\",\n }),\n ),\n }),\n async execute(_toolCallId: string, params: Record<string, unknown>) {\n const { goal, minSuccessRate, limit } = params as {\n goal?: string;\n minSuccessRate?: number;\n limit?: number;\n };\n\n try {\n const hasGoalFilter = goal && goal.trim().length > 0;\n const patterns = workflowStore.getPatterns({\n minSuccessRate: minSuccessRate ?? 0,\n limit: hasGoalFilter ? undefined : (limit ?? 20),\n });\n\n // If goal supplied, further filter by keyword overlap\n let filtered = patterns;\n if (hasGoalFilter) {\n const keywords = new Set(extractGoalKeywords(goal));\n filtered = patterns.filter((p) =>\n p.exampleGoals.some((g) => extractGoalKeywords(g).some((k) => keywords.has(k))),\n );\n // Apply limit after goal filtering\n filtered = filtered.slice(0, limit ?? 20);\n }\n\n if (filtered.length === 0) {\n return {\n content: [\n {\n type: \"text\",\n text: goal\n ? `No workflow patterns found matching goal: \"${goal}\"`\n : \"No workflow patterns recorded yet.\",\n },\n ],\n details: [],\n };\n }\n\n const lines = filtered.map((p, i) => {\n const rate = `${Math.round(p.successRate * 100)}%`;\n const seq = p.toolSequence.join(\" → \");\n const goals = p.exampleGoals.map((g) => `\"${g}\"`).join(\", \");\n return (\n `${i + 1}. [${rate} success, ${p.totalCount}× used, avg ${p.avgDurationMs}ms]\\n` +\n ` Tools: ${seq || \"(empty)\"}\\n` +\n ` Example goals: ${goals || \"(none)\"}`\n );\n });\n\n const summary = `Found ${filtered.length} workflow pattern(s)${goal ? ` matching \"${goal}\"` : \"\"}:\\n\\n${lines.join(\"\\n\\n\")}`;\n\n return {\n content: [{ type: \"text\", text: summary }],\n details: filtered,\n };\n } catch (err) {\n const e = err instanceof Error ? err : new Error(String(err));\n capturePluginError(e, {\n subsystem: \"workflows\",\n operation: \"memory-workflows\",\n phase: \"runtime\",\n });\n const msg = e.message;\n if (/not open|connection is not open|The database connection is not open/i.test(msg)) {\n return {\n content: [\n {\n type: \"text\",\n text: \"Workflow patterns are temporarily unavailable (database not ready). Try again in a moment.\",\n },\n ],\n details: [],\n };\n }\n throw e;\n }\n },\n });\n}\n"],"mappings":";;;;;;;;;;AAiBA,SAAgB,sBAAsB,KAA2B,KAA8B;CAC7F,MAAM,EAAE,kBAAkB;CAK1B,IAAI,aAAa;EACf,MAAM;EACN,OAAO;EACP,aACE;EACF,YAAY,KAAK,OAAO;GACtB,MAAM,KAAK,SACT,KAAK,OAAO,EACV,aACE,uGACJ,CAAC,CACH;GACA,gBAAgB,KAAK,SACnB,KAAK,OAAO;IACV,SAAS;IACT,SAAS;IACT,aAAa;GACf,CAAC,CACH;GACA,OAAO,KAAK,SACV,KAAK,QAAQ;IACX,SAAS;IACT,SAAS;IACT,aAAa;GACf,CAAC,CACH;EACF,CAAC;EACD,MAAM,QAAQ,aAAqB,QAAiC;GAClE,MAAM,EAAE,MAAM,gBAAgB,UAAU;GAMxC,IAAI;IACF,MAAM,gBAAgB,QAAQ,KAAK,KAAK,EAAE,SAAS;IACnD,MAAM,WAAW,cAAc,YAAY;KACzC,gBAAgB,kBAAkB;KAClC,OAAO,gBAAgB,KAAA,IAAa,SAAS;IAC/C,CAAC;IAGD,IAAI,WAAW;IACf,IAAI,eAAe;KACjB,MAAM,WAAW,IAAI,IAAI,oBAAoB,IAAI,CAAC;KAClD,WAAW,SAAS,QAAQ,MAC1B,EAAE,aAAa,MAAM,MAAM,oBAAoB,CAAC,EAAE,MAAM,MAAM,SAAS,IAAI,CAAC,CAAC,CAAC,CAChF;KAEA,WAAW,SAAS,MAAM,GAAG,SAAS,EAAE;IAC1C;IAEA,IAAI,SAAS,WAAW,GACtB,OAAO;KACL,SAAS,CACP;MACE,MAAM;MACN,MAAM,OACF,8CAA8C,KAAK,KACnD;KACN,CACF;KACA,SAAS,CAAC;IACZ;IAGF,MAAM,QAAQ,SAAS,KAAK,GAAG,MAAM;KACnC,MAAM,OAAO,GAAG,KAAK,MAAM,EAAE,cAAc,GAAG,EAAE;KAChD,MAAM,MAAM,EAAE,aAAa,KAAK,KAAK;KACrC,MAAM,QAAQ,EAAE,aAAa,KAAK,MAAM,IAAI,EAAE,EAAE,EAAE,KAAK,IAAI;KAC3D,OACE,GAAG,IAAI,EAAE,KAAK,KAAK,YAAY,EAAE,WAAW,cAAc,EAAE,cAAc,iBAC7D,OAAO,UAAU,sBACT,SAAS;IAElC,CAAC;IAID,OAAO;KACL,SAAS,CAAC;MAAE,MAAM;MAAQ,MAAM,SAHT,SAAS,OAAO,sBAAsB,OAAO,cAAc,KAAK,KAAK,GAAG,OAAO,MAAM,KAAK,MAAM;KAG/E,CAAC;KACzC,SAAS;IACX;GACF,SAAS,KAAK;IACZ,MAAM,IAAI,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC;IAC5D,mBAAmB,GAAG;KACpB,WAAW;KACX,WAAW;KACX,OAAO;IACT,CAAC;IACD,MAAM,MAAM,EAAE;IACd,IAAI,uEAAuE,KAAK,GAAG,GACjF,OAAO;KACL,SAAS,CACP;MACE,MAAM;MACN,MAAM;KACR,CACF;KACA,SAAS,CAAC;IACZ;IAEF,MAAM;GACR;EACF;CACF,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"issue-types.js","names":[],"sources":["../../types/issue-types.ts"],"sourcesContent":["/**\n * Issue Lifecycle Tracking — Type Definitions (Issue #137)\n *\n * Structured problem→resolution memory with state machine transitions.\n */\n\nexport type IssueStatus = \"open\" | \"diagnosed\" | \"fix-attempted\" | \"resolved\" | \"verified\" | \"wont-fix\";\n\nexport type IssueSeverity = \"low\" | \"medium\" | \"high\" | \"critical\";\n\nexport interface Issue {\n id: string;\n title: string;\n status: IssueStatus;\n severity: IssueSeverity;\n symptoms: string[];\n rootCause?: string;\n fix?: string;\n rollback?: string;\n relatedFacts: string[];\n detectedAt: string;\n resolvedAt?: string;\n verifiedAt?: string;\n tags: string[];\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateIssueInput {\n title: string;\n severity?: IssueSeverity;\n symptoms: string[];\n tags?: string[];\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Valid state transitions for the issue lifecycle.\n * Maps each status to the set of statuses it may transition to.\n */\nexport const ISSUE_TRANSITIONS: Record<IssueStatus, IssueStatus[]> = {\n open: [\"diagnosed\", \"fix-attempted\", \"wont-fix\"],\n diagnosed: [\"fix-attempted\", \"wont-fix\"],\n \"fix-attempted\": [\"resolved\", \"open\", \"wont-fix\"],\n resolved: [\"verified\", \"open\"],\n verified: [],\n \"wont-fix\": [\"open\"],\n};\n\nexport type DuplicateClosureDecision = \"safe\" | \"needs-reconciliation\";\n\nexport interface DuplicateClosureAssessment {\n candidateRef: string;\n replacementRef?: string | null;\n decision: DuplicateClosureDecision;\n reasons: string[];\n}\n\nexport interface DuplicateClosureGuardProof {\n decision: DuplicateClosureDecision;\n assessments: DuplicateClosureAssessment[];\n}\n"],"mappings":";;;;;AAyCA,MAAa,oBAAwD;CACnE,MAAM;EAAC;EAAa;EAAiB;
|
|
1
|
+
{"version":3,"file":"issue-types.js","names":[],"sources":["../../types/issue-types.ts"],"sourcesContent":["/**\n * Issue Lifecycle Tracking — Type Definitions (Issue #137)\n *\n * Structured problem→resolution memory with state machine transitions.\n */\n\nexport type IssueStatus = \"open\" | \"diagnosed\" | \"fix-attempted\" | \"resolved\" | \"verified\" | \"wont-fix\";\n\nexport type IssueSeverity = \"low\" | \"medium\" | \"high\" | \"critical\";\n\nexport interface Issue {\n id: string;\n title: string;\n status: IssueStatus;\n severity: IssueSeverity;\n symptoms: string[];\n rootCause?: string;\n fix?: string;\n rollback?: string;\n relatedFacts: string[];\n detectedAt: string;\n resolvedAt?: string;\n verifiedAt?: string;\n tags: string[];\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface CreateIssueInput {\n title: string;\n severity?: IssueSeverity;\n symptoms: string[];\n tags?: string[];\n metadata?: Record<string, unknown>;\n}\n\n/**\n * Valid state transitions for the issue lifecycle.\n * Maps each status to the set of statuses it may transition to.\n */\nexport const ISSUE_TRANSITIONS: Record<IssueStatus, IssueStatus[]> = {\n open: [\"diagnosed\", \"fix-attempted\", \"wont-fix\"],\n diagnosed: [\"fix-attempted\", \"wont-fix\"],\n \"fix-attempted\": [\"resolved\", \"open\", \"wont-fix\"],\n resolved: [\"verified\", \"open\"],\n verified: [],\n \"wont-fix\": [\"open\"],\n};\n\nexport type DuplicateClosureDecision = \"safe\" | \"needs-reconciliation\";\n\nexport interface DuplicateClosureAssessment {\n candidateRef: string;\n replacementRef?: string | null;\n decision: DuplicateClosureDecision;\n reasons: string[];\n}\n\nexport interface DuplicateClosureGuardProof {\n decision: DuplicateClosureDecision;\n assessments: DuplicateClosureAssessment[];\n}\n"],"mappings":";;;;;AAyCA,MAAa,oBAAwD;CACnE,MAAM;EAAC;EAAa;EAAiB;CAAU;CAC/C,WAAW,CAAC,iBAAiB,UAAU;CACvC,iBAAiB;EAAC;EAAY;EAAQ;CAAU;CAChD,UAAU,CAAC,YAAY,MAAM;CAC7B,UAAU,CAAC;CACX,YAAY,CAAC,MAAM;AACrB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"learnings-types.js","names":[],"sources":["../../types/learnings-types.ts"],"sourcesContent":["/**\n * Learnings Intake Buffer — Type Definitions (Issue #617)\n *\n * Structured staging area for capturing errors, lessons, and feature requests\n * before promoting them to permanent memory or documentation.\n *\n * Three entry types map to distinct intake files:\n * - \"error\" → ERRORS.md — tool/workflow failures with recurrence tracking\n * - \"learning\" → LEARNINGS.md — stable lessons and best practices\n * - \"feature_request\" → FEATURE_REQUESTS.md — improvement ideas not yet in memory\n */\n\n/** Entry classification. */\nexport type LearningEntryType = \"error\" | \"learning\" | \"feature_request\";\n\n/**\n * Lifecycle status for staged entries.\n *\n * Transitions:\n * open → promoted | wont_promote\n * promoted → (terminal)\n * wont_promote → open (allows reopening)\n */\nexport type LearningEntryStatus = \"open\" | \"promoted\" | \"wont_promote\";\n\n/** Valid status transitions. */\nexport const LEARNING_STATUS_TRANSITIONS: Record<LearningEntryStatus, LearningEntryStatus[]> = {\n open: [\"promoted\", \"wont_promote\"],\n promoted: [],\n wont_promote: [\"open\"],\n};\n\n/** A single staged intake entry. */\nexport interface LearningEntry {\n id: string;\n /** Human-readable sequential label, e.g. \"ERR-001\", \"LRN-003\", \"FR-002\". */\n slug: string;\n type: LearningEntryType;\n status: LearningEntryStatus;\n /** Functional area this entry concerns (e.g. \"forge-dispatch\", \"council-review\"). */\n area: string;\n /** The main content: what happened, the lesson, or the request. */\n content: string;\n /** How many times this exact issue / lesson has recurred. */\n recurrence: number;\n /** Where the entry was promoted to (fact category, SKILL.md, etc.). */\n promotedTo?: string;\n /** Tags for cross-linking. */\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\n/** Input to create a new staged entry. */\nexport interface CreateLearningEntryInput {\n type: LearningEntryType;\n area: string;\n content: string;\n tags?: string[];\n}\n"],"mappings":";;AA0BA,MAAa,8BAAkF;CAC7F,MAAM,CAAC,YAAY,
|
|
1
|
+
{"version":3,"file":"learnings-types.js","names":[],"sources":["../../types/learnings-types.ts"],"sourcesContent":["/**\n * Learnings Intake Buffer — Type Definitions (Issue #617)\n *\n * Structured staging area for capturing errors, lessons, and feature requests\n * before promoting them to permanent memory or documentation.\n *\n * Three entry types map to distinct intake files:\n * - \"error\" → ERRORS.md — tool/workflow failures with recurrence tracking\n * - \"learning\" → LEARNINGS.md — stable lessons and best practices\n * - \"feature_request\" → FEATURE_REQUESTS.md — improvement ideas not yet in memory\n */\n\n/** Entry classification. */\nexport type LearningEntryType = \"error\" | \"learning\" | \"feature_request\";\n\n/**\n * Lifecycle status for staged entries.\n *\n * Transitions:\n * open → promoted | wont_promote\n * promoted → (terminal)\n * wont_promote → open (allows reopening)\n */\nexport type LearningEntryStatus = \"open\" | \"promoted\" | \"wont_promote\";\n\n/** Valid status transitions. */\nexport const LEARNING_STATUS_TRANSITIONS: Record<LearningEntryStatus, LearningEntryStatus[]> = {\n open: [\"promoted\", \"wont_promote\"],\n promoted: [],\n wont_promote: [\"open\"],\n};\n\n/** A single staged intake entry. */\nexport interface LearningEntry {\n id: string;\n /** Human-readable sequential label, e.g. \"ERR-001\", \"LRN-003\", \"FR-002\". */\n slug: string;\n type: LearningEntryType;\n status: LearningEntryStatus;\n /** Functional area this entry concerns (e.g. \"forge-dispatch\", \"council-review\"). */\n area: string;\n /** The main content: what happened, the lesson, or the request. */\n content: string;\n /** How many times this exact issue / lesson has recurred. */\n recurrence: number;\n /** Where the entry was promoted to (fact category, SKILL.md, etc.). */\n promotedTo?: string;\n /** Tags for cross-linking. */\n tags: string[];\n createdAt: string;\n updatedAt: string;\n}\n\n/** Input to create a new staged entry. */\nexport interface CreateLearningEntryInput {\n type: LearningEntryType;\n area: string;\n content: string;\n tags?: string[];\n}\n"],"mappings":";;AA0BA,MAAa,8BAAkF;CAC7F,MAAM,CAAC,YAAY,cAAc;CACjC,UAAU,CAAC;CACX,cAAc,CAAC,MAAM;AACvB"}
|
package/dist/types/memory.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.js","names":[],"sources":["../../types/memory.ts"],"sourcesContent":["/**\n * Shared memory types used by backends and plugin.\n */\n\nimport type { DecayClass, MemoryCategory } from \"../config.js\";\n\n// Re-export types that are commonly needed\nexport type { MemoryCategory, DecayClass };\n\n/** Memory tier for dynamic tiering (hot = always loaded, warm = semantic search, cold = archived). */\nexport type MemoryTier = \"hot\" | \"warm\" | \"cold\" | \"structural\";\n\nexport type MemoryEntry = {\n id: string;\n text: string;\n /** Optional lineage context describing why this fact/decision was stored. */\n why?: string | null;\n category: MemoryCategory;\n importance: number;\n entity: string | null;\n key: string | null;\n value: string | null;\n source: string;\n createdAt: number;\n sourceDate?: number | null;\n decayClass: DecayClass;\n expiresAt: number | null;\n lastConfirmedAt: number;\n confidence: number;\n summary?: string | null;\n tags?: string[] | null;\n recallCount?: number;\n lastAccessed?: number | null;\n /** Incremented on every memory_recall hit (#237). */\n accessCount?: number;\n /** ISO 8601 timestamp of last recall hit (#237). */\n lastAccessedAt?: string | null;\n /** Number of index-only exposures (does not inflate recall_count). */\n indexedCount?: number;\n /** Epoch seconds of last index-only exposure. Separate signal from last_accessed. */\n lastIndexed?: number | null;\n supersededAt?: number | null;\n supersededBy?: string | null;\n /** When the fact became true in the real world (epoch seconds). */\n validFrom?: number | null;\n /** When the fact stopped being true (epoch seconds); null if still current. */\n validUntil?: number | null;\n /** Id of the fact this one supersedes (replaces). */\n supersedesId?: string | null;\n /** Dynamic tier — hot (session), warm (recent), cold (archived). */\n tier?: MemoryTier | null;\n /** Memory scope — global, user, agent, or session. */\n scope?: MemoryScope;\n /** Scope target (e.g. userId for user scope, agentId for agent scope, sessionId for session scope). Null for global. */\n scopeTarget?: string | null;\n /** Procedural memory: fact is a procedure summary. */\n procedureType?: \"positive\" | \"negative\" | null;\n successCount?: number;\n lastValidated?: number | null;\n sourceSessions?: string | null;\n /** Embedding model used to generate this fact's vector (if stored). */\n embeddingModel?: string | null;\n /** Provenance: session id associated with extraction or store. */\n provenanceSession?: string | null;\n /** Provenance: conversation turn number within the source session, when known. */\n sourceTurn?: number | null;\n /** Provenance: extraction path used to create the fact. */\n extractionMethod?: string | null;\n /** Provenance: confidence score for the extraction path (0-1). */\n extractionConfidence?: number | null;\n /** Reinforcement tracking — number of times this fact was reinforced by user praise. */\n reinforcedCount?: number;\n /** When this fact was last reinforced (epoch seconds). */\n lastReinforcedAt?: number | null;\n /** Array of user praise quotes that reinforced this fact. */\n reinforcedQuotes?: string[] | null;\n /**\n * Structured provenance JSON stored on the fact row itself (Issue #1195).\n * E.g. { \"sourceFactIds\": [\"...\"], \"consolidatedAt\": 1700000000, \"method\": \"dream-cycle\" }\n */\n provenanceJson?: string | null;\n /**\n * Future-date decay freeze: epoch seconds until which confidence decay is paused.\n * Set when the fact text contains a future date (reminder, deadline, event).\n * Null = no freeze (normal decay applies).\n */\n decayFreezeUntil?: number | null;\n /**\n * Force-preservation: epoch seconds until which this fact MUST NOT be trimmed.\n * Null = no forced preservation (normal tier-based retention applies).\n * Implemented by trimToBudget().\n */\n preserveUntil?: number | null;\n /**\n * Force-preservation tags: if any of these tags are present, the fact is\n * protected from trimming regardless of importance tier.\n * Set via `memory preserve <id> --tag <tag>`.\n */\n preserveTags?: string[] | null;\n};\n\n/** Memory scoping — global (all), user (per-user), agent (per-agent), session (ephemeral). */\nexport const MEMORY_SCOPES = [\"global\", \"user\", \"agent\", \"session\"] as const;\nexport type MemoryScope = (typeof MEMORY_SCOPES)[number];\n\n/** Scope filter for recall — include global + matching user/agent/session. Empty = all (backward compat). */\nexport type ScopeFilter = {\n global?: boolean;\n userId?: string | null;\n agentId?: string | null;\n sessionId?: string | null;\n};\n\n/** One step in a procedure recipe. */\nexport type ProcedureStep = {\n tool: string;\n args?: Record<string, unknown>;\n summary?: string;\n};\n\nexport const GENERATED_SKILL_LIFECYCLE_STATES = [\n \"draft\",\n \"experimental\",\n \"trusted\",\n \"demoted\",\n \"archived\",\n \"uninstalled\",\n \"rejected\",\n] as const;\nexport type GeneratedSkillLifecycleState = (typeof GENERATED_SKILL_LIFECYCLE_STATES)[number];\n\nexport const GENERATED_SKILL_TELEMETRY_DECISIONS = [\"selected\", \"considered\", \"skipped\"] as const;\nexport type GeneratedSkillTelemetryDecision = (typeof GENERATED_SKILL_TELEMETRY_DECISIONS)[number];\n\nexport const GENERATED_SKILL_TELEMETRY_OUTCOMES = [\"success\", \"failure\", \"partial\", \"unknown\"] as const;\nexport type GeneratedSkillTelemetryOutcome = (typeof GENERATED_SKILL_TELEMETRY_OUTCOMES)[number];\n\nexport type GeneratedSkillTelemetryEntry = {\n id: string;\n procedureId: string;\n skillName: string;\n skillVersion: number;\n requestHash?: string | null;\n requestSummary?: string | null;\n decision: GeneratedSkillTelemetryDecision;\n confidence?: number | null;\n reason?: string | null;\n taskOutcome?: GeneratedSkillTelemetryOutcome | null;\n userCorrection: boolean;\n correctionReason?: string | null;\n falseNegativeSignal: boolean;\n causedRework: boolean;\n savedToolCalls?: number | null;\n savedTimeMs?: number | null;\n scope?: MemoryScope | null;\n scopeTarget?: string | null;\n agentId?: string | null;\n sessionId?: string | null;\n createdAt: number;\n};\n\n/** Stored procedure (procedures table). */\nexport type ProcedureEntry = {\n id: string;\n taskPattern: string;\n recipeJson: string;\n procedureType: \"positive\" | \"negative\";\n successCount: number;\n failureCount: number;\n lastValidated: number | null;\n lastFailed: number | null;\n confidence: number;\n ttlDays: number;\n promotedToSkill: number;\n skillPath: string | null;\n createdAt: number;\n updatedAt: number;\n sourceSessions?: string;\n /** Phase 2: Reinforcement tracking — number of times this procedure was reinforced by user praise. */\n reinforcedCount?: number;\n /** Phase 2: When this procedure was last reinforced (epoch seconds). */\n lastReinforcedAt?: number | null;\n /** Phase 2: Array of user praise quotes that reinforced this procedure. */\n reinforcedQuotes?: string[] | null;\n /** Phase 2: When this procedure was auto-promoted via reinforcement (epoch seconds). */\n promotedAt?: number | null;\n /** Generated skill lifecycle state. */\n skillState?: GeneratedSkillLifecycleState | null;\n /** Latest lifecycle transition reason (manual or policy-driven). */\n skillStateReason?: string | null;\n /** Generated skill version counter. */\n skillVersion: number;\n /** When the generated skill was first materialized (epoch seconds). */\n skillGeneratedAt?: number | null;\n /** When the skill_state last changed (epoch seconds). */\n skillStateChangedAt?: number | null;\n /** Memory scope (global, user, agent, session). */\n scope?: string;\n /** Scope target (userId, agentId, or sessionId). */\n scopeTarget?: string | null;\n /** Procedure feedback loop (#782): highest version number for this procedure. */\n version?: number;\n /** Procedure feedback loop (#782): last known outcome — 'success' | 'failure' | 'unknown'. */\n lastOutcome?: \"success\" | \"failure\" | \"unknown\";\n /** Procedure feedback loop (#782): success rate as a fraction [0,1] (total successes / total attempts). */\n successRate?: number;\n /** Procedure feedback loop (#782): avoidance notes across all versions. */\n avoidanceNotes?: string[];\n};\n\nexport type SearchResult = {\n entry: MemoryEntry;\n score: number;\n backend: \"sqlite\" | \"lancedb\";\n};\n\n/** Valid outcome values for an episodic memory record (#781). */\nexport type EpisodeOutcome = \"success\" | \"failure\" | \"partial\" | \"unknown\";\n\n/**\n * Episodic memory record — a structured event with an explicit outcome and timestamp (#781).\n * Episodes are stored in the separate `episodes` SQLite table (not in `facts`).\n * They are indexed in LanceDB with category=\"episode\" for semantic search.\n */\nexport type Episode = {\n id: string;\n /** Discriminated literal — always \"episode\". */\n category: \"episode\";\n /** What happened (e.g. \"deployed openclaw to production\"). */\n event: string;\n /** Outcome of the event. Failures are auto-boosted to importance >= 0.8. */\n outcome: EpisodeOutcome;\n /** Unix epoch seconds — when the event occurred. Defaults to now. */\n timestamp: number;\n /** Optional duration in milliseconds. */\n duration?: number;\n /** Context: environment state, what led up to it, etc. */\n context?: string;\n /** IDs of related facts (linked via memory_links). */\n relatedFactIds?: string[];\n /** ID of the procedure that triggered this episode, if any. */\n procedureId?: string;\n /** Memory scope — global, user, agent, or session. */\n scope: \"global\" | \"user\" | \"agent\" | \"session\";\n /** Scope target (userId, agentId, or sessionId). Null for global scope. */\n scopeTarget?: string | null;\n agentId?: string;\n userId?: string;\n sessionId?: string;\n importance: number;\n tags: string[];\n decayClass: DecayClass;\n createdAt: number;\n verifiedAt?: number;\n};\n"],"mappings":";;AAsGA,MAAa,gBAAgB;CAAC;CAAU;CAAQ;CAAS;CAAU;AAkBnE,MAAa,mCAAmC;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;CACD"}
|
|
1
|
+
{"version":3,"file":"memory.js","names":[],"sources":["../../types/memory.ts"],"sourcesContent":["/**\n * Shared memory types used by backends and plugin.\n */\n\nimport type { DecayClass, MemoryCategory } from \"../config.js\";\n\n// Re-export types that are commonly needed\nexport type { MemoryCategory, DecayClass };\n\n/** Memory tier for dynamic tiering (hot = always loaded, warm = semantic search, cold = archived). */\nexport type MemoryTier = \"hot\" | \"warm\" | \"cold\" | \"structural\";\n\nexport type MemoryEntry = {\n id: string;\n text: string;\n /** Optional lineage context describing why this fact/decision was stored. */\n why?: string | null;\n category: MemoryCategory;\n importance: number;\n entity: string | null;\n key: string | null;\n value: string | null;\n source: string;\n createdAt: number;\n sourceDate?: number | null;\n decayClass: DecayClass;\n expiresAt: number | null;\n lastConfirmedAt: number;\n confidence: number;\n summary?: string | null;\n tags?: string[] | null;\n recallCount?: number;\n lastAccessed?: number | null;\n /** Incremented on every memory_recall hit (#237). */\n accessCount?: number;\n /** ISO 8601 timestamp of last recall hit (#237). */\n lastAccessedAt?: string | null;\n /** Number of index-only exposures (does not inflate recall_count). */\n indexedCount?: number;\n /** Epoch seconds of last index-only exposure. Separate signal from last_accessed. */\n lastIndexed?: number | null;\n supersededAt?: number | null;\n supersededBy?: string | null;\n /** When the fact became true in the real world (epoch seconds). */\n validFrom?: number | null;\n /** When the fact stopped being true (epoch seconds); null if still current. */\n validUntil?: number | null;\n /** Id of the fact this one supersedes (replaces). */\n supersedesId?: string | null;\n /** Dynamic tier — hot (session), warm (recent), cold (archived). */\n tier?: MemoryTier | null;\n /** Memory scope — global, user, agent, or session. */\n scope?: MemoryScope;\n /** Scope target (e.g. userId for user scope, agentId for agent scope, sessionId for session scope). Null for global. */\n scopeTarget?: string | null;\n /** Procedural memory: fact is a procedure summary. */\n procedureType?: \"positive\" | \"negative\" | null;\n successCount?: number;\n lastValidated?: number | null;\n sourceSessions?: string | null;\n /** Embedding model used to generate this fact's vector (if stored). */\n embeddingModel?: string | null;\n /** Provenance: session id associated with extraction or store. */\n provenanceSession?: string | null;\n /** Provenance: conversation turn number within the source session, when known. */\n sourceTurn?: number | null;\n /** Provenance: extraction path used to create the fact. */\n extractionMethod?: string | null;\n /** Provenance: confidence score for the extraction path (0-1). */\n extractionConfidence?: number | null;\n /** Reinforcement tracking — number of times this fact was reinforced by user praise. */\n reinforcedCount?: number;\n /** When this fact was last reinforced (epoch seconds). */\n lastReinforcedAt?: number | null;\n /** Array of user praise quotes that reinforced this fact. */\n reinforcedQuotes?: string[] | null;\n /**\n * Structured provenance JSON stored on the fact row itself (Issue #1195).\n * E.g. { \"sourceFactIds\": [\"...\"], \"consolidatedAt\": 1700000000, \"method\": \"dream-cycle\" }\n */\n provenanceJson?: string | null;\n /**\n * Future-date decay freeze: epoch seconds until which confidence decay is paused.\n * Set when the fact text contains a future date (reminder, deadline, event).\n * Null = no freeze (normal decay applies).\n */\n decayFreezeUntil?: number | null;\n /**\n * Force-preservation: epoch seconds until which this fact MUST NOT be trimmed.\n * Null = no forced preservation (normal tier-based retention applies).\n * Implemented by trimToBudget().\n */\n preserveUntil?: number | null;\n /**\n * Force-preservation tags: if any of these tags are present, the fact is\n * protected from trimming regardless of importance tier.\n * Set via `memory preserve <id> --tag <tag>`.\n */\n preserveTags?: string[] | null;\n};\n\n/** Memory scoping — global (all), user (per-user), agent (per-agent), session (ephemeral). */\nexport const MEMORY_SCOPES = [\"global\", \"user\", \"agent\", \"session\"] as const;\nexport type MemoryScope = (typeof MEMORY_SCOPES)[number];\n\n/** Scope filter for recall — include global + matching user/agent/session. Empty = all (backward compat). */\nexport type ScopeFilter = {\n global?: boolean;\n userId?: string | null;\n agentId?: string | null;\n sessionId?: string | null;\n};\n\n/** One step in a procedure recipe. */\nexport type ProcedureStep = {\n tool: string;\n args?: Record<string, unknown>;\n summary?: string;\n};\n\nexport const GENERATED_SKILL_LIFECYCLE_STATES = [\n \"draft\",\n \"experimental\",\n \"trusted\",\n \"demoted\",\n \"archived\",\n \"uninstalled\",\n \"rejected\",\n] as const;\nexport type GeneratedSkillLifecycleState = (typeof GENERATED_SKILL_LIFECYCLE_STATES)[number];\n\nexport const GENERATED_SKILL_TELEMETRY_DECISIONS = [\"selected\", \"considered\", \"skipped\"] as const;\nexport type GeneratedSkillTelemetryDecision = (typeof GENERATED_SKILL_TELEMETRY_DECISIONS)[number];\n\nexport const GENERATED_SKILL_TELEMETRY_OUTCOMES = [\"success\", \"failure\", \"partial\", \"unknown\"] as const;\nexport type GeneratedSkillTelemetryOutcome = (typeof GENERATED_SKILL_TELEMETRY_OUTCOMES)[number];\n\nexport type GeneratedSkillTelemetryEntry = {\n id: string;\n procedureId: string;\n skillName: string;\n skillVersion: number;\n requestHash?: string | null;\n requestSummary?: string | null;\n decision: GeneratedSkillTelemetryDecision;\n confidence?: number | null;\n reason?: string | null;\n taskOutcome?: GeneratedSkillTelemetryOutcome | null;\n userCorrection: boolean;\n correctionReason?: string | null;\n falseNegativeSignal: boolean;\n causedRework: boolean;\n savedToolCalls?: number | null;\n savedTimeMs?: number | null;\n scope?: MemoryScope | null;\n scopeTarget?: string | null;\n agentId?: string | null;\n sessionId?: string | null;\n createdAt: number;\n};\n\n/** Stored procedure (procedures table). */\nexport type ProcedureEntry = {\n id: string;\n taskPattern: string;\n recipeJson: string;\n procedureType: \"positive\" | \"negative\";\n successCount: number;\n failureCount: number;\n lastValidated: number | null;\n lastFailed: number | null;\n confidence: number;\n ttlDays: number;\n promotedToSkill: number;\n skillPath: string | null;\n createdAt: number;\n updatedAt: number;\n sourceSessions?: string;\n /** Phase 2: Reinforcement tracking — number of times this procedure was reinforced by user praise. */\n reinforcedCount?: number;\n /** Phase 2: When this procedure was last reinforced (epoch seconds). */\n lastReinforcedAt?: number | null;\n /** Phase 2: Array of user praise quotes that reinforced this procedure. */\n reinforcedQuotes?: string[] | null;\n /** Phase 2: When this procedure was auto-promoted via reinforcement (epoch seconds). */\n promotedAt?: number | null;\n /** Generated skill lifecycle state. */\n skillState?: GeneratedSkillLifecycleState | null;\n /** Latest lifecycle transition reason (manual or policy-driven). */\n skillStateReason?: string | null;\n /** Generated skill version counter. */\n skillVersion: number;\n /** When the generated skill was first materialized (epoch seconds). */\n skillGeneratedAt?: number | null;\n /** When the skill_state last changed (epoch seconds). */\n skillStateChangedAt?: number | null;\n /** Memory scope (global, user, agent, session). */\n scope?: string;\n /** Scope target (userId, agentId, or sessionId). */\n scopeTarget?: string | null;\n /** Procedure feedback loop (#782): highest version number for this procedure. */\n version?: number;\n /** Procedure feedback loop (#782): last known outcome — 'success' | 'failure' | 'unknown'. */\n lastOutcome?: \"success\" | \"failure\" | \"unknown\";\n /** Procedure feedback loop (#782): success rate as a fraction [0,1] (total successes / total attempts). */\n successRate?: number;\n /** Procedure feedback loop (#782): avoidance notes across all versions. */\n avoidanceNotes?: string[];\n};\n\nexport type SearchResult = {\n entry: MemoryEntry;\n score: number;\n backend: \"sqlite\" | \"lancedb\";\n};\n\n/** Valid outcome values for an episodic memory record (#781). */\nexport type EpisodeOutcome = \"success\" | \"failure\" | \"partial\" | \"unknown\";\n\n/**\n * Episodic memory record — a structured event with an explicit outcome and timestamp (#781).\n * Episodes are stored in the separate `episodes` SQLite table (not in `facts`).\n * They are indexed in LanceDB with category=\"episode\" for semantic search.\n */\nexport type Episode = {\n id: string;\n /** Discriminated literal — always \"episode\". */\n category: \"episode\";\n /** What happened (e.g. \"deployed openclaw to production\"). */\n event: string;\n /** Outcome of the event. Failures are auto-boosted to importance >= 0.8. */\n outcome: EpisodeOutcome;\n /** Unix epoch seconds — when the event occurred. Defaults to now. */\n timestamp: number;\n /** Optional duration in milliseconds. */\n duration?: number;\n /** Context: environment state, what led up to it, etc. */\n context?: string;\n /** IDs of related facts (linked via memory_links). */\n relatedFactIds?: string[];\n /** ID of the procedure that triggered this episode, if any. */\n procedureId?: string;\n /** Memory scope — global, user, agent, or session. */\n scope: \"global\" | \"user\" | \"agent\" | \"session\";\n /** Scope target (userId, agentId, or sessionId). Null for global scope. */\n scopeTarget?: string | null;\n agentId?: string;\n userId?: string;\n sessionId?: string;\n importance: number;\n tags: string[];\n decayClass: DecayClass;\n createdAt: number;\n verifiedAt?: number;\n};\n"],"mappings":";;AAsGA,MAAa,gBAAgB;CAAC;CAAU;CAAQ;CAAS;AAAS;AAkBlE,MAAa,mCAAmC;CAC9C;CACA;CACA;CACA;CACA;CACA;CACA;AACF"}
|