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":"apim-gateway-fetch.js","names":[],"sources":["../../utils/apim-gateway-fetch.ts"],"sourcesContent":["/**\n * Azure API Management gateway utilities.\n *\n * Azure API Management (APIM) gateways reject standard Bearer-token auth.\n * Instead they require:\n * - `api-key` header with the subscription key, AND\n * - `Ocp-Apim-Subscription-Key` header (legacy alias)\n *\n * This module provides:\n * - `isAzureApiManagementGatewayUrl()` — detect APIM gateway base URLs\n * - `createApimGatewayFetch()` — wrap globalThis.fetch to inject APIM auth headers\n */\n\nconst APIM_GATEWAY_PATTERNS = [/\\.management\\.azure-api\\.net$/i, /\\.scm\\.azure-api\\.net$/i, /\\.azure-api\\.net$/i];\n\n/**\n * Returns true when `baseURL` looks like an Azure API Management gateway endpoint.\n */\nexport function isAzureApiManagementGatewayUrl(baseURL: string): boolean {\n try {\n const url = new URL(baseURL);\n return APIM_GATEWAY_PATTERNS.some((p) => p.test(url.hostname));\n } catch {\n return APIM_GATEWAY_PATTERNS.some((p) => p.test(baseURL));\n }\n}\n\n/**\n * Creates a fetch function that injects Azure API Management authentication headers.\n *\n * Replaces Bearer auth with `api-key` + `Ocp-Apim-Subscription-Key` headers.\n */\nexport function createApimGatewayFetch(apiKey: string): typeof globalThis.fetch {\n return async (...[input, init]: Parameters<typeof globalThis.fetch>): Promise<Response> => {\n const headers = new Headers(init?.headers);\n // Strip the SDK's default Authorization: Bearer — APIM gateways reject duplicate auth.\n headers.delete(\"Authorization\");\n // Inject APIM auth headers.\n headers.set(\"api-key\", apiKey);\n if (!headers.has(\"Ocp-Apim-Subscription-Key\")) {\n headers.set(\"Ocp-Apim-Subscription-Key\", apiKey);\n }\n return globalThis.fetch(input, { ...init, headers });\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,wBAAwB;CAAC;CAAkC;CAA2B;
|
|
1
|
+
{"version":3,"file":"apim-gateway-fetch.js","names":[],"sources":["../../utils/apim-gateway-fetch.ts"],"sourcesContent":["/**\n * Azure API Management gateway utilities.\n *\n * Azure API Management (APIM) gateways reject standard Bearer-token auth.\n * Instead they require:\n * - `api-key` header with the subscription key, AND\n * - `Ocp-Apim-Subscription-Key` header (legacy alias)\n *\n * This module provides:\n * - `isAzureApiManagementGatewayUrl()` — detect APIM gateway base URLs\n * - `createApimGatewayFetch()` — wrap globalThis.fetch to inject APIM auth headers\n */\n\nconst APIM_GATEWAY_PATTERNS = [/\\.management\\.azure-api\\.net$/i, /\\.scm\\.azure-api\\.net$/i, /\\.azure-api\\.net$/i];\n\n/**\n * Returns true when `baseURL` looks like an Azure API Management gateway endpoint.\n */\nexport function isAzureApiManagementGatewayUrl(baseURL: string): boolean {\n try {\n const url = new URL(baseURL);\n return APIM_GATEWAY_PATTERNS.some((p) => p.test(url.hostname));\n } catch {\n return APIM_GATEWAY_PATTERNS.some((p) => p.test(baseURL));\n }\n}\n\n/**\n * Creates a fetch function that injects Azure API Management authentication headers.\n *\n * Replaces Bearer auth with `api-key` + `Ocp-Apim-Subscription-Key` headers.\n */\nexport function createApimGatewayFetch(apiKey: string): typeof globalThis.fetch {\n return async (...[input, init]: Parameters<typeof globalThis.fetch>): Promise<Response> => {\n const headers = new Headers(init?.headers);\n // Strip the SDK's default Authorization: Bearer — APIM gateways reject duplicate auth.\n headers.delete(\"Authorization\");\n // Inject APIM auth headers.\n headers.set(\"api-key\", apiKey);\n if (!headers.has(\"Ocp-Apim-Subscription-Key\")) {\n headers.set(\"Ocp-Apim-Subscription-Key\", apiKey);\n }\n return globalThis.fetch(input, { ...init, headers });\n };\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,MAAM,wBAAwB;CAAC;CAAkC;CAA2B;AAAoB;;;;AAKhH,SAAgB,+BAA+B,SAA0B;CACvE,IAAI;EACF,MAAM,MAAM,IAAI,IAAI,OAAO;EAC3B,OAAO,sBAAsB,MAAM,MAAM,EAAE,KAAK,IAAI,QAAQ,CAAC;CAC/D,QAAQ;EACN,OAAO,sBAAsB,MAAM,MAAM,EAAE,KAAK,OAAO,CAAC;CAC1D;AACF;;;;;;AAOA,SAAgB,uBAAuB,QAAyC;CAC9E,OAAO,OAAO,GAAG,CAAC,OAAO,UAAkE;EACzF,MAAM,UAAU,IAAI,QAAQ,MAAM,OAAO;EAEzC,QAAQ,OAAO,eAAe;EAE9B,QAAQ,IAAI,WAAW,MAAM;EAC7B,IAAI,CAAC,QAAQ,IAAI,2BAA2B,GAC1C,QAAQ,IAAI,6BAA6B,MAAM;EAEjD,OAAO,WAAW,MAAM,OAAO;GAAE,GAAG;GAAM;EAAQ,CAAC;CACrD;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"atomic-write.js","names":[],"sources":["../../utils/atomic-write.ts"],"sourcesContent":["/**\n * Atomic file and skill-directory write helpers (Issue #1405).\n *\n * Partial skill directories created by a process crash between individual\n * `writeFileSync` calls can be mistaken for valid installed skills. These\n * helpers prevent that by:\n *\n * • `atomicWriteFile` – write to a temp file then rename (one file).\n * • `atomicWriteSkillDir` – write all sidecar files into a temp directory,\n * stamp `.openclaw-skill-complete`, then rename the\n * temp directory to the final location (multi-file).\n * • `isSkillDirComplete` – returns true only when the marker is present.\n *\n * The marker name is exported so loaders can skip in-progress directories.\n */\n\nimport { chmodSync, existsSync, mkdirSync, renameSync, readdirSync, rmSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { randomBytes } from \"node:crypto\";\nimport { basename, dirname, join } from \"node:path\";\n\n/** Marker file written as the last step inside an atomic skill directory. */\nexport const SKILL_COMPLETE_MARKER = \".openclaw-skill-complete\";\n\n/** Prefix used for hidden sibling directories that are still being written. */\nexport const SKILL_ATOMIC_TEMP_PREFIX = \".openclaw-skill-tmp-\";\n\n/**\n * Returns `true` when `dir` exists and contains no entries (empty directory).\n */\nfunction isEmptyDir(dir: string): boolean {\n if (!existsSync(dir)) return false;\n try {\n const entries = readdirSync(dir);\n return entries.length === 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Validates that a relative path is safe for use within a skill directory.\n * Rejects paths that:\n * - contain null bytes\n * - are absolute (POSIX or Windows)\n * - attempt parent directory traversal (..)\n * - are empty\n * - collide with the completion marker name\n *\n * Throws an error if the path is unsafe.\n */\nfunction assertSafeRelativeSkillPath(relPath: string): void {\n if (relPath.includes(\"\\0\")) {\n throw new Error(`Unsafe path: contains null byte: ${relPath}`);\n }\n if (relPath.startsWith(\"/\") || /^[A-Za-z]:[\\\\/]/.test(relPath)) {\n throw new Error(`Unsafe path: absolute path not allowed: ${relPath}`);\n }\n if (relPath.includes(\"..\")) {\n throw new Error(`Unsafe path: parent directory traversal not allowed: ${relPath}`);\n }\n if (relPath.trim().length === 0) {\n throw new Error(\"Unsafe path: empty path not allowed\");\n }\n if (relPath === SKILL_COMPLETE_MARKER || basename(relPath) === SKILL_COMPLETE_MARKER) {\n throw new Error(`Unsafe path: collision with completion marker: ${relPath}`);\n }\n}\n\n/**\n * Atomically write a single file.\n *\n * Writes content to `${targetPath}.tmp-${pid}-${rand}` then renames it to\n * `targetPath`. If any step fails the temp file is removed and the error is\n * re-thrown, so `targetPath` is never left in a partially-written state.\n */\nexport function atomicWriteFile(targetPath: string, content: string): void {\n const dir = dirname(targetPath);\n const rand = randomBytes(8).toString(\"hex\");\n const tmpPath = `${targetPath}.tmp-${process.pid}-${rand}`;\n\n mkdirSync(dir, { recursive: true });\n try {\n writeFileSync(tmpPath, content, \"utf-8\");\n renameSync(tmpPath, targetPath);\n } catch (err) {\n try {\n if (existsSync(tmpPath)) unlinkSync(tmpPath);\n } catch {\n // best-effort temp cleanup; swallow to surface original error\n }\n throw err;\n }\n}\n\n/**\n * Atomically write a multi-file skill directory.\n *\n * All files in `files` (keyed by path relative to `skillDir`) are written\n * into a temporary sibling directory `${SKILL_ATOMIC_TEMP_PREFIX}${pid}-${rand}`. A\n * `.openclaw-skill-complete` marker is written as the very last file. The\n * temp directory is then atomically renamed to `skillDir`.\n *\n * If `skillDir` already exists and is non-empty, this helper fails instead of\n * moving the existing directory aside. That keeps replacement crash-atomic: a\n * process crash can leave only a hidden temp sibling, never a missing final\n * directory. Empty directories (e.g. from `allocateDraftSkillDir` reservation\n * locks) are allowed and will be atomically replaced by the rename, preserving\n * the allocation lock throughout the write operation.\n *\n * Pass files in the order you want them written. Convention: put `SKILL.md`\n * last so it is the final content file before the marker.\n */\nexport type AtomicWriteSkillDirOptions = {\n /** Relative paths (keys of `files`) written with owner-execute permission (e.g. `scripts/replay.sh`). */\n executableRelativePaths?: readonly string[];\n};\n\nexport function atomicWriteSkillDir(\n skillDir: string,\n files: Record<string, string>,\n options?: AtomicWriteSkillDirOptions,\n): void {\n const executable = new Set(options?.executableRelativePaths ?? []);\n const parent = dirname(skillDir);\n const rand = randomBytes(8).toString(\"hex\");\n const tmpDir = join(parent, `${SKILL_ATOMIC_TEMP_PREFIX}${process.pid}-${rand}`);\n\n try {\n if (existsSync(skillDir) && !isEmptyDir(skillDir)) {\n throw new Error(`Atomic skill directory target already exists: ${skillDir}`);\n }\n\n // Write every sidecar into the temp directory.\n for (const [relPath, content] of Object.entries(files)) {\n assertSafeRelativeSkillPath(relPath);\n const fullPath = join(tmpDir, relPath);\n mkdirSync(dirname(fullPath), { recursive: true });\n const fileMode = executable.has(relPath) ? 0o755 : undefined;\n writeFileSync(fullPath, content, { encoding: \"utf-8\", ...(fileMode != null ? { mode: fileMode } : {}) });\n if (fileMode != null) {\n chmodSync(fullPath, fileMode);\n }\n }\n\n // Stamp the completion marker as the final write inside the temp dir.\n writeFileSync(join(tmpDir, SKILL_COMPLETE_MARKER), new Date().toISOString(), \"utf-8\");\n\n // Atomic promotion: temp dir → final skill dir. On POSIX, renameSync\n // atomically replaces empty directories, preserving the allocation lock.\n // On Windows, renameSync fails if the target exists (even when empty), so\n // we move the reservation aside, rename the temp dir, then clean up. This\n // maintains crash-safety: if we crash after moving the reservation but before\n // rename completes, the reservation survives as a .bak directory.\n let reservationBackup: string | null = null;\n if (process.platform === \"win32\" && existsSync(skillDir) && isEmptyDir(skillDir)) {\n reservationBackup = join(parent, `.${basename(skillDir)}.bak-${process.pid}-${randomBytes(4).toString(\"hex\")}`);\n renameSync(skillDir, reservationBackup);\n }\n try {\n renameSync(tmpDir, skillDir);\n // Success: remove the reservation backup if we made one.\n if (reservationBackup && existsSync(reservationBackup)) {\n rmSync(reservationBackup, { recursive: true, force: true });\n }\n } catch (err) {\n // Restore the reservation lock if rename failed.\n if (reservationBackup && existsSync(reservationBackup)) {\n try {\n renameSync(reservationBackup, skillDir);\n } catch {\n // best-effort restoration\n }\n }\n throw err;\n }\n } catch (err) {\n try {\n if (existsSync(tmpDir)) rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // best-effort temp cleanup; swallow to surface original error\n }\n throw err;\n }\n}\n\n/**\n * Returns `true` when `skillDir` contains the completion marker, indicating\n * that the directory was written atomically and is not an in-progress write.\n */\nexport function isSkillDirComplete(skillDir: string): boolean {\n return existsSync(join(skillDir, SKILL_COMPLETE_MARKER));\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"atomic-write.js","names":[],"sources":["../../utils/atomic-write.ts"],"sourcesContent":["/**\n * Atomic file and skill-directory write helpers (Issue #1405).\n *\n * Partial skill directories created by a process crash between individual\n * `writeFileSync` calls can be mistaken for valid installed skills. These\n * helpers prevent that by:\n *\n * • `atomicWriteFile` – write to a temp file then rename (one file).\n * • `atomicWriteSkillDir` – write all sidecar files into a temp directory,\n * stamp `.openclaw-skill-complete`, then rename the\n * temp directory to the final location (multi-file).\n * • `isSkillDirComplete` – returns true only when the marker is present.\n *\n * The marker name is exported so loaders can skip in-progress directories.\n */\n\nimport { chmodSync, existsSync, mkdirSync, renameSync, readdirSync, rmSync, unlinkSync, writeFileSync } from \"node:fs\";\nimport { randomBytes } from \"node:crypto\";\nimport { basename, dirname, join } from \"node:path\";\n\n/** Marker file written as the last step inside an atomic skill directory. */\nexport const SKILL_COMPLETE_MARKER = \".openclaw-skill-complete\";\n\n/** Prefix used for hidden sibling directories that are still being written. */\nexport const SKILL_ATOMIC_TEMP_PREFIX = \".openclaw-skill-tmp-\";\n\n/**\n * Returns `true` when `dir` exists and contains no entries (empty directory).\n */\nfunction isEmptyDir(dir: string): boolean {\n if (!existsSync(dir)) return false;\n try {\n const entries = readdirSync(dir);\n return entries.length === 0;\n } catch {\n return false;\n }\n}\n\n/**\n * Validates that a relative path is safe for use within a skill directory.\n * Rejects paths that:\n * - contain null bytes\n * - are absolute (POSIX or Windows)\n * - attempt parent directory traversal (..)\n * - are empty\n * - collide with the completion marker name\n *\n * Throws an error if the path is unsafe.\n */\nfunction assertSafeRelativeSkillPath(relPath: string): void {\n if (relPath.includes(\"\\0\")) {\n throw new Error(`Unsafe path: contains null byte: ${relPath}`);\n }\n if (relPath.startsWith(\"/\") || /^[A-Za-z]:[\\\\/]/.test(relPath)) {\n throw new Error(`Unsafe path: absolute path not allowed: ${relPath}`);\n }\n if (relPath.includes(\"..\")) {\n throw new Error(`Unsafe path: parent directory traversal not allowed: ${relPath}`);\n }\n if (relPath.trim().length === 0) {\n throw new Error(\"Unsafe path: empty path not allowed\");\n }\n if (relPath === SKILL_COMPLETE_MARKER || basename(relPath) === SKILL_COMPLETE_MARKER) {\n throw new Error(`Unsafe path: collision with completion marker: ${relPath}`);\n }\n}\n\n/**\n * Atomically write a single file.\n *\n * Writes content to `${targetPath}.tmp-${pid}-${rand}` then renames it to\n * `targetPath`. If any step fails the temp file is removed and the error is\n * re-thrown, so `targetPath` is never left in a partially-written state.\n */\nexport function atomicWriteFile(targetPath: string, content: string): void {\n const dir = dirname(targetPath);\n const rand = randomBytes(8).toString(\"hex\");\n const tmpPath = `${targetPath}.tmp-${process.pid}-${rand}`;\n\n mkdirSync(dir, { recursive: true });\n try {\n writeFileSync(tmpPath, content, \"utf-8\");\n renameSync(tmpPath, targetPath);\n } catch (err) {\n try {\n if (existsSync(tmpPath)) unlinkSync(tmpPath);\n } catch {\n // best-effort temp cleanup; swallow to surface original error\n }\n throw err;\n }\n}\n\n/**\n * Atomically write a multi-file skill directory.\n *\n * All files in `files` (keyed by path relative to `skillDir`) are written\n * into a temporary sibling directory `${SKILL_ATOMIC_TEMP_PREFIX}${pid}-${rand}`. A\n * `.openclaw-skill-complete` marker is written as the very last file. The\n * temp directory is then atomically renamed to `skillDir`.\n *\n * If `skillDir` already exists and is non-empty, this helper fails instead of\n * moving the existing directory aside. That keeps replacement crash-atomic: a\n * process crash can leave only a hidden temp sibling, never a missing final\n * directory. Empty directories (e.g. from `allocateDraftSkillDir` reservation\n * locks) are allowed and will be atomically replaced by the rename, preserving\n * the allocation lock throughout the write operation.\n *\n * Pass files in the order you want them written. Convention: put `SKILL.md`\n * last so it is the final content file before the marker.\n */\nexport type AtomicWriteSkillDirOptions = {\n /** Relative paths (keys of `files`) written with owner-execute permission (e.g. `scripts/replay.sh`). */\n executableRelativePaths?: readonly string[];\n};\n\nexport function atomicWriteSkillDir(\n skillDir: string,\n files: Record<string, string>,\n options?: AtomicWriteSkillDirOptions,\n): void {\n const executable = new Set(options?.executableRelativePaths ?? []);\n const parent = dirname(skillDir);\n const rand = randomBytes(8).toString(\"hex\");\n const tmpDir = join(parent, `${SKILL_ATOMIC_TEMP_PREFIX}${process.pid}-${rand}`);\n\n try {\n if (existsSync(skillDir) && !isEmptyDir(skillDir)) {\n throw new Error(`Atomic skill directory target already exists: ${skillDir}`);\n }\n\n // Write every sidecar into the temp directory.\n for (const [relPath, content] of Object.entries(files)) {\n assertSafeRelativeSkillPath(relPath);\n const fullPath = join(tmpDir, relPath);\n mkdirSync(dirname(fullPath), { recursive: true });\n const fileMode = executable.has(relPath) ? 0o755 : undefined;\n writeFileSync(fullPath, content, { encoding: \"utf-8\", ...(fileMode != null ? { mode: fileMode } : {}) });\n if (fileMode != null) {\n chmodSync(fullPath, fileMode);\n }\n }\n\n // Stamp the completion marker as the final write inside the temp dir.\n writeFileSync(join(tmpDir, SKILL_COMPLETE_MARKER), new Date().toISOString(), \"utf-8\");\n\n // Atomic promotion: temp dir → final skill dir. On POSIX, renameSync\n // atomically replaces empty directories, preserving the allocation lock.\n // On Windows, renameSync fails if the target exists (even when empty), so\n // we move the reservation aside, rename the temp dir, then clean up. This\n // maintains crash-safety: if we crash after moving the reservation but before\n // rename completes, the reservation survives as a .bak directory.\n let reservationBackup: string | null = null;\n if (process.platform === \"win32\" && existsSync(skillDir) && isEmptyDir(skillDir)) {\n reservationBackup = join(parent, `.${basename(skillDir)}.bak-${process.pid}-${randomBytes(4).toString(\"hex\")}`);\n renameSync(skillDir, reservationBackup);\n }\n try {\n renameSync(tmpDir, skillDir);\n // Success: remove the reservation backup if we made one.\n if (reservationBackup && existsSync(reservationBackup)) {\n rmSync(reservationBackup, { recursive: true, force: true });\n }\n } catch (err) {\n // Restore the reservation lock if rename failed.\n if (reservationBackup && existsSync(reservationBackup)) {\n try {\n renameSync(reservationBackup, skillDir);\n } catch {\n // best-effort restoration\n }\n }\n throw err;\n }\n } catch (err) {\n try {\n if (existsSync(tmpDir)) rmSync(tmpDir, { recursive: true, force: true });\n } catch {\n // best-effort temp cleanup; swallow to surface original error\n }\n throw err;\n }\n}\n\n/**\n * Returns `true` when `skillDir` contains the completion marker, indicating\n * that the directory was written atomically and is not an in-progress write.\n */\nexport function isSkillDirComplete(skillDir: string): boolean {\n return existsSync(join(skillDir, SKILL_COMPLETE_MARKER));\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth-failover.js","names":[],"sources":["../../utils/auth-failover.ts"],"sourcesContent":["/**\n * OAuth/API failover with exponential backoff.\n * When a provider has both OAuth and API key, we prefer OAuth by default.\n * On OAuth failure we record backoff and use API until the backoff expires;\n * backoff schedule: 5min → 30min → 1h → 2h → 4h. Counter resets every X hours.\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\n\nconst DEFAULT_BACKOFF_MINUTES = [5, 30, 60, 120, 240];\nconst DEFAULT_RESET_AFTER_HOURS = 24;\n\ntype AuthFailoverOptions = {\n /** Backoff delays in minutes per level (0-indexed). Default [5, 30, 60, 120, 240]. */\n backoffScheduleMinutes?: number[];\n /** Reset backoff levels after this many hours. Default 24. */\n resetBackoffAfterHours?: number;\n /** Path to JSON file for persistence (survives restart). */\n statePath?: string;\n};\n\ntype AuthFailoverState = {\n lastResetTs: number;\n providers: Record<string, { backoffUntil: number; level: number }>;\n};\n\nfunction loadState(statePath: string): AuthFailoverState {\n if (!statePath || !existsSync(statePath)) {\n return { lastResetTs: Date.now(), providers: {} };\n }\n try {\n const raw = readFileSync(statePath, \"utf-8\");\n const data = JSON.parse(raw) as AuthFailoverState;\n if (typeof data.lastResetTs !== \"number\" || typeof data.providers !== \"object\") {\n return { lastResetTs: Date.now(), providers: {} };\n }\n return data;\n } catch {\n return { lastResetTs: Date.now(), providers: {} };\n }\n}\n\nfunction saveState(statePath: string, state: AuthFailoverState): void {\n if (!statePath) return;\n try {\n writeFileSync(statePath, JSON.stringify(state, null, 0), \"utf-8\");\n } catch {\n /* non-fatal */\n }\n}\n\n/**\n * Returns true when OAuth for this provider is in backoff (we should use API instead).\n * Also runs reset-if-due so the counter is cleared after resetBackoffAfterHours.\n */\nexport function isOAuthInBackoff(provider: string, options: AuthFailoverOptions = {}): boolean {\n const _schedule = options.backoffScheduleMinutes ?? DEFAULT_BACKOFF_MINUTES;\n const resetHours = options.resetBackoffAfterHours ?? DEFAULT_RESET_AFTER_HOURS;\n const statePath = options.statePath;\n const state = statePath ? loadState(statePath) : { lastResetTs: Date.now(), providers: {} };\n\n const now = Date.now();\n if (now - state.lastResetTs >= resetHours * 60 * 60 * 1000) {\n state.lastResetTs = now;\n state.providers = {};\n if (statePath) saveState(statePath, state);\n return false;\n }\n\n const entry = state.providers[provider];\n if (!entry) return false;\n if (now >= entry.backoffUntil) return false;\n return true;\n}\n\n/**\n * Record an OAuth failure for the provider. Advances backoff level (cap at schedule length - 1)\n * and sets backoffUntil = now + schedule[level] minutes. Persists state when statePath is set.\n */\nexport function recordOAuthFailure(provider: string, options: AuthFailoverOptions = {}): void {\n const schedule = options.backoffScheduleMinutes ?? DEFAULT_BACKOFF_MINUTES;\n const statePath = options.statePath;\n const state = statePath ? loadState(statePath) : { lastResetTs: Date.now(), providers: {} };\n\n const now = Date.now();\n const entry = state.providers[provider];\n const nextLevel = entry ? Math.min(entry.level + 1, schedule.length - 1) : 0;\n const delayMs = schedule[nextLevel] * 60 * 1000;\n state.providers[provider] = { backoffUntil: now + delayMs, level: nextLevel };\n if (statePath) saveState(statePath, state);\n}\n\n/**\n * Clear backoff for a provider (e.g. after manual reset or success).\n * If statePath is set, loads and saves; otherwise no-op for persistence.\n */\nfunction _clearOAuthBackoff(provider: string, options: AuthFailoverOptions = {}): void {\n const statePath = options.statePath;\n if (!statePath) return;\n const state = loadState(statePath);\n if (state.providers[provider]) {\n delete state.providers[provider];\n saveState(statePath, state);\n }\n}\n\n/**\n * Clear all backoff state (and optionally reset lastResetTs).\n */\nexport function resetAllBackoff(options: AuthFailoverOptions & { resetTimer?: boolean } = {}): void {\n const statePath = options.statePath;\n const state = statePath ? loadState(statePath) : { lastResetTs: Date.now(), providers: {} };\n state.providers = {};\n if (options.resetTimer !== false) state.lastResetTs = Date.now();\n if (statePath) saveState(statePath, state);\n}\n\nexport { DEFAULT_BACKOFF_MINUTES, DEFAULT_RESET_AFTER_HOURS };\n"],"mappings":";;;;;;;;AASA,MAAM,0BAA0B;CAAC;CAAG;CAAI;CAAI;CAAK;
|
|
1
|
+
{"version":3,"file":"auth-failover.js","names":[],"sources":["../../utils/auth-failover.ts"],"sourcesContent":["/**\n * OAuth/API failover with exponential backoff.\n * When a provider has both OAuth and API key, we prefer OAuth by default.\n * On OAuth failure we record backoff and use API until the backoff expires;\n * backoff schedule: 5min → 30min → 1h → 2h → 4h. Counter resets every X hours.\n */\n\nimport { existsSync, readFileSync, writeFileSync } from \"node:fs\";\n\nconst DEFAULT_BACKOFF_MINUTES = [5, 30, 60, 120, 240];\nconst DEFAULT_RESET_AFTER_HOURS = 24;\n\ntype AuthFailoverOptions = {\n /** Backoff delays in minutes per level (0-indexed). Default [5, 30, 60, 120, 240]. */\n backoffScheduleMinutes?: number[];\n /** Reset backoff levels after this many hours. Default 24. */\n resetBackoffAfterHours?: number;\n /** Path to JSON file for persistence (survives restart). */\n statePath?: string;\n};\n\ntype AuthFailoverState = {\n lastResetTs: number;\n providers: Record<string, { backoffUntil: number; level: number }>;\n};\n\nfunction loadState(statePath: string): AuthFailoverState {\n if (!statePath || !existsSync(statePath)) {\n return { lastResetTs: Date.now(), providers: {} };\n }\n try {\n const raw = readFileSync(statePath, \"utf-8\");\n const data = JSON.parse(raw) as AuthFailoverState;\n if (typeof data.lastResetTs !== \"number\" || typeof data.providers !== \"object\") {\n return { lastResetTs: Date.now(), providers: {} };\n }\n return data;\n } catch {\n return { lastResetTs: Date.now(), providers: {} };\n }\n}\n\nfunction saveState(statePath: string, state: AuthFailoverState): void {\n if (!statePath) return;\n try {\n writeFileSync(statePath, JSON.stringify(state, null, 0), \"utf-8\");\n } catch {\n /* non-fatal */\n }\n}\n\n/**\n * Returns true when OAuth for this provider is in backoff (we should use API instead).\n * Also runs reset-if-due so the counter is cleared after resetBackoffAfterHours.\n */\nexport function isOAuthInBackoff(provider: string, options: AuthFailoverOptions = {}): boolean {\n const _schedule = options.backoffScheduleMinutes ?? DEFAULT_BACKOFF_MINUTES;\n const resetHours = options.resetBackoffAfterHours ?? DEFAULT_RESET_AFTER_HOURS;\n const statePath = options.statePath;\n const state = statePath ? loadState(statePath) : { lastResetTs: Date.now(), providers: {} };\n\n const now = Date.now();\n if (now - state.lastResetTs >= resetHours * 60 * 60 * 1000) {\n state.lastResetTs = now;\n state.providers = {};\n if (statePath) saveState(statePath, state);\n return false;\n }\n\n const entry = state.providers[provider];\n if (!entry) return false;\n if (now >= entry.backoffUntil) return false;\n return true;\n}\n\n/**\n * Record an OAuth failure for the provider. Advances backoff level (cap at schedule length - 1)\n * and sets backoffUntil = now + schedule[level] minutes. Persists state when statePath is set.\n */\nexport function recordOAuthFailure(provider: string, options: AuthFailoverOptions = {}): void {\n const schedule = options.backoffScheduleMinutes ?? DEFAULT_BACKOFF_MINUTES;\n const statePath = options.statePath;\n const state = statePath ? loadState(statePath) : { lastResetTs: Date.now(), providers: {} };\n\n const now = Date.now();\n const entry = state.providers[provider];\n const nextLevel = entry ? Math.min(entry.level + 1, schedule.length - 1) : 0;\n const delayMs = schedule[nextLevel] * 60 * 1000;\n state.providers[provider] = { backoffUntil: now + delayMs, level: nextLevel };\n if (statePath) saveState(statePath, state);\n}\n\n/**\n * Clear backoff for a provider (e.g. after manual reset or success).\n * If statePath is set, loads and saves; otherwise no-op for persistence.\n */\nfunction _clearOAuthBackoff(provider: string, options: AuthFailoverOptions = {}): void {\n const statePath = options.statePath;\n if (!statePath) return;\n const state = loadState(statePath);\n if (state.providers[provider]) {\n delete state.providers[provider];\n saveState(statePath, state);\n }\n}\n\n/**\n * Clear all backoff state (and optionally reset lastResetTs).\n */\nexport function resetAllBackoff(options: AuthFailoverOptions & { resetTimer?: boolean } = {}): void {\n const statePath = options.statePath;\n const state = statePath ? loadState(statePath) : { lastResetTs: Date.now(), providers: {} };\n state.providers = {};\n if (options.resetTimer !== false) state.lastResetTs = Date.now();\n if (statePath) saveState(statePath, state);\n}\n\nexport { DEFAULT_BACKOFF_MINUTES, DEFAULT_RESET_AFTER_HOURS };\n"],"mappings":";;;;;;;;AASA,MAAM,0BAA0B;CAAC;CAAG;CAAI;CAAI;CAAK;AAAG;AAiBpD,SAAS,UAAU,WAAsC;CACvD,IAAI,CAAC,aAAa,CAAC,WAAW,SAAS,GACrC,OAAO;EAAE,aAAa,KAAK,IAAI;EAAG,WAAW,CAAC;CAAE;CAElD,IAAI;EACF,MAAM,MAAM,aAAa,WAAW,OAAO;EAC3C,MAAM,OAAO,KAAK,MAAM,GAAG;EAC3B,IAAI,OAAO,KAAK,gBAAgB,YAAY,OAAO,KAAK,cAAc,UACpE,OAAO;GAAE,aAAa,KAAK,IAAI;GAAG,WAAW,CAAC;EAAE;EAElD,OAAO;CACT,QAAQ;EACN,OAAO;GAAE,aAAa,KAAK,IAAI;GAAG,WAAW,CAAC;EAAE;CAClD;AACF;AAEA,SAAS,UAAU,WAAmB,OAAgC;CACpE,IAAI,CAAC,WAAW;CAChB,IAAI;EACF,cAAc,WAAW,KAAK,UAAU,OAAO,MAAM,CAAC,GAAG,OAAO;CAClE,QAAQ,CAER;AACF;;;;;AAMA,SAAgB,iBAAiB,UAAkB,UAA+B,CAAC,GAAY;CAC3E,QAAQ;CAC1B,MAAM,aAAa,QAAQ,0BAAA;CAC3B,MAAM,YAAY,QAAQ;CAC1B,MAAM,QAAQ,YAAY,UAAU,SAAS,IAAI;EAAE,aAAa,KAAK,IAAI;EAAG,WAAW,CAAC;CAAE;CAE1F,MAAM,MAAM,KAAK,IAAI;CACrB,IAAI,MAAM,MAAM,eAAe,aAAa,KAAK,KAAK,KAAM;EAC1D,MAAM,cAAc;EACpB,MAAM,YAAY,CAAC;EACnB,IAAI,WAAW,UAAU,WAAW,KAAK;EACzC,OAAO;CACT;CAEA,MAAM,QAAQ,MAAM,UAAU;CAC9B,IAAI,CAAC,OAAO,OAAO;CACnB,IAAI,OAAO,MAAM,cAAc,OAAO;CACtC,OAAO;AACT;;;;;AAMA,SAAgB,mBAAmB,UAAkB,UAA+B,CAAC,GAAS;CAC5F,MAAM,WAAW,QAAQ,0BAA0B;CACnD,MAAM,YAAY,QAAQ;CAC1B,MAAM,QAAQ,YAAY,UAAU,SAAS,IAAI;EAAE,aAAa,KAAK,IAAI;EAAG,WAAW,CAAC;CAAE;CAE1F,MAAM,MAAM,KAAK,IAAI;CACrB,MAAM,QAAQ,MAAM,UAAU;CAC9B,MAAM,YAAY,QAAQ,KAAK,IAAI,MAAM,QAAQ,GAAG,SAAS,SAAS,CAAC,IAAI;CAC3E,MAAM,UAAU,SAAS,aAAa,KAAK;CAC3C,MAAM,UAAU,YAAY;EAAE,cAAc,MAAM;EAAS,OAAO;CAAU;CAC5E,IAAI,WAAW,UAAU,WAAW,KAAK;AAC3C;;;;AAmBA,SAAgB,gBAAgB,UAA0D,CAAC,GAAS;CAClG,MAAM,YAAY,QAAQ;CAC1B,MAAM,QAAQ,YAAY,UAAU,SAAS,IAAI;EAAE,aAAa,KAAK,IAAI;EAAG,WAAW,CAAC;CAAE;CAC1F,MAAM,YAAY,CAAC;CACnB,IAAI,QAAQ,eAAe,OAAO,MAAM,cAAc,KAAK,IAAI;CAC/D,IAAI,WAAW,UAAU,WAAW,KAAK;AAC3C"}
|
package/dist/utils/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","names":[],"sources":["../../utils/auth.ts"],"sourcesContent":["/**\n * Shared OAuth-profile detection utility used by both the provider-routing logic in\n * init-databases.ts and the auth-order tests. Keeping the implementation in a single\n * exported function ensures that tests exercise the real production code path.\n */\n\n/**\n * Returns true when the auth order for a provider includes at least one OAuth/token profile\n * (i.e. not just the plain API-key profile). Used to decide whether to route through the gateway.\n * API-key-only profiles end with ':api' or ':default' (e.g. 'anthropic:api', 'google:default').\n *\n * Comparison is case-insensitive to tolerate common capitalisation typos in operator config\n * (e.g. 'openai:Api' or 'openai:DEFAULT') without silently routing through the gateway.\n */\nexport function hasOAuthProfiles(\n order: string[] | undefined,\n provider: string,\n opts?: { onOAuthProfile?: (profile: string) => void },\n): boolean {\n if (!order || order.length === 0) return false;\n const providerLower = provider.trim().toLowerCase();\n const apiOnlyPatternsLower = [`${providerLower}:api`, `${providerLower}:default`];\n return order.some((p) => {\n const pLower = p.toLowerCase();\n const isApiOnly = apiOnlyPatternsLower.includes(pLower);\n if (!isApiOnly && opts?.onOAuthProfile) {\n opts.onOAuthProfile(p);\n }\n return !isApiOnly;\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,iBACd,OACA,UACA,MACS;CACT,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,OAAO;CACzC,MAAM,gBAAgB,SAAS,
|
|
1
|
+
{"version":3,"file":"auth.js","names":[],"sources":["../../utils/auth.ts"],"sourcesContent":["/**\n * Shared OAuth-profile detection utility used by both the provider-routing logic in\n * init-databases.ts and the auth-order tests. Keeping the implementation in a single\n * exported function ensures that tests exercise the real production code path.\n */\n\n/**\n * Returns true when the auth order for a provider includes at least one OAuth/token profile\n * (i.e. not just the plain API-key profile). Used to decide whether to route through the gateway.\n * API-key-only profiles end with ':api' or ':default' (e.g. 'anthropic:api', 'google:default').\n *\n * Comparison is case-insensitive to tolerate common capitalisation typos in operator config\n * (e.g. 'openai:Api' or 'openai:DEFAULT') without silently routing through the gateway.\n */\nexport function hasOAuthProfiles(\n order: string[] | undefined,\n provider: string,\n opts?: { onOAuthProfile?: (profile: string) => void },\n): boolean {\n if (!order || order.length === 0) return false;\n const providerLower = provider.trim().toLowerCase();\n const apiOnlyPatternsLower = [`${providerLower}:api`, `${providerLower}:default`];\n return order.some((p) => {\n const pLower = p.toLowerCase();\n const isApiOnly = apiOnlyPatternsLower.includes(pLower);\n if (!isApiOnly && opts?.onOAuthProfile) {\n opts.onOAuthProfile(p);\n }\n return !isApiOnly;\n });\n}\n"],"mappings":";;;;;;;;;;;;;;AAcA,SAAgB,iBACd,OACA,UACA,MACS;CACT,IAAI,CAAC,SAAS,MAAM,WAAW,GAAG,OAAO;CACzC,MAAM,gBAAgB,SAAS,KAAK,EAAE,YAAY;CAClD,MAAM,uBAAuB,CAAC,GAAG,cAAc,OAAO,GAAG,cAAc,SAAS;CAChF,OAAO,MAAM,MAAM,MAAM;EACvB,MAAM,SAAS,EAAE,YAAY;EAC7B,MAAM,YAAY,qBAAqB,SAAS,MAAM;EACtD,IAAI,CAAC,aAAa,MAAM,gBACtB,KAAK,eAAe,CAAC;EAEvB,OAAO,CAAC;CACV,CAAC;AACH"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction-model-watchdog.js","names":[],"sources":["../../utils/compaction-model-watchdog.ts"],"sourcesContent":["import { isHeavyModel } from \"./model-tier.js\";\n\nexport const DEFAULT_COMPACTION_MODEL = \"minimax/MiniMax-M2.7\";\n\nexport type CompactionFallbackPolicy = \"default-mini\" | \"inherit-agent-primary\" | \"inherit-defaults-primary\";\n\nexport type CompactionModelSelection = {\n model: string;\n provider: string;\n reason: string;\n inherited: boolean;\n unsupportedPerAgentCompactionPaths: string[];\n /**\n * When using inherit-* fallback policy, config does not set compaction.model or defaults primary;\n * OpenClaw may compact with the active session chat model (not derivable from file config).\n */\n routingUnknownFromConfig?: boolean;\n};\n\nexport type CompactionWatchdogContext = \"verify\" | \"before_compaction\" | \"after_compaction\";\nexport type CompactionModelStrength = {\n tooStrong: boolean;\n reason: string;\n provider: string;\n model: string;\n};\n\nexport type CompactionHookModelMetadata = {\n model: string;\n provider: string;\n source: string;\n};\n\nexport type CompactionHookIdentity = {\n agentId?: string;\n sessionId?: string;\n source: string;\n};\n\nfunction readAgentList(root: Record<string, unknown> | undefined): Record<string, unknown>[] {\n const agents = root?.agents as Record<string, unknown> | undefined;\n const list = agents?.list;\n if (!Array.isArray(list)) return [];\n const entries: Record<string, unknown>[] = [];\n for (const entry of list) {\n if (!entry || typeof entry !== \"object\") continue;\n entries.push(entry as Record<string, unknown>);\n }\n return entries;\n}\n\nfunction collectUnsupportedPerAgentCompactionPaths(root: Record<string, unknown> | undefined): string[] {\n const paths: string[] = [];\n const list = readAgentList(root);\n for (let idx = 0; idx < list.length; idx += 1) {\n const entry = list[idx];\n const model = readCompactionModelFromAgentBlock(entry);\n if (!model) continue;\n const rawId = entry.id;\n const id = typeof rawId === \"string\" && rawId.trim().length > 0 ? rawId.trim() : `index=${String(idx)}`;\n paths.push(`agents.list[id=${id}].compaction.model`);\n }\n return paths;\n}\n\nfunction readAgentsDefaults(root: Record<string, unknown> | undefined): Record<string, unknown> | undefined {\n return (root?.agents as Record<string, unknown> | undefined)?.defaults as Record<string, unknown> | undefined;\n}\n\nfunction readCompactionModelFromAgentBlock(agentBlock: Record<string, unknown> | undefined): string | undefined {\n const compaction = agentBlock?.compaction as Record<string, unknown> | undefined;\n const model = compaction?.model;\n if (typeof model !== \"string\") return undefined;\n const trimmed = model.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction readPrimaryModelFromAgentBlock(agentBlock: Record<string, unknown> | undefined): string | undefined {\n const model = agentBlock?.model as Record<string, unknown> | undefined;\n const primary = model?.primary;\n if (typeof primary !== \"string\") return undefined;\n const trimmed = primary.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction readPathValue(obj: unknown, path: readonly string[]): unknown {\n let cur: unknown = obj;\n for (const key of path) {\n if (!cur || typeof cur !== \"object\") return undefined;\n cur = (cur as Record<string, unknown>)[key];\n }\n return cur;\n}\n\nfunction readString(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nexport function inferCompactionProvider(model: string): string {\n const trimmed = model.trim();\n if (!trimmed) return \"unknown\";\n if (trimmed.includes(\"/\")) return (trimmed.split(\"/\")[0] ?? \"unknown\").trim().toLowerCase() || \"unknown\";\n const lower = trimmed.toLowerCase();\n if (lower.startsWith(\"minimax-\")) return \"minimax\";\n if (lower.startsWith(\"gemini-\")) return \"google\";\n if (lower.startsWith(\"claude-\")) return \"anthropic\";\n if (lower.startsWith(\"gpt-\") || /^o[0-9]/.test(lower)) return \"openai\";\n return \"unknown\";\n}\n\nexport function resolveCompactionModelSelection(\n root: Record<string, unknown> | undefined,\n opts?: { fallbackPolicy?: CompactionFallbackPolicy; defaultModel?: string },\n): CompactionModelSelection {\n const fallbackPolicy = opts?.fallbackPolicy ?? \"default-mini\";\n const defaultModel = opts?.defaultModel?.trim() || DEFAULT_COMPACTION_MODEL;\n const defaults = readAgentsDefaults(root);\n const unsupportedPerAgentCompactionPaths = collectUnsupportedPerAgentCompactionPaths(root);\n\n const defaultsCompaction = readCompactionModelFromAgentBlock(defaults);\n if (defaultsCompaction) {\n return {\n model: defaultsCompaction,\n provider: inferCompactionProvider(defaultsCompaction),\n reason: \"agents.defaults.compaction.model explicitly set\",\n inherited: false,\n unsupportedPerAgentCompactionPaths,\n };\n }\n\n if (fallbackPolicy === \"inherit-agent-primary\" || fallbackPolicy === \"inherit-defaults-primary\") {\n const defaultsPrimary = readPrimaryModelFromAgentBlock(defaults);\n if (defaultsPrimary) {\n return {\n model: defaultsPrimary,\n provider: inferCompactionProvider(defaultsPrimary),\n reason: \"inherited from agents.defaults.model.primary (compaction model unset)\",\n inherited: true,\n unsupportedPerAgentCompactionPaths,\n };\n }\n return {\n model: \"\",\n provider: \"unknown\",\n reason:\n \"agents.defaults.compaction.model and agents.defaults.model.primary unset; OpenClaw compaction may follow the active session chat model\",\n inherited: false,\n unsupportedPerAgentCompactionPaths,\n routingUnknownFromConfig: true,\n };\n }\n\n return {\n model: defaultModel,\n provider: inferCompactionProvider(defaultModel),\n reason: \"default mini compaction model (compaction model unset)\",\n inherited: false,\n unsupportedPerAgentCompactionPaths,\n };\n}\n\nfunction extractCompactionHookIdentityFromSource(\n sourceObj: unknown,\n sourceName: string,\n): CompactionHookIdentity | null {\n const agentPaths: string[][] = [\n [\"agentId\"],\n [\"agent\", \"id\"],\n [\"context\", \"agentId\"],\n [\"metadata\", \"agentId\"],\n [\"metadata\", \"agent\", \"id\"],\n [\"payload\", \"agentId\"],\n [\"session\", \"agentId\"],\n [\"ctx\", \"agentId\"],\n ];\n const sessionPaths: string[][] = [\n [\"sessionId\"],\n [\"session\", \"id\"],\n [\"context\", \"sessionId\"],\n [\"metadata\", \"sessionId\"],\n [\"payload\", \"sessionId\"],\n [\"ctx\", \"sessionId\"],\n ];\n let agentId: string | undefined;\n let sessionId: string | undefined;\n for (const path of agentPaths) {\n agentId = readString(readPathValue(sourceObj, path));\n if (agentId) break;\n }\n for (const path of sessionPaths) {\n sessionId = readString(readPathValue(sourceObj, path));\n if (sessionId) break;\n }\n if (!agentId && !sessionId) return null;\n return { agentId, sessionId, source: sourceName };\n}\n\n/**\n * Best-effort extraction of hook identity fields used to contextualize fallback watchdog warnings.\n * Returns null when neither agent nor session metadata is available.\n */\nexport function resolveCompactionHookIdentity(\n event: unknown,\n hookCtx?: unknown,\n apiContext?: unknown,\n): CompactionHookIdentity | null {\n const candidates = [\n extractCompactionHookIdentityFromSource(event, \"event\"),\n extractCompactionHookIdentityFromSource(hookCtx, \"hookCtx\"),\n extractCompactionHookIdentityFromSource(apiContext, \"api.context\"),\n ].filter(Boolean) as CompactionHookIdentity[];\n if (candidates.length === 0) return null;\n\n const merged: CompactionHookIdentity = {\n source: candidates.map((c) => c.source).join(\"+\"),\n };\n for (const candidate of candidates) {\n if (!merged.agentId && candidate.agentId) merged.agentId = candidate.agentId;\n if (!merged.sessionId && candidate.sessionId) merged.sessionId = candidate.sessionId;\n }\n return merged;\n}\n\nexport function describeCompactionFallbackScope(identity: CompactionHookIdentity | null | undefined): string {\n const agentId = identity?.agentId?.trim() || \"unknown\";\n const sessionId = identity?.sessionId?.trim() || \"unknown\";\n const source = identity?.source?.trim() || \"unavailable\";\n const scope =\n agentId === \"unknown\"\n ? \"unknown agent context; fallback uses global defaults only\"\n : agentId === \"main\"\n ? \"main agent context; fallback uses global defaults only\"\n : `non-main agent context (${agentId}); fallback uses global defaults only`;\n return `agent=${agentId}, session=${sessionId}, source=${source}; ${scope}`;\n}\n\nfunction isExplicitMiniOrNanoModel(model: string): boolean {\n const lower = (model.split(\"/\").pop() ?? model).toLowerCase();\n return /\\bmini\\b|\\bnano\\b|\\blite\\b|\\bhaiku\\b/.test(lower);\n}\n\nfunction isMiniMaxM27(model: string): boolean {\n const lower = (model.split(\"/\").pop() ?? model).toLowerCase();\n return /minimax[-_]?m2\\.7(?:[-_]?high[-_]?speed)?/.test(lower);\n}\n\nfunction isMiniMaxFamilyModel(model: string): boolean {\n const lower = model.toLowerCase();\n return lower.includes(\"minimax\");\n}\n\nfunction matchesHighTierCompactionFamily(model: string): string | null {\n const lower = (model.split(\"/\").pop() ?? model).toLowerCase();\n\n // Check for gpt-5.4-pro first (most specific)\n if (/\\bgpt-5\\.4-pro\\b/.test(lower)) return \"gpt-5.4-pro high-tier model\";\n // Then check for broader GPT-5 class models\n if (/\\bgpt-5\\b|\\bgpt-5\\.\\d+\\b/.test(lower)) return \"GPT-5 class model\";\n if (/\\bpro\\b|\\bfull\\b|high[-_]?tier|\\bheavy\\b|\\bultra\\b|\\blarge\\b/.test(lower)) {\n return \"pro/full/high-tier model keyword match\";\n }\n return null;\n}\n\nfunction parseProviderAndModelFromText(raw: string): { provider?: string; model?: string } | null {\n const modelMatch = raw.match(/\\bmodel\\s*[=:]\\s*([a-z0-9._:/-]+)/i);\n if (!modelMatch?.[1]) return null;\n const model = modelMatch[1].trim();\n if (!model) return null;\n const providerMatch = raw.match(/\\bprovider\\s*[=:]\\s*([a-z0-9._-]+)/i);\n const provider = providerMatch?.[1]?.trim();\n return { provider, model };\n}\n\nfunction extractCompactionModelMetadataFromSource(\n sourceObj: unknown,\n sourceName: string,\n): CompactionHookModelMetadata | null {\n const candidatePaths: Array<{ modelPath: string[]; providerPath?: string[] }> = [\n { modelPath: [\"compaction\", \"model\"], providerPath: [\"compaction\", \"provider\"] },\n { modelPath: [\"metadata\", \"compaction\", \"model\"], providerPath: [\"metadata\", \"compaction\", \"provider\"] },\n { modelPath: [\"context\", \"compaction\", \"model\"], providerPath: [\"context\", \"compaction\", \"provider\"] },\n { modelPath: [\"payload\", \"compaction\", \"model\"], providerPath: [\"payload\", \"compaction\", \"provider\"] },\n { modelPath: [\"logMetadata\", \"compaction\", \"model\"], providerPath: [\"logMetadata\", \"compaction\", \"provider\"] },\n { modelPath: [\"resolvedCompactionModel\"], providerPath: [\"resolvedCompactionProvider\"] },\n { modelPath: [\"selectedCompactionModel\"], providerPath: [\"selectedCompactionProvider\"] },\n { modelPath: [\"compactionModel\"], providerPath: [\"compactionProvider\"] },\n { modelPath: [\"compaction_model\"], providerPath: [\"compaction_provider\"] },\n { modelPath: [\"model\"], providerPath: [\"provider\"] },\n ];\n\n for (const candidate of candidatePaths) {\n const model = readString(readPathValue(sourceObj, candidate.modelPath));\n if (!model) continue;\n const provider =\n readString(candidate.providerPath ? readPathValue(sourceObj, candidate.providerPath) : undefined) ??\n inferCompactionProvider(model);\n return {\n model,\n provider,\n source: `${sourceName}.${candidate.modelPath.join(\".\")}`,\n };\n }\n\n const textPaths: string[][] = [[\"metadata\"], [\"logMetadata\"], [\"context\"], [\"details\"]];\n for (const textPath of textPaths) {\n const raw = readString(readPathValue(sourceObj, textPath));\n if (!raw) continue;\n const parsed = parseProviderAndModelFromText(raw);\n if (!parsed?.model) continue;\n return {\n model: parsed.model,\n provider: (parsed.provider ?? inferCompactionProvider(parsed.model)).toLowerCase(),\n source: `${sourceName}.${textPath.join(\".\")} (text)`,\n };\n }\n\n return null;\n}\n\n/**\n * Best-effort extraction of compaction provider/model from hook payloads.\n * Returns null when runtime does not expose compaction model metadata.\n */\nexport function resolveCompactionHookModelMetadata(\n event: unknown,\n hookCtx?: unknown,\n): CompactionHookModelMetadata | null {\n const eventCandidate = extractCompactionModelMetadataFromSource(event, \"event\");\n if (eventCandidate) return eventCandidate;\n const hookCandidate = extractCompactionModelMetadataFromSource(hookCtx, \"hookCtx\");\n if (hookCandidate) return hookCandidate;\n return null;\n}\n\n/**\n * Explain whether a compaction model is cost-safe. Policy:\n * - allow MiniMax M2.7 / M2.7-highspeed\n * - allow explicit mini/nano\n * - allow local ollama models\n * - flag high-tier families and unknown non-mini/non-nano/non-MiniMax models\n */\nexport function classifyCompactionModelStrength(model: string): CompactionModelStrength {\n const normalizedModel = model.trim();\n const lowerTail = (normalizedModel.split(\"/\").pop() ?? normalizedModel).toLowerCase();\n const provider = inferCompactionProvider(normalizedModel);\n\n if (!normalizedModel) {\n return {\n tooStrong: false,\n reason: \"model metadata unavailable\",\n provider,\n model: normalizedModel,\n };\n }\n if (provider === \"ollama\") {\n return {\n tooStrong: false,\n reason: \"local ollama model (no remote LLM cost)\",\n provider,\n model: normalizedModel,\n };\n }\n // Explicit denylist families from incident request.\n // Exclude o3-mini and o3-nano (allowlisted as legitimate cheap models)\n if (/\\bo3\\b/i.test(lowerTail) && !/o3-mini|o3-nano/i.test(lowerTail)) {\n return {\n tooStrong: true,\n reason: \"o3 class model\",\n provider,\n model: normalizedModel,\n };\n }\n if (/\\bsonnet\\b|\\bopus\\b/.test(lowerTail)) {\n return {\n tooStrong: true,\n reason: \"Claude Sonnet/Opus class model\",\n provider,\n model: normalizedModel,\n };\n }\n if (isMiniMaxM27(normalizedModel)) {\n return {\n tooStrong: false,\n reason: \"MiniMax M2.7 allowlist\",\n provider,\n model: normalizedModel,\n };\n }\n if (isExplicitMiniOrNanoModel(normalizedModel)) {\n return {\n tooStrong: false,\n reason: \"mini/nano allowlist\",\n provider,\n model: normalizedModel,\n };\n }\n\n const explicitHighTier = matchesHighTierCompactionFamily(normalizedModel);\n if (explicitHighTier) {\n return {\n tooStrong: true,\n reason: explicitHighTier,\n provider,\n model: normalizedModel,\n };\n }\n\n if (isHeavyModel(normalizedModel)) {\n return {\n tooStrong: true,\n reason: \"heavy-tier model classification\",\n provider,\n model: normalizedModel,\n };\n }\n\n if (!isMiniMaxFamilyModel(normalizedModel)) {\n return {\n tooStrong: true,\n reason: \"unknown non-mini/non-nano/non-MiniMax model\",\n provider,\n model: normalizedModel,\n };\n }\n\n return {\n tooStrong: false,\n reason: \"MiniMax provider allowlist\",\n provider,\n model: normalizedModel,\n };\n}\n\n/**\n * Builds a user-visible watchdog warning string when a compaction model is too strong.\n */\nexport function buildCompactionWatchdogAlert(opts: {\n stage: \"before_compaction\" | \"after_compaction\" | \"verify\";\n model: string;\n provider?: string;\n source?: string;\n}): string | null {\n const assessment = classifyCompactionModelStrength(opts.model);\n if (!assessment.tooStrong) return null;\n const provider = opts.provider?.trim() || assessment.provider;\n const sourcePart = opts.source ? `, source=${opts.source}` : \"\";\n return `memory-hybrid: compaction model watchdog alert (${opts.stage}) — provider=${provider}, model=${assessment.model}, reason=${assessment.reason}${sourcePart}. Prefer mini/nano compaction routing (recommended default: ${DEFAULT_COMPACTION_MODEL}).`;\n}\n\n/**\n * Cost watchdog classification for compaction model safety.\n * Treat only explicit mini-safe allowlist models as safe:\n * - minimax/* (current plugin default family)\n * - ollama/* (local/offline)\n * - any explicit *mini* or *nano* model name\n *\n * Everything else is treated as stronger-than-mini to match warning wording.\n */\nexport function isCompactionModelTooStrong(model: string): boolean {\n return classifyCompactionModelStrength(model).tooStrong;\n}\n\nexport function buildCompactionModelWatchdogWarning(\n selection: CompactionModelSelection,\n opts?: { context?: CompactionWatchdogContext; recommendedModel?: string },\n): string | null {\n if (selection.routingUnknownFromConfig) return null;\n if (!isCompactionModelTooStrong(selection.model)) return null;\n const recommendedModel = opts?.recommendedModel?.trim() || DEFAULT_COMPACTION_MODEL;\n const contextPrefix = opts?.context ? `${opts.context}: ` : \"\";\n return `${contextPrefix}compaction routing uses a stronger-than-mini model (provider=${selection.provider}, model=${selection.model}, reason=${selection.reason}). Set agents.defaults.compaction.model to a mini/nano model such as ${recommendedModel}.`;\n}\n\nexport function buildUnsupportedPerAgentCompactionWarning(\n selection: CompactionModelSelection,\n opts?: { context?: CompactionWatchdogContext },\n): string | null {\n if (selection.unsupportedPerAgentCompactionPaths.length === 0) return null;\n const contextPrefix = opts?.context ? `${opts.context}: ` : \"\";\n const paths = selection.unsupportedPerAgentCompactionPaths.slice(0, 3).join(\", \");\n const extraCount = selection.unsupportedPerAgentCompactionPaths.length - 3;\n const moreSuffix = extraCount > 0 ? ` (+${extraCount} more)` : \"\";\n return `${contextPrefix}unsupported per-agent compaction key(s) detected (${paths}${moreSuffix}). OpenClaw core currently resolves compaction from agents.defaults.compaction.model only; move compaction overrides to agents.defaults.compaction.model.`;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"compaction-model-watchdog.js","names":[],"sources":["../../utils/compaction-model-watchdog.ts"],"sourcesContent":["import { isHeavyModel } from \"./model-tier.js\";\n\nexport const DEFAULT_COMPACTION_MODEL = \"minimax/MiniMax-M2.7\";\n\nexport type CompactionFallbackPolicy = \"default-mini\" | \"inherit-agent-primary\" | \"inherit-defaults-primary\";\n\nexport type CompactionModelSelection = {\n model: string;\n provider: string;\n reason: string;\n inherited: boolean;\n unsupportedPerAgentCompactionPaths: string[];\n /**\n * When using inherit-* fallback policy, config does not set compaction.model or defaults primary;\n * OpenClaw may compact with the active session chat model (not derivable from file config).\n */\n routingUnknownFromConfig?: boolean;\n};\n\nexport type CompactionWatchdogContext = \"verify\" | \"before_compaction\" | \"after_compaction\";\nexport type CompactionModelStrength = {\n tooStrong: boolean;\n reason: string;\n provider: string;\n model: string;\n};\n\nexport type CompactionHookModelMetadata = {\n model: string;\n provider: string;\n source: string;\n};\n\nexport type CompactionHookIdentity = {\n agentId?: string;\n sessionId?: string;\n source: string;\n};\n\nfunction readAgentList(root: Record<string, unknown> | undefined): Record<string, unknown>[] {\n const agents = root?.agents as Record<string, unknown> | undefined;\n const list = agents?.list;\n if (!Array.isArray(list)) return [];\n const entries: Record<string, unknown>[] = [];\n for (const entry of list) {\n if (!entry || typeof entry !== \"object\") continue;\n entries.push(entry as Record<string, unknown>);\n }\n return entries;\n}\n\nfunction collectUnsupportedPerAgentCompactionPaths(root: Record<string, unknown> | undefined): string[] {\n const paths: string[] = [];\n const list = readAgentList(root);\n for (let idx = 0; idx < list.length; idx += 1) {\n const entry = list[idx];\n const model = readCompactionModelFromAgentBlock(entry);\n if (!model) continue;\n const rawId = entry.id;\n const id = typeof rawId === \"string\" && rawId.trim().length > 0 ? rawId.trim() : `index=${String(idx)}`;\n paths.push(`agents.list[id=${id}].compaction.model`);\n }\n return paths;\n}\n\nfunction readAgentsDefaults(root: Record<string, unknown> | undefined): Record<string, unknown> | undefined {\n return (root?.agents as Record<string, unknown> | undefined)?.defaults as Record<string, unknown> | undefined;\n}\n\nfunction readCompactionModelFromAgentBlock(agentBlock: Record<string, unknown> | undefined): string | undefined {\n const compaction = agentBlock?.compaction as Record<string, unknown> | undefined;\n const model = compaction?.model;\n if (typeof model !== \"string\") return undefined;\n const trimmed = model.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction readPrimaryModelFromAgentBlock(agentBlock: Record<string, unknown> | undefined): string | undefined {\n const model = agentBlock?.model as Record<string, unknown> | undefined;\n const primary = model?.primary;\n if (typeof primary !== \"string\") return undefined;\n const trimmed = primary.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nfunction readPathValue(obj: unknown, path: readonly string[]): unknown {\n let cur: unknown = obj;\n for (const key of path) {\n if (!cur || typeof cur !== \"object\") return undefined;\n cur = (cur as Record<string, unknown>)[key];\n }\n return cur;\n}\n\nfunction readString(value: unknown): string | undefined {\n if (typeof value !== \"string\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n}\n\nexport function inferCompactionProvider(model: string): string {\n const trimmed = model.trim();\n if (!trimmed) return \"unknown\";\n if (trimmed.includes(\"/\")) return (trimmed.split(\"/\")[0] ?? \"unknown\").trim().toLowerCase() || \"unknown\";\n const lower = trimmed.toLowerCase();\n if (lower.startsWith(\"minimax-\")) return \"minimax\";\n if (lower.startsWith(\"gemini-\")) return \"google\";\n if (lower.startsWith(\"claude-\")) return \"anthropic\";\n if (lower.startsWith(\"gpt-\") || /^o[0-9]/.test(lower)) return \"openai\";\n return \"unknown\";\n}\n\nexport function resolveCompactionModelSelection(\n root: Record<string, unknown> | undefined,\n opts?: { fallbackPolicy?: CompactionFallbackPolicy; defaultModel?: string },\n): CompactionModelSelection {\n const fallbackPolicy = opts?.fallbackPolicy ?? \"default-mini\";\n const defaultModel = opts?.defaultModel?.trim() || DEFAULT_COMPACTION_MODEL;\n const defaults = readAgentsDefaults(root);\n const unsupportedPerAgentCompactionPaths = collectUnsupportedPerAgentCompactionPaths(root);\n\n const defaultsCompaction = readCompactionModelFromAgentBlock(defaults);\n if (defaultsCompaction) {\n return {\n model: defaultsCompaction,\n provider: inferCompactionProvider(defaultsCompaction),\n reason: \"agents.defaults.compaction.model explicitly set\",\n inherited: false,\n unsupportedPerAgentCompactionPaths,\n };\n }\n\n if (fallbackPolicy === \"inherit-agent-primary\" || fallbackPolicy === \"inherit-defaults-primary\") {\n const defaultsPrimary = readPrimaryModelFromAgentBlock(defaults);\n if (defaultsPrimary) {\n return {\n model: defaultsPrimary,\n provider: inferCompactionProvider(defaultsPrimary),\n reason: \"inherited from agents.defaults.model.primary (compaction model unset)\",\n inherited: true,\n unsupportedPerAgentCompactionPaths,\n };\n }\n return {\n model: \"\",\n provider: \"unknown\",\n reason:\n \"agents.defaults.compaction.model and agents.defaults.model.primary unset; OpenClaw compaction may follow the active session chat model\",\n inherited: false,\n unsupportedPerAgentCompactionPaths,\n routingUnknownFromConfig: true,\n };\n }\n\n return {\n model: defaultModel,\n provider: inferCompactionProvider(defaultModel),\n reason: \"default mini compaction model (compaction model unset)\",\n inherited: false,\n unsupportedPerAgentCompactionPaths,\n };\n}\n\nfunction extractCompactionHookIdentityFromSource(\n sourceObj: unknown,\n sourceName: string,\n): CompactionHookIdentity | null {\n const agentPaths: string[][] = [\n [\"agentId\"],\n [\"agent\", \"id\"],\n [\"context\", \"agentId\"],\n [\"metadata\", \"agentId\"],\n [\"metadata\", \"agent\", \"id\"],\n [\"payload\", \"agentId\"],\n [\"session\", \"agentId\"],\n [\"ctx\", \"agentId\"],\n ];\n const sessionPaths: string[][] = [\n [\"sessionId\"],\n [\"session\", \"id\"],\n [\"context\", \"sessionId\"],\n [\"metadata\", \"sessionId\"],\n [\"payload\", \"sessionId\"],\n [\"ctx\", \"sessionId\"],\n ];\n let agentId: string | undefined;\n let sessionId: string | undefined;\n for (const path of agentPaths) {\n agentId = readString(readPathValue(sourceObj, path));\n if (agentId) break;\n }\n for (const path of sessionPaths) {\n sessionId = readString(readPathValue(sourceObj, path));\n if (sessionId) break;\n }\n if (!agentId && !sessionId) return null;\n return { agentId, sessionId, source: sourceName };\n}\n\n/**\n * Best-effort extraction of hook identity fields used to contextualize fallback watchdog warnings.\n * Returns null when neither agent nor session metadata is available.\n */\nexport function resolveCompactionHookIdentity(\n event: unknown,\n hookCtx?: unknown,\n apiContext?: unknown,\n): CompactionHookIdentity | null {\n const candidates = [\n extractCompactionHookIdentityFromSource(event, \"event\"),\n extractCompactionHookIdentityFromSource(hookCtx, \"hookCtx\"),\n extractCompactionHookIdentityFromSource(apiContext, \"api.context\"),\n ].filter(Boolean) as CompactionHookIdentity[];\n if (candidates.length === 0) return null;\n\n const merged: CompactionHookIdentity = {\n source: candidates.map((c) => c.source).join(\"+\"),\n };\n for (const candidate of candidates) {\n if (!merged.agentId && candidate.agentId) merged.agentId = candidate.agentId;\n if (!merged.sessionId && candidate.sessionId) merged.sessionId = candidate.sessionId;\n }\n return merged;\n}\n\nexport function describeCompactionFallbackScope(identity: CompactionHookIdentity | null | undefined): string {\n const agentId = identity?.agentId?.trim() || \"unknown\";\n const sessionId = identity?.sessionId?.trim() || \"unknown\";\n const source = identity?.source?.trim() || \"unavailable\";\n const scope =\n agentId === \"unknown\"\n ? \"unknown agent context; fallback uses global defaults only\"\n : agentId === \"main\"\n ? \"main agent context; fallback uses global defaults only\"\n : `non-main agent context (${agentId}); fallback uses global defaults only`;\n return `agent=${agentId}, session=${sessionId}, source=${source}; ${scope}`;\n}\n\nfunction isExplicitMiniOrNanoModel(model: string): boolean {\n const lower = (model.split(\"/\").pop() ?? model).toLowerCase();\n return /\\bmini\\b|\\bnano\\b|\\blite\\b|\\bhaiku\\b/.test(lower);\n}\n\nfunction isMiniMaxM27(model: string): boolean {\n const lower = (model.split(\"/\").pop() ?? model).toLowerCase();\n return /minimax[-_]?m2\\.7(?:[-_]?high[-_]?speed)?/.test(lower);\n}\n\nfunction isMiniMaxFamilyModel(model: string): boolean {\n const lower = model.toLowerCase();\n return lower.includes(\"minimax\");\n}\n\nfunction matchesHighTierCompactionFamily(model: string): string | null {\n const lower = (model.split(\"/\").pop() ?? model).toLowerCase();\n\n // Check for gpt-5.4-pro first (most specific)\n if (/\\bgpt-5\\.4-pro\\b/.test(lower)) return \"gpt-5.4-pro high-tier model\";\n // Then check for broader GPT-5 class models\n if (/\\bgpt-5\\b|\\bgpt-5\\.\\d+\\b/.test(lower)) return \"GPT-5 class model\";\n if (/\\bpro\\b|\\bfull\\b|high[-_]?tier|\\bheavy\\b|\\bultra\\b|\\blarge\\b/.test(lower)) {\n return \"pro/full/high-tier model keyword match\";\n }\n return null;\n}\n\nfunction parseProviderAndModelFromText(raw: string): { provider?: string; model?: string } | null {\n const modelMatch = raw.match(/\\bmodel\\s*[=:]\\s*([a-z0-9._:/-]+)/i);\n if (!modelMatch?.[1]) return null;\n const model = modelMatch[1].trim();\n if (!model) return null;\n const providerMatch = raw.match(/\\bprovider\\s*[=:]\\s*([a-z0-9._-]+)/i);\n const provider = providerMatch?.[1]?.trim();\n return { provider, model };\n}\n\nfunction extractCompactionModelMetadataFromSource(\n sourceObj: unknown,\n sourceName: string,\n): CompactionHookModelMetadata | null {\n const candidatePaths: Array<{ modelPath: string[]; providerPath?: string[] }> = [\n { modelPath: [\"compaction\", \"model\"], providerPath: [\"compaction\", \"provider\"] },\n { modelPath: [\"metadata\", \"compaction\", \"model\"], providerPath: [\"metadata\", \"compaction\", \"provider\"] },\n { modelPath: [\"context\", \"compaction\", \"model\"], providerPath: [\"context\", \"compaction\", \"provider\"] },\n { modelPath: [\"payload\", \"compaction\", \"model\"], providerPath: [\"payload\", \"compaction\", \"provider\"] },\n { modelPath: [\"logMetadata\", \"compaction\", \"model\"], providerPath: [\"logMetadata\", \"compaction\", \"provider\"] },\n { modelPath: [\"resolvedCompactionModel\"], providerPath: [\"resolvedCompactionProvider\"] },\n { modelPath: [\"selectedCompactionModel\"], providerPath: [\"selectedCompactionProvider\"] },\n { modelPath: [\"compactionModel\"], providerPath: [\"compactionProvider\"] },\n { modelPath: [\"compaction_model\"], providerPath: [\"compaction_provider\"] },\n { modelPath: [\"model\"], providerPath: [\"provider\"] },\n ];\n\n for (const candidate of candidatePaths) {\n const model = readString(readPathValue(sourceObj, candidate.modelPath));\n if (!model) continue;\n const provider =\n readString(candidate.providerPath ? readPathValue(sourceObj, candidate.providerPath) : undefined) ??\n inferCompactionProvider(model);\n return {\n model,\n provider,\n source: `${sourceName}.${candidate.modelPath.join(\".\")}`,\n };\n }\n\n const textPaths: string[][] = [[\"metadata\"], [\"logMetadata\"], [\"context\"], [\"details\"]];\n for (const textPath of textPaths) {\n const raw = readString(readPathValue(sourceObj, textPath));\n if (!raw) continue;\n const parsed = parseProviderAndModelFromText(raw);\n if (!parsed?.model) continue;\n return {\n model: parsed.model,\n provider: (parsed.provider ?? inferCompactionProvider(parsed.model)).toLowerCase(),\n source: `${sourceName}.${textPath.join(\".\")} (text)`,\n };\n }\n\n return null;\n}\n\n/**\n * Best-effort extraction of compaction provider/model from hook payloads.\n * Returns null when runtime does not expose compaction model metadata.\n */\nexport function resolveCompactionHookModelMetadata(\n event: unknown,\n hookCtx?: unknown,\n): CompactionHookModelMetadata | null {\n const eventCandidate = extractCompactionModelMetadataFromSource(event, \"event\");\n if (eventCandidate) return eventCandidate;\n const hookCandidate = extractCompactionModelMetadataFromSource(hookCtx, \"hookCtx\");\n if (hookCandidate) return hookCandidate;\n return null;\n}\n\n/**\n * Explain whether a compaction model is cost-safe. Policy:\n * - allow MiniMax M2.7 / M2.7-highspeed\n * - allow explicit mini/nano\n * - allow local ollama models\n * - flag high-tier families and unknown non-mini/non-nano/non-MiniMax models\n */\nexport function classifyCompactionModelStrength(model: string): CompactionModelStrength {\n const normalizedModel = model.trim();\n const lowerTail = (normalizedModel.split(\"/\").pop() ?? normalizedModel).toLowerCase();\n const provider = inferCompactionProvider(normalizedModel);\n\n if (!normalizedModel) {\n return {\n tooStrong: false,\n reason: \"model metadata unavailable\",\n provider,\n model: normalizedModel,\n };\n }\n if (provider === \"ollama\") {\n return {\n tooStrong: false,\n reason: \"local ollama model (no remote LLM cost)\",\n provider,\n model: normalizedModel,\n };\n }\n // Explicit denylist families from incident request.\n // Exclude o3-mini and o3-nano (allowlisted as legitimate cheap models)\n if (/\\bo3\\b/i.test(lowerTail) && !/o3-mini|o3-nano/i.test(lowerTail)) {\n return {\n tooStrong: true,\n reason: \"o3 class model\",\n provider,\n model: normalizedModel,\n };\n }\n if (/\\bsonnet\\b|\\bopus\\b/.test(lowerTail)) {\n return {\n tooStrong: true,\n reason: \"Claude Sonnet/Opus class model\",\n provider,\n model: normalizedModel,\n };\n }\n if (isMiniMaxM27(normalizedModel)) {\n return {\n tooStrong: false,\n reason: \"MiniMax M2.7 allowlist\",\n provider,\n model: normalizedModel,\n };\n }\n if (isExplicitMiniOrNanoModel(normalizedModel)) {\n return {\n tooStrong: false,\n reason: \"mini/nano allowlist\",\n provider,\n model: normalizedModel,\n };\n }\n\n const explicitHighTier = matchesHighTierCompactionFamily(normalizedModel);\n if (explicitHighTier) {\n return {\n tooStrong: true,\n reason: explicitHighTier,\n provider,\n model: normalizedModel,\n };\n }\n\n if (isHeavyModel(normalizedModel)) {\n return {\n tooStrong: true,\n reason: \"heavy-tier model classification\",\n provider,\n model: normalizedModel,\n };\n }\n\n if (!isMiniMaxFamilyModel(normalizedModel)) {\n return {\n tooStrong: true,\n reason: \"unknown non-mini/non-nano/non-MiniMax model\",\n provider,\n model: normalizedModel,\n };\n }\n\n return {\n tooStrong: false,\n reason: \"MiniMax provider allowlist\",\n provider,\n model: normalizedModel,\n };\n}\n\n/**\n * Builds a user-visible watchdog warning string when a compaction model is too strong.\n */\nexport function buildCompactionWatchdogAlert(opts: {\n stage: \"before_compaction\" | \"after_compaction\" | \"verify\";\n model: string;\n provider?: string;\n source?: string;\n}): string | null {\n const assessment = classifyCompactionModelStrength(opts.model);\n if (!assessment.tooStrong) return null;\n const provider = opts.provider?.trim() || assessment.provider;\n const sourcePart = opts.source ? `, source=${opts.source}` : \"\";\n return `memory-hybrid: compaction model watchdog alert (${opts.stage}) — provider=${provider}, model=${assessment.model}, reason=${assessment.reason}${sourcePart}. Prefer mini/nano compaction routing (recommended default: ${DEFAULT_COMPACTION_MODEL}).`;\n}\n\n/**\n * Cost watchdog classification for compaction model safety.\n * Treat only explicit mini-safe allowlist models as safe:\n * - minimax/* (current plugin default family)\n * - ollama/* (local/offline)\n * - any explicit *mini* or *nano* model name\n *\n * Everything else is treated as stronger-than-mini to match warning wording.\n */\nexport function isCompactionModelTooStrong(model: string): boolean {\n return classifyCompactionModelStrength(model).tooStrong;\n}\n\nexport function buildCompactionModelWatchdogWarning(\n selection: CompactionModelSelection,\n opts?: { context?: CompactionWatchdogContext; recommendedModel?: string },\n): string | null {\n if (selection.routingUnknownFromConfig) return null;\n if (!isCompactionModelTooStrong(selection.model)) return null;\n const recommendedModel = opts?.recommendedModel?.trim() || DEFAULT_COMPACTION_MODEL;\n const contextPrefix = opts?.context ? `${opts.context}: ` : \"\";\n return `${contextPrefix}compaction routing uses a stronger-than-mini model (provider=${selection.provider}, model=${selection.model}, reason=${selection.reason}). Set agents.defaults.compaction.model to a mini/nano model such as ${recommendedModel}.`;\n}\n\nexport function buildUnsupportedPerAgentCompactionWarning(\n selection: CompactionModelSelection,\n opts?: { context?: CompactionWatchdogContext },\n): string | null {\n if (selection.unsupportedPerAgentCompactionPaths.length === 0) return null;\n const contextPrefix = opts?.context ? `${opts.context}: ` : \"\";\n const paths = selection.unsupportedPerAgentCompactionPaths.slice(0, 3).join(\", \");\n const extraCount = selection.unsupportedPerAgentCompactionPaths.length - 3;\n const moreSuffix = extraCount > 0 ? ` (+${extraCount} more)` : \"\";\n return `${contextPrefix}unsupported per-agent compaction key(s) detected (${paths}${moreSuffix}). OpenClaw core currently resolves compaction from agents.defaults.compaction.model only; move compaction overrides to agents.defaults.compaction.model.`;\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"consolidation-controls.js","names":[],"sources":["../../utils/consolidation-controls.ts"],"sourcesContent":["import type { DecayClass } from \"../config.js\";\n\nexport const CONSOLIDATED_FACT_DECAY_CLASS: DecayClass = \"durable\";\nconst CONSOLIDATED_RETRIEVAL_SCORE_MULTIPLIER = 0.85;\n\ntype ConsolidatedFactLike = {\n source?: string | null;\n key?: string | null;\n tags?: string[] | null;\n};\n\nexport function isConsolidatedDerivedFact(entry: ConsolidatedFactLike): boolean {\n const source = (entry.source ?? \"\").trim().toLowerCase();\n if (source === \"consolidation\" || source === \"dream-cycle\") return true;\n\n const key = (entry.key ?? \"\").trim().toLowerCase();\n if (key === \"consolidated\") return true;\n\n return (entry.tags ?? []).some((tag) => tag.trim().toLowerCase() === \"consolidated\");\n}\n\nexport function applyConsolidationRetrievalControls(score: number, entry: ConsolidatedFactLike): number {\n return isConsolidatedDerivedFact(entry) ? score * CONSOLIDATED_RETRIEVAL_SCORE_MULTIPLIER : score;\n}\n"],"mappings":";AAEA,MAAa,gCAA4C;AACzD,MAAM,0CAA0C;AAQhD,SAAgB,0BAA0B,OAAsC;CAC9E,MAAM,UAAU,MAAM,UAAU,IAAI,
|
|
1
|
+
{"version":3,"file":"consolidation-controls.js","names":[],"sources":["../../utils/consolidation-controls.ts"],"sourcesContent":["import type { DecayClass } from \"../config.js\";\n\nexport const CONSOLIDATED_FACT_DECAY_CLASS: DecayClass = \"durable\";\nconst CONSOLIDATED_RETRIEVAL_SCORE_MULTIPLIER = 0.85;\n\ntype ConsolidatedFactLike = {\n source?: string | null;\n key?: string | null;\n tags?: string[] | null;\n};\n\nexport function isConsolidatedDerivedFact(entry: ConsolidatedFactLike): boolean {\n const source = (entry.source ?? \"\").trim().toLowerCase();\n if (source === \"consolidation\" || source === \"dream-cycle\") return true;\n\n const key = (entry.key ?? \"\").trim().toLowerCase();\n if (key === \"consolidated\") return true;\n\n return (entry.tags ?? []).some((tag) => tag.trim().toLowerCase() === \"consolidated\");\n}\n\nexport function applyConsolidationRetrievalControls(score: number, entry: ConsolidatedFactLike): number {\n return isConsolidatedDerivedFact(entry) ? score * CONSOLIDATED_RETRIEVAL_SCORE_MULTIPLIER : score;\n}\n"],"mappings":";AAEA,MAAa,gCAA4C;AACzD,MAAM,0CAA0C;AAQhD,SAAgB,0BAA0B,OAAsC;CAC9E,MAAM,UAAU,MAAM,UAAU,IAAI,KAAK,EAAE,YAAY;CACvD,IAAI,WAAW,mBAAmB,WAAW,eAAe,OAAO;CAGnE,KADa,MAAM,OAAO,IAAI,KAAK,EAAE,YAC/B,MAAM,gBAAgB,OAAO;CAEnC,QAAQ,MAAM,QAAQ,CAAC,GAAG,MAAM,QAAQ,IAAI,KAAK,EAAE,YAAY,MAAM,cAAc;AACrF;AAEA,SAAgB,oCAAoC,OAAe,OAAqC;CACtG,OAAO,0BAA0B,KAAK,IAAI,QAAQ,0CAA0C;AAC9F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"constants.js","names":[],"sources":["../../utils/constants.ts"],"sourcesContent":["import { getEnv } from \"./env-manager.js\";\n/**\n * Shared constants to avoid magic numbers across the plugin.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Plugin identifier used across the codebase. */\nexport const PLUGIN_ID = \"openclaw-hybrid-memory\";\n\n/** Path to marker file written by config-mode/config-set; cleared when gateway loads plugin. */\nexport function getRestartPendingPath(): string {\n return join(homedir(), \".openclaw\", \".restart-pending.openclaw-hybrid-memory\");\n}\n\n/** Max characters for a single fact in reflection/consolidation prompts. */\nexport const REFLECTION_MAX_FACT_LENGTH = 300;\n/** Max facts per category in reflection prompt. */\nexport const REFLECTION_MAX_FACTS_PER_CATEGORY = 50;\n/** Max characters for credential notes in vault (truncation). */\nexport const CREDENTIAL_NOTES_MAX_CHARS = 500;\n/** Max characters for credential URL metadata (truncation). */\nexport const CREDENTIAL_URL_MAX_CHARS = 2000;\n\n/** Max characters for a single fact shown in LLM prompts (classify, consolidate). */\nexport const FACT_PREVIEW_MAX_CHARS = 300;\n/** Max characters for a new fact candidate in classify prompt. */\nexport const CLASSIFY_CANDIDATE_MAX_CHARS = 500;\n/** Default minimum vector similarity score for search. */\nexport const DEFAULT_MIN_SCORE = 0.3;\n/** Default importance for CLI-stored facts. */\nexport const CLI_STORE_IMPORTANCE = 0.7;\n/** Default importance for daily-scan/batch-stored facts. */\nexport const BATCH_STORE_IMPORTANCE = 0.8;\n/** Default importance for reflection/pattern/meta/rule facts. */\nexport const REFLECTION_IMPORTANCE = 0.9;\n/** Max characters for merged text in consolidation. */\nexport const CONSOLIDATION_MERGE_MAX_CHARS = 5000;\n/** Max characters for reflection pattern text. */\nexport const REFLECTION_PATTERN_MAX_CHARS = 500;\n/** Max characters for reflection meta-pattern text. */\nexport const REFLECTION_META_MAX_CHARS = 300;\n/** Cosine similarity threshold for deduplicating reflection patterns. */\nexport const REFLECTION_DEDUPE_THRESHOLD = 0.85;\n/** Cosine similarity threshold for distillation/ingest deduplication. */\nexport const DISTILL_DEDUP_THRESHOLD = 0.85;\n/** LLM temperature for reflection/rules prompts. */\nexport const REFLECTION_TEMPERATURE = 0.2;\n/** Batch throttle delay (ms) between embedding batches. */\nexport const BATCH_THROTTLE_MS = 200;\n/** SQLite busy timeout (ms). Mitigates SQLITE_BUSY under concurrent writers (#875). */\nexport const SQLITE_BUSY_TIMEOUT_MS = 30_000;\n/**\n * Max OR-joined FTS terms for `FactsDB.search` interactive fast path.\n * Long prompts expand to huge MATCH expressions; capping keeps FTS5 work bounded (WhatsApp / gateway latency).\n */\nexport const INTERACTIVE_FTS_MAX_OR_TERMS = 16;\n/** Seconds per day. */\nexport const SECONDS_PER_DAY = 86400;\n\n/** Max tokens for HOT tier (always-loaded session context). */\nconst _HOT_TIER_MAX_TOKENS = 2000;\n\n// Python Bridge constants\n/** Maximum number of retries for Python bridge startup. */\nexport const PYTHON_BRIDGE_MAX_RETRIES = 3;\n/** Timeout for Python bridge ping health check (ms). */\nexport const PYTHON_BRIDGE_PING_TIMEOUT_MS = 5_000;\n/** Grace period for Python bridge shutdown (ms). */\nexport const PYTHON_BRIDGE_SHUTDOWN_WAIT_MS = 2_000;\n\n// Ollama embedding provider constants\n/** Maximum consecutive failures before circuit breaker opens. */\nexport const OLLAMA_MAX_FAILS = 3;\n/** Circuit breaker cooldown period after max failures (ms). */\nexport const OLLAMA_COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes\n\n// LLM chat completion constants\n/** Default timeout for chat completion requests (ms). */\nexport const DEFAULT_CHAT_TIMEOUT_MS = 45_000;\n\n/** Daily narrative prompts aggregate many events — allow longer than default chat timeout (#935, #936). */\nexport const NARRATIVE_CHAT_TIMEOUT_MS = 120_000;\n\n// VectorDB constants\n/** Threshold for warning about consecutive optimize failures. */\nexport const VECTORDB_OPTIMIZE_FAILURE_WARN_THRESHOLD = 3;\n/** Retryable LanceDB commit conflicts: max write retries before surfacing failure. */\nexport const VECTORDB_WRITE_CONFLICT_MAX_RETRIES = 4;\n/** Retryable LanceDB commit conflicts: initial backoff delay (ms). */\nexport const VECTORDB_WRITE_CONFLICT_BASE_BACKOFF_MS = 50;\n/** Retryable LanceDB commit conflicts: cap backoff delay (ms). */\nexport const VECTORDB_WRITE_CONFLICT_MAX_BACKOFF_MS = 1000;\n/** Maximum retries for VectorDB initialization during concurrent re-registration. */\nexport const VECTORDB_INIT_MAX_RETRIES = 10;\n/** Delay between VectorDB initialization retries (ms). */\nexport const VECTORDB_INIT_RETRY_DELAY_MS = 500;\n\n/**\n * UUID v1–v5 validation regex (case-insensitive).\n * Used as the security boundary before LanceDB string interpolation and\n * as the user-facing guard in memory_forget.\n * Centralised here to avoid drift between vector-db.ts and memory-tools.ts.\n */\nexport const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n/**\n * Substring of the LanceDB error thrown on vector-dimension mismatch.\n * Used to suppress known schema errors from GlitchTip reporting.\n */\nexport const LANCE_NO_VECTOR_COL_MSG = \"No vector column found\";\n\n/** Timeout (ms) for vectorDB reader drain. */\nexport const VECTORDB_READER_DRAIN_TIMEOUT_MS = 30_000;\n\n/** Timeout (ms) for VectorDB initialization (connect + table open). */\nexport const VECTORDB_INIT_TIMEOUT_MS = 60_000;\n\n/** Timeout (ms) for VectorDB count query. */\nexport const VECTORDB_COUNT_TIMEOUT_MS = 30_000;\n\n/** Timeout (ms) for VectorDB getVectorsByFactIds batch query. */\nexport const VECTORDB_GET_VECTORS_TIMEOUT_MS = 45_000;\n\n/** Timeout (ms) for reflection dedupe corpus loading (all batches).\n * Set to 900s (15 minutes) to handle large corpora (e.g., 10,687 patterns) with API rate limiting.\n * With 200ms pause per 20-item batch, this allows ~7,500 API-embedded patterns plus overhead.\n */\nexport const REFLECTION_DEDUPE_LOAD_TIMEOUT_MS = 900_000;\n\n/** Maximum rows to process per dream-cycle reflection dedupe run (prevents unbounded backlog work). */\nexport const REFLECTION_DEDUPE_MAX_ROWS_PER_RUN = 2000;\n\n/** Circuit breaker: max consecutive 429s before pausing reflection dedupe for this run. */\nexport const REFLECTION_DEDUPE_429_CIRCUIT_BREAKER_THRESHOLD = 10;\n\n/** Circuit breaker: backoff delay (ms) after hitting 429 threshold. */\nexport const REFLECTION_DEDUPE_429_CIRCUIT_BREAKER_BACKOFF_MS = 30_000;\n\n/** LanceDB semantic search: maximum rows to request from vectorSearch (#882). */\nexport const LANCE_VECTOR_SEARCH_MAX_LIMIT = 1000;\n\n/** Default session transcript file suffix (override with OPENCLAW_SESSION_LOG_SUFFIX, e.g. `.jsonl`). */\nexport function getSessionLogFileSuffix(): string {\n const raw = getEnv(\"OPENCLAW_SESSION_LOG_SUFFIX\")?.trim();\n if (!raw) return \".jsonl\";\n return raw.startsWith(\".\") ? raw : `.${raw}`;\n}\n\n/**\n * Regex pattern matching action verbs in skill examples and validation.\n * Used by skill-crystallizer to detect when example goals need action-verb prefix\n * and by skill-validator to verify examples contain concrete actions.\n * Centralized to prevent crystallizer-validator desync (#1421).\n */\nexport const ACTION_VERB_PATTERN =\n /\\b(?:use|run|create|fix|validate|apply|checks?|install|deploy(?:ment)?|open|read|write|execute|verify|follow|review|summari[sz]e|analyse|analyze|test|investigate|configure|build|setup|set up|update|delete|remove|add|generate|migrate|publish|debug|scan|format|start|stop|restart|connect)\\b/i;\n"],"mappings":";;;;;;;;AASA,MAAa,YAAY;;AAGzB,SAAgB,wBAAgC;CAC9C,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"constants.js","names":[],"sources":["../../utils/constants.ts"],"sourcesContent":["import { getEnv } from \"./env-manager.js\";\n/**\n * Shared constants to avoid magic numbers across the plugin.\n */\n\nimport { homedir } from \"node:os\";\nimport { join } from \"node:path\";\n\n/** Plugin identifier used across the codebase. */\nexport const PLUGIN_ID = \"openclaw-hybrid-memory\";\n\n/** Path to marker file written by config-mode/config-set; cleared when gateway loads plugin. */\nexport function getRestartPendingPath(): string {\n return join(homedir(), \".openclaw\", \".restart-pending.openclaw-hybrid-memory\");\n}\n\n/** Max characters for a single fact in reflection/consolidation prompts. */\nexport const REFLECTION_MAX_FACT_LENGTH = 300;\n/** Max facts per category in reflection prompt. */\nexport const REFLECTION_MAX_FACTS_PER_CATEGORY = 50;\n/** Max characters for credential notes in vault (truncation). */\nexport const CREDENTIAL_NOTES_MAX_CHARS = 500;\n/** Max characters for credential URL metadata (truncation). */\nexport const CREDENTIAL_URL_MAX_CHARS = 2000;\n\n/** Max characters for a single fact shown in LLM prompts (classify, consolidate). */\nexport const FACT_PREVIEW_MAX_CHARS = 300;\n/** Max characters for a new fact candidate in classify prompt. */\nexport const CLASSIFY_CANDIDATE_MAX_CHARS = 500;\n/** Default minimum vector similarity score for search. */\nexport const DEFAULT_MIN_SCORE = 0.3;\n/** Default importance for CLI-stored facts. */\nexport const CLI_STORE_IMPORTANCE = 0.7;\n/** Default importance for daily-scan/batch-stored facts. */\nexport const BATCH_STORE_IMPORTANCE = 0.8;\n/** Default importance for reflection/pattern/meta/rule facts. */\nexport const REFLECTION_IMPORTANCE = 0.9;\n/** Max characters for merged text in consolidation. */\nexport const CONSOLIDATION_MERGE_MAX_CHARS = 5000;\n/** Max characters for reflection pattern text. */\nexport const REFLECTION_PATTERN_MAX_CHARS = 500;\n/** Max characters for reflection meta-pattern text. */\nexport const REFLECTION_META_MAX_CHARS = 300;\n/** Cosine similarity threshold for deduplicating reflection patterns. */\nexport const REFLECTION_DEDUPE_THRESHOLD = 0.85;\n/** Cosine similarity threshold for distillation/ingest deduplication. */\nexport const DISTILL_DEDUP_THRESHOLD = 0.85;\n/** LLM temperature for reflection/rules prompts. */\nexport const REFLECTION_TEMPERATURE = 0.2;\n/** Batch throttle delay (ms) between embedding batches. */\nexport const BATCH_THROTTLE_MS = 200;\n/** SQLite busy timeout (ms). Mitigates SQLITE_BUSY under concurrent writers (#875). */\nexport const SQLITE_BUSY_TIMEOUT_MS = 30_000;\n/**\n * Max OR-joined FTS terms for `FactsDB.search` interactive fast path.\n * Long prompts expand to huge MATCH expressions; capping keeps FTS5 work bounded (WhatsApp / gateway latency).\n */\nexport const INTERACTIVE_FTS_MAX_OR_TERMS = 16;\n/** Seconds per day. */\nexport const SECONDS_PER_DAY = 86400;\n\n/** Max tokens for HOT tier (always-loaded session context). */\nconst _HOT_TIER_MAX_TOKENS = 2000;\n\n// Python Bridge constants\n/** Maximum number of retries for Python bridge startup. */\nexport const PYTHON_BRIDGE_MAX_RETRIES = 3;\n/** Timeout for Python bridge ping health check (ms). */\nexport const PYTHON_BRIDGE_PING_TIMEOUT_MS = 5_000;\n/** Grace period for Python bridge shutdown (ms). */\nexport const PYTHON_BRIDGE_SHUTDOWN_WAIT_MS = 2_000;\n\n// Ollama embedding provider constants\n/** Maximum consecutive failures before circuit breaker opens. */\nexport const OLLAMA_MAX_FAILS = 3;\n/** Circuit breaker cooldown period after max failures (ms). */\nexport const OLLAMA_COOLDOWN_MS = 5 * 60 * 1000; // 5 minutes\n\n// LLM chat completion constants\n/** Default timeout for chat completion requests (ms). */\nexport const DEFAULT_CHAT_TIMEOUT_MS = 45_000;\n\n/** Daily narrative prompts aggregate many events — allow longer than default chat timeout (#935, #936). */\nexport const NARRATIVE_CHAT_TIMEOUT_MS = 120_000;\n\n// VectorDB constants\n/** Threshold for warning about consecutive optimize failures. */\nexport const VECTORDB_OPTIMIZE_FAILURE_WARN_THRESHOLD = 3;\n/** Retryable LanceDB commit conflicts: max write retries before surfacing failure. */\nexport const VECTORDB_WRITE_CONFLICT_MAX_RETRIES = 4;\n/** Retryable LanceDB commit conflicts: initial backoff delay (ms). */\nexport const VECTORDB_WRITE_CONFLICT_BASE_BACKOFF_MS = 50;\n/** Retryable LanceDB commit conflicts: cap backoff delay (ms). */\nexport const VECTORDB_WRITE_CONFLICT_MAX_BACKOFF_MS = 1000;\n/** Maximum retries for VectorDB initialization during concurrent re-registration. */\nexport const VECTORDB_INIT_MAX_RETRIES = 10;\n/** Delay between VectorDB initialization retries (ms). */\nexport const VECTORDB_INIT_RETRY_DELAY_MS = 500;\n\n/**\n * UUID v1–v5 validation regex (case-insensitive).\n * Used as the security boundary before LanceDB string interpolation and\n * as the user-facing guard in memory_forget.\n * Centralised here to avoid drift between vector-db.ts and memory-tools.ts.\n */\nexport const UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i;\n\n/**\n * Substring of the LanceDB error thrown on vector-dimension mismatch.\n * Used to suppress known schema errors from GlitchTip reporting.\n */\nexport const LANCE_NO_VECTOR_COL_MSG = \"No vector column found\";\n\n/** Timeout (ms) for vectorDB reader drain. */\nexport const VECTORDB_READER_DRAIN_TIMEOUT_MS = 30_000;\n\n/** Timeout (ms) for VectorDB initialization (connect + table open). */\nexport const VECTORDB_INIT_TIMEOUT_MS = 60_000;\n\n/** Timeout (ms) for VectorDB count query. */\nexport const VECTORDB_COUNT_TIMEOUT_MS = 30_000;\n\n/** Timeout (ms) for VectorDB getVectorsByFactIds batch query. */\nexport const VECTORDB_GET_VECTORS_TIMEOUT_MS = 45_000;\n\n/** Timeout (ms) for reflection dedupe corpus loading (all batches).\n * Set to 900s (15 minutes) to handle large corpora (e.g., 10,687 patterns) with API rate limiting.\n * With 200ms pause per 20-item batch, this allows ~7,500 API-embedded patterns plus overhead.\n */\nexport const REFLECTION_DEDUPE_LOAD_TIMEOUT_MS = 900_000;\n\n/** Maximum rows to process per dream-cycle reflection dedupe run (prevents unbounded backlog work). */\nexport const REFLECTION_DEDUPE_MAX_ROWS_PER_RUN = 2000;\n\n/** Circuit breaker: max consecutive 429s before pausing reflection dedupe for this run. */\nexport const REFLECTION_DEDUPE_429_CIRCUIT_BREAKER_THRESHOLD = 10;\n\n/** Circuit breaker: backoff delay (ms) after hitting 429 threshold. */\nexport const REFLECTION_DEDUPE_429_CIRCUIT_BREAKER_BACKOFF_MS = 30_000;\n\n/** LanceDB semantic search: maximum rows to request from vectorSearch (#882). */\nexport const LANCE_VECTOR_SEARCH_MAX_LIMIT = 1000;\n\n/** Default session transcript file suffix (override with OPENCLAW_SESSION_LOG_SUFFIX, e.g. `.jsonl`). */\nexport function getSessionLogFileSuffix(): string {\n const raw = getEnv(\"OPENCLAW_SESSION_LOG_SUFFIX\")?.trim();\n if (!raw) return \".jsonl\";\n return raw.startsWith(\".\") ? raw : `.${raw}`;\n}\n\n/**\n * Regex pattern matching action verbs in skill examples and validation.\n * Used by skill-crystallizer to detect when example goals need action-verb prefix\n * and by skill-validator to verify examples contain concrete actions.\n * Centralized to prevent crystallizer-validator desync (#1421).\n */\nexport const ACTION_VERB_PATTERN =\n /\\b(?:use|run|create|fix|validate|apply|checks?|install|deploy(?:ment)?|open|read|write|execute|verify|follow|review|summari[sz]e|analyse|analyze|test|investigate|configure|build|setup|set up|update|delete|remove|add|generate|migrate|publish|debug|scan|format|start|stop|restart|connect)\\b/i;\n"],"mappings":";;;;;;;;AASA,MAAa,YAAY;;AAGzB,SAAgB,wBAAgC;CAC9C,OAAO,KAAK,QAAQ,GAAG,aAAa,yCAAyC;AAC/E;;AASA,MAAa,2BAA2B;;AASxC,MAAa,uBAAuB;;AAEpC,MAAa,yBAAyB;;AAEtC,MAAa,wBAAwB;;AAErC,MAAa,gCAAgC;;AAM7C,MAAa,8BAA8B;;AAE3C,MAAa,0BAA0B;;AAEvC,MAAa,yBAAyB;;AAItC,MAAa,yBAAyB;;AAOtC,MAAa,kBAAkB;;AAS/B,MAAa,gCAAgC;;AAE7C,MAAa,iCAAiC;;AAM9C,MAAa,qBAAqB,MAAS;;AAI3C,MAAa,0BAA0B;;AAGvC,MAAa,4BAA4B;;AAUzC,MAAa,yCAAyC;;;;;;;AAYtD,MAAa,aAAa;;;;;AAM1B,MAAa,0BAA0B;;AAGvC,MAAa,mCAAmC;;AAGhD,MAAa,2BAA2B;;AAGxC,MAAa,4BAA4B;;AAGzC,MAAa,kCAAkC;;;;;AAM/C,MAAa,oCAAoC;;AAGjD,MAAa,qCAAqC;;AAMlD,MAAa,mDAAmD;;AAGhE,MAAa,gCAAgC;;AAG7C,SAAgB,0BAAkC;CAChD,MAAM,MAAM,OAAO,6BAA6B,GAAG,KAAK;CACxD,IAAI,CAAC,KAAK,OAAO;CACjB,OAAO,IAAI,WAAW,GAAG,IAAI,MAAM,IAAI;AACzC;;;;;;;AAQA,MAAa,sBACX"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"date-detector.js","names":[],"sources":["../../utils/date-detector.ts"],"sourcesContent":["/**\n * Future-date detection for decay freeze protection (#144).\n *\n * Extracts the latest future date from a text string using regex-based\n * matching. No heavy NLP dependencies — only built-in Date arithmetic.\n *\n * Supported formats:\n * - ISO: \"2026-03-20\", \"2026-03-20T14:00:00\"\n * - Month-day: \"March 20\", \"Mar 20th\", \"20 March\"\n * - Relative: \"tomorrow\", \"next week\", \"next Tuesday\", \"next month\"\n * - Offset: \"in 3 days\", \"in 2 weeks\", \"in 1 month\", \"in 6 months\"\n *\n * Deliberately skipped (vague, non-actionable):\n * \"soon\", \"eventually\", \"later\", \"someday\", \"sometime\", \"at some point\"\n */\n\nimport type { FutureDateProtectionConfig } from \"../config.js\";\n\nexport type { FutureDateProtectionConfig };\n\nconst MONTH_MAP: Record<string, number> = {\n jan: 0,\n january: 0,\n feb: 1,\n february: 1,\n mar: 2,\n march: 2,\n apr: 3,\n april: 3,\n may: 4,\n jun: 5,\n june: 5,\n jul: 6,\n july: 6,\n aug: 7,\n august: 7,\n sep: 8,\n september: 8,\n oct: 9,\n october: 9,\n nov: 10,\n november: 10,\n dec: 11,\n december: 11,\n};\n\nconst WEEKDAY_MAP: Record<string, number> = {\n sunday: 0,\n sun: 0,\n monday: 1,\n mon: 1,\n tuesday: 2,\n tue: 2,\n wednesday: 3,\n wed: 3,\n thursday: 4,\n thu: 4,\n friday: 5,\n fri: 5,\n saturday: 6,\n sat: 6,\n};\n\n// Fix #4: hoist month-name pattern and regex source to module level so they\n// are computed once instead of being rebuilt on every extractCandidates call.\nconst MONTH_NAMES_PATTERN = Object.keys(MONTH_MAP)\n .filter(\n (k) =>\n k.length > 3 || [\"jan\", \"feb\", \"mar\", \"apr\", \"may\", \"jun\", \"jul\", \"aug\", \"sep\", \"oct\", \"nov\", \"dec\"].includes(k),\n )\n .map((k) => k.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"))\n .join(\"|\");\n\nconst MD_RE_SOURCE = `\\\\b(?:(${MONTH_NAMES_PATTERN})\\\\s+(\\\\d{1,2})(?:st|nd|rd|th)?|(\\\\d{1,2})(?:st|nd|rd|th)?\\\\s+(${MONTH_NAMES_PATTERN}))\\\\b`;\n\n/**\n * Try all date patterns against `text`. Return all candidate epoch-second\n * timestamps that are in the future relative to `nowMs`.\n */\nfunction extractCandidates(text: string, nowMs: number): number[] {\n const results: number[] = [];\n const now = new Date(nowMs);\n const todayMidnightMs = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());\n\n // -------------------------------------------------------------------------\n // 1. ISO date: \"2026-03-20\" or \"2026-03-20T14:00:00\"\n // -------------------------------------------------------------------------\n const isoRe = /\\b(\\d{4})-(\\d{2})-(\\d{2})(?:T\\d{2}:\\d{2}(?::\\d{2})?)?/g;\n let m: RegExpExecArray | null;\n while ((m = isoRe.exec(text)) !== null) {\n const y = Number.parseInt(m[1]!, 10);\n const moRaw = Number.parseInt(m[2]!, 10);\n const d = Number.parseInt(m[3]!, 10);\n // Fix #7: reject out-of-range month/day before Date.UTC to avoid silent normalization\n if (moRaw < 1 || moRaw > 12 || d < 1 || d > 31) continue;\n const mo = moRaw - 1;\n const ts = Date.UTC(y, mo, d);\n if (!Number.isNaN(ts)) {\n // Verify Date.UTC did not silently normalize (e.g. \"2026-02-30\" → March)\n const check = new Date(ts);\n if (check.getUTCMonth() !== mo || check.getUTCDate() !== d) continue;\n if (ts > todayMidnightMs) results.push(Math.floor(ts / 1000));\n }\n }\n\n // -------------------------------------------------------------------------\n // 2. Month-name day: \"March 20\", \"Mar 20th\", \"20 March\", \"20th March\"\n // Uses module-level MD_RE_SOURCE (Fix #4).\n // -------------------------------------------------------------------------\n const mdRe = new RegExp(MD_RE_SOURCE, \"gi\");\n while ((m = mdRe.exec(text)) !== null) {\n let monthStr: string;\n let dayNum: number;\n if (m[1] !== undefined && m[2] !== undefined) {\n // \"March 20\"\n monthStr = m[1].toLowerCase();\n dayNum = Number.parseInt(m[2], 10);\n } else {\n // \"20 March\"\n monthStr = m[4]?.toLowerCase();\n dayNum = Number.parseInt(m[3]!, 10);\n }\n const moIdx = MONTH_MAP[monthStr];\n if (moIdx === undefined) continue;\n // Fix #3: reject out-of-range day before Date.UTC to avoid silent normalization\n if (dayNum < 1 || dayNum > 31) continue;\n\n // Try current year first, then next year\n for (const yearOffset of [0, 1]) {\n const y = now.getUTCFullYear() + yearOffset;\n const ts = Date.UTC(y, moIdx, dayNum);\n if (!Number.isNaN(ts)) {\n // Verify no silent normalization (e.g. Feb 30 → March)\n const check = new Date(ts);\n if (check.getUTCMonth() !== moIdx || check.getUTCDate() !== dayNum) continue;\n if (ts > todayMidnightMs) {\n results.push(Math.floor(ts / 1000));\n break; // take the first future occurrence\n }\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // 3. \"tomorrow\"\n // -------------------------------------------------------------------------\n if (/\\btomorrow\\b/i.test(text)) {\n const ts = todayMidnightMs + 86400 * 1000;\n results.push(Math.floor(ts / 1000));\n }\n\n // -------------------------------------------------------------------------\n // 4. \"next <weekday>\": \"next Tuesday\", \"next Monday\"\n // -------------------------------------------------------------------------\n const nextWdRe =\n /\\bnext\\s+(sunday|monday|tuesday|wednesday|thursday|friday|saturday|sun|mon|tue|wed|thu|fri|sat)\\b/gi;\n while ((m = nextWdRe.exec(text)) !== null) {\n const targetWd = WEEKDAY_MAP[m[1]?.toLowerCase()];\n if (targetWd === undefined) continue;\n const currentWd = now.getUTCDay();\n let daysAhead = targetWd - currentWd;\n if (daysAhead <= 0) daysAhead += 7; // always at least 1 day ahead\n // \"next\" implies the week after the upcoming one when the day would otherwise be <7 days away\n if (daysAhead < 7) daysAhead += 7;\n const ts = todayMidnightMs + daysAhead * 86400 * 1000;\n results.push(Math.floor(ts / 1000));\n }\n\n // -------------------------------------------------------------------------\n // 5. \"next week\" → ~7 days, \"next month\" → ~30 days, \"next year\" → ~365 days\n // -------------------------------------------------------------------------\n if (/\\bnext\\s+week\\b/i.test(text)) {\n results.push(Math.floor((todayMidnightMs + 7 * 86400 * 1000) / 1000));\n }\n if (/\\bnext\\s+month\\b/i.test(text)) {\n results.push(Math.floor((todayMidnightMs + 30 * 86400 * 1000) / 1000));\n }\n if (/\\bnext\\s+year\\b/i.test(text)) {\n results.push(Math.floor((todayMidnightMs + 365 * 86400 * 1000) / 1000));\n }\n\n // -------------------------------------------------------------------------\n // 6. \"in N days/weeks/months\": \"in 3 days\", \"in 2 weeks\", \"in 1 month\"\n // -------------------------------------------------------------------------\n const inOffsetRe = /\\bin\\s+(\\d+)\\s+(day|days|week|weeks|month|months)\\b/gi;\n while ((m = inOffsetRe.exec(text)) !== null) {\n const n = Number.parseInt(m[1]!, 10);\n const unit = m[2]?.toLowerCase();\n let daysAhead = 0;\n if (unit === \"day\" || unit === \"days\") daysAhead = n;\n else if (unit === \"week\" || unit === \"weeks\") daysAhead = n * 7;\n else if (unit === \"month\" || unit === \"months\") daysAhead = n * 30;\n if (daysAhead > 0) {\n results.push(Math.floor((todayMidnightMs + daysAhead * 86400 * 1000) / 1000));\n }\n }\n\n return results;\n}\n\n/**\n * Detect the latest future date in `text`.\n *\n * @param text - The fact text to scan.\n * @param cfg - Future-date protection config.\n * @param nowMs - Current time in milliseconds (defaults to Date.now()). Override in tests.\n * @returns Epoch seconds of the freeze-until timestamp, or null if none found / not applicable.\n */\nexport function detectFutureDate(\n text: string,\n cfg: Pick<FutureDateProtectionConfig, \"enabled\" | \"maxFreezeDays\">,\n nowMs: number = Date.now(),\n): number | null {\n if (!cfg.enabled) return null;\n\n // Recurring events (\"every Monday\", \"weekly\", \"daily at 9am\") have no single\n // future deadline, so they should not trigger a decay freeze.\n if (/\\b(every|daily|weekly|monthly|yearly|recurring|biweekly|each)\\b/i.test(text)) return null;\n\n const candidates = extractCandidates(text, nowMs);\n if (candidates.length === 0) return null;\n\n // Take the latest future date\n const latest = Math.max(...candidates);\n // Fix #2: guard against NaN/non-finite (should not happen after extractCandidates validation,\n // but defend against future callers)\n if (!Number.isFinite(latest)) return null;\n\n // Enforce maxFreezeDays cap (0 = no limit)\n if (cfg.maxFreezeDays > 0) {\n const maxFreezeMs = nowMs + cfg.maxFreezeDays * 86400 * 1000;\n const maxFreezeSec = Math.floor(maxFreezeMs / 1000);\n return Math.min(latest, maxFreezeSec);\n }\n\n return latest;\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"date-detector.js","names":[],"sources":["../../utils/date-detector.ts"],"sourcesContent":["/**\n * Future-date detection for decay freeze protection (#144).\n *\n * Extracts the latest future date from a text string using regex-based\n * matching. No heavy NLP dependencies — only built-in Date arithmetic.\n *\n * Supported formats:\n * - ISO: \"2026-03-20\", \"2026-03-20T14:00:00\"\n * - Month-day: \"March 20\", \"Mar 20th\", \"20 March\"\n * - Relative: \"tomorrow\", \"next week\", \"next Tuesday\", \"next month\"\n * - Offset: \"in 3 days\", \"in 2 weeks\", \"in 1 month\", \"in 6 months\"\n *\n * Deliberately skipped (vague, non-actionable):\n * \"soon\", \"eventually\", \"later\", \"someday\", \"sometime\", \"at some point\"\n */\n\nimport type { FutureDateProtectionConfig } from \"../config.js\";\n\nexport type { FutureDateProtectionConfig };\n\nconst MONTH_MAP: Record<string, number> = {\n jan: 0,\n january: 0,\n feb: 1,\n february: 1,\n mar: 2,\n march: 2,\n apr: 3,\n april: 3,\n may: 4,\n jun: 5,\n june: 5,\n jul: 6,\n july: 6,\n aug: 7,\n august: 7,\n sep: 8,\n september: 8,\n oct: 9,\n october: 9,\n nov: 10,\n november: 10,\n dec: 11,\n december: 11,\n};\n\nconst WEEKDAY_MAP: Record<string, number> = {\n sunday: 0,\n sun: 0,\n monday: 1,\n mon: 1,\n tuesday: 2,\n tue: 2,\n wednesday: 3,\n wed: 3,\n thursday: 4,\n thu: 4,\n friday: 5,\n fri: 5,\n saturday: 6,\n sat: 6,\n};\n\n// Fix #4: hoist month-name pattern and regex source to module level so they\n// are computed once instead of being rebuilt on every extractCandidates call.\nconst MONTH_NAMES_PATTERN = Object.keys(MONTH_MAP)\n .filter(\n (k) =>\n k.length > 3 || [\"jan\", \"feb\", \"mar\", \"apr\", \"may\", \"jun\", \"jul\", \"aug\", \"sep\", \"oct\", \"nov\", \"dec\"].includes(k),\n )\n .map((k) => k.replace(/[.*+?^${}()|[\\]\\\\]/g, \"\\\\$&\"))\n .join(\"|\");\n\nconst MD_RE_SOURCE = `\\\\b(?:(${MONTH_NAMES_PATTERN})\\\\s+(\\\\d{1,2})(?:st|nd|rd|th)?|(\\\\d{1,2})(?:st|nd|rd|th)?\\\\s+(${MONTH_NAMES_PATTERN}))\\\\b`;\n\n/**\n * Try all date patterns against `text`. Return all candidate epoch-second\n * timestamps that are in the future relative to `nowMs`.\n */\nfunction extractCandidates(text: string, nowMs: number): number[] {\n const results: number[] = [];\n const now = new Date(nowMs);\n const todayMidnightMs = Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate());\n\n // -------------------------------------------------------------------------\n // 1. ISO date: \"2026-03-20\" or \"2026-03-20T14:00:00\"\n // -------------------------------------------------------------------------\n const isoRe = /\\b(\\d{4})-(\\d{2})-(\\d{2})(?:T\\d{2}:\\d{2}(?::\\d{2})?)?/g;\n let m: RegExpExecArray | null;\n while ((m = isoRe.exec(text)) !== null) {\n const y = Number.parseInt(m[1]!, 10);\n const moRaw = Number.parseInt(m[2]!, 10);\n const d = Number.parseInt(m[3]!, 10);\n // Fix #7: reject out-of-range month/day before Date.UTC to avoid silent normalization\n if (moRaw < 1 || moRaw > 12 || d < 1 || d > 31) continue;\n const mo = moRaw - 1;\n const ts = Date.UTC(y, mo, d);\n if (!Number.isNaN(ts)) {\n // Verify Date.UTC did not silently normalize (e.g. \"2026-02-30\" → March)\n const check = new Date(ts);\n if (check.getUTCMonth() !== mo || check.getUTCDate() !== d) continue;\n if (ts > todayMidnightMs) results.push(Math.floor(ts / 1000));\n }\n }\n\n // -------------------------------------------------------------------------\n // 2. Month-name day: \"March 20\", \"Mar 20th\", \"20 March\", \"20th March\"\n // Uses module-level MD_RE_SOURCE (Fix #4).\n // -------------------------------------------------------------------------\n const mdRe = new RegExp(MD_RE_SOURCE, \"gi\");\n while ((m = mdRe.exec(text)) !== null) {\n let monthStr: string;\n let dayNum: number;\n if (m[1] !== undefined && m[2] !== undefined) {\n // \"March 20\"\n monthStr = m[1].toLowerCase();\n dayNum = Number.parseInt(m[2], 10);\n } else {\n // \"20 March\"\n monthStr = m[4]?.toLowerCase();\n dayNum = Number.parseInt(m[3]!, 10);\n }\n const moIdx = MONTH_MAP[monthStr];\n if (moIdx === undefined) continue;\n // Fix #3: reject out-of-range day before Date.UTC to avoid silent normalization\n if (dayNum < 1 || dayNum > 31) continue;\n\n // Try current year first, then next year\n for (const yearOffset of [0, 1]) {\n const y = now.getUTCFullYear() + yearOffset;\n const ts = Date.UTC(y, moIdx, dayNum);\n if (!Number.isNaN(ts)) {\n // Verify no silent normalization (e.g. Feb 30 → March)\n const check = new Date(ts);\n if (check.getUTCMonth() !== moIdx || check.getUTCDate() !== dayNum) continue;\n if (ts > todayMidnightMs) {\n results.push(Math.floor(ts / 1000));\n break; // take the first future occurrence\n }\n }\n }\n }\n\n // -------------------------------------------------------------------------\n // 3. \"tomorrow\"\n // -------------------------------------------------------------------------\n if (/\\btomorrow\\b/i.test(text)) {\n const ts = todayMidnightMs + 86400 * 1000;\n results.push(Math.floor(ts / 1000));\n }\n\n // -------------------------------------------------------------------------\n // 4. \"next <weekday>\": \"next Tuesday\", \"next Monday\"\n // -------------------------------------------------------------------------\n const nextWdRe =\n /\\bnext\\s+(sunday|monday|tuesday|wednesday|thursday|friday|saturday|sun|mon|tue|wed|thu|fri|sat)\\b/gi;\n while ((m = nextWdRe.exec(text)) !== null) {\n const targetWd = WEEKDAY_MAP[m[1]?.toLowerCase()];\n if (targetWd === undefined) continue;\n const currentWd = now.getUTCDay();\n let daysAhead = targetWd - currentWd;\n if (daysAhead <= 0) daysAhead += 7; // always at least 1 day ahead\n // \"next\" implies the week after the upcoming one when the day would otherwise be <7 days away\n if (daysAhead < 7) daysAhead += 7;\n const ts = todayMidnightMs + daysAhead * 86400 * 1000;\n results.push(Math.floor(ts / 1000));\n }\n\n // -------------------------------------------------------------------------\n // 5. \"next week\" → ~7 days, \"next month\" → ~30 days, \"next year\" → ~365 days\n // -------------------------------------------------------------------------\n if (/\\bnext\\s+week\\b/i.test(text)) {\n results.push(Math.floor((todayMidnightMs + 7 * 86400 * 1000) / 1000));\n }\n if (/\\bnext\\s+month\\b/i.test(text)) {\n results.push(Math.floor((todayMidnightMs + 30 * 86400 * 1000) / 1000));\n }\n if (/\\bnext\\s+year\\b/i.test(text)) {\n results.push(Math.floor((todayMidnightMs + 365 * 86400 * 1000) / 1000));\n }\n\n // -------------------------------------------------------------------------\n // 6. \"in N days/weeks/months\": \"in 3 days\", \"in 2 weeks\", \"in 1 month\"\n // -------------------------------------------------------------------------\n const inOffsetRe = /\\bin\\s+(\\d+)\\s+(day|days|week|weeks|month|months)\\b/gi;\n while ((m = inOffsetRe.exec(text)) !== null) {\n const n = Number.parseInt(m[1]!, 10);\n const unit = m[2]?.toLowerCase();\n let daysAhead = 0;\n if (unit === \"day\" || unit === \"days\") daysAhead = n;\n else if (unit === \"week\" || unit === \"weeks\") daysAhead = n * 7;\n else if (unit === \"month\" || unit === \"months\") daysAhead = n * 30;\n if (daysAhead > 0) {\n results.push(Math.floor((todayMidnightMs + daysAhead * 86400 * 1000) / 1000));\n }\n }\n\n return results;\n}\n\n/**\n * Detect the latest future date in `text`.\n *\n * @param text - The fact text to scan.\n * @param cfg - Future-date protection config.\n * @param nowMs - Current time in milliseconds (defaults to Date.now()). Override in tests.\n * @returns Epoch seconds of the freeze-until timestamp, or null if none found / not applicable.\n */\nexport function detectFutureDate(\n text: string,\n cfg: Pick<FutureDateProtectionConfig, \"enabled\" | \"maxFreezeDays\">,\n nowMs: number = Date.now(),\n): number | null {\n if (!cfg.enabled) return null;\n\n // Recurring events (\"every Monday\", \"weekly\", \"daily at 9am\") have no single\n // future deadline, so they should not trigger a decay freeze.\n if (/\\b(every|daily|weekly|monthly|yearly|recurring|biweekly|each)\\b/i.test(text)) return null;\n\n const candidates = extractCandidates(text, nowMs);\n if (candidates.length === 0) return null;\n\n // Take the latest future date\n const latest = Math.max(...candidates);\n // Fix #2: guard against NaN/non-finite (should not happen after extractCandidates validation,\n // but defend against future callers)\n if (!Number.isFinite(latest)) return null;\n\n // Enforce maxFreezeDays cap (0 = no limit)\n if (cfg.maxFreezeDays > 0) {\n const maxFreezeMs = nowMs + cfg.maxFreezeDays * 86400 * 1000;\n const maxFreezeSec = Math.floor(maxFreezeMs / 1000);\n return Math.min(latest, maxFreezeSec);\n }\n\n return latest;\n}\n"],"mappings":"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"}
|
package/dist/utils/dates.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dates.js","names":[],"sources":["../../utils/dates.ts"],"sourcesContent":["/**\n * Date parsing for facts (sourceDate, etc.).\n */\n\n/** Parse sourceDate from ISO-8601 (YYYY-MM-DD) or Unix timestamp (seconds). Date strings are interpreted as UTC midnight. Returns null if invalid. */\nexport function parseSourceDate(v: string | number | null | undefined): number | null {\n if (v == null) return null;\n if (typeof v === \"number\") return v > 0 ? v : null;\n const s = String(v).trim();\n if (!s) return null;\n const iso = /^(\\d{4})-(\\d{2})-(\\d{2})(?:T\\d{2}:\\d{2}:\\d{2})?/.exec(s);\n if (iso) {\n const [, y, m, d] = iso;\n const ms = Date.UTC(Number.parseInt(y!, 10), Number.parseInt(m!, 10) - 1, Number.parseInt(d!, 10));\n return Number.isNaN(ms) ? null : Math.floor(ms / 1000);\n }\n const n = Number.parseInt(s, 10);\n return !Number.isNaN(n) && n > 0 ? n : null;\n}\n"],"mappings":";;;;;AAKA,SAAgB,gBAAgB,GAAsD;CACpF,IAAI,KAAK,MAAM,OAAO;CACtB,IAAI,OAAO,MAAM,UAAU,OAAO,IAAI,IAAI,IAAI;CAC9C,MAAM,IAAI,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"dates.js","names":[],"sources":["../../utils/dates.ts"],"sourcesContent":["/**\n * Date parsing for facts (sourceDate, etc.).\n */\n\n/** Parse sourceDate from ISO-8601 (YYYY-MM-DD) or Unix timestamp (seconds). Date strings are interpreted as UTC midnight. Returns null if invalid. */\nexport function parseSourceDate(v: string | number | null | undefined): number | null {\n if (v == null) return null;\n if (typeof v === \"number\") return v > 0 ? v : null;\n const s = String(v).trim();\n if (!s) return null;\n const iso = /^(\\d{4})-(\\d{2})-(\\d{2})(?:T\\d{2}:\\d{2}:\\d{2})?/.exec(s);\n if (iso) {\n const [, y, m, d] = iso;\n const ms = Date.UTC(Number.parseInt(y!, 10), Number.parseInt(m!, 10) - 1, Number.parseInt(d!, 10));\n return Number.isNaN(ms) ? null : Math.floor(ms / 1000);\n }\n const n = Number.parseInt(s, 10);\n return !Number.isNaN(n) && n > 0 ? n : null;\n}\n"],"mappings":";;;;;AAKA,SAAgB,gBAAgB,GAAsD;CACpF,IAAI,KAAK,MAAM,OAAO;CACtB,IAAI,OAAO,MAAM,UAAU,OAAO,IAAI,IAAI,IAAI;CAC9C,MAAM,IAAI,OAAO,CAAC,EAAE,KAAK;CACzB,IAAI,CAAC,GAAG,OAAO;CACf,MAAM,MAAM,kDAAkD,KAAK,CAAC;CACpE,IAAI,KAAK;EACP,MAAM,GAAG,GAAG,GAAG,KAAK;EACpB,MAAM,KAAK,KAAK,IAAI,OAAO,SAAS,GAAI,EAAE,GAAG,OAAO,SAAS,GAAI,EAAE,IAAI,GAAG,OAAO,SAAS,GAAI,EAAE,CAAC;EACjG,OAAO,OAAO,MAAM,EAAE,IAAI,OAAO,KAAK,MAAM,KAAK,GAAI;CACvD;CACA,MAAM,IAAI,OAAO,SAAS,GAAG,EAAE;CAC/B,OAAO,CAAC,OAAO,MAAM,CAAC,KAAK,IAAI,IAAI,IAAI;AACzC"}
|
package/dist/utils/decay.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"decay.js","names":[],"sources":["../../utils/decay.ts"],"sourcesContent":["/**\n * Decay classification and TTL for facts.\n * Uses language-keywords for text patterns (English + dynamic languages from .language-keywords.json).\n */\n\nimport type { DecayClass } from \"../config.js\";\nimport { TTL_DEFAULTS } from \"../config.js\";\nimport {\n getDecayActiveKeys,\n getDecayActiveRegex,\n getDecayCheckpointKeys,\n getDecayPermanentEntities,\n getDecayPermanentKeys,\n getDecayPermanentRegex,\n getDecaySessionKeys,\n getDecaySessionRegex,\n} from \"./language-keywords.js\";\n\nexport function calculateExpiry(decayClass: DecayClass, fromTimestamp = Math.floor(Date.now() / 1000)): number | null {\n const ttl = TTL_DEFAULTS[decayClass];\n return ttl ? fromTimestamp + ttl : null;\n}\n\nexport type DecayClassificationContext = {\n source?: string | null;\n category?: string | null;\n importance?: number | null;\n};\n\nfunction categoryIn(category: string, values: readonly string[]): boolean {\n return values.includes(category.toLowerCase());\n}\n\nexport function classifyDecay(\n entity: string | null,\n key: string | null,\n _value: string | null,\n text: string,\n context: DecayClassificationContext = {},\n): DecayClass {\n const keyLower = (key || \"\").toLowerCase();\n const textLower = text.toLowerCase();\n const entityLower = (entity || \"\").toLowerCase();\n const sourceLower = (context.source || \"\").toLowerCase();\n const categoryLower = (context.category || \"\").toLowerCase();\n const importance = context.importance ?? null;\n\n if (/^(?:pr|pull request|issue)\\s*#?\\d+$/i.test(entity || \"\")) return \"normal\";\n if (/^sprint\\b/i.test(entity || \"\")) return \"normal\";\n\n if (getDecayPermanentKeys().some((k) => keyLower.includes(k))) return \"permanent\";\n if (getDecayPermanentRegex().test(textLower)) return \"permanent\";\n if (entityLower && getDecayPermanentEntities().some((e) => entityLower === e)) return \"permanent\";\n\n if (getDecaySessionKeys().some((k) => keyLower.includes(k))) return \"session\";\n if (getDecaySessionRegex().test(textLower)) return \"session\";\n\n if (getDecayActiveKeys().some((k) => keyLower.includes(k))) return \"active\";\n if (getDecayActiveRegex().test(textLower)) return \"active\";\n\n if (getDecayCheckpointKeys().some((k) => keyLower.includes(k))) return \"checkpoint\";\n if (sourceLower === \"dream-cycle-src\") return \"checkpoint\";\n\n if (sourceLower === \"auto-capture\" || sourceLower === \"implicit-feedback\") return \"normal\";\n if (sourceLower === \"distillation\") {\n return categoryIn(categoryLower, [\"rule\", \"edict\", \"preference\", \"decision\"]) ? \"durable\" : \"normal\";\n }\n if (sourceLower.startsWith(\"seed:\")) {\n return categoryIn(categoryLower, [\"rule\", \"edict\", \"fact\"]) ? \"permanent\" : \"durable\";\n }\n\n if (categoryIn(categoryLower, [\"rule\", \"edict\"])) return \"permanent\";\n if (categoryIn(categoryLower, [\"preference\", \"decision\"])) return \"durable\";\n if (categoryIn(categoryLower, [\"pattern\", \"coding_task\", \"monitoring\"])) return \"short\";\n\n if (importance != null) {\n if (importance >= 0.8) return \"stable\";\n if (importance < 0.4) return \"short\";\n }\n\n return \"normal\";\n}\n"],"mappings":";;;;AAkBA,SAAgB,gBAAgB,YAAwB,gBAAgB,KAAK,MAAM,KAAK,
|
|
1
|
+
{"version":3,"file":"decay.js","names":[],"sources":["../../utils/decay.ts"],"sourcesContent":["/**\n * Decay classification and TTL for facts.\n * Uses language-keywords for text patterns (English + dynamic languages from .language-keywords.json).\n */\n\nimport type { DecayClass } from \"../config.js\";\nimport { TTL_DEFAULTS } from \"../config.js\";\nimport {\n getDecayActiveKeys,\n getDecayActiveRegex,\n getDecayCheckpointKeys,\n getDecayPermanentEntities,\n getDecayPermanentKeys,\n getDecayPermanentRegex,\n getDecaySessionKeys,\n getDecaySessionRegex,\n} from \"./language-keywords.js\";\n\nexport function calculateExpiry(decayClass: DecayClass, fromTimestamp = Math.floor(Date.now() / 1000)): number | null {\n const ttl = TTL_DEFAULTS[decayClass];\n return ttl ? fromTimestamp + ttl : null;\n}\n\nexport type DecayClassificationContext = {\n source?: string | null;\n category?: string | null;\n importance?: number | null;\n};\n\nfunction categoryIn(category: string, values: readonly string[]): boolean {\n return values.includes(category.toLowerCase());\n}\n\nexport function classifyDecay(\n entity: string | null,\n key: string | null,\n _value: string | null,\n text: string,\n context: DecayClassificationContext = {},\n): DecayClass {\n const keyLower = (key || \"\").toLowerCase();\n const textLower = text.toLowerCase();\n const entityLower = (entity || \"\").toLowerCase();\n const sourceLower = (context.source || \"\").toLowerCase();\n const categoryLower = (context.category || \"\").toLowerCase();\n const importance = context.importance ?? null;\n\n if (/^(?:pr|pull request|issue)\\s*#?\\d+$/i.test(entity || \"\")) return \"normal\";\n if (/^sprint\\b/i.test(entity || \"\")) return \"normal\";\n\n if (getDecayPermanentKeys().some((k) => keyLower.includes(k))) return \"permanent\";\n if (getDecayPermanentRegex().test(textLower)) return \"permanent\";\n if (entityLower && getDecayPermanentEntities().some((e) => entityLower === e)) return \"permanent\";\n\n if (getDecaySessionKeys().some((k) => keyLower.includes(k))) return \"session\";\n if (getDecaySessionRegex().test(textLower)) return \"session\";\n\n if (getDecayActiveKeys().some((k) => keyLower.includes(k))) return \"active\";\n if (getDecayActiveRegex().test(textLower)) return \"active\";\n\n if (getDecayCheckpointKeys().some((k) => keyLower.includes(k))) return \"checkpoint\";\n if (sourceLower === \"dream-cycle-src\") return \"checkpoint\";\n\n if (sourceLower === \"auto-capture\" || sourceLower === \"implicit-feedback\") return \"normal\";\n if (sourceLower === \"distillation\") {\n return categoryIn(categoryLower, [\"rule\", \"edict\", \"preference\", \"decision\"]) ? \"durable\" : \"normal\";\n }\n if (sourceLower.startsWith(\"seed:\")) {\n return categoryIn(categoryLower, [\"rule\", \"edict\", \"fact\"]) ? \"permanent\" : \"durable\";\n }\n\n if (categoryIn(categoryLower, [\"rule\", \"edict\"])) return \"permanent\";\n if (categoryIn(categoryLower, [\"preference\", \"decision\"])) return \"durable\";\n if (categoryIn(categoryLower, [\"pattern\", \"coding_task\", \"monitoring\"])) return \"short\";\n\n if (importance != null) {\n if (importance >= 0.8) return \"stable\";\n if (importance < 0.4) return \"short\";\n }\n\n return \"normal\";\n}\n"],"mappings":";;;;AAkBA,SAAgB,gBAAgB,YAAwB,gBAAgB,KAAK,MAAM,KAAK,IAAI,IAAI,GAAI,GAAkB;CACpH,MAAM,MAAM,aAAa;CACzB,OAAO,MAAM,gBAAgB,MAAM;AACrC;AAQA,SAAS,WAAW,UAAkB,QAAoC;CACxE,OAAO,OAAO,SAAS,SAAS,YAAY,CAAC;AAC/C;AAEA,SAAgB,cACd,QACA,KACA,QACA,MACA,UAAsC,CAAC,GAC3B;CACZ,MAAM,YAAY,OAAO,IAAI,YAAY;CACzC,MAAM,YAAY,KAAK,YAAY;CACnC,MAAM,eAAe,UAAU,IAAI,YAAY;CAC/C,MAAM,eAAe,QAAQ,UAAU,IAAI,YAAY;CACvD,MAAM,iBAAiB,QAAQ,YAAY,IAAI,YAAY;CAC3D,MAAM,aAAa,QAAQ,cAAc;CAEzC,IAAI,uCAAuC,KAAK,UAAU,EAAE,GAAG,OAAO;CACtE,IAAI,aAAa,KAAK,UAAU,EAAE,GAAG,OAAO;CAE5C,IAAI,sBAAsB,EAAE,MAAM,MAAM,SAAS,SAAS,CAAC,CAAC,GAAG,OAAO;CACtE,IAAI,uBAAuB,EAAE,KAAK,SAAS,GAAG,OAAO;CACrD,IAAI,eAAe,0BAA0B,EAAE,MAAM,MAAM,gBAAgB,CAAC,GAAG,OAAO;CAEtF,IAAI,oBAAoB,EAAE,MAAM,MAAM,SAAS,SAAS,CAAC,CAAC,GAAG,OAAO;CACpE,IAAI,qBAAqB,EAAE,KAAK,SAAS,GAAG,OAAO;CAEnD,IAAI,mBAAmB,EAAE,MAAM,MAAM,SAAS,SAAS,CAAC,CAAC,GAAG,OAAO;CACnE,IAAI,oBAAoB,EAAE,KAAK,SAAS,GAAG,OAAO;CAElD,IAAI,uBAAuB,EAAE,MAAM,MAAM,SAAS,SAAS,CAAC,CAAC,GAAG,OAAO;CACvE,IAAI,gBAAgB,mBAAmB,OAAO;CAE9C,IAAI,gBAAgB,kBAAkB,gBAAgB,qBAAqB,OAAO;CAClF,IAAI,gBAAgB,gBAClB,OAAO,WAAW,eAAe;EAAC;EAAQ;EAAS;EAAc;CAAU,CAAC,IAAI,YAAY;CAE9F,IAAI,YAAY,WAAW,OAAO,GAChC,OAAO,WAAW,eAAe;EAAC;EAAQ;EAAS;CAAM,CAAC,IAAI,cAAc;CAG9E,IAAI,WAAW,eAAe,CAAC,QAAQ,OAAO,CAAC,GAAG,OAAO;CACzD,IAAI,WAAW,eAAe,CAAC,cAAc,UAAU,CAAC,GAAG,OAAO;CAClE,IAAI,WAAW,eAAe;EAAC;EAAW;EAAe;CAAY,CAAC,GAAG,OAAO;CAEhF,IAAI,cAAc,MAAM;EACtB,IAAI,cAAc,IAAK,OAAO;EAC9B,IAAI,aAAa,IAAK,OAAO;CAC/B;CAEA,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"duration.js","names":[],"sources":["../../utils/duration.ts"],"sourcesContent":["/**\n * Duration string parser for human-friendly time thresholds.\n *\n * Converts strings like \"1d12h30m\", \"2h\", \"45m\" to total minutes.\n * Used for active task stale detection and other configurable time windows.\n */\n\n/**\n * Parse a human-friendly duration string into total minutes.\n *\n * Supported components (combined in d → h → m order):\n * - `d` — days (1d = 1440 min)\n * - `h` — hours (1h = 60 min)\n * - `m` — minutes\n *\n * Examples:\n * ```\n * parseDuration(\"30m\") // → 30\n * parseDuration(\"2h\") // → 120\n * parseDuration(\"1d\") // → 1440\n * parseDuration(\"1d12h\") // → 2160\n * parseDuration(\"1d12h30m\") // → 2190\n * parseDuration(\"1440\") // → 1440 (plain number = minutes, backward compat)\n * ```\n *\n * @param input Duration string or plain integer string (treated as minutes).\n * @returns Total number of minutes (always > 0).\n * @throws {Error} If input is empty, unrecognised, or resolves to zero/negative minutes.\n */\nexport function parseDuration(input: string): number {\n const trimmed = (input ?? \"\").trim().toLowerCase();\n\n if (!trimmed) {\n throw new Error(\n 'parseDuration: empty input. Expected a duration like \"1d12h30m\", \"2h\", \"45m\", or a plain number (minutes).',\n );\n }\n\n // Plain integer: treat as minutes (backward compat for staleHours migration)\n if (/^\\d+$/.test(trimmed)) {\n const minutes = Number.parseInt(trimmed, 10);\n if (minutes <= 0) {\n throw new Error(`parseDuration: duration must be > 0 minutes (got \"${input}\").`);\n }\n return minutes;\n }\n\n // Duration string: optional d / h / m components in strict d→h→m order.\n // Regex: at least one component must be present; each component is \\d+<unit>.\n const durationRegex = /^(?:(\\d+)d)?(?:(\\d+)h)?(?:(\\d+)m)?$/;\n const match = durationRegex.exec(trimmed);\n\n if (!match) {\n throw new Error(\n `parseDuration: unrecognised duration \"${input}\". Expected format like \"1d12h30m\", \"2h\", \"45m\", \"1d\", or a plain number (minutes). Components must appear in d→h→m order.`,\n );\n }\n\n const [, daysStr, hoursStr, minutesStr] = match;\n\n // The regex matches the empty string too — reject that case explicitly.\n if (!daysStr && !hoursStr && !minutesStr) {\n throw new Error(\n `parseDuration: unrecognised duration \"${input}\". Expected at least one component: d (days), h (hours), or m (minutes).`,\n );\n }\n\n const totalMinutes =\n (daysStr ? Number.parseInt(daysStr, 10) * 1440 : 0) +\n (hoursStr ? Number.parseInt(hoursStr, 10) * 60 : 0) +\n (minutesStr ? Number.parseInt(minutesStr, 10) : 0);\n\n if (totalMinutes <= 0) {\n throw new Error(`parseDuration: duration must be > 0 minutes (got \"${input}\").`);\n }\n\n return totalMinutes;\n}\n\n/**\n * Format a minute count back to a compact human-friendly string.\n * Used in warning messages so we show \"24h\" not \"1440min\".\n *\n * Examples:\n * ```\n * formatDuration(30) // \"30m\"\n * formatDuration(60) // \"1h\"\n * formatDuration(90) // \"1h30m\"\n * formatDuration(1440) // \"1d\"\n * formatDuration(1500) // \"1d1h\"\n * formatDuration(2190) // \"1d12h30m\"\n * ```\n */\nexport function formatDuration(totalMinutes: number): string {\n if (totalMinutes <= 0) return \"0m\";\n\n const days = Math.floor(totalMinutes / 1440);\n const hours = Math.floor((totalMinutes % 1440) / 60);\n const minutes = totalMinutes % 60;\n\n const parts: string[] = [];\n if (days > 0) parts.push(`${days}d`);\n if (hours > 0) parts.push(`${hours}h`);\n if (minutes > 0) parts.push(`${minutes}m`);\n\n return parts.join(\"\") || \"0m\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,cAAc,OAAuB;CACnD,MAAM,WAAW,SAAS,IAAI,
|
|
1
|
+
{"version":3,"file":"duration.js","names":[],"sources":["../../utils/duration.ts"],"sourcesContent":["/**\n * Duration string parser for human-friendly time thresholds.\n *\n * Converts strings like \"1d12h30m\", \"2h\", \"45m\" to total minutes.\n * Used for active task stale detection and other configurable time windows.\n */\n\n/**\n * Parse a human-friendly duration string into total minutes.\n *\n * Supported components (combined in d → h → m order):\n * - `d` — days (1d = 1440 min)\n * - `h` — hours (1h = 60 min)\n * - `m` — minutes\n *\n * Examples:\n * ```\n * parseDuration(\"30m\") // → 30\n * parseDuration(\"2h\") // → 120\n * parseDuration(\"1d\") // → 1440\n * parseDuration(\"1d12h\") // → 2160\n * parseDuration(\"1d12h30m\") // → 2190\n * parseDuration(\"1440\") // → 1440 (plain number = minutes, backward compat)\n * ```\n *\n * @param input Duration string or plain integer string (treated as minutes).\n * @returns Total number of minutes (always > 0).\n * @throws {Error} If input is empty, unrecognised, or resolves to zero/negative minutes.\n */\nexport function parseDuration(input: string): number {\n const trimmed = (input ?? \"\").trim().toLowerCase();\n\n if (!trimmed) {\n throw new Error(\n 'parseDuration: empty input. Expected a duration like \"1d12h30m\", \"2h\", \"45m\", or a plain number (minutes).',\n );\n }\n\n // Plain integer: treat as minutes (backward compat for staleHours migration)\n if (/^\\d+$/.test(trimmed)) {\n const minutes = Number.parseInt(trimmed, 10);\n if (minutes <= 0) {\n throw new Error(`parseDuration: duration must be > 0 minutes (got \"${input}\").`);\n }\n return minutes;\n }\n\n // Duration string: optional d / h / m components in strict d→h→m order.\n // Regex: at least one component must be present; each component is \\d+<unit>.\n const durationRegex = /^(?:(\\d+)d)?(?:(\\d+)h)?(?:(\\d+)m)?$/;\n const match = durationRegex.exec(trimmed);\n\n if (!match) {\n throw new Error(\n `parseDuration: unrecognised duration \"${input}\". Expected format like \"1d12h30m\", \"2h\", \"45m\", \"1d\", or a plain number (minutes). Components must appear in d→h→m order.`,\n );\n }\n\n const [, daysStr, hoursStr, minutesStr] = match;\n\n // The regex matches the empty string too — reject that case explicitly.\n if (!daysStr && !hoursStr && !minutesStr) {\n throw new Error(\n `parseDuration: unrecognised duration \"${input}\". Expected at least one component: d (days), h (hours), or m (minutes).`,\n );\n }\n\n const totalMinutes =\n (daysStr ? Number.parseInt(daysStr, 10) * 1440 : 0) +\n (hoursStr ? Number.parseInt(hoursStr, 10) * 60 : 0) +\n (minutesStr ? Number.parseInt(minutesStr, 10) : 0);\n\n if (totalMinutes <= 0) {\n throw new Error(`parseDuration: duration must be > 0 minutes (got \"${input}\").`);\n }\n\n return totalMinutes;\n}\n\n/**\n * Format a minute count back to a compact human-friendly string.\n * Used in warning messages so we show \"24h\" not \"1440min\".\n *\n * Examples:\n * ```\n * formatDuration(30) // \"30m\"\n * formatDuration(60) // \"1h\"\n * formatDuration(90) // \"1h30m\"\n * formatDuration(1440) // \"1d\"\n * formatDuration(1500) // \"1d1h\"\n * formatDuration(2190) // \"1d12h30m\"\n * ```\n */\nexport function formatDuration(totalMinutes: number): string {\n if (totalMinutes <= 0) return \"0m\";\n\n const days = Math.floor(totalMinutes / 1440);\n const hours = Math.floor((totalMinutes % 1440) / 60);\n const minutes = totalMinutes % 60;\n\n const parts: string[] = [];\n if (days > 0) parts.push(`${days}d`);\n if (hours > 0) parts.push(`${hours}h`);\n if (minutes > 0) parts.push(`${minutes}m`);\n\n return parts.join(\"\") || \"0m\";\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,SAAgB,cAAc,OAAuB;CACnD,MAAM,WAAW,SAAS,IAAI,KAAK,EAAE,YAAY;CAEjD,IAAI,CAAC,SACH,MAAM,IAAI,MACR,kHACF;CAIF,IAAI,QAAQ,KAAK,OAAO,GAAG;EACzB,MAAM,UAAU,OAAO,SAAS,SAAS,EAAE;EAC3C,IAAI,WAAW,GACb,MAAM,IAAI,MAAM,qDAAqD,MAAM,IAAI;EAEjF,OAAO;CACT;CAKA,MAAM,QAAQ,sCAAc,KAAK,OAAO;CAExC,IAAI,CAAC,OACH,MAAM,IAAI,MACR,yCAAyC,MAAM,2HACjD;CAGF,MAAM,GAAG,SAAS,UAAU,cAAc;CAG1C,IAAI,CAAC,WAAW,CAAC,YAAY,CAAC,YAC5B,MAAM,IAAI,MACR,yCAAyC,MAAM,yEACjD;CAGF,MAAM,gBACH,UAAU,OAAO,SAAS,SAAS,EAAE,IAAI,OAAO,MAChD,WAAW,OAAO,SAAS,UAAU,EAAE,IAAI,KAAK,MAChD,aAAa,OAAO,SAAS,YAAY,EAAE,IAAI;CAElD,IAAI,gBAAgB,GAClB,MAAM,IAAI,MAAM,qDAAqD,MAAM,IAAI;CAGjF,OAAO;AACT;;;;;;;;;;;;;;;AAgBA,SAAgB,eAAe,cAA8B;CAC3D,IAAI,gBAAgB,GAAG,OAAO;CAE9B,MAAM,OAAO,KAAK,MAAM,eAAe,IAAI;CAC3C,MAAM,QAAQ,KAAK,MAAO,eAAe,OAAQ,EAAE;CACnD,MAAM,UAAU,eAAe;CAE/B,MAAM,QAAkB,CAAC;CACzB,IAAI,OAAO,GAAG,MAAM,KAAK,GAAG,KAAK,EAAE;CACnC,IAAI,QAAQ,GAAG,MAAM,KAAK,GAAG,MAAM,EAAE;CACrC,IAAI,UAAU,GAAG,MAAM,KAAK,GAAG,QAAQ,EAAE;CAEzC,OAAO,MAAM,KAAK,EAAE,KAAK;AAC3B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"embed-call.js","names":[],"sources":["../../utils/embed-call.ts"],"sourcesContent":["/**\n * Shared embedding call policy: timeout + limited retries with exponential backoff (#871).\n */\n\nexport const EMBED_CALL_TIMEOUT_MS = 120_000;\n/** Total attempts (initial + retries). */\nexport const EMBED_CALL_MAX_ATTEMPTS = 3;\nexport const EMBED_CALL_BASE_DELAY_MS = 400;\n\nexport function embedCallWithTimeout<T>(promise: Promise<T>, label: string): Promise<T> {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<T>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error(`embedding timed out after ${EMBED_CALL_TIMEOUT_MS}ms (${label})`)),\n EMBED_CALL_TIMEOUT_MS,\n );\n\n // In Node.js, avoid keeping the event loop alive just because of this timer.\n const maybeTimer = timeoutId as unknown as { unref?: () => void } | undefined;\n if (maybeTimer && typeof maybeTimer.unref === \"function\") {\n maybeTimer.unref();\n }\n });\n\n return Promise.race([promise, timeoutPromise]).finally(() => {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n });\n}\n\n/**\n * Run an embedding producer with per-attempt timeout and exponential backoff between failures.\n */\nexport async function embedCallWithTimeoutAndRetry<T>(producer: () => Promise<T>, label: string): Promise<T> {\n let last: unknown;\n for (let attempt = 0; attempt < EMBED_CALL_MAX_ATTEMPTS; attempt++) {\n try {\n return await embedCallWithTimeout(producer(), `${label} (attempt ${attempt + 1})`);\n } catch (err) {\n last = err;\n if (attempt < EMBED_CALL_MAX_ATTEMPTS - 1) {\n await new Promise((r) => setTimeout(r, EMBED_CALL_BASE_DELAY_MS * 2 ** attempt));\n }\n }\n }\n throw last;\n}\n"],"mappings":";;;;AAIA,MAAa,wBAAwB;AAKrC,SAAgB,qBAAwB,SAAqB,OAA2B;CACtF,IAAI;CAEJ,MAAM,iBAAiB,IAAI,SAAY,GAAG,WAAW;EACnD,YAAY,iBACJ,uBAAO,IAAI,MAAM,6BAA6B,sBAAsB,MAAM,MAAM,
|
|
1
|
+
{"version":3,"file":"embed-call.js","names":[],"sources":["../../utils/embed-call.ts"],"sourcesContent":["/**\n * Shared embedding call policy: timeout + limited retries with exponential backoff (#871).\n */\n\nexport const EMBED_CALL_TIMEOUT_MS = 120_000;\n/** Total attempts (initial + retries). */\nexport const EMBED_CALL_MAX_ATTEMPTS = 3;\nexport const EMBED_CALL_BASE_DELAY_MS = 400;\n\nexport function embedCallWithTimeout<T>(promise: Promise<T>, label: string): Promise<T> {\n let timeoutId: ReturnType<typeof setTimeout> | undefined;\n\n const timeoutPromise = new Promise<T>((_, reject) => {\n timeoutId = setTimeout(\n () => reject(new Error(`embedding timed out after ${EMBED_CALL_TIMEOUT_MS}ms (${label})`)),\n EMBED_CALL_TIMEOUT_MS,\n );\n\n // In Node.js, avoid keeping the event loop alive just because of this timer.\n const maybeTimer = timeoutId as unknown as { unref?: () => void } | undefined;\n if (maybeTimer && typeof maybeTimer.unref === \"function\") {\n maybeTimer.unref();\n }\n });\n\n return Promise.race([promise, timeoutPromise]).finally(() => {\n if (timeoutId !== undefined) {\n clearTimeout(timeoutId);\n }\n });\n}\n\n/**\n * Run an embedding producer with per-attempt timeout and exponential backoff between failures.\n */\nexport async function embedCallWithTimeoutAndRetry<T>(producer: () => Promise<T>, label: string): Promise<T> {\n let last: unknown;\n for (let attempt = 0; attempt < EMBED_CALL_MAX_ATTEMPTS; attempt++) {\n try {\n return await embedCallWithTimeout(producer(), `${label} (attempt ${attempt + 1})`);\n } catch (err) {\n last = err;\n if (attempt < EMBED_CALL_MAX_ATTEMPTS - 1) {\n await new Promise((r) => setTimeout(r, EMBED_CALL_BASE_DELAY_MS * 2 ** attempt));\n }\n }\n }\n throw last;\n}\n"],"mappings":";;;;AAIA,MAAa,wBAAwB;AAKrC,SAAgB,qBAAwB,SAAqB,OAA2B;CACtF,IAAI;CAEJ,MAAM,iBAAiB,IAAI,SAAY,GAAG,WAAW;EACnD,YAAY,iBACJ,uBAAO,IAAI,MAAM,6BAA6B,sBAAsB,MAAM,MAAM,EAAE,CAAC,GACzF,qBACF;EAGA,MAAM,aAAa;EACnB,IAAI,cAAc,OAAO,WAAW,UAAU,YAC5C,WAAW,MAAM;CAErB,CAAC;CAED,OAAO,QAAQ,KAAK,CAAC,SAAS,cAAc,CAAC,EAAE,cAAc;EAC3D,IAAI,cAAc,KAAA,GAChB,aAAa,SAAS;CAE1B,CAAC;AACH;;;;AAKA,eAAsB,6BAAgC,UAA4B,OAA2B;CAC3G,IAAI;CACJ,KAAK,IAAI,UAAU,GAAG,UAAA,GAAmC,WACvD,IAAI;EACF,OAAO,MAAM,qBAAqB,SAAS,GAAG,GAAG,MAAM,YAAY,UAAU,EAAE,EAAE;CACnF,SAAS,KAAK;EACZ,OAAO;EACP,IAAI,UAAA,GACF,MAAM,IAAI,SAAS,MAAM,WAAW,GAAA,MAA8B,KAAK,OAAO,CAAC;CAEnF;CAEF,MAAM;AACR"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-lookup-resolve.js","names":[],"sources":["../../utils/entity-lookup-resolve.ts"],"sourcesContent":["import type { EntityLookupConfig } from \"../config/types/retrieval.js\";\n\nexport type FactsDbWithKnownEntities = {\n getKnownEntities?: () => string[];\n};\n\n/**\n * Effective entity names for auto-recall entity lookup + retrieval directives.\n * Manual `entities` wins; when empty and `autoFromFacts`, use DISTINCT entity from facts (capped).\n */\nexport function resolveEntityLookupNames(\n entityLookup: EntityLookupConfig,\n factsDb: FactsDbWithKnownEntities,\n): string[] {\n if (entityLookup.entities.length > 0) return entityLookup.entities;\n if (!entityLookup.autoFromFacts) return [];\n const raw = factsDb.getKnownEntities?.() ?? [];\n const filtered = raw.filter((e) => typeof e === \"string\" && e.trim().length > 0);\n // Stable order before capping so the same DB always yields the same subset under maxAutoEntities\n // (SQL DISTINCT without ORDER BY is not guaranteed deterministic).\n const sorted = [...filtered].sort((a, b) =>\n a.trim().localeCompare(b.trim(), undefined, { sensitivity: \"base\", numeric: true }),\n );\n return sorted.slice(0, entityLookup.maxAutoEntities);\n}\n"],"mappings":";;;;;AAUA,SAAgB,yBACd,cACA,SACU;CACV,IAAI,aAAa,SAAS,SAAS,GAAG,OAAO,aAAa;CAC1D,IAAI,CAAC,aAAa,eAAe,OAAO,
|
|
1
|
+
{"version":3,"file":"entity-lookup-resolve.js","names":[],"sources":["../../utils/entity-lookup-resolve.ts"],"sourcesContent":["import type { EntityLookupConfig } from \"../config/types/retrieval.js\";\n\nexport type FactsDbWithKnownEntities = {\n getKnownEntities?: () => string[];\n};\n\n/**\n * Effective entity names for auto-recall entity lookup + retrieval directives.\n * Manual `entities` wins; when empty and `autoFromFacts`, use DISTINCT entity from facts (capped).\n */\nexport function resolveEntityLookupNames(\n entityLookup: EntityLookupConfig,\n factsDb: FactsDbWithKnownEntities,\n): string[] {\n if (entityLookup.entities.length > 0) return entityLookup.entities;\n if (!entityLookup.autoFromFacts) return [];\n const raw = factsDb.getKnownEntities?.() ?? [];\n const filtered = raw.filter((e) => typeof e === \"string\" && e.trim().length > 0);\n // Stable order before capping so the same DB always yields the same subset under maxAutoEntities\n // (SQL DISTINCT without ORDER BY is not guaranteed deterministic).\n const sorted = [...filtered].sort((a, b) =>\n a.trim().localeCompare(b.trim(), undefined, { sensitivity: \"base\", numeric: true }),\n );\n return sorted.slice(0, entityLookup.maxAutoEntities);\n}\n"],"mappings":";;;;;AAUA,SAAgB,yBACd,cACA,SACU;CACV,IAAI,aAAa,SAAS,SAAS,GAAG,OAAO,aAAa;CAC1D,IAAI,CAAC,aAAa,eAAe,OAAO,CAAC;CAQzC,OAHe,CAAC,IAJJ,QAAQ,mBAAmB,KAAK,CAAC,GACxB,QAAQ,MAAM,OAAO,MAAM,YAAY,EAAE,KAAK,EAAE,SAAS,CAGpD,CAAC,EAAE,MAAM,GAAG,MACpC,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,GAAG,KAAA,GAAW;EAAE,aAAa;EAAQ,SAAS;CAAK,CAAC,CAExE,EAAE,MAAM,GAAG,aAAa,eAAe;AACrD"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-mention-quality.js","names":[],"sources":["../../utils/entity-mention-quality.ts"],"sourcesContent":["import type { EntityMentionLabel } from \"../backends/facts-db/entity-layer.js\";\nimport { normalizeEntityKey } from \"../backends/facts-db/entity-layer.js\";\n\nexport type EntityMentionRejectReason =\n | \"short\"\n | \"numeric\"\n | \"timestamp_fragment\"\n | \"generic_term\"\n | \"low_confidence\"\n | \"role_person\"\n | \"bad_type\";\n\nexport type EntityMentionCandidate = {\n label: string;\n surfaceText: string;\n normalizedSurface?: string;\n confidence?: number;\n};\n\nexport type CanonicalizedEntityMention = {\n accepted: true;\n label: EntityMentionLabel;\n surfaceText: string;\n normalizedSurface: string;\n confidence: number;\n};\n\nexport type RejectedEntityMention = {\n accepted: false;\n reason: EntityMentionRejectReason;\n};\n\nconst GENERIC_TERMS = new Set([\n \"api\",\n \"cli\",\n \"pr\",\n \"issue\",\n \"status\",\n \"task\",\n \"done\",\n \"open\",\n \"closed\",\n \"blocked\",\n \"waiting\",\n \"failed\",\n \"session\",\n \"memory\",\n \"fact\",\n]);\n\nconst MODEL_SUFFIX = \"mini|pro|plus|turbo|ultra|flash|preview|vision|instruct|sonnet|opus|haiku|codex|nano\";\nconst MODEL_VERSION = `\\\\d+[a-z]?(?:\\\\.\\\\d+[a-z]?)*(?:[-\\\\s](?:${MODEL_SUFFIX})\\\\b)?`;\nconst MODEL_MATCHER = new RegExp(\n `\\\\b(gpt[-\\\\s]?${MODEL_VERSION}|claude[-\\\\s]?${MODEL_VERSION}|gemini[-\\\\s]?${MODEL_VERSION}|minimax[-\\\\s]?${MODEL_VERSION}|o(?:1|3|4)(?:[-\\\\s](?:mini|pro|plus|turbo|ultra|flash|preview|vision)\\\\b)?|(gpt|claude|gemini|minimax|sonnet|opus|haiku|codex)[-\\\\s](pro|plus|turbo|ultra|flash|preview|vision|instruct|mini|nano))\\\\b`,\n \"i\",\n);\n\nconst CANONICAL_LABEL_MAP: Array<{ match: RegExp; label: EntityMentionLabel; canonicalNormalized: string }> = [\n { match: MODEL_MATCHER, label: \"MODEL\", canonicalNormalized: \"\" },\n { match: /^gh$/i, label: \"TOOL\", canonicalNormalized: \"gh\" },\n { match: /^npm$/i, label: \"TOOL\", canonicalNormalized: \"npm\" },\n { match: /^jq$/i, label: \"TOOL\", canonicalNormalized: \"jq\" },\n { match: /\\bgithub\\b/i, label: \"SERVICE\", canonicalNormalized: \"github\" },\n { match: /\\bwhatsapp\\b/i, label: \"SERVICE\", canonicalNormalized: \"whatsapp\" },\n { match: /\\btelegram\\b/i, label: \"SERVICE\", canonicalNormalized: \"telegram\" },\n { match: /\\bhome assistant\\b|^ha$/i, label: \"SERVICE\", canonicalNormalized: \"home assistant\" },\n { match: /\\bduckflux\\b/i, label: \"PROJECT\", canonicalNormalized: \"duckflux\" },\n {\n match: /\\bhybrid[- ]memory\\b|\\bopenclaw-hybrid-memory\\b/i,\n label: \"PROJECT\",\n canonicalNormalized: \"openclaw-hybrid-memory\",\n },\n { match: /^(forge|scholar|maeve|ralph)$/i, label: \"AGENT\", canonicalNormalized: \"\" },\n];\n\nconst ACRONYM_ALLOWLIST = new Map<string, EntityMentionLabel>([\n [\"HA\", \"SERVICE\"],\n [\"CI\", \"TOOL\"],\n]);\n\nconst MIN_CONFIDENCE_THRESHOLD = 0.75;\n\nfunction normalizeSurfaceText(value: string): string {\n return value.trim();\n}\n\nfunction isNumericLike(value: string): boolean {\n return /^[\\d\\-:./]+$/.test(value);\n}\n\nfunction includesTimestampFragment(value: string): boolean {\n return /\\b\\d{9,13}\\b/.test(value);\n}\n\nfunction normalizeModelCanonical(value: string): string {\n const normalized = normalizeEntityKey(value).replace(/\\s+/g, \"-\");\n return normalized;\n}\n\nfunction canonicalizeByKnownMap(\n surfaceText: string,\n normalizedSurface: string,\n fallbackLabel: EntityMentionLabel,\n): { label: EntityMentionLabel; normalizedSurface: string } {\n for (const rule of CANONICAL_LABEL_MAP) {\n const surfaceMatch = rule.match.exec(surfaceText);\n const normalizedMatch = rule.match.exec(normalizedSurface);\n if (!surfaceMatch && !normalizedMatch) continue;\n if (fallbackLabel === \"PERSON\" && rule.label === \"MODEL\") {\n continue;\n }\n if (rule.label === \"MODEL\") {\n const matchedText = surfaceMatch ? surfaceMatch[0] : normalizedMatch![0];\n return {\n label: \"MODEL\",\n normalizedSurface: normalizeModelCanonical(matchedText),\n };\n }\n const finalNormalized = normalizedMatch ? normalizedSurface : normalizeEntityKey(surfaceText);\n const matchedPart = normalizedMatch?.[0] || surfaceMatch?.[0] || \"\";\n const isFullMatch = matchedPart.length === finalNormalized.length;\n return {\n label: rule.label,\n normalizedSurface:\n rule.canonicalNormalized && (finalNormalized === rule.canonicalNormalized || isFullMatch)\n ? rule.canonicalNormalized\n : finalNormalized,\n };\n }\n return { label: fallbackLabel, normalizedSurface };\n}\n\nexport function makeEntityMentionKey(label: string, normalizedSurface: string): string {\n return `${label}\\u0000${normalizedSurface}`;\n}\n\nexport function countReason(target: Record<string, number>, reason: string): void {\n target[reason] = (target[reason] ?? 0) + 1;\n}\n\nexport function canonicalizeEntityMention(\n candidate: EntityMentionCandidate,\n): CanonicalizedEntityMention | RejectedEntityMention {\n const baseSurface = normalizeSurfaceText(candidate.surfaceText ?? \"\");\n if (baseSurface.length < 2) return { accepted: false, reason: \"short\" };\n const acronymType = ACRONYM_ALLOWLIST.get(baseSurface.toUpperCase());\n const hasKnownShortMapping = CANONICAL_LABEL_MAP.some((rule) => rule.match.test(baseSurface));\n if (baseSurface.length < 3 && !acronymType && !hasKnownShortMapping) return { accepted: false, reason: \"short\" };\n if (isNumericLike(baseSurface)) return { accepted: false, reason: \"numeric\" };\n if (includesTimestampFragment(baseSurface)) return { accepted: false, reason: \"timestamp_fragment\" };\n\n let label = String(candidate.label ?? \"\").toUpperCase() as EntityMentionLabel;\n if (!label) return { accepted: false, reason: \"bad_type\" };\n const supportedLabels: EntityMentionLabel[] = [\n \"PERSON\",\n \"ORG\",\n \"SERVICE\",\n \"TOOL\",\n \"MODEL\",\n \"PROJECT\",\n \"AGENT\",\n \"ROLE\",\n \"PRODUCT\",\n \"LOCATION\",\n ];\n if (!supportedLabels.includes(label)) {\n if (MODEL_MATCHER.test(baseSurface)) {\n label = \"MODEL\";\n } else {\n return { accepted: false, reason: \"bad_type\" };\n }\n }\n\n let normalizedSurface = normalizeEntityKey(candidate.normalizedSurface?.trim() || baseSurface);\n if (!normalizedSurface) return { accepted: false, reason: \"short\" };\n if (GENERIC_TERMS.has(normalizedSurface)) return { accepted: false, reason: \"generic_term\" };\n if (label === \"PERSON\" && /^(surgeon|forge|scholar|council|agent|assistant)$/i.test(normalizedSurface)) {\n return { accepted: false, reason: \"role_person\" };\n }\n\n if (acronymType) {\n label = acronymType;\n }\n\n const canonical = canonicalizeByKnownMap(baseSurface, normalizedSurface, label);\n label = canonical.label;\n normalizedSurface = canonical.normalizedSurface || normalizedSurface;\n\n const confidence = Number.isFinite(candidate.confidence)\n ? Math.max(0, Math.min(Number(candidate.confidence), 1))\n : 0.75;\n if (confidence < MIN_CONFIDENCE_THRESHOLD) return { accepted: false, reason: \"low_confidence\" };\n\n return {\n accepted: true,\n label,\n surfaceText: baseSurface,\n normalizedSurface,\n confidence,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"entity-mention-quality.js","names":[],"sources":["../../utils/entity-mention-quality.ts"],"sourcesContent":["import type { EntityMentionLabel } from \"../backends/facts-db/entity-layer.js\";\nimport { normalizeEntityKey } from \"../backends/facts-db/entity-layer.js\";\n\nexport type EntityMentionRejectReason =\n | \"short\"\n | \"numeric\"\n | \"timestamp_fragment\"\n | \"generic_term\"\n | \"low_confidence\"\n | \"role_person\"\n | \"bad_type\";\n\nexport type EntityMentionCandidate = {\n label: string;\n surfaceText: string;\n normalizedSurface?: string;\n confidence?: number;\n};\n\nexport type CanonicalizedEntityMention = {\n accepted: true;\n label: EntityMentionLabel;\n surfaceText: string;\n normalizedSurface: string;\n confidence: number;\n};\n\nexport type RejectedEntityMention = {\n accepted: false;\n reason: EntityMentionRejectReason;\n};\n\nconst GENERIC_TERMS = new Set([\n \"api\",\n \"cli\",\n \"pr\",\n \"issue\",\n \"status\",\n \"task\",\n \"done\",\n \"open\",\n \"closed\",\n \"blocked\",\n \"waiting\",\n \"failed\",\n \"session\",\n \"memory\",\n \"fact\",\n]);\n\nconst MODEL_SUFFIX = \"mini|pro|plus|turbo|ultra|flash|preview|vision|instruct|sonnet|opus|haiku|codex|nano\";\nconst MODEL_VERSION = `\\\\d+[a-z]?(?:\\\\.\\\\d+[a-z]?)*(?:[-\\\\s](?:${MODEL_SUFFIX})\\\\b)?`;\nconst MODEL_MATCHER = new RegExp(\n `\\\\b(gpt[-\\\\s]?${MODEL_VERSION}|claude[-\\\\s]?${MODEL_VERSION}|gemini[-\\\\s]?${MODEL_VERSION}|minimax[-\\\\s]?${MODEL_VERSION}|o(?:1|3|4)(?:[-\\\\s](?:mini|pro|plus|turbo|ultra|flash|preview|vision)\\\\b)?|(gpt|claude|gemini|minimax|sonnet|opus|haiku|codex)[-\\\\s](pro|plus|turbo|ultra|flash|preview|vision|instruct|mini|nano))\\\\b`,\n \"i\",\n);\n\nconst CANONICAL_LABEL_MAP: Array<{ match: RegExp; label: EntityMentionLabel; canonicalNormalized: string }> = [\n { match: MODEL_MATCHER, label: \"MODEL\", canonicalNormalized: \"\" },\n { match: /^gh$/i, label: \"TOOL\", canonicalNormalized: \"gh\" },\n { match: /^npm$/i, label: \"TOOL\", canonicalNormalized: \"npm\" },\n { match: /^jq$/i, label: \"TOOL\", canonicalNormalized: \"jq\" },\n { match: /\\bgithub\\b/i, label: \"SERVICE\", canonicalNormalized: \"github\" },\n { match: /\\bwhatsapp\\b/i, label: \"SERVICE\", canonicalNormalized: \"whatsapp\" },\n { match: /\\btelegram\\b/i, label: \"SERVICE\", canonicalNormalized: \"telegram\" },\n { match: /\\bhome assistant\\b|^ha$/i, label: \"SERVICE\", canonicalNormalized: \"home assistant\" },\n { match: /\\bduckflux\\b/i, label: \"PROJECT\", canonicalNormalized: \"duckflux\" },\n {\n match: /\\bhybrid[- ]memory\\b|\\bopenclaw-hybrid-memory\\b/i,\n label: \"PROJECT\",\n canonicalNormalized: \"openclaw-hybrid-memory\",\n },\n { match: /^(forge|scholar|maeve|ralph)$/i, label: \"AGENT\", canonicalNormalized: \"\" },\n];\n\nconst ACRONYM_ALLOWLIST = new Map<string, EntityMentionLabel>([\n [\"HA\", \"SERVICE\"],\n [\"CI\", \"TOOL\"],\n]);\n\nconst MIN_CONFIDENCE_THRESHOLD = 0.75;\n\nfunction normalizeSurfaceText(value: string): string {\n return value.trim();\n}\n\nfunction isNumericLike(value: string): boolean {\n return /^[\\d\\-:./]+$/.test(value);\n}\n\nfunction includesTimestampFragment(value: string): boolean {\n return /\\b\\d{9,13}\\b/.test(value);\n}\n\nfunction normalizeModelCanonical(value: string): string {\n const normalized = normalizeEntityKey(value).replace(/\\s+/g, \"-\");\n return normalized;\n}\n\nfunction canonicalizeByKnownMap(\n surfaceText: string,\n normalizedSurface: string,\n fallbackLabel: EntityMentionLabel,\n): { label: EntityMentionLabel; normalizedSurface: string } {\n for (const rule of CANONICAL_LABEL_MAP) {\n const surfaceMatch = rule.match.exec(surfaceText);\n const normalizedMatch = rule.match.exec(normalizedSurface);\n if (!surfaceMatch && !normalizedMatch) continue;\n if (fallbackLabel === \"PERSON\" && rule.label === \"MODEL\") {\n continue;\n }\n if (rule.label === \"MODEL\") {\n const matchedText = surfaceMatch ? surfaceMatch[0] : normalizedMatch![0];\n return {\n label: \"MODEL\",\n normalizedSurface: normalizeModelCanonical(matchedText),\n };\n }\n const finalNormalized = normalizedMatch ? normalizedSurface : normalizeEntityKey(surfaceText);\n const matchedPart = normalizedMatch?.[0] || surfaceMatch?.[0] || \"\";\n const isFullMatch = matchedPart.length === finalNormalized.length;\n return {\n label: rule.label,\n normalizedSurface:\n rule.canonicalNormalized && (finalNormalized === rule.canonicalNormalized || isFullMatch)\n ? rule.canonicalNormalized\n : finalNormalized,\n };\n }\n return { label: fallbackLabel, normalizedSurface };\n}\n\nexport function makeEntityMentionKey(label: string, normalizedSurface: string): string {\n return `${label}\\u0000${normalizedSurface}`;\n}\n\nexport function countReason(target: Record<string, number>, reason: string): void {\n target[reason] = (target[reason] ?? 0) + 1;\n}\n\nexport function canonicalizeEntityMention(\n candidate: EntityMentionCandidate,\n): CanonicalizedEntityMention | RejectedEntityMention {\n const baseSurface = normalizeSurfaceText(candidate.surfaceText ?? \"\");\n if (baseSurface.length < 2) return { accepted: false, reason: \"short\" };\n const acronymType = ACRONYM_ALLOWLIST.get(baseSurface.toUpperCase());\n const hasKnownShortMapping = CANONICAL_LABEL_MAP.some((rule) => rule.match.test(baseSurface));\n if (baseSurface.length < 3 && !acronymType && !hasKnownShortMapping) return { accepted: false, reason: \"short\" };\n if (isNumericLike(baseSurface)) return { accepted: false, reason: \"numeric\" };\n if (includesTimestampFragment(baseSurface)) return { accepted: false, reason: \"timestamp_fragment\" };\n\n let label = String(candidate.label ?? \"\").toUpperCase() as EntityMentionLabel;\n if (!label) return { accepted: false, reason: \"bad_type\" };\n const supportedLabels: EntityMentionLabel[] = [\n \"PERSON\",\n \"ORG\",\n \"SERVICE\",\n \"TOOL\",\n \"MODEL\",\n \"PROJECT\",\n \"AGENT\",\n \"ROLE\",\n \"PRODUCT\",\n \"LOCATION\",\n ];\n if (!supportedLabels.includes(label)) {\n if (MODEL_MATCHER.test(baseSurface)) {\n label = \"MODEL\";\n } else {\n return { accepted: false, reason: \"bad_type\" };\n }\n }\n\n let normalizedSurface = normalizeEntityKey(candidate.normalizedSurface?.trim() || baseSurface);\n if (!normalizedSurface) return { accepted: false, reason: \"short\" };\n if (GENERIC_TERMS.has(normalizedSurface)) return { accepted: false, reason: \"generic_term\" };\n if (label === \"PERSON\" && /^(surgeon|forge|scholar|council|agent|assistant)$/i.test(normalizedSurface)) {\n return { accepted: false, reason: \"role_person\" };\n }\n\n if (acronymType) {\n label = acronymType;\n }\n\n const canonical = canonicalizeByKnownMap(baseSurface, normalizedSurface, label);\n label = canonical.label;\n normalizedSurface = canonical.normalizedSurface || normalizedSurface;\n\n const confidence = Number.isFinite(candidate.confidence)\n ? Math.max(0, Math.min(Number(candidate.confidence), 1))\n : 0.75;\n if (confidence < MIN_CONFIDENCE_THRESHOLD) return { accepted: false, reason: \"low_confidence\" };\n\n return {\n accepted: true,\n label,\n surfaceText: baseSurface,\n normalizedSurface,\n confidence,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"entity-stopwords.js","names":[],"sources":["../../utils/entity-stopwords.ts"],"sourcesContent":["/**\n * Entity stop words (#1190): common nouns and role labels that should not become\n * graph entities. Keep this list intentionally conservative: project names,\n * filenames, hostnames, people, and org names must still pass through.\n */\n\nexport const DEFAULT_ENTITY_STOP_WORDS = [\n \"agent\",\n \"api\",\n \"assistant\",\n \"credential\",\n \"credentials\",\n \"convention\",\n \"fact\",\n \"facts\",\n \"memory\",\n \"owner\",\n \"preference\",\n \"system\",\n \"the\",\n \"user\",\n \"users\",\n] as const;\n\nexport function normalizeEntityStopWord(value: string): string {\n return value\n .normalize(\"NFKD\")\n .replace(/\\p{M}/gu, \"\")\n .toLowerCase()\n .replace(/[_-]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nexport function buildEntityStopWordSet(extra: readonly string[] = []): Set<string> {\n const stopWords = new Set<string>();\n for (const word of DEFAULT_ENTITY_STOP_WORDS) {\n const normalized = normalizeEntityStopWord(word);\n if (normalized) stopWords.add(normalized);\n }\n for (const word of extra) {\n if (typeof word !== \"string\") continue;\n const normalized = normalizeEntityStopWord(word);\n if (normalized) stopWords.add(normalized);\n }\n return stopWords;\n}\n\nexport function isEntityStopWord(value: string | null | undefined, extra: readonly string[] = []): boolean {\n if (typeof value !== \"string\") return false;\n const normalized = normalizeEntityStopWord(value);\n if (!normalized) return false;\n return buildEntityStopWordSet(extra).has(normalized);\n}\n\nexport function filterEntityStopWords<T extends string | null | undefined>(\n entities: readonly T[],\n extra: readonly string[] = [],\n): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const entity of entities) {\n if (typeof entity !== \"string\") continue;\n const trimmed = entity.trim();\n if (!trimmed || isEntityStopWord(trimmed, extra)) continue;\n const key = normalizeEntityStopWord(trimmed);\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(trimmed);\n }\n return out;\n}\n"],"mappings":";;;;;;AAMA,MAAa,4BAA4B;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;
|
|
1
|
+
{"version":3,"file":"entity-stopwords.js","names":[],"sources":["../../utils/entity-stopwords.ts"],"sourcesContent":["/**\n * Entity stop words (#1190): common nouns and role labels that should not become\n * graph entities. Keep this list intentionally conservative: project names,\n * filenames, hostnames, people, and org names must still pass through.\n */\n\nexport const DEFAULT_ENTITY_STOP_WORDS = [\n \"agent\",\n \"api\",\n \"assistant\",\n \"credential\",\n \"credentials\",\n \"convention\",\n \"fact\",\n \"facts\",\n \"memory\",\n \"owner\",\n \"preference\",\n \"system\",\n \"the\",\n \"user\",\n \"users\",\n] as const;\n\nexport function normalizeEntityStopWord(value: string): string {\n return value\n .normalize(\"NFKD\")\n .replace(/\\p{M}/gu, \"\")\n .toLowerCase()\n .replace(/[_-]+/g, \" \")\n .replace(/\\s+/g, \" \")\n .trim();\n}\n\nexport function buildEntityStopWordSet(extra: readonly string[] = []): Set<string> {\n const stopWords = new Set<string>();\n for (const word of DEFAULT_ENTITY_STOP_WORDS) {\n const normalized = normalizeEntityStopWord(word);\n if (normalized) stopWords.add(normalized);\n }\n for (const word of extra) {\n if (typeof word !== \"string\") continue;\n const normalized = normalizeEntityStopWord(word);\n if (normalized) stopWords.add(normalized);\n }\n return stopWords;\n}\n\nexport function isEntityStopWord(value: string | null | undefined, extra: readonly string[] = []): boolean {\n if (typeof value !== \"string\") return false;\n const normalized = normalizeEntityStopWord(value);\n if (!normalized) return false;\n return buildEntityStopWordSet(extra).has(normalized);\n}\n\nexport function filterEntityStopWords<T extends string | null | undefined>(\n entities: readonly T[],\n extra: readonly string[] = [],\n): string[] {\n const seen = new Set<string>();\n const out: string[] = [];\n for (const entity of entities) {\n if (typeof entity !== \"string\") continue;\n const trimmed = entity.trim();\n if (!trimmed || isEntityStopWord(trimmed, extra)) continue;\n const key = normalizeEntityStopWord(trimmed);\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(trimmed);\n }\n return out;\n}\n"],"mappings":";;;;;;AAMA,MAAa,4BAA4B;CACvC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;AACF;AAEA,SAAgB,wBAAwB,OAAuB;CAC7D,OAAO,MACJ,UAAU,MAAM,EAChB,QAAQ,WAAW,EAAE,EACrB,YAAY,EACZ,QAAQ,UAAU,GAAG,EACrB,QAAQ,QAAQ,GAAG,EACnB,KAAK;AACV;AAEA,SAAgB,uBAAuB,QAA2B,CAAC,GAAgB;CACjF,MAAM,4BAAY,IAAI,IAAY;CAClC,KAAK,MAAM,QAAQ,2BAA2B;EAC5C,MAAM,aAAa,wBAAwB,IAAI;EAC/C,IAAI,YAAY,UAAU,IAAI,UAAU;CAC1C;CACA,KAAK,MAAM,QAAQ,OAAO;EACxB,IAAI,OAAO,SAAS,UAAU;EAC9B,MAAM,aAAa,wBAAwB,IAAI;EAC/C,IAAI,YAAY,UAAU,IAAI,UAAU;CAC1C;CACA,OAAO;AACT;AAEA,SAAgB,iBAAiB,OAAkC,QAA2B,CAAC,GAAY;CACzG,IAAI,OAAO,UAAU,UAAU,OAAO;CACtC,MAAM,aAAa,wBAAwB,KAAK;CAChD,IAAI,CAAC,YAAY,OAAO;CACxB,OAAO,uBAAuB,KAAK,EAAE,IAAI,UAAU;AACrD;AAEA,SAAgB,sBACd,UACA,QAA2B,CAAC,GAClB;CACV,MAAM,uBAAO,IAAI,IAAY;CAC7B,MAAM,MAAgB,CAAC;CACvB,KAAK,MAAM,UAAU,UAAU;EAC7B,IAAI,OAAO,WAAW,UAAU;EAChC,MAAM,UAAU,OAAO,KAAK;EAC5B,IAAI,CAAC,WAAW,iBAAiB,SAAS,KAAK,GAAG;EAClD,MAAM,MAAM,wBAAwB,OAAO;EAC3C,IAAI,KAAK,IAAI,GAAG,GAAG;EACnB,KAAK,IAAI,GAAG;EACZ,IAAI,KAAK,OAAO;CAClB;CACA,OAAO;AACT"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env-manager.js","names":[],"sources":["../../utils/env-manager.ts"],"sourcesContent":["/**\n * Centralized environment variable access to resolve security scanner warnings.\n */\nexport function getEnv(key: string): string | undefined {\n return process.env[key];\n}\n\nexport function setEnv(key: string, value: string | undefined): void {\n if (value === undefined) {\n delete process.env[key];\n } else {\n process.env[key] = value;\n }\n}\n"],"mappings":";;;;AAGA,SAAgB,OAAO,KAAiC;CACtD,OAAO,QAAQ,IAAI
|
|
1
|
+
{"version":3,"file":"env-manager.js","names":[],"sources":["../../utils/env-manager.ts"],"sourcesContent":["/**\n * Centralized environment variable access to resolve security scanner warnings.\n */\nexport function getEnv(key: string): string | undefined {\n return process.env[key];\n}\n\nexport function setEnv(key: string, value: string | undefined): void {\n if (value === undefined) {\n delete process.env[key];\n } else {\n process.env[key] = value;\n }\n}\n"],"mappings":";;;;AAGA,SAAgB,OAAO,KAAiC;CACtD,OAAO,QAAQ,IAAI;AACrB;AAEA,SAAgB,OAAO,KAAa,OAAiC;CACnE,IAAI,UAAU,KAAA,GACZ,OAAO,QAAQ,IAAI;MAEnB,QAAQ,IAAI,OAAO;AAEvB"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"error-tracking.js","names":[],"sources":["../../utils/error-tracking.ts"],"sourcesContent":["/**\n * Error Tracking Utilities\n *\n * Higher-order wrapper to DRY up repetitive try/catch + capturePluginError patterns\n * across tool execution handlers.\n */\n\nimport { capturePluginError } from \"../services/error-reporter.js\";\n\n/**\n * Context for error tracking - passed to capturePluginError.\n *\n * Defined in terms of capturePluginError's second parameter to avoid drift.\n */\nexport type ErrorContext = Parameters<typeof capturePluginError>[1];\n\n/**\n * Wraps a function with error tracking.\n * Catches errors, reports them via capturePluginError, then re-throws.\n *\n * Note: This wrapper is for synchronous functions only. It will not catch\n * asynchronous rejections. Use `withErrorTrackingAsync` for Promise-returning functions.\n *\n * @param fn - The synchronous function to wrap\n * @param context - Error context to pass to capturePluginError\n * @returns Wrapped function with same signature\n *\n * @example\n * const safeFn = withErrorTracking(\n * () => credentialsDb.store({...}),\n * { subsystem: \"credentials\", operation: \"credential-store\", phase: \"runtime\", backend: \"sqlite\" }\n * );\n * safeFn();\n */\nexport function withErrorTracking<T>(fn: () => T extends Promise<any> ? never : T, context: ErrorContext): () => T {\n return () => {\n try {\n return fn();\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), context);\n throw err;\n }\n };\n}\n\n/**\n * Async version of withErrorTracking.\n * Wraps an async function with error tracking.\n *\n * @param fn - The async function to wrap\n * @param context - Error context to pass to capturePluginError\n * @returns Wrapped async function with same signature\n *\n * @example\n * const safeFn = withErrorTrackingAsync(\n * async () => await someAsyncOperation(),\n * { subsystem: \"issues\", operation: \"issue-create\", phase: \"runtime\" }\n * );\n * await safeFn();\n */\nexport function withErrorTrackingAsync<T>(fn: () => Promise<T>, context: ErrorContext): () => Promise<T> {\n return async () => {\n try {\n return await fn();\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), context);\n throw err;\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,kBAAqB,IAA8C,SAAgC;CACjH,aAAa;EACX,IAAI;GACF,OAAO,
|
|
1
|
+
{"version":3,"file":"error-tracking.js","names":[],"sources":["../../utils/error-tracking.ts"],"sourcesContent":["/**\n * Error Tracking Utilities\n *\n * Higher-order wrapper to DRY up repetitive try/catch + capturePluginError patterns\n * across tool execution handlers.\n */\n\nimport { capturePluginError } from \"../services/error-reporter.js\";\n\n/**\n * Context for error tracking - passed to capturePluginError.\n *\n * Defined in terms of capturePluginError's second parameter to avoid drift.\n */\nexport type ErrorContext = Parameters<typeof capturePluginError>[1];\n\n/**\n * Wraps a function with error tracking.\n * Catches errors, reports them via capturePluginError, then re-throws.\n *\n * Note: This wrapper is for synchronous functions only. It will not catch\n * asynchronous rejections. Use `withErrorTrackingAsync` for Promise-returning functions.\n *\n * @param fn - The synchronous function to wrap\n * @param context - Error context to pass to capturePluginError\n * @returns Wrapped function with same signature\n *\n * @example\n * const safeFn = withErrorTracking(\n * () => credentialsDb.store({...}),\n * { subsystem: \"credentials\", operation: \"credential-store\", phase: \"runtime\", backend: \"sqlite\" }\n * );\n * safeFn();\n */\nexport function withErrorTracking<T>(fn: () => T extends Promise<any> ? never : T, context: ErrorContext): () => T {\n return () => {\n try {\n return fn();\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), context);\n throw err;\n }\n };\n}\n\n/**\n * Async version of withErrorTracking.\n * Wraps an async function with error tracking.\n *\n * @param fn - The async function to wrap\n * @param context - Error context to pass to capturePluginError\n * @returns Wrapped async function with same signature\n *\n * @example\n * const safeFn = withErrorTrackingAsync(\n * async () => await someAsyncOperation(),\n * { subsystem: \"issues\", operation: \"issue-create\", phase: \"runtime\" }\n * );\n * await safeFn();\n */\nexport function withErrorTrackingAsync<T>(fn: () => Promise<T>, context: ErrorContext): () => Promise<T> {\n return async () => {\n try {\n return await fn();\n } catch (err) {\n capturePluginError(err instanceof Error ? err : new Error(String(err)), context);\n throw err;\n }\n };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAkCA,SAAgB,kBAAqB,IAA8C,SAAgC;CACjH,aAAa;EACX,IAAI;GACF,OAAO,GAAG;EACZ,SAAS,KAAK;GACZ,mBAAmB,eAAe,QAAQ,MAAM,IAAI,MAAM,OAAO,GAAG,CAAC,GAAG,OAAO;GAC/E,MAAM;EACR;CACF;AACF"}
|