@tenphi/akno-core 0.1.0
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/LICENSE +83 -0
- package/README.md +26 -0
- package/config/default.jsonc +548 -0
- package/dist/bench/answer-corpus.d.ts +48 -0
- package/dist/bench/answer-corpus.d.ts.map +1 -0
- package/dist/bench/answer-corpus.js +491 -0
- package/dist/bench/answer-corpus.js.map +1 -0
- package/dist/bench/answer.d.ts +156 -0
- package/dist/bench/answer.d.ts.map +1 -0
- package/dist/bench/answer.js +608 -0
- package/dist/bench/answer.js.map +1 -0
- package/dist/bench/auto-recall-answer-corpus.d.ts +21 -0
- package/dist/bench/auto-recall-answer-corpus.d.ts.map +1 -0
- package/dist/bench/auto-recall-answer-corpus.js +245 -0
- package/dist/bench/auto-recall-answer-corpus.js.map +1 -0
- package/dist/bench/auto-recall-answer.d.ts +198 -0
- package/dist/bench/auto-recall-answer.d.ts.map +1 -0
- package/dist/bench/auto-recall-answer.js +720 -0
- package/dist/bench/auto-recall-answer.js.map +1 -0
- package/dist/bench/auto-recall-corpus.d.ts +45 -0
- package/dist/bench/auto-recall-corpus.d.ts.map +1 -0
- package/dist/bench/auto-recall-corpus.js +221 -0
- package/dist/bench/auto-recall-corpus.js.map +1 -0
- package/dist/bench/auto-recall.d.ts +171 -0
- package/dist/bench/auto-recall.d.ts.map +1 -0
- package/dist/bench/auto-recall.js +651 -0
- package/dist/bench/auto-recall.js.map +1 -0
- package/dist/bench/entity-resolution.d.ts +37 -0
- package/dist/bench/entity-resolution.d.ts.map +1 -0
- package/dist/bench/entity-resolution.js +121 -0
- package/dist/bench/entity-resolution.js.map +1 -0
- package/dist/bench/graph.d.ts +57 -0
- package/dist/bench/graph.d.ts.map +1 -0
- package/dist/bench/graph.js +573 -0
- package/dist/bench/graph.js.map +1 -0
- package/dist/bench/llm-ranking-probe.d.ts +27 -0
- package/dist/bench/llm-ranking-probe.d.ts.map +1 -0
- package/dist/bench/llm-ranking-probe.js +105 -0
- package/dist/bench/llm-ranking-probe.js.map +1 -0
- package/dist/bench/merge-discovery-corpus.d.ts +26 -0
- package/dist/bench/merge-discovery-corpus.d.ts.map +1 -0
- package/dist/bench/merge-discovery-corpus.js +114 -0
- package/dist/bench/merge-discovery-corpus.js.map +1 -0
- package/dist/bench/merge-discovery-review.d.ts +81 -0
- package/dist/bench/merge-discovery-review.d.ts.map +1 -0
- package/dist/bench/merge-discovery-review.js +149 -0
- package/dist/bench/merge-discovery-review.js.map +1 -0
- package/dist/bench/merge-discovery.d.ts +122 -0
- package/dist/bench/merge-discovery.d.ts.map +1 -0
- package/dist/bench/merge-discovery.js +390 -0
- package/dist/bench/merge-discovery.js.map +1 -0
- package/dist/bench/mixed-retrieval.d.ts +35 -0
- package/dist/bench/mixed-retrieval.d.ts.map +1 -0
- package/dist/bench/mixed-retrieval.js +355 -0
- package/dist/bench/mixed-retrieval.js.map +1 -0
- package/dist/bench/ranking-corpus.d.ts +29 -0
- package/dist/bench/ranking-corpus.d.ts.map +1 -0
- package/dist/bench/ranking-corpus.js +514 -0
- package/dist/bench/ranking-corpus.js.map +1 -0
- package/dist/bench/ranking-end-to-end.d.ts +112 -0
- package/dist/bench/ranking-end-to-end.d.ts.map +1 -0
- package/dist/bench/ranking-end-to-end.js +479 -0
- package/dist/bench/ranking-end-to-end.js.map +1 -0
- package/dist/bench/ranking-latency.d.ts +71 -0
- package/dist/bench/ranking-latency.d.ts.map +1 -0
- package/dist/bench/ranking-latency.js +130 -0
- package/dist/bench/ranking-latency.js.map +1 -0
- package/dist/bench/ranking-matrix.d.ts +141 -0
- package/dist/bench/ranking-matrix.d.ts.map +1 -0
- package/dist/bench/ranking-matrix.js +526 -0
- package/dist/bench/ranking-matrix.js.map +1 -0
- package/dist/bench/ranking-review.d.ts +93 -0
- package/dist/bench/ranking-review.d.ts.map +1 -0
- package/dist/bench/ranking-review.js +249 -0
- package/dist/bench/ranking-review.js.map +1 -0
- package/dist/bench/ranking.d.ts +122 -0
- package/dist/bench/ranking.d.ts.map +1 -0
- package/dist/bench/ranking.js +547 -0
- package/dist/bench/ranking.js.map +1 -0
- package/dist/bench.d.ts +60 -0
- package/dist/bench.d.ts.map +1 -0
- package/dist/bench.js +167 -0
- package/dist/bench.js.map +1 -0
- package/dist/config/jsonc.d.ts +8 -0
- package/dist/config/jsonc.d.ts.map +1 -0
- package/dist/config/jsonc.js +85 -0
- package/dist/config/jsonc.js.map +1 -0
- package/dist/config/load.d.ts +43 -0
- package/dist/config/load.d.ts.map +1 -0
- package/dist/config/load.js +608 -0
- package/dist/config/load.js.map +1 -0
- package/dist/config/paths.d.ts +10 -0
- package/dist/config/paths.d.ts.map +1 -0
- package/dist/config/paths.js +38 -0
- package/dist/config/paths.js.map +1 -0
- package/dist/config/schema.d.ts +698 -0
- package/dist/config/schema.d.ts.map +1 -0
- package/dist/config/schema.js +394 -0
- package/dist/config/schema.js.map +1 -0
- package/dist/config/write-rules.d.ts +41 -0
- package/dist/config/write-rules.d.ts.map +1 -0
- package/dist/config/write-rules.js +225 -0
- package/dist/config/write-rules.js.map +1 -0
- package/dist/config/write-setup.d.ts +42 -0
- package/dist/config/write-setup.d.ts.map +1 -0
- package/dist/config/write-setup.js +358 -0
- package/dist/config/write-setup.js.map +1 -0
- package/dist/context.d.ts +64 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +14 -0
- package/dist/context.js.map +1 -0
- package/dist/doctor.d.ts +115 -0
- package/dist/doctor.d.ts.map +1 -0
- package/dist/doctor.js +388 -0
- package/dist/doctor.js.map +1 -0
- package/dist/index/chunk.d.ts +62 -0
- package/dist/index/chunk.d.ts.map +1 -0
- package/dist/index/chunk.js +307 -0
- package/dist/index/chunk.js.map +1 -0
- package/dist/index/defer.d.ts +32 -0
- package/dist/index/defer.d.ts.map +1 -0
- package/dist/index/defer.js +66 -0
- package/dist/index/defer.js.map +1 -0
- package/dist/index/derive.d.ts +99 -0
- package/dist/index/derive.d.ts.map +1 -0
- package/dist/index/derive.js +378 -0
- package/dist/index/derive.js.map +1 -0
- package/dist/index/entity-resolution.d.ts +68 -0
- package/dist/index/entity-resolution.d.ts.map +1 -0
- package/dist/index/entity-resolution.js +305 -0
- package/dist/index/entity-resolution.js.map +1 -0
- package/dist/index/graph.d.ts +46 -0
- package/dist/index/graph.d.ts.map +1 -0
- package/dist/index/graph.js +618 -0
- package/dist/index/graph.js.map +1 -0
- package/dist/index/indexer.d.ts +301 -0
- package/dist/index/indexer.d.ts.map +1 -0
- package/dist/index/indexer.js +1398 -0
- package/dist/index/indexer.js.map +1 -0
- package/dist/index/revision-barrier.d.ts +30 -0
- package/dist/index/revision-barrier.d.ts.map +1 -0
- package/dist/index/revision-barrier.js +140 -0
- package/dist/index/revision-barrier.js.map +1 -0
- package/dist/index.d.ts +55 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +37 -0
- package/dist/index.js.map +1 -0
- package/dist/ingest/adoption-eligibility.d.ts +4 -0
- package/dist/ingest/adoption-eligibility.d.ts.map +1 -0
- package/dist/ingest/adoption-eligibility.js +18 -0
- package/dist/ingest/adoption-eligibility.js.map +1 -0
- package/dist/ingest/availability.d.ts +15 -0
- package/dist/ingest/availability.d.ts.map +1 -0
- package/dist/ingest/availability.js +37 -0
- package/dist/ingest/availability.js.map +1 -0
- package/dist/ingest/extract.d.ts +58 -0
- package/dist/ingest/extract.d.ts.map +1 -0
- package/dist/ingest/extract.js +255 -0
- package/dist/ingest/extract.js.map +1 -0
- package/dist/ingest/fetch.d.ts +35 -0
- package/dist/ingest/fetch.d.ts.map +1 -0
- package/dist/ingest/fetch.js +142 -0
- package/dist/ingest/fetch.js.map +1 -0
- package/dist/ingest/inbox.d.ts +53 -0
- package/dist/ingest/inbox.d.ts.map +1 -0
- package/dist/ingest/inbox.js +110 -0
- package/dist/ingest/inbox.js.map +1 -0
- package/dist/ingest/name.d.ts +72 -0
- package/dist/ingest/name.d.ts.map +1 -0
- package/dist/ingest/name.js +208 -0
- package/dist/ingest/name.js.map +1 -0
- package/dist/ingest/parts.d.ts +78 -0
- package/dist/ingest/parts.d.ts.map +1 -0
- package/dist/ingest/parts.js +52 -0
- package/dist/ingest/parts.js.map +1 -0
- package/dist/ingest/rendition.d.ts +75 -0
- package/dist/ingest/rendition.d.ts.map +1 -0
- package/dist/ingest/rendition.js +92 -0
- package/dist/ingest/rendition.js.map +1 -0
- package/dist/ingest/store.d.ts +66 -0
- package/dist/ingest/store.d.ts.map +1 -0
- package/dist/ingest/store.js +92 -0
- package/dist/ingest/store.js.map +1 -0
- package/dist/kb/folders.d.ts +37 -0
- package/dist/kb/folders.d.ts.map +1 -0
- package/dist/kb/folders.js +133 -0
- package/dist/kb/folders.js.map +1 -0
- package/dist/kb/frontmatter.d.ts +91 -0
- package/dist/kb/frontmatter.d.ts.map +1 -0
- package/dist/kb/frontmatter.js +274 -0
- package/dist/kb/frontmatter.js.map +1 -0
- package/dist/kb/line-facts.d.ts +29 -0
- package/dist/kb/line-facts.d.ts.map +1 -0
- package/dist/kb/line-facts.js +23 -0
- package/dist/kb/line-facts.js.map +1 -0
- package/dist/kb/page.d.ts +92 -0
- package/dist/kb/page.d.ts.map +1 -0
- package/dist/kb/page.js +243 -0
- package/dist/kb/page.js.map +1 -0
- package/dist/kb/scan.d.ts +38 -0
- package/dist/kb/scan.d.ts.map +1 -0
- package/dist/kb/scan.js +109 -0
- package/dist/kb/scan.js.map +1 -0
- package/dist/kb/words.d.ts +30 -0
- package/dist/kb/words.d.ts.map +1 -0
- package/dist/kb/words.js +107 -0
- package/dist/kb/words.js.map +1 -0
- package/dist/maintenance/adopt.d.ts +65 -0
- package/dist/maintenance/adopt.d.ts.map +1 -0
- package/dist/maintenance/adopt.js +238 -0
- package/dist/maintenance/adopt.js.map +1 -0
- package/dist/maintenance/budget.d.ts +55 -0
- package/dist/maintenance/budget.d.ts.map +1 -0
- package/dist/maintenance/budget.js +61 -0
- package/dist/maintenance/budget.js.map +1 -0
- package/dist/maintenance/conflicts.d.ts +105 -0
- package/dist/maintenance/conflicts.d.ts.map +1 -0
- package/dist/maintenance/conflicts.js +368 -0
- package/dist/maintenance/conflicts.js.map +1 -0
- package/dist/maintenance/contradictions.d.ts +34 -0
- package/dist/maintenance/contradictions.d.ts.map +1 -0
- package/dist/maintenance/contradictions.js +199 -0
- package/dist/maintenance/contradictions.js.map +1 -0
- package/dist/maintenance/curate.d.ts +156 -0
- package/dist/maintenance/curate.d.ts.map +1 -0
- package/dist/maintenance/curate.js +2070 -0
- package/dist/maintenance/curate.js.map +1 -0
- package/dist/maintenance/dream.d.ts +139 -0
- package/dist/maintenance/dream.d.ts.map +1 -0
- package/dist/maintenance/dream.js +1632 -0
- package/dist/maintenance/dream.js.map +1 -0
- package/dist/maintenance/graph-candidates.d.ts +19 -0
- package/dist/maintenance/graph-candidates.d.ts.map +1 -0
- package/dist/maintenance/graph-candidates.js +120 -0
- package/dist/maintenance/graph-candidates.js.map +1 -0
- package/dist/maintenance/housekeeping.d.ts +81 -0
- package/dist/maintenance/housekeeping.d.ts.map +1 -0
- package/dist/maintenance/housekeeping.js +281 -0
- package/dist/maintenance/housekeeping.js.map +1 -0
- package/dist/maintenance/link-repairs.d.ts +58 -0
- package/dist/maintenance/link-repairs.d.ts.map +1 -0
- package/dist/maintenance/link-repairs.js +281 -0
- package/dist/maintenance/link-repairs.js.map +1 -0
- package/dist/maintenance/log.d.ts +41 -0
- package/dist/maintenance/log.d.ts.map +1 -0
- package/dist/maintenance/log.js +72 -0
- package/dist/maintenance/log.js.map +1 -0
- package/dist/maintenance/managed-item-routing.d.ts +48 -0
- package/dist/maintenance/managed-item-routing.d.ts.map +1 -0
- package/dist/maintenance/managed-item-routing.js +281 -0
- package/dist/maintenance/managed-item-routing.js.map +1 -0
- package/dist/maintenance/managed-item-sources.d.ts +38 -0
- package/dist/maintenance/managed-item-sources.d.ts.map +1 -0
- package/dist/maintenance/managed-item-sources.js +324 -0
- package/dist/maintenance/managed-item-sources.js.map +1 -0
- package/dist/maintenance/managed-items.d.ts +162 -0
- package/dist/maintenance/managed-items.d.ts.map +1 -0
- package/dist/maintenance/managed-items.js +1329 -0
- package/dist/maintenance/managed-items.js.map +1 -0
- package/dist/maintenance/merge-classifier.d.ts +19 -0
- package/dist/maintenance/merge-classifier.d.ts.map +1 -0
- package/dist/maintenance/merge-classifier.js +49 -0
- package/dist/maintenance/merge-classifier.js.map +1 -0
- package/dist/maintenance/model-telemetry.d.ts +45 -0
- package/dist/maintenance/model-telemetry.d.ts.map +1 -0
- package/dist/maintenance/model-telemetry.js +107 -0
- package/dist/maintenance/model-telemetry.js.map +1 -0
- package/dist/maintenance/observe.d.ts +72 -0
- package/dist/maintenance/observe.d.ts.map +1 -0
- package/dist/maintenance/observe.js +265 -0
- package/dist/maintenance/observe.js.map +1 -0
- package/dist/maintenance/path-policy.d.ts +83 -0
- package/dist/maintenance/path-policy.d.ts.map +1 -0
- package/dist/maintenance/path-policy.js +360 -0
- package/dist/maintenance/path-policy.js.map +1 -0
- package/dist/maintenance/plans.d.ts +366 -0
- package/dist/maintenance/plans.d.ts.map +1 -0
- package/dist/maintenance/plans.js +4060 -0
- package/dist/maintenance/plans.js.map +1 -0
- package/dist/maintenance/profile.d.ts +33 -0
- package/dist/maintenance/profile.d.ts.map +1 -0
- package/dist/maintenance/profile.js +99 -0
- package/dist/maintenance/profile.js.map +1 -0
- package/dist/maintenance/recovery.d.ts +38 -0
- package/dist/maintenance/recovery.d.ts.map +1 -0
- package/dist/maintenance/recovery.js +217 -0
- package/dist/maintenance/recovery.js.map +1 -0
- package/dist/maintenance/repair.d.ts +37 -0
- package/dist/maintenance/repair.d.ts.map +1 -0
- package/dist/maintenance/repair.js +183 -0
- package/dist/maintenance/repair.js.map +1 -0
- package/dist/maintenance/rule-drift.d.ts +104 -0
- package/dist/maintenance/rule-drift.d.ts.map +1 -0
- package/dist/maintenance/rule-drift.js +507 -0
- package/dist/maintenance/rule-drift.js.map +1 -0
- package/dist/maintenance/run-verification.d.ts +44 -0
- package/dist/maintenance/run-verification.d.ts.map +1 -0
- package/dist/maintenance/run-verification.js +253 -0
- package/dist/maintenance/run-verification.js.map +1 -0
- package/dist/maintenance/runs.d.ts +142 -0
- package/dist/maintenance/runs.d.ts.map +1 -0
- package/dist/maintenance/runs.js +439 -0
- package/dist/maintenance/runs.js.map +1 -0
- package/dist/maintenance/semantic-merge-discovery.d.ts +54 -0
- package/dist/maintenance/semantic-merge-discovery.d.ts.map +1 -0
- package/dist/maintenance/semantic-merge-discovery.js +308 -0
- package/dist/maintenance/semantic-merge-discovery.js.map +1 -0
- package/dist/maintenance/temporal.d.ts +44 -0
- package/dist/maintenance/temporal.d.ts.map +1 -0
- package/dist/maintenance/temporal.js +295 -0
- package/dist/maintenance/temporal.js.map +1 -0
- package/dist/models/client.d.ts +214 -0
- package/dist/models/client.d.ts.map +1 -0
- package/dist/models/client.js +904 -0
- package/dist/models/client.js.map +1 -0
- package/dist/models/provider-api.d.ts +28 -0
- package/dist/models/provider-api.d.ts.map +1 -0
- package/dist/models/provider-api.js +320 -0
- package/dist/models/provider-api.js.map +1 -0
- package/dist/open.d.ts +163 -0
- package/dist/open.d.ts.map +1 -0
- package/dist/open.js +382 -0
- package/dist/open.js.map +1 -0
- package/dist/ops/adopt.d.ts +12 -0
- package/dist/ops/adopt.d.ts.map +1 -0
- package/dist/ops/adopt.js +138 -0
- package/dist/ops/adopt.js.map +1 -0
- package/dist/ops/answer.d.ts +29 -0
- package/dist/ops/answer.d.ts.map +1 -0
- package/dist/ops/answer.js +557 -0
- package/dist/ops/answer.js.map +1 -0
- package/dist/ops/context.d.ts +18 -0
- package/dist/ops/context.d.ts.map +1 -0
- package/dist/ops/context.js +713 -0
- package/dist/ops/context.js.map +1 -0
- package/dist/ops/folder.d.ts +23 -0
- package/dist/ops/folder.d.ts.map +1 -0
- package/dist/ops/folder.js +130 -0
- package/dist/ops/folder.js.map +1 -0
- package/dist/ops/forget.d.ts +14 -0
- package/dist/ops/forget.d.ts.map +1 -0
- package/dist/ops/forget.js +212 -0
- package/dist/ops/forget.js.map +1 -0
- package/dist/ops/graph.d.ts +10 -0
- package/dist/ops/graph.d.ts.map +1 -0
- package/dist/ops/graph.js +480 -0
- package/dist/ops/graph.js.map +1 -0
- package/dist/ops/ingest.d.ts +40 -0
- package/dist/ops/ingest.d.ts.map +1 -0
- package/dist/ops/ingest.js +434 -0
- package/dist/ops/ingest.js.map +1 -0
- package/dist/ops/list.d.ts +9 -0
- package/dist/ops/list.d.ts.map +1 -0
- package/dist/ops/list.js +217 -0
- package/dist/ops/list.js.map +1 -0
- package/dist/ops/move.d.ts +20 -0
- package/dist/ops/move.d.ts.map +1 -0
- package/dist/ops/move.js +173 -0
- package/dist/ops/move.js.map +1 -0
- package/dist/ops/read.d.ts +9 -0
- package/dist/ops/read.d.ts.map +1 -0
- package/dist/ops/read.js +271 -0
- package/dist/ops/read.js.map +1 -0
- package/dist/ops/recall.d.ts +13 -0
- package/dist/ops/recall.d.ts.map +1 -0
- package/dist/ops/recall.js +297 -0
- package/dist/ops/recall.js.map +1 -0
- package/dist/ops/remember.d.ts +34 -0
- package/dist/ops/remember.d.ts.map +1 -0
- package/dist/ops/remember.js +660 -0
- package/dist/ops/remember.js.map +1 -0
- package/dist/ops/timeline.d.ts +11 -0
- package/dist/ops/timeline.d.ts.map +1 -0
- package/dist/ops/timeline.js +111 -0
- package/dist/ops/timeline.js.map +1 -0
- package/dist/ops/undo.d.ts +9 -0
- package/dist/ops/undo.d.ts.map +1 -0
- package/dist/ops/undo.js +38 -0
- package/dist/ops/undo.js.map +1 -0
- package/dist/ops/write.d.ts +55 -0
- package/dist/ops/write.d.ts.map +1 -0
- package/dist/ops/write.js +463 -0
- package/dist/ops/write.js.map +1 -0
- package/dist/recall/assemble.d.ts +81 -0
- package/dist/recall/assemble.d.ts.map +1 -0
- package/dist/recall/assemble.js +603 -0
- package/dist/recall/assemble.js.map +1 -0
- package/dist/recall/expand.d.ts +55 -0
- package/dist/recall/expand.d.ts.map +1 -0
- package/dist/recall/expand.js +282 -0
- package/dist/recall/expand.js.map +1 -0
- package/dist/recall/graph-arm.d.ts +17 -0
- package/dist/recall/graph-arm.d.ts.map +1 -0
- package/dist/recall/graph-arm.js +227 -0
- package/dist/recall/graph-arm.js.map +1 -0
- package/dist/recall/llm-rerank.d.ts +55 -0
- package/dist/recall/llm-rerank.d.ts.map +1 -0
- package/dist/recall/llm-rerank.js +213 -0
- package/dist/recall/llm-rerank.js.map +1 -0
- package/dist/recall/reranker-calibration.d.ts +23 -0
- package/dist/recall/reranker-calibration.d.ts.map +1 -0
- package/dist/recall/reranker-calibration.js +161 -0
- package/dist/recall/reranker-calibration.js.map +1 -0
- package/dist/recall/search.d.ts +107 -0
- package/dist/recall/search.d.ts.map +1 -0
- package/dist/recall/search.js +530 -0
- package/dist/recall/search.js.map +1 -0
- package/dist/reserved.d.ts +39 -0
- package/dist/reserved.d.ts.map +1 -0
- package/dist/reserved.js +63 -0
- package/dist/reserved.js.map +1 -0
- package/dist/rules/compile.d.ts +27 -0
- package/dist/rules/compile.d.ts.map +1 -0
- package/dist/rules/compile.js +105 -0
- package/dist/rules/compile.js.map +1 -0
- package/dist/setup/model-free.d.ts +15 -0
- package/dist/setup/model-free.d.ts.map +1 -0
- package/dist/setup/model-free.js +26 -0
- package/dist/setup/model-free.js.map +1 -0
- package/dist/setup/openai.d.ts +46 -0
- package/dist/setup/openai.d.ts.map +1 -0
- package/dist/setup/openai.js +165 -0
- package/dist/setup/openai.js.map +1 -0
- package/dist/store/db.d.ts +61 -0
- package/dist/store/db.d.ts.map +1 -0
- package/dist/store/db.js +265 -0
- package/dist/store/db.js.map +1 -0
- package/dist/store/ids.d.ts +29 -0
- package/dist/store/ids.d.ts.map +1 -0
- package/dist/store/ids.js +0 -0
- package/dist/store/ids.js.map +1 -0
- package/dist/store/migrations.d.ts +54 -0
- package/dist/store/migrations.d.ts.map +1 -0
- package/dist/store/migrations.js +923 -0
- package/dist/store/migrations.js.map +1 -0
- package/dist/store/vectors.d.ts +34 -0
- package/dist/store/vectors.d.ts.map +1 -0
- package/dist/store/vectors.js +158 -0
- package/dist/store/vectors.js.map +1 -0
- package/dist/timeline/documents.d.ts +23 -0
- package/dist/timeline/documents.d.ts.map +1 -0
- package/dist/timeline/documents.js +262 -0
- package/dist/timeline/documents.js.map +1 -0
- package/dist/watch/watcher.d.ts +43 -0
- package/dist/watch/watcher.d.ts.map +1 -0
- package/dist/watch/watcher.js +143 -0
- package/dist/watch/watcher.js.map +1 -0
- package/dist/write/atomic.d.ts +34 -0
- package/dist/write/atomic.d.ts.map +1 -0
- package/dist/write/atomic.js +58 -0
- package/dist/write/atomic.js.map +1 -0
- package/dist/write/conflict.d.ts +43 -0
- package/dist/write/conflict.d.ts.map +1 -0
- package/dist/write/conflict.js +149 -0
- package/dist/write/conflict.js.map +1 -0
- package/dist/write/edit.d.ts +59 -0
- package/dist/write/edit.d.ts.map +1 -0
- package/dist/write/edit.js +231 -0
- package/dist/write/edit.js.map +1 -0
- package/dist/write/gate.d.ts +59 -0
- package/dist/write/gate.d.ts.map +1 -0
- package/dist/write/gate.js +99 -0
- package/dist/write/gate.js.map +1 -0
- package/dist/write/journal.d.ts +90 -0
- package/dist/write/journal.d.ts.map +1 -0
- package/dist/write/journal.js +187 -0
- package/dist/write/journal.js.map +1 -0
- package/dist/write/ledger.d.ts +28 -0
- package/dist/write/ledger.d.ts.map +1 -0
- package/dist/write/ledger.js +141 -0
- package/dist/write/ledger.js.map +1 -0
- package/dist/write/placement.d.ts +29 -0
- package/dist/write/placement.d.ts.map +1 -0
- package/dist/write/placement.js +140 -0
- package/dist/write/placement.js.map +1 -0
- package/dist/write/remember-fallback.d.ts +18 -0
- package/dist/write/remember-fallback.d.ts.map +1 -0
- package/dist/write/remember-fallback.js +48 -0
- package/dist/write/remember-fallback.js.map +1 -0
- package/dist/write/retain.d.ts +61 -0
- package/dist/write/retain.d.ts.map +1 -0
- package/dist/write/retain.js +272 -0
- package/dist/write/retain.js.map +1 -0
- package/package.json +50 -0
- package/swift/extract.swift +232 -0
|
@@ -0,0 +1,660 @@
|
|
|
1
|
+
import { RememberInput, } from '@tenphi/akno-protocol';
|
|
2
|
+
import { ModelClient } from "../models/client.js";
|
|
3
|
+
import { runRetain } from "../write/retain.js";
|
|
4
|
+
import { newPrefixedId, sha256 } from "../store/ids.js";
|
|
5
|
+
import { isReserved } from "../reserved.js";
|
|
6
|
+
import { folderCatalog } from "../kb/folders.js";
|
|
7
|
+
import { parsePage } from "../kb/page.js";
|
|
8
|
+
import { contentWords } from "../kb/words.js";
|
|
9
|
+
import { detectConflict } from "../write/conflict.js";
|
|
10
|
+
import { fileEntry } from "../write/journal.js";
|
|
11
|
+
import { writeFileAtomic } from "../write/atomic.js";
|
|
12
|
+
import { managedSourceReference, placeManagedItems } from "../write/placement.js";
|
|
13
|
+
import { resolveRememberFallback } from "../write/remember-fallback.js";
|
|
14
|
+
import { recall } from "./recall.js";
|
|
15
|
+
import { appendToLedger, titleFromSlug } from "./write.js";
|
|
16
|
+
import fsp from 'node:fs/promises';
|
|
17
|
+
import path from 'node:path';
|
|
18
|
+
/**
|
|
19
|
+
* Hand over a transcript or notes; Akno runs the retain mission with its own
|
|
20
|
+
* model and produces the writes itself. **The answer for a host that does not want
|
|
21
|
+
* to build a curator** — an agent that wants control uses `write` and ignores this.
|
|
22
|
+
*
|
|
23
|
+
* The steps, in order: retain mission → route by recall → conflict check →
|
|
24
|
+
* edit the file → journal → the indexer follows. `remember` never writes a fact.
|
|
25
|
+
* It writes a *sentence into a file*, and facts appear afterwards because facts are
|
|
26
|
+
* derived from sentences.
|
|
27
|
+
*
|
|
28
|
+
* **Two things the caller controls, and one it does not.** It supplies the text and,
|
|
29
|
+
* optionally, a `mission` — what to pay attention to in this particular text, which is the
|
|
30
|
+
* knowledge only the caller has: that a message was forwarded and belongs on somebody else's
|
|
31
|
+
* page, that a channel is mostly logistics, that a subject matters today. What it cannot
|
|
32
|
+
* supply is a replacement for the standing rules; the mission is appended to them. A host
|
|
33
|
+
* that wants to decide phrasing and placement itself has `write` for exactly that.
|
|
34
|
+
*
|
|
35
|
+
* **Retain runs on the cycle's model when one is configured.** It is a maintenance tier, and
|
|
36
|
+
* the tier's output is largely a function of the model behind it — the same measurement that
|
|
37
|
+
* put `maintenance.model` there in the first place. A knowledge base pointing the nightly
|
|
38
|
+
* cycle at a strong model wants its conversation digests to use it too, and would not expect
|
|
39
|
+
* to configure that twice.
|
|
40
|
+
*/
|
|
41
|
+
export async function remember(ctx, rawInput) {
|
|
42
|
+
const input = RememberInput.parse(rawInput);
|
|
43
|
+
// Retain is a tier with a mission and an on/off switch like the others. Honouring
|
|
44
|
+
// both here is what makes that config real: a `mission` nothing reads is a promise the file
|
|
45
|
+
// makes and the code breaks.
|
|
46
|
+
if (!ctx.config.maintenance.retain.enabled) {
|
|
47
|
+
return {
|
|
48
|
+
status: 'ok',
|
|
49
|
+
outcome: 'noop',
|
|
50
|
+
note: 'the retain tier is disabled in config (maintenance.retain.enabled) — nothing was kept',
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
const curator = ctx.config.maintenance.model
|
|
54
|
+
? new ModelClient(ctx.config.maintenance.model)
|
|
55
|
+
: ctx.models.derive;
|
|
56
|
+
// The caller's mission wins over the install's, because it is the more specific of the two:
|
|
57
|
+
// config states a standing policy, a call states what is true of this text.
|
|
58
|
+
const mission = input.mission ?? ctx.config.maintenance.retain.mission;
|
|
59
|
+
const catalog = folderCatalog(ctx.config, ctx.store);
|
|
60
|
+
const retained = await runRetain(input.text, curator, {
|
|
61
|
+
today: new Date().toISOString().slice(0, 10),
|
|
62
|
+
...(mission ? { mission } : {}),
|
|
63
|
+
folders: catalog,
|
|
64
|
+
});
|
|
65
|
+
if (retained.error) {
|
|
66
|
+
// No model means no `remember`. Saying so is the whole contract —
|
|
67
|
+
// silently keeping nothing would look identical to "nothing was worth keeping".
|
|
68
|
+
return {
|
|
69
|
+
status: 'degraded',
|
|
70
|
+
degraded: [curator.degradedReason({})],
|
|
71
|
+
outcome: 'noop',
|
|
72
|
+
note: `the retain mission could not run: ${retained.error}`,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
if (retained.candidates.length === 0 && retained.events.length === 0) {
|
|
76
|
+
return {
|
|
77
|
+
status: 'ok',
|
|
78
|
+
outcome: 'noop',
|
|
79
|
+
considered: [],
|
|
80
|
+
note: 'nothing in that text was worth keeping — no durable claim, decision or preference',
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
// ── Route ───────────────────────────────────────────────────────────────
|
|
84
|
+
const routed = await Promise.all(retained.candidates.map(async (candidate) => ({
|
|
85
|
+
candidate,
|
|
86
|
+
...(await route(ctx, candidate)),
|
|
87
|
+
})));
|
|
88
|
+
const configuredFallback = await resolveRememberFallback(ctx, catalog);
|
|
89
|
+
let fallbackNeeded = false;
|
|
90
|
+
let fallbackUsed = false;
|
|
91
|
+
// `kept` covers a claim bound for a page that does not exist yet, not just one that routed.
|
|
92
|
+
// It used to mean "routed", and once creating became possible that made every new page read
|
|
93
|
+
// back as a claim that had been dropped.
|
|
94
|
+
const considered = routed.map((entry) => {
|
|
95
|
+
const candidateFallback = entry.candidate.page;
|
|
96
|
+
const candidateFallbackExists = candidateFallback !== undefined && pageExists(ctx, candidateFallback);
|
|
97
|
+
const candidateFallbackCanCreate = candidateFallback !== undefined &&
|
|
98
|
+
!candidateFallbackExists &&
|
|
99
|
+
admittedFolder(catalog, candidateFallback) !== null;
|
|
100
|
+
const ordinarySlug = entry.slug ?? (candidateFallbackCanCreate ? candidateFallback : null);
|
|
101
|
+
const canUseConfiguredFallback = ordinarySlug === null && configuredFallback !== null && configuredFallback.status !== 'unavailable';
|
|
102
|
+
if (ordinarySlug === null)
|
|
103
|
+
fallbackNeeded = true;
|
|
104
|
+
if (canUseConfiguredFallback)
|
|
105
|
+
fallbackUsed = true;
|
|
106
|
+
const slug = ordinarySlug ?? (canUseConfiguredFallback ? configuredFallback.slug : null);
|
|
107
|
+
return {
|
|
108
|
+
claim: entry.candidate.text,
|
|
109
|
+
kept: slug !== null,
|
|
110
|
+
slug,
|
|
111
|
+
score: Math.round(entry.score * 1000) / 1000,
|
|
112
|
+
destination: entry.slug
|
|
113
|
+
? 'existing_admitted_page'
|
|
114
|
+
: candidateFallbackCanCreate
|
|
115
|
+
? 'new_managed_page'
|
|
116
|
+
: canUseConfiguredFallback
|
|
117
|
+
? 'configured_fallback'
|
|
118
|
+
: 'no_writable_destination',
|
|
119
|
+
written: false,
|
|
120
|
+
};
|
|
121
|
+
});
|
|
122
|
+
if (input.dry_run) {
|
|
123
|
+
const folders = configuredFallback && configuredFallback.status !== 'unavailable'
|
|
124
|
+
? []
|
|
125
|
+
: requiredFolders(ctx, catalog, routed);
|
|
126
|
+
const needsApproval = routed.some((entry, index) => {
|
|
127
|
+
if (considered[index].kept || folders.some((folder) => folder.folder === suggestedFolder(entry))) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
const refused = entry.candidate.page && pageExists(ctx, entry.candidate.page) ? entry.candidate.page : undefined;
|
|
131
|
+
return unroutedReasonCode(entry.nearest, refused, entry.blocked) === 'routing_uncertain';
|
|
132
|
+
});
|
|
133
|
+
const noWritableDestination = routed.some((entry, index) => {
|
|
134
|
+
if (considered[index].kept || folders.some((folder) => folder.folder === suggestedFolder(entry))) {
|
|
135
|
+
return false;
|
|
136
|
+
}
|
|
137
|
+
const refused = entry.candidate.page && pageExists(ctx, entry.candidate.page) ? entry.candidate.page : undefined;
|
|
138
|
+
return unroutedReasonCode(entry.nearest, refused, entry.blocked) === 'no_writable_destination';
|
|
139
|
+
});
|
|
140
|
+
return {
|
|
141
|
+
status: 'ok',
|
|
142
|
+
outcome: folders.length > 0
|
|
143
|
+
? 'requires_folder'
|
|
144
|
+
: noWritableDestination
|
|
145
|
+
? 'no_writable_destination'
|
|
146
|
+
: needsApproval
|
|
147
|
+
? 'requires_approval'
|
|
148
|
+
: 'ok',
|
|
149
|
+
considered,
|
|
150
|
+
...(folders.length > 0 ? { requires_folder: folders } : {}),
|
|
151
|
+
...fallbackResult(fallbackNeeded, fallbackUsed, configuredFallback),
|
|
152
|
+
note: 'dry run — nothing was written',
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
// ── Place and write ─────────────────────────────────────────────────────
|
|
156
|
+
// One group per target means one placement-model call per page and one journal entry for the
|
|
157
|
+
// whole remember operation. A turn that keeps three related facts is one undoable decision.
|
|
158
|
+
const wrote = [];
|
|
159
|
+
const approvals = [];
|
|
160
|
+
const foldersNeeded = [];
|
|
161
|
+
const groups = new Map();
|
|
162
|
+
for (const [index, entry] of routed.entries()) {
|
|
163
|
+
const target = considered[index].slug;
|
|
164
|
+
if (target === null) {
|
|
165
|
+
const refusedPage = entry.candidate.page && pageExists(ctx, entry.candidate.page) ? entry.candidate.page : undefined;
|
|
166
|
+
approvals.push(proposeUnrouted(ctx, entry.candidate, entry.nearest, refusedPage, entry.blocked));
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
// **Creating a page on a guess is fine. Appending to an existing one is not.**
|
|
170
|
+
//
|
|
171
|
+
// `entry.slug === null` is routing having looked at the ranked candidates and said no page
|
|
172
|
+
// here is a home for this. The claim then fell through to `candidate.page` — a slug the
|
|
173
|
+
// retain model wrote before any of those candidates were scored — and if that slug happened
|
|
174
|
+
// to name a real page, the claim was appended to it with nothing having judged the pairing.
|
|
175
|
+
//
|
|
176
|
+
// Regression shape: a Vulpine Mutual renewal targeted an unrelated existing page that the
|
|
177
|
+
// reranker scored far below threshold. Routing refused correctly, but the fallback used to
|
|
178
|
+
// outrank that refusal and append under a newly invented heading.
|
|
179
|
+
//
|
|
180
|
+
// A guess that names *no* page still creates one: that is the case the fallback exists for,
|
|
181
|
+
// and a new page is inspectable and cheap to move. A guess that lands on somebody else's
|
|
182
|
+
// page becomes a question instead.
|
|
183
|
+
if (considered[index].destination !== 'configured_fallback' &&
|
|
184
|
+
entry.slug === null &&
|
|
185
|
+
pageExists(ctx, target)) {
|
|
186
|
+
considered[index].kept = false;
|
|
187
|
+
considered[index].slug = null;
|
|
188
|
+
considered[index].destination = 'no_writable_destination';
|
|
189
|
+
approvals.push(proposeUnrouted(ctx, entry.candidate, entry.nearest, target, entry.blocked));
|
|
190
|
+
continue;
|
|
191
|
+
}
|
|
192
|
+
const group = groups.get(target) ?? { entries: [], configuredFallback: false };
|
|
193
|
+
group.entries.push({ index, candidate: entry.candidate, blocked: entry.blocked });
|
|
194
|
+
if (considered[index].destination === 'configured_fallback')
|
|
195
|
+
group.configuredFallback = true;
|
|
196
|
+
groups.set(target, group);
|
|
197
|
+
}
|
|
198
|
+
const pending = [];
|
|
199
|
+
for (const [slug, group] of groups) {
|
|
200
|
+
const row = ctx.store.db
|
|
201
|
+
.prepare('SELECT id, rel_path, role, remember_management FROM pages WHERE slug = ?')
|
|
202
|
+
.get(slug);
|
|
203
|
+
if (row && (row.role !== 'knowledge' || row.remember_management !== 'integrate')) {
|
|
204
|
+
for (const entry of group.entries) {
|
|
205
|
+
considered[entry.index].kept = false;
|
|
206
|
+
considered[entry.index].slug = null;
|
|
207
|
+
considered[entry.index].destination = 'no_writable_destination';
|
|
208
|
+
approvals.push(proposeUnrouted(ctx, entry.candidate, [], slug, entry.blocked));
|
|
209
|
+
}
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
// The same policy, for a page that does not exist yet.
|
|
213
|
+
//
|
|
214
|
+
// The check above reads `role` and `remember_management` off the page row, so a folder
|
|
215
|
+
// declared `remember: "deny"` protected only the pages already in it — creation consulted
|
|
216
|
+
// nothing but whether the parent directory existed. A physical reference folder could
|
|
217
|
+
// therefore receive a new managed page even though no rule admitted injection there.
|
|
218
|
+
// Declining to create is the same answer the folder already gives to writing.
|
|
219
|
+
const parent = slug.slice(0, slug.lastIndexOf('/'));
|
|
220
|
+
if (!row && admittedFolder(catalog, slug) === null) {
|
|
221
|
+
for (const entry of group.entries) {
|
|
222
|
+
considered[entry.index].kept = false;
|
|
223
|
+
considered[entry.index].slug = null;
|
|
224
|
+
}
|
|
225
|
+
foldersNeeded.push({
|
|
226
|
+
folder: parent,
|
|
227
|
+
nearest: catalog
|
|
228
|
+
.filter((folder) => folder.eligible)
|
|
229
|
+
.map((folder) => folder.path)
|
|
230
|
+
.slice(0, 8),
|
|
231
|
+
});
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
const relPath = row?.rel_path ?? `${slug}.md`;
|
|
235
|
+
const current = row
|
|
236
|
+
? await fsp.readFile(path.join(ctx.config.aknoPath, relPath), 'utf8')
|
|
237
|
+
: newManagedPage(group.configuredFallback ? titleFromSlug(slug) : titleFor(group.entries[0].candidate, slug));
|
|
238
|
+
const accepted = [];
|
|
239
|
+
for (const entry of group.entries) {
|
|
240
|
+
if (row) {
|
|
241
|
+
const page = parsePage(relPath, current);
|
|
242
|
+
const conflict = detectConflict({
|
|
243
|
+
store: ctx.store,
|
|
244
|
+
pageId: row.id,
|
|
245
|
+
slug,
|
|
246
|
+
body: page.body,
|
|
247
|
+
bodyStartLine: page.bodyLine,
|
|
248
|
+
incoming: entry.candidate.text,
|
|
249
|
+
});
|
|
250
|
+
if (conflict) {
|
|
251
|
+
approvals.push({
|
|
252
|
+
proposal_id: recordConflictProposal(ctx, slug, entry.candidate, conflict.existing),
|
|
253
|
+
reason: `'${slug}' already claims something different: ${conflict.existing}. Ask which is current.`,
|
|
254
|
+
reason_code: 'conflict',
|
|
255
|
+
nearest: [slug],
|
|
256
|
+
});
|
|
257
|
+
continue;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
accepted.push(entry);
|
|
261
|
+
}
|
|
262
|
+
if (accepted.length === 0)
|
|
263
|
+
continue;
|
|
264
|
+
const items = accepted.map(({ candidate }) => ({
|
|
265
|
+
id: newPrefixedId('itm'),
|
|
266
|
+
text: candidate.text,
|
|
267
|
+
source: managedSourceReference(input.source ?? 'remember'),
|
|
268
|
+
origin: candidate.origin ?? 'unknown',
|
|
269
|
+
}));
|
|
270
|
+
const placed = await placeManagedItems(current, items, curator);
|
|
271
|
+
// Placement failure is recoverable and visible in Markdown: no claim is lost, and the next
|
|
272
|
+
// curate pass has an explicit `## Unsorted` inbox to integrate.
|
|
273
|
+
pending.push({
|
|
274
|
+
slug,
|
|
275
|
+
relPath,
|
|
276
|
+
content: placed.content,
|
|
277
|
+
existed: Boolean(row),
|
|
278
|
+
indexes: accepted.map((entry) => entry.index),
|
|
279
|
+
sources: accepted.flatMap((entry, itemIndex) => entry.candidate.evidence
|
|
280
|
+
? [
|
|
281
|
+
{
|
|
282
|
+
itemId: items[itemIndex].id,
|
|
283
|
+
sourceRef: items[itemIndex].source,
|
|
284
|
+
origin: items[itemIndex].origin,
|
|
285
|
+
evidence: entry.candidate.evidence,
|
|
286
|
+
evidenceHash: sha256(entry.candidate.evidence),
|
|
287
|
+
inputHash: sha256(input.text),
|
|
288
|
+
},
|
|
289
|
+
]
|
|
290
|
+
: []),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
const files = [];
|
|
294
|
+
const managedSources = [];
|
|
295
|
+
for (const page of pending) {
|
|
296
|
+
const result = await writeFileAtomic(ctx.config.aknoPath, page.relPath, page.content);
|
|
297
|
+
files.push(fileEntry(result));
|
|
298
|
+
page.indexes.forEach((index) => {
|
|
299
|
+
considered[index].written = true;
|
|
300
|
+
});
|
|
301
|
+
wrote.push({ slug: page.slug, action: page.existed ? 'appended' : 'created' });
|
|
302
|
+
managedSources.push(...page.sources);
|
|
303
|
+
}
|
|
304
|
+
for (const event of retained.events) {
|
|
305
|
+
const ledger = await appendToLedger(ctx, event);
|
|
306
|
+
if (ledger.file)
|
|
307
|
+
files.push(ledger.file);
|
|
308
|
+
wrote.push({
|
|
309
|
+
slug: ctx.config.paths.timeline.replace(/\.(md|markdown)$/i, ''),
|
|
310
|
+
line: ledger.line,
|
|
311
|
+
action: 'event',
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
const changeId = files.length > 0
|
|
315
|
+
? ctx.journal.record({
|
|
316
|
+
actor: ctx.actor,
|
|
317
|
+
op: 'remember',
|
|
318
|
+
summary: `remembered ${considered.filter((entry) => entry.written).length} item(s)`,
|
|
319
|
+
files,
|
|
320
|
+
})
|
|
321
|
+
: null;
|
|
322
|
+
persistManagedSources(ctx, managedSources);
|
|
323
|
+
let added = 0;
|
|
324
|
+
if (files.length > 0) {
|
|
325
|
+
const paths = [...new Set(files.map((file) => file.relPath))];
|
|
326
|
+
const report = await ctx.indexer.runForeground({ only: paths, modelPaths: [] });
|
|
327
|
+
ctx.derive.schedule(paths);
|
|
328
|
+
added = report.factsDerived;
|
|
329
|
+
}
|
|
330
|
+
// Deduplicated: three findings bound for the same new folder are one thing to do, not three.
|
|
331
|
+
const folders = deduplicateRequiredFolders(foldersNeeded);
|
|
332
|
+
// `requires_folder` outranks `requires_approval` in the outcome, because they ask different
|
|
333
|
+
// people. A folder is the caller's to declare, now, without leaving the turn; an approval
|
|
334
|
+
// waits on the user. Reporting the first as the second is how a caller learns to stop.
|
|
335
|
+
const noWritableDestination = approvals.some((approval) => approval.reason_code === 'no_writable_destination');
|
|
336
|
+
const routingApproval = approvals.some((approval) => approval.reason_code !== 'no_writable_destination');
|
|
337
|
+
const outcome = folders.length > 0
|
|
338
|
+
? 'requires_folder'
|
|
339
|
+
: noWritableDestination
|
|
340
|
+
? 'no_writable_destination'
|
|
341
|
+
: approvals.length > 0
|
|
342
|
+
? 'requires_approval'
|
|
343
|
+
: null;
|
|
344
|
+
const held = [
|
|
345
|
+
folders.length > 0
|
|
346
|
+
? `${folders.map((required) => `'${required.folder}'`).join(', ')} ` +
|
|
347
|
+
`${folders.length === 1 ? 'has' : 'have'} not been declared — call \`folder\` with a ` +
|
|
348
|
+
'description of what belongs there, then repeat this'
|
|
349
|
+
: null,
|
|
350
|
+
noWritableDestination ? 'some claims have no authorized writable destination' : null,
|
|
351
|
+
routingApproval ? 'some claims need the user to say where they go or resolve a conflict' : null,
|
|
352
|
+
].filter(Boolean);
|
|
353
|
+
if (wrote.length === 0) {
|
|
354
|
+
return {
|
|
355
|
+
status: 'ok',
|
|
356
|
+
outcome: outcome ?? 'noop',
|
|
357
|
+
considered,
|
|
358
|
+
...(approvals.length > 0 ? { approvals } : {}),
|
|
359
|
+
...(folders.length > 0 ? { requires_folder: folders } : {}),
|
|
360
|
+
...fallbackResult(fallbackNeeded, fallbackUsed, configuredFallback),
|
|
361
|
+
note: held.length > 0 ? `nothing was written: ${held.join('; ')}` : 'nothing was written',
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
return {
|
|
365
|
+
status: 'ok',
|
|
366
|
+
outcome: outcome ?? 'ok',
|
|
367
|
+
...(changeId ? { change_id: changeId } : {}),
|
|
368
|
+
wrote,
|
|
369
|
+
facts: { retired: 0, added },
|
|
370
|
+
considered,
|
|
371
|
+
...(approvals.length > 0 ? { approvals } : {}),
|
|
372
|
+
...(folders.length > 0 ? { requires_folder: folders } : {}),
|
|
373
|
+
...fallbackResult(fallbackNeeded, fallbackUsed, configuredFallback),
|
|
374
|
+
...(held.length > 0 ? { note: held.join('; ') } : {}),
|
|
375
|
+
};
|
|
376
|
+
}
|
|
377
|
+
function fallbackResult(needed, used, resolution) {
|
|
378
|
+
if (!needed || !resolution)
|
|
379
|
+
return {};
|
|
380
|
+
if (resolution.status === 'unavailable') {
|
|
381
|
+
return {
|
|
382
|
+
fallback: { slug: resolution.slug, status: 'unavailable', reason: resolution.reason },
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
if (!used)
|
|
386
|
+
return {};
|
|
387
|
+
return {
|
|
388
|
+
fallback: { slug: resolution.slug, status: 'used' },
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
function persistManagedSources(ctx, sources) {
|
|
392
|
+
if (sources.length === 0 || ctx.store.readOnly)
|
|
393
|
+
return;
|
|
394
|
+
const insert = ctx.store.db.prepare(`INSERT OR IGNORE INTO managed_item_sources(
|
|
395
|
+
item_id, source_ref, origin, evidence, evidence_hash, input_hash, created_at
|
|
396
|
+
) VALUES (?, ?, ?, ?, ?, ?, ?)`);
|
|
397
|
+
const createdAt = new Date().toISOString();
|
|
398
|
+
ctx.store.transaction(() => {
|
|
399
|
+
for (const source of sources) {
|
|
400
|
+
insert.run(source.itemId, source.sourceRef, source.origin, source.evidence, source.evidenceHash, source.inputHash, createdAt);
|
|
401
|
+
}
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
/**
|
|
405
|
+
* A title for a page being created: the subject the curator named, unless that subject is not
|
|
406
|
+
* what the page is.
|
|
407
|
+
*
|
|
408
|
+
* `write` derives one from the slug when none is given, and a slug-derived title reads like a
|
|
409
|
+
* filename ("Vulpine Claim 2026 08"). The subject is a phrase a person wrote, so it wins wherever
|
|
410
|
+
* the two are about the same thing.
|
|
411
|
+
*
|
|
412
|
+
* They are not always about the same thing, and that is this function's whole problem. The slug
|
|
413
|
+
* comes from routing, which scored ranked candidates; the subject came off one claim. A narrow
|
|
414
|
+
* equipment claim can correctly open `expeditions/blackwater-bay-survey` while still being too
|
|
415
|
+
* specific to name that broader page. A claim is
|
|
416
|
+
* superseded in a week; the title it installed outlives it by months.
|
|
417
|
+
*
|
|
418
|
+
* **One shared content word is enough to keep the subject.** The question is not whether the two
|
|
419
|
+
* agree closely, only whether they are about the same thing at all: `people/ada-marlow` and "Ada
|
|
420
|
+
* Marlow" share both words, `vulpine-claim-2026-08` and "Vulpine claim" share two, while a broad
|
|
421
|
+
* expedition slug and one equipment calibration share none. Falling back costs a plainer title, which is worth
|
|
422
|
+
* paying — a slug-derived title is dull but it is never about the wrong thing.
|
|
423
|
+
*/
|
|
424
|
+
function titleFor(candidate, slug) {
|
|
425
|
+
const subject = candidate.subject.trim();
|
|
426
|
+
if (subject.length === 0)
|
|
427
|
+
return titleFromSlug(slug);
|
|
428
|
+
const named = contentWords(slug.slice(slug.lastIndexOf('/') + 1));
|
|
429
|
+
for (const word of contentWords(subject)) {
|
|
430
|
+
if (named.has(word))
|
|
431
|
+
return subject.charAt(0).toUpperCase() + subject.slice(1);
|
|
432
|
+
}
|
|
433
|
+
return titleFromSlug(slug);
|
|
434
|
+
}
|
|
435
|
+
function newManagedPage(title) {
|
|
436
|
+
return `---\ntitle: ${JSON.stringify(title)}\nakno:\n role: knowledge\n management:\n remember: integrate\n---\n\n# ${title}\n`;
|
|
437
|
+
}
|
|
438
|
+
/**
|
|
439
|
+
* An internal `recall` finds where a claim belongs. **Best score at or above
|
|
440
|
+
* `route_threshold` wins and the claim is appended there; below that a page is created** at
|
|
441
|
+
* the slug the curator suggested.
|
|
442
|
+
*
|
|
443
|
+
* The second half is new, and it is the difference between a knowledge base that grows and one
|
|
444
|
+
* that only thickens. Below the threshold used to mean a proposal — a question for the user
|
|
445
|
+
* about where a claim goes — and a proposal is a page nobody writes. It fell hardest on
|
|
446
|
+
* exactly the material a knowledge base most needs new pages for: a finding about the world
|
|
447
|
+
* belongs in `research/` or `wiki/`, those folders have the `source` role, routing refuses
|
|
448
|
+
* source pages on principle, and so every finding reached the one outcome that stores
|
|
449
|
+
* nothing. Creating is the honest answer to "nothing here holds this".
|
|
450
|
+
*
|
|
451
|
+
* 0.5 is a placeholder and cannot be tuned by intuition, because the failure it guards against
|
|
452
|
+
* is invisible until someone reads it back months later. The mechanism is here; only the
|
|
453
|
+
* number moves.
|
|
454
|
+
*/
|
|
455
|
+
/**
|
|
456
|
+
* Routing thresholds **`relevance`, never `score`.**
|
|
457
|
+
*
|
|
458
|
+
* `score` orders one result set: the best hit is 1.0 whether it is a perfect match or
|
|
459
|
+
* the least bad of a bad batch. Thresholding it meant every document found a home and
|
|
460
|
+
* "A document with no home stays put" could never fire — routing looked like it
|
|
461
|
+
* worked and was in fact unconditional.
|
|
462
|
+
*
|
|
463
|
+
* `relevance` is absolute when a cross-encoder or the embedding arm supplied one. With
|
|
464
|
+
* neither — a lexical-only search — there is no number to compare, so routing refuses
|
|
465
|
+
* and asks. The failure this guards against — a fact quietly landing on a plausible wrong
|
|
466
|
+
* page — is invisible until someone reads it back months later, and a guess is worse than a
|
|
467
|
+
* question.
|
|
468
|
+
*/
|
|
469
|
+
/**
|
|
470
|
+
* Two questions, asked in order.
|
|
471
|
+
*
|
|
472
|
+
* The claim scores first, because the claim is what makes routing *specific*: when two pages
|
|
473
|
+
* could own a subject, only the attribute text says which. But a cross-encoder judges "does
|
|
474
|
+
* this passage answer this query", and a claim carries an attribute the owning page has no
|
|
475
|
+
* reason to already state. The invented regression fixture makes an amenity-heavy claim score
|
|
476
|
+
* poorly against the owning trip page, while the venue subject alone scores that page strongly.
|
|
477
|
+
* Same index, same options, same threshold.
|
|
478
|
+
*
|
|
479
|
+
* So the subject is asked only when the claim found nobody — the ownership question after the
|
|
480
|
+
* answers-this question came back empty. It costs a second recall exactly when the alternative
|
|
481
|
+
* was creating a page, never on the path that already routed, and it can only rescue a miss:
|
|
482
|
+
* a claim that routed keeps the destination its own text chose.
|
|
483
|
+
*/
|
|
484
|
+
async function route(ctx, candidate) {
|
|
485
|
+
const byClaim = await scoreDestinations(ctx, `${candidate.subject}. ${candidate.text}`, candidate);
|
|
486
|
+
if (byClaim.slug !== null || byClaim.blocked !== null)
|
|
487
|
+
return byClaim;
|
|
488
|
+
const subject = candidate.subject.trim();
|
|
489
|
+
if (subject.length === 0)
|
|
490
|
+
return byClaim;
|
|
491
|
+
const bySubject = await scoreDestinations(ctx, subject, candidate);
|
|
492
|
+
if (bySubject.slug !== null || bySubject.blocked !== null)
|
|
493
|
+
return bySubject;
|
|
494
|
+
// Neither routed. Report whichever came closer, and prefer the claim pass's suggestions:
|
|
495
|
+
// they were drawn against the text the user actually said.
|
|
496
|
+
const closer = bySubject.score > byClaim.score ? bySubject : byClaim;
|
|
497
|
+
return { ...closer, nearest: byClaim.nearest.length > 0 ? byClaim.nearest : bySubject.nearest };
|
|
498
|
+
}
|
|
499
|
+
async function scoreDestinations(ctx, query, candidate) {
|
|
500
|
+
const result = await recall(ctx, {
|
|
501
|
+
query,
|
|
502
|
+
mode: 'lookup',
|
|
503
|
+
limit: 5,
|
|
504
|
+
// Summaries only: routing is a decision about *which page*, and line windows
|
|
505
|
+
// are budget spent on text nobody reads here.
|
|
506
|
+
depth: 'summary',
|
|
507
|
+
// The query is the claim itself; a model rewriting it cannot improve the match.
|
|
508
|
+
expand: false,
|
|
509
|
+
});
|
|
510
|
+
// Never route into a source page: it is somebody else's words, and the rule is
|
|
511
|
+
// explicit that only claims become facts. Appending a claim to evidence would make
|
|
512
|
+
// the role boundary meaningless.
|
|
513
|
+
//
|
|
514
|
+
// And never into a reserved path. The ledger is the one that bites: it is a plain `full`
|
|
515
|
+
// page, so nothing above disqualified it, and a ledger that already records events about a
|
|
516
|
+
// subject is *the best lexical match for that subject* — which is how a claim about an
|
|
517
|
+
// ongoing complaint came to be appended below the event list of the page recording it, in
|
|
518
|
+
// prose the event parser cannot see. The destination that scores highest is not always a
|
|
519
|
+
// destination.
|
|
520
|
+
const candidates = result.results
|
|
521
|
+
.filter((entry) => entry.type === 'page')
|
|
522
|
+
.filter((card) => card.role === 'knowledge' &&
|
|
523
|
+
!isReserved(card.slug, ctx.config) &&
|
|
524
|
+
isInsideSuggestedFolder(card.slug, candidate.page))
|
|
525
|
+
.map((card) => {
|
|
526
|
+
const policy = ctx.store.db
|
|
527
|
+
.prepare('SELECT remember_management FROM pages WHERE slug = ?')
|
|
528
|
+
.get(card.slug);
|
|
529
|
+
return { ...card, writable: policy?.remember_management === 'integrate' };
|
|
530
|
+
});
|
|
531
|
+
// Suggestions are drawn from the same set, not from every card. Offering a source page as
|
|
532
|
+
// somewhere a claim "could go instead" proposes a destination this very function would refuse.
|
|
533
|
+
// The regression uses evidence-only pages that look topically close but cannot own the claim.
|
|
534
|
+
const nearest = candidates
|
|
535
|
+
.filter((card) => card.writable)
|
|
536
|
+
.slice(0, 4)
|
|
537
|
+
.map((card) => card.slug);
|
|
538
|
+
// **The best-judged candidate, not the best-ranked one.**
|
|
539
|
+
//
|
|
540
|
+
// This took the first card with a relevance and thresholded only that. Card order is
|
|
541
|
+
// `score * rankFactor`, and a card's score is the best of its chunks, so the page that leads
|
|
542
|
+
// is the page with one strong passage — not necessarily the page a cross-encoder considers
|
|
543
|
+
// the right home. When the leader fell below the threshold, routing refused and the claim
|
|
544
|
+
// dropped to the retain model's guessed slug, with every remaining candidate unread.
|
|
545
|
+
//
|
|
546
|
+
// The invented regression ranks an unrelated page first while a later subscription page scores
|
|
547
|
+
// strongly. Reading only the leader discards that measurement. Taking the maximum cannot route
|
|
548
|
+
// anything the old code would have refused *and*
|
|
549
|
+
// the threshold rejects; it can only stop a low leader from hiding a qualified page behind it.
|
|
550
|
+
const scored = candidates.filter((card) => card.relevance !== undefined);
|
|
551
|
+
if (scored.length === 0)
|
|
552
|
+
return { slug: null, score: 0, nearest, blocked: null };
|
|
553
|
+
const best = scored.reduce((winner, card) => (card.relevance > winner.relevance ? card : winner));
|
|
554
|
+
const relevance = best.relevance;
|
|
555
|
+
if (relevance < ctx.config.routeThreshold) {
|
|
556
|
+
return { slug: null, score: relevance, nearest, blocked: null };
|
|
557
|
+
}
|
|
558
|
+
if (!best.writable) {
|
|
559
|
+
// A read-only page winning semantically is evidence that a weaker writable match is the
|
|
560
|
+
// wrong home. Preserve that decision and let the new-page or typed hold path handle it.
|
|
561
|
+
return { slug: null, score: relevance, nearest, blocked: best.slug };
|
|
562
|
+
}
|
|
563
|
+
return { slug: best.slug, score: relevance, nearest, blocked: null };
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* The retain model chooses the taxonomy branch from the complete folder catalog; recall chooses a
|
|
567
|
+
* page inside that branch. Letting recall cross the branch boundary turns topical similarity into
|
|
568
|
+
* ownership — a general music finding can otherwise land on a person's repertoire page.
|
|
569
|
+
*/
|
|
570
|
+
function isInsideSuggestedFolder(slug, suggestedPage) {
|
|
571
|
+
// No taxonomy choice means no automatic destination. The retain prompt asks for one whenever
|
|
572
|
+
// an existing folder fits; omission is uncertainty, and uncertainty should become a proposal.
|
|
573
|
+
if (!suggestedPage)
|
|
574
|
+
return false;
|
|
575
|
+
const folder = suggestedPage.slice(0, suggestedPage.lastIndexOf('/'));
|
|
576
|
+
return slug.startsWith(`${folder}/`);
|
|
577
|
+
}
|
|
578
|
+
export { isInsideSuggestedFolder as isInsideSuggestedFolderForTesting };
|
|
579
|
+
/** Does a page with this slug already exist, whatever its role? */
|
|
580
|
+
function pageExists(ctx, slug) {
|
|
581
|
+
return ctx.store.db.prepare('SELECT 1 FROM pages WHERE slug = ?').get(slug) !== undefined;
|
|
582
|
+
}
|
|
583
|
+
function suggestedFolder(entry) {
|
|
584
|
+
const slug = entry.candidate.page;
|
|
585
|
+
return slug ? slug.slice(0, slug.lastIndexOf('/')) : null;
|
|
586
|
+
}
|
|
587
|
+
function requiredFolders(ctx, catalog, routed) {
|
|
588
|
+
const required = routed.flatMap((entry) => {
|
|
589
|
+
const slug = entry.slug ?? entry.candidate.page ?? null;
|
|
590
|
+
if (!slug || entry.slug !== null || pageExists(ctx, slug) || admittedFolder(catalog, slug))
|
|
591
|
+
return [];
|
|
592
|
+
return [
|
|
593
|
+
{
|
|
594
|
+
folder: slug.slice(0, slug.lastIndexOf('/')),
|
|
595
|
+
nearest: catalog
|
|
596
|
+
.filter((folder) => folder.eligible)
|
|
597
|
+
.map((folder) => folder.path)
|
|
598
|
+
.slice(0, 8),
|
|
599
|
+
},
|
|
600
|
+
];
|
|
601
|
+
});
|
|
602
|
+
return deduplicateRequiredFolders(required);
|
|
603
|
+
}
|
|
604
|
+
function deduplicateRequiredFolders(folders) {
|
|
605
|
+
return folders.filter((required, index) => folders.findIndex((other) => other.folder === required.folder) === index);
|
|
606
|
+
}
|
|
607
|
+
/** The page does not exist yet, so only an explicit folder rule can admit its creation. */
|
|
608
|
+
function admittedFolder(catalog, slug) {
|
|
609
|
+
const parent = slug.slice(0, slug.lastIndexOf('/'));
|
|
610
|
+
return (catalog.find((folder) => folder.path === parent &&
|
|
611
|
+
folder.role === 'knowledge' &&
|
|
612
|
+
folder.remember === 'integrate' &&
|
|
613
|
+
folder.creatable) ?? null);
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* `refusedPage` names an existing page the retain model suggested and routing declined to use.
|
|
617
|
+
* It gets its own wording: the other two say a page has to be *found* or *made*, and neither
|
|
618
|
+
* describes the case where one was named, exists, and was judged not to be a home for this.
|
|
619
|
+
*/
|
|
620
|
+
function proposeUnrouted(ctx, candidate, nearest, refusedPage, blockedPage) {
|
|
621
|
+
const id = newPrefixedId('prop');
|
|
622
|
+
const threshold = ctx.config.routeThreshold;
|
|
623
|
+
const stored = blockedPage
|
|
624
|
+
? `the strongest match for "${candidate.subject}" is read-only: "${blockedPage}"`
|
|
625
|
+
: refusedPage
|
|
626
|
+
? `nothing scored above ${threshold} for "${candidate.subject}"; the suggested page "${refusedPage}" exists but was not judged a home for it`
|
|
627
|
+
: nearest.length > 0
|
|
628
|
+
? `no page scored above ${threshold} for "${candidate.subject}"`
|
|
629
|
+
: `no page exists that could hold "${candidate.subject}" — every near match is source material`;
|
|
630
|
+
ctx.store.db
|
|
631
|
+
.prepare(`INSERT INTO proposals(id, at, kind, reason, subject, payload, nearest, status)
|
|
632
|
+
VALUES(?, ?, 'route', ?, ?, ?, ?, 'pending')`)
|
|
633
|
+
.run(id, new Date().toISOString(), stored, candidate.subject, JSON.stringify({ append: candidate.text }), JSON.stringify(nearest));
|
|
634
|
+
return {
|
|
635
|
+
proposal_id: id,
|
|
636
|
+
reason: blockedPage
|
|
637
|
+
? `"${blockedPage}" is the best match for "${candidate.subject}" but is read-only — create or name a managed destination rather than using a weaker page`
|
|
638
|
+
: refusedPage
|
|
639
|
+
? `"${refusedPage}" already exists and nothing judged it a home for "${candidate.subject}" — ask where this goes rather than appending to it`
|
|
640
|
+
: nearest.length > 0
|
|
641
|
+
? `nothing scored above ${threshold} for "${candidate.subject}" — ask where this goes`
|
|
642
|
+
: `no page could hold "${candidate.subject}" — every near match is source material, so this needs a new page`,
|
|
643
|
+
reason_code: unroutedReasonCode(nearest, refusedPage, blockedPage),
|
|
644
|
+
nearest,
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
function unroutedReasonCode(nearest, refusedPage, blockedPage) {
|
|
648
|
+
return blockedPage || (!refusedPage && nearest.length === 0)
|
|
649
|
+
? 'no_writable_destination'
|
|
650
|
+
: 'routing_uncertain';
|
|
651
|
+
}
|
|
652
|
+
function recordConflictProposal(ctx, slug, candidate, existing) {
|
|
653
|
+
const id = newPrefixedId('prop');
|
|
654
|
+
ctx.store.db
|
|
655
|
+
.prepare(`INSERT INTO proposals(id, at, kind, reason, subject, payload, nearest, status)
|
|
656
|
+
VALUES(?, ?, 'conflict', ?, ?, ?, ?, 'pending')`)
|
|
657
|
+
.run(id, new Date().toISOString(), `'${slug}' already claims: ${existing}`, slug, JSON.stringify({ slug, append: candidate.text }), JSON.stringify([slug]));
|
|
658
|
+
return id;
|
|
659
|
+
}
|
|
660
|
+
//# sourceMappingURL=remember.js.map
|