agent-bober 0.17.0 → 0.18.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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +25 -0
- package/README.md +169 -6
- package/dist/chat/answerer.d.ts +17 -0
- package/dist/chat/answerer.d.ts.map +1 -0
- package/dist/chat/answerer.js +52 -0
- package/dist/chat/answerer.js.map +1 -0
- package/dist/chat/approval-cursor.d.ts +25 -0
- package/dist/chat/approval-cursor.d.ts.map +1 -0
- package/dist/chat/approval-cursor.js +61 -0
- package/dist/chat/approval-cursor.js.map +1 -0
- package/dist/chat/approval-reader.d.ts +11 -0
- package/dist/chat/approval-reader.d.ts.map +1 -0
- package/dist/chat/approval-reader.js +20 -0
- package/dist/chat/approval-reader.js.map +1 -0
- package/dist/chat/careful-sidecar.d.ts +15 -0
- package/dist/chat/careful-sidecar.d.ts.map +1 -0
- package/dist/chat/careful-sidecar.js +39 -0
- package/dist/chat/careful-sidecar.js.map +1 -0
- package/dist/chat/chat-session.d.ts +129 -0
- package/dist/chat/chat-session.d.ts.map +1 -0
- package/dist/chat/chat-session.js +470 -0
- package/dist/chat/chat-session.js.map +1 -0
- package/dist/chat/completion-tailer.d.ts +27 -0
- package/dist/chat/completion-tailer.d.ts.map +1 -0
- package/dist/chat/completion-tailer.js +187 -0
- package/dist/chat/completion-tailer.js.map +1 -0
- package/dist/chat/conversation-store.d.ts +22 -0
- package/dist/chat/conversation-store.d.ts.map +1 -0
- package/dist/chat/conversation-store.js +66 -0
- package/dist/chat/conversation-store.js.map +1 -0
- package/dist/chat/cursor-store.d.ts +23 -0
- package/dist/chat/cursor-store.d.ts.map +1 -0
- package/dist/chat/cursor-store.js +42 -0
- package/dist/chat/cursor-store.js.map +1 -0
- package/dist/chat/pid-sidecar.d.ts +22 -0
- package/dist/chat/pid-sidecar.d.ts.map +1 -0
- package/dist/chat/pid-sidecar.js +42 -0
- package/dist/chat/pid-sidecar.js.map +1 -0
- package/dist/chat/roster-reader.d.ts +16 -0
- package/dist/chat/roster-reader.d.ts.map +1 -0
- package/dist/chat/roster-reader.js +39 -0
- package/dist/chat/roster-reader.js.map +1 -0
- package/dist/chat/run-spawner.d.ts +71 -0
- package/dist/chat/run-spawner.d.ts.map +1 -0
- package/dist/chat/run-spawner.js +121 -0
- package/dist/chat/run-spawner.js.map +1 -0
- package/dist/chat/slash-commands.d.ts +36 -0
- package/dist/chat/slash-commands.d.ts.map +1 -0
- package/dist/chat/slash-commands.js +133 -0
- package/dist/chat/slash-commands.js.map +1 -0
- package/dist/chat/steer-cleanup.d.ts +12 -0
- package/dist/chat/steer-cleanup.d.ts.map +1 -0
- package/dist/chat/steer-cleanup.js +60 -0
- package/dist/chat/steer-cleanup.js.map +1 -0
- package/dist/chat/turn-classifier.d.ts +43 -0
- package/dist/chat/turn-classifier.d.ts.map +1 -0
- package/dist/chat/turn-classifier.js +150 -0
- package/dist/chat/turn-classifier.js.map +1 -0
- package/dist/cli/commands/blackboard.d.ts +33 -0
- package/dist/cli/commands/blackboard.d.ts.map +1 -0
- package/dist/cli/commands/blackboard.js +114 -0
- package/dist/cli/commands/blackboard.js.map +1 -0
- package/dist/cli/commands/chat.d.ts +13 -0
- package/dist/cli/commands/chat.d.ts.map +1 -0
- package/dist/cli/commands/chat.js +43 -0
- package/dist/cli/commands/chat.js.map +1 -0
- package/dist/cli/commands/evolve.d.ts +18 -0
- package/dist/cli/commands/evolve.d.ts.map +1 -0
- package/dist/cli/commands/evolve.js +72 -0
- package/dist/cli/commands/evolve.js.map +1 -0
- package/dist/cli/commands/facts.d.ts +15 -0
- package/dist/cli/commands/facts.d.ts.map +1 -0
- package/dist/cli/commands/facts.js +213 -0
- package/dist/cli/commands/facts.js.map +1 -0
- package/dist/cli/commands/init.d.ts +9 -0
- package/dist/cli/commands/init.d.ts.map +1 -1
- package/dist/cli/commands/init.js +1 -1
- package/dist/cli/commands/init.js.map +1 -1
- package/dist/cli/commands/medical.d.ts +24 -0
- package/dist/cli/commands/medical.d.ts.map +1 -0
- package/dist/cli/commands/medical.js +132 -0
- package/dist/cli/commands/medical.js.map +1 -0
- package/dist/cli/commands/memory.d.ts +2 -1
- package/dist/cli/commands/memory.d.ts.map +1 -1
- package/dist/cli/commands/memory.js +94 -7
- package/dist/cli/commands/memory.js.map +1 -1
- package/dist/cli/commands/plan.d.ts.map +1 -1
- package/dist/cli/commands/plan.js +29 -3
- package/dist/cli/commands/plan.js.map +1 -1
- package/dist/cli/commands/replay.d.ts +17 -0
- package/dist/cli/commands/replay.d.ts.map +1 -0
- package/dist/cli/commands/replay.js +261 -0
- package/dist/cli/commands/replay.js.map +1 -0
- package/dist/cli/commands/run.d.ts +9 -0
- package/dist/cli/commands/run.d.ts.map +1 -1
- package/dist/cli/commands/run.js +27 -1
- package/dist/cli/commands/run.js.map +1 -1
- package/dist/cli/commands/sprint.d.ts.map +1 -1
- package/dist/cli/commands/sprint.js +17 -3
- package/dist/cli/commands/sprint.js.map +1 -1
- package/dist/cli/commands/update.d.ts +18 -0
- package/dist/cli/commands/update.d.ts.map +1 -0
- package/dist/cli/commands/update.js +81 -0
- package/dist/cli/commands/update.js.map +1 -0
- package/dist/cli/index.js +15 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/config/defaults.d.ts.map +1 -1
- package/dist/config/defaults.js +8 -0
- package/dist/config/defaults.js.map +1 -1
- package/dist/config/role-providers.d.ts +1 -1
- package/dist/config/role-providers.d.ts.map +1 -1
- package/dist/config/role-providers.js +6 -1
- package/dist/config/role-providers.js.map +1 -1
- package/dist/config/schema.d.ts +467 -222
- package/dist/config/schema.d.ts.map +1 -1
- package/dist/config/schema.js +25 -0
- package/dist/config/schema.js.map +1 -1
- package/dist/contracts/eval-result.d.ts +84 -84
- package/dist/contracts/spec.d.ts +28 -28
- package/dist/contracts/sprint-contract.d.ts +4 -4
- package/dist/fleet/critic-deep.d.ts +53 -0
- package/dist/fleet/critic-deep.d.ts.map +1 -0
- package/dist/fleet/critic-deep.js +199 -0
- package/dist/fleet/critic-deep.js.map +1 -0
- package/dist/fleet/decomposer-deep-constants.d.ts +4 -0
- package/dist/fleet/decomposer-deep-constants.d.ts.map +1 -0
- package/dist/fleet/decomposer-deep-constants.js +14 -0
- package/dist/fleet/decomposer-deep-constants.js.map +1 -0
- package/dist/fleet/decomposer-deep-types.d.ts +8 -0
- package/dist/fleet/decomposer-deep-types.d.ts.map +1 -0
- package/dist/fleet/decomposer-deep-types.js +9 -0
- package/dist/fleet/decomposer-deep-types.js.map +1 -0
- package/dist/fleet/decomposer-deep.d.ts +51 -0
- package/dist/fleet/decomposer-deep.d.ts.map +1 -0
- package/dist/fleet/decomposer-deep.js +261 -0
- package/dist/fleet/decomposer-deep.js.map +1 -0
- package/dist/fleet/decomposer.d.ts +22 -0
- package/dist/fleet/decomposer.d.ts.map +1 -0
- package/dist/fleet/decomposer.js +144 -0
- package/dist/fleet/decomposer.js.map +1 -0
- package/dist/fleet/index.d.ts +93 -0
- package/dist/fleet/index.d.ts.map +1 -1
- package/dist/fleet/index.js +230 -2
- package/dist/fleet/index.js.map +1 -1
- package/dist/fleet/manifest-write.d.ts +30 -0
- package/dist/fleet/manifest-write.d.ts.map +1 -0
- package/dist/fleet/manifest-write.js +90 -0
- package/dist/fleet/manifest-write.js.map +1 -0
- package/dist/fleet/manifest.d.ts +6 -6
- package/dist/fleet/shared-blackboard.d.ts +51 -0
- package/dist/fleet/shared-blackboard.d.ts.map +1 -0
- package/dist/fleet/shared-blackboard.js +79 -0
- package/dist/fleet/shared-blackboard.js.map +1 -0
- package/dist/fleet/synthesis.d.ts +16 -0
- package/dist/fleet/synthesis.d.ts.map +1 -0
- package/dist/fleet/synthesis.js +22 -0
- package/dist/fleet/synthesis.js.map +1 -0
- package/dist/fleet/tier-policy.d.ts +18 -0
- package/dist/fleet/tier-policy.d.ts.map +1 -0
- package/dist/fleet/tier-policy.js +53 -0
- package/dist/fleet/tier-policy.js.map +1 -0
- package/dist/fleet/tool-role-guard.d.ts +24 -0
- package/dist/fleet/tool-role-guard.d.ts.map +1 -0
- package/dist/fleet/tool-role-guard.js +55 -0
- package/dist/fleet/tool-role-guard.js.map +1 -0
- package/dist/incident/resolution-verify.d.ts +4 -4
- package/dist/incident/types.d.ts +8 -8
- package/dist/mcp/run-manager.d.ts +9 -1
- package/dist/mcp/run-manager.d.ts.map +1 -1
- package/dist/mcp/run-manager.js.map +1 -1
- package/dist/medical/adapters/apple-health.d.ts +32 -0
- package/dist/medical/adapters/apple-health.d.ts.map +1 -0
- package/dist/medical/adapters/apple-health.js +100 -0
- package/dist/medical/adapters/apple-health.js.map +1 -0
- package/dist/medical/audit.d.ts +31 -0
- package/dist/medical/audit.d.ts.map +1 -0
- package/dist/medical/audit.js +56 -0
- package/dist/medical/audit.js.map +1 -0
- package/dist/medical/consent.d.ts +37 -0
- package/dist/medical/consent.d.ts.map +1 -0
- package/dist/medical/consent.js +73 -0
- package/dist/medical/consent.js.map +1 -0
- package/dist/medical/disclaimer.d.ts +11 -0
- package/dist/medical/disclaimer.d.ts.map +1 -0
- package/dist/medical/disclaimer.js +21 -0
- package/dist/medical/disclaimer.js.map +1 -0
- package/dist/medical/egress.d.ts +30 -0
- package/dist/medical/egress.d.ts.map +1 -0
- package/dist/medical/egress.js +50 -0
- package/dist/medical/egress.js.map +1 -0
- package/dist/medical/engine.d.ts +73 -0
- package/dist/medical/engine.d.ts.map +1 -0
- package/dist/medical/engine.js +333 -0
- package/dist/medical/engine.js.map +1 -0
- package/dist/medical/guardrails.d.ts +47 -0
- package/dist/medical/guardrails.d.ts.map +1 -0
- package/dist/medical/guardrails.js +92 -0
- package/dist/medical/guardrails.js.map +1 -0
- package/dist/medical/health-store.d.ts +80 -0
- package/dist/medical/health-store.d.ts.map +1 -0
- package/dist/medical/health-store.js +204 -0
- package/dist/medical/health-store.js.map +1 -0
- package/dist/medical/inference.d.ts +22 -0
- package/dist/medical/inference.d.ts.map +1 -0
- package/dist/medical/inference.js +39 -0
- package/dist/medical/inference.js.map +1 -0
- package/dist/medical/ingestion.d.ts +33 -0
- package/dist/medical/ingestion.d.ts.map +1 -0
- package/dist/medical/ingestion.js +52 -0
- package/dist/medical/ingestion.js.map +1 -0
- package/dist/medical/numerics.d.ts +66 -0
- package/dist/medical/numerics.d.ts.map +1 -0
- package/dist/medical/numerics.js +216 -0
- package/dist/medical/numerics.js.map +1 -0
- package/dist/medical/red-flag.d.ts +42 -0
- package/dist/medical/red-flag.d.ts.map +1 -0
- package/dist/medical/red-flag.js +161 -0
- package/dist/medical/red-flag.js.map +1 -0
- package/dist/medical/refusal.d.ts +46 -0
- package/dist/medical/refusal.d.ts.map +1 -0
- package/dist/medical/refusal.js +131 -0
- package/dist/medical/refusal.js.map +1 -0
- package/dist/medical/retrieval/grounding-critic.d.ts +34 -0
- package/dist/medical/retrieval/grounding-critic.d.ts.map +1 -0
- package/dist/medical/retrieval/grounding-critic.js +153 -0
- package/dist/medical/retrieval/grounding-critic.js.map +1 -0
- package/dist/medical/retrieval/literature.d.ts +78 -0
- package/dist/medical/retrieval/literature.d.ts.map +1 -0
- package/dist/medical/retrieval/literature.js +292 -0
- package/dist/medical/retrieval/literature.js.map +1 -0
- package/dist/medical/retrieval/medline-source.d.ts +72 -0
- package/dist/medical/retrieval/medline-source.d.ts.map +1 -0
- package/dist/medical/retrieval/medline-source.js +120 -0
- package/dist/medical/retrieval/medline-source.js.map +1 -0
- package/dist/medical/team.d.ts +15 -0
- package/dist/medical/team.d.ts.map +1 -0
- package/dist/medical/team.js +44 -0
- package/dist/medical/team.js.map +1 -0
- package/dist/medical/types.d.ts +175 -0
- package/dist/medical/types.d.ts.map +1 -0
- package/dist/medical/types.js +8 -0
- package/dist/medical/types.js.map +1 -0
- package/dist/medical/whoop/whoop-client.d.ts +114 -0
- package/dist/medical/whoop/whoop-client.d.ts.map +1 -0
- package/dist/medical/whoop/whoop-client.js +226 -0
- package/dist/medical/whoop/whoop-client.js.map +1 -0
- package/dist/medical/whoop/whoop-sync.d.ts +25 -0
- package/dist/medical/whoop/whoop-sync.d.ts.map +1 -0
- package/dist/medical/whoop/whoop-sync.js +97 -0
- package/dist/medical/whoop/whoop-sync.js.map +1 -0
- package/dist/medical/whoop/whoop-token.d.ts +44 -0
- package/dist/medical/whoop/whoop-token.d.ts.map +1 -0
- package/dist/medical/whoop/whoop-token.js +66 -0
- package/dist/medical/whoop/whoop-token.js.map +1 -0
- package/dist/orchestrator/context-handoff.d.ts +60 -60
- package/dist/orchestrator/contract-materialization.d.ts +33 -0
- package/dist/orchestrator/contract-materialization.d.ts.map +1 -0
- package/dist/orchestrator/contract-materialization.js +113 -0
- package/dist/orchestrator/contract-materialization.js.map +1 -0
- package/dist/orchestrator/memory/distill.d.ts +4 -1
- package/dist/orchestrator/memory/distill.d.ts.map +1 -1
- package/dist/orchestrator/memory/distill.js +41 -1
- package/dist/orchestrator/memory/distill.js.map +1 -1
- package/dist/orchestrator/memory/fact-detector.d.ts +61 -0
- package/dist/orchestrator/memory/fact-detector.d.ts.map +1 -0
- package/dist/orchestrator/memory/fact-detector.js +153 -0
- package/dist/orchestrator/memory/fact-detector.js.map +1 -0
- package/dist/orchestrator/memory/fact-judge.d.ts +19 -0
- package/dist/orchestrator/memory/fact-judge.d.ts.map +1 -0
- package/dist/orchestrator/memory/fact-judge.js +121 -0
- package/dist/orchestrator/memory/fact-judge.js.map +1 -0
- package/dist/orchestrator/memory/fact-retrieve.d.ts +52 -0
- package/dist/orchestrator/memory/fact-retrieve.d.ts.map +1 -0
- package/dist/orchestrator/memory/fact-retrieve.js +117 -0
- package/dist/orchestrator/memory/fact-retrieve.js.map +1 -0
- package/dist/orchestrator/memory/hygiene.d.ts +66 -0
- package/dist/orchestrator/memory/hygiene.d.ts.map +1 -0
- package/dist/orchestrator/memory/hygiene.js +153 -0
- package/dist/orchestrator/memory/hygiene.js.map +1 -0
- package/dist/orchestrator/memory/reconcile.d.ts +47 -0
- package/dist/orchestrator/memory/reconcile.d.ts.map +1 -0
- package/dist/orchestrator/memory/reconcile.js +116 -0
- package/dist/orchestrator/memory/reconcile.js.map +1 -0
- package/dist/orchestrator/memory/retrieve.d.ts +5 -3
- package/dist/orchestrator/memory/retrieve.d.ts.map +1 -1
- package/dist/orchestrator/memory/retrieve.js +9 -6
- package/dist/orchestrator/memory/retrieve.js.map +1 -1
- package/dist/orchestrator/pipeline.d.ts +19 -3
- package/dist/orchestrator/pipeline.d.ts.map +1 -1
- package/dist/orchestrator/pipeline.js +63 -52
- package/dist/orchestrator/pipeline.js.map +1 -1
- package/dist/orchestrator/planner-agent.d.ts.map +1 -1
- package/dist/orchestrator/planner-agent.js +16 -1
- package/dist/orchestrator/planner-agent.js.map +1 -1
- package/dist/orchestrator/selfimprove/eval-guards.d.ts +51 -0
- package/dist/orchestrator/selfimprove/eval-guards.d.ts.map +1 -0
- package/dist/orchestrator/selfimprove/eval-guards.js +120 -0
- package/dist/orchestrator/selfimprove/eval-guards.js.map +1 -0
- package/dist/orchestrator/selfimprove/gepa.d.ts +74 -0
- package/dist/orchestrator/selfimprove/gepa.d.ts.map +1 -0
- package/dist/orchestrator/selfimprove/gepa.js +227 -0
- package/dist/orchestrator/selfimprove/gepa.js.map +1 -0
- package/dist/orchestrator/selfimprove/replay-harness.d.ts +65 -0
- package/dist/orchestrator/selfimprove/replay-harness.d.ts.map +1 -0
- package/dist/orchestrator/selfimprove/replay-harness.js +134 -0
- package/dist/orchestrator/selfimprove/replay-harness.js.map +1 -0
- package/dist/orchestrator/selfimprove/replay-store.d.ts +45 -0
- package/dist/orchestrator/selfimprove/replay-store.d.ts.map +1 -0
- package/dist/orchestrator/selfimprove/replay-store.js +119 -0
- package/dist/orchestrator/selfimprove/replay-store.js.map +1 -0
- package/dist/orchestrator/selfimprove/replay-types.d.ts +32 -0
- package/dist/orchestrator/selfimprove/replay-types.d.ts.map +1 -0
- package/dist/orchestrator/selfimprove/replay-types.js +15 -0
- package/dist/orchestrator/selfimprove/replay-types.js.map +1 -0
- package/dist/orchestrator/workflow/engine.d.ts +3 -1
- package/dist/orchestrator/workflow/engine.d.ts.map +1 -1
- package/dist/orchestrator/workflow/selector.d.ts +18 -0
- package/dist/orchestrator/workflow/selector.d.ts.map +1 -1
- package/dist/orchestrator/workflow/selector.js +41 -0
- package/dist/orchestrator/workflow/selector.js.map +1 -1
- package/dist/orchestrator/workflow/ts-engine.d.ts +3 -1
- package/dist/orchestrator/workflow/ts-engine.d.ts.map +1 -1
- package/dist/orchestrator/workflow/ts-engine.js +2 -2
- package/dist/orchestrator/workflow/ts-engine.js.map +1 -1
- package/dist/orchestrator/workflow/workflow-engine.d.ts +3 -1
- package/dist/orchestrator/workflow/workflow-engine.d.ts.map +1 -1
- package/dist/orchestrator/workflow/workflow-engine.js +3 -3
- package/dist/orchestrator/workflow/workflow-engine.js.map +1 -1
- package/dist/state/facts.d.ts +110 -0
- package/dist/state/facts.d.ts.map +1 -0
- package/dist/state/facts.js +208 -0
- package/dist/state/facts.js.map +1 -0
- package/dist/state/guidance.d.ts +37 -0
- package/dist/state/guidance.d.ts.map +1 -0
- package/dist/state/guidance.js +144 -0
- package/dist/state/guidance.js.map +1 -0
- package/dist/state/history.d.ts +2 -2
- package/dist/state/index.d.ts +1 -1
- package/dist/state/index.d.ts.map +1 -1
- package/dist/state/index.js +1 -1
- package/dist/state/index.js.map +1 -1
- package/dist/state/memory.d.ts +43 -11
- package/dist/state/memory.d.ts.map +1 -1
- package/dist/state/memory.js +93 -15
- package/dist/state/memory.js.map +1 -1
- package/dist/state/pause.d.ts +47 -0
- package/dist/state/pause.d.ts.map +1 -0
- package/dist/state/pause.js +146 -0
- package/dist/state/pause.js.map +1 -0
- package/dist/state/sprint-state.d.ts +8 -0
- package/dist/state/sprint-state.d.ts.map +1 -1
- package/dist/state/sprint-state.js +32 -1
- package/dist/state/sprint-state.js.map +1 -1
- package/dist/teams/registry.d.ts +16 -0
- package/dist/teams/registry.d.ts.map +1 -0
- package/dist/teams/registry.js +53 -0
- package/dist/teams/registry.js.map +1 -0
- package/dist/teams/types.d.ts +25 -0
- package/dist/teams/types.d.ts.map +1 -0
- package/dist/teams/types.js +2 -0
- package/dist/teams/types.js.map +1 -0
- package/package.json +3 -1
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PURE deterministic, STORE-ONLY retrieval of relevant project facts for the planner.
|
|
3
|
+
*
|
|
4
|
+
* Opens the FactStore once, reads active facts for the requested scope, then
|
|
5
|
+
* performs all ranking in memory (pure). Mirrors the retrieve.ts pattern for lessons.
|
|
6
|
+
*
|
|
7
|
+
* PURE beyond the single store open/read/close — no network, no Date.now(), no LLM.
|
|
8
|
+
*/
|
|
9
|
+
import { FactStore, factsDbPath, ensureFactsDir } from "../../state/facts.js";
|
|
10
|
+
// ── Constants ─────────────────────────────────────────────────────────
|
|
11
|
+
/** Default number of facts to return. */
|
|
12
|
+
const DEFAULT_TOP_K = 5;
|
|
13
|
+
/** Default character budget for the serialized planner block. */
|
|
14
|
+
const DEFAULT_CHAR_BUDGET = 1200;
|
|
15
|
+
// ── Helpers ───────────────────────────────────────────────────────────
|
|
16
|
+
/**
|
|
17
|
+
* Tokenize a string into lowercase alphanumeric tokens.
|
|
18
|
+
* COPIED verbatim from retrieve.ts:30-35 (not exported there).
|
|
19
|
+
* Splits on any non-alphanumeric character and filters empty tokens.
|
|
20
|
+
*/
|
|
21
|
+
function tokenize(s) {
|
|
22
|
+
return s
|
|
23
|
+
.toLowerCase()
|
|
24
|
+
.split(/[^a-z0-9]+/)
|
|
25
|
+
.filter((t) => t.length > 0);
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Score a FactRecord against a set of lowercased keyword tokens.
|
|
29
|
+
* Counts shared tokens across subject, predicate, and value.
|
|
30
|
+
* Returns 0 if there is no overlap.
|
|
31
|
+
*/
|
|
32
|
+
function scoreFact(record, keywordTokens) {
|
|
33
|
+
if (keywordTokens.size === 0)
|
|
34
|
+
return 0;
|
|
35
|
+
const recordTokens = new Set([
|
|
36
|
+
...tokenize(record.subject),
|
|
37
|
+
...tokenize(record.predicate),
|
|
38
|
+
...tokenize(record.value),
|
|
39
|
+
]);
|
|
40
|
+
let count = 0;
|
|
41
|
+
for (const t of keywordTokens) {
|
|
42
|
+
if (recordTokens.has(t)) {
|
|
43
|
+
count++;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
return count;
|
|
47
|
+
}
|
|
48
|
+
// ── Core ──────────────────────────────────────────────────────────────
|
|
49
|
+
/**
|
|
50
|
+
* Load and rank at most `topK` active facts from the store that are relevant
|
|
51
|
+
* to the given `keywords` and `scope`.
|
|
52
|
+
*
|
|
53
|
+
* ONE store open/read/close, then pure ranking. Scope isolation is enforced by
|
|
54
|
+
* the SQL WHERE clause in FactStore.getActiveFacts(scope) — facts in scope A
|
|
55
|
+
* NEVER surface when querying scope B.
|
|
56
|
+
*
|
|
57
|
+
* Scoring: deterministic token-overlap between lowercased keyword tokens and
|
|
58
|
+
* each record's subject + predicate + value tokens.
|
|
59
|
+
* Sort: score DESC, then id ASC (byte-stable tiebreak, mirrors retrieve.ts:103).
|
|
60
|
+
*
|
|
61
|
+
* Design note: when keywords is empty, keywordTokens.size === 0, scoreFact
|
|
62
|
+
* returns 0 for all records, and the score > 0 filter yields an empty result.
|
|
63
|
+
* This matches retrieve.ts behaviour. Project facts are few and always written
|
|
64
|
+
* with known predicates, so callers should pass relevant keywords (e.g. from
|
|
65
|
+
* the user prompt) to surface them.
|
|
66
|
+
*
|
|
67
|
+
* @param projectRoot - Absolute path to the project root containing .bober/
|
|
68
|
+
* @param scope - Fact scope (e.g. "" for default/programming team)
|
|
69
|
+
* @param keywords - Keywords to rank against (e.g. from the user prompt)
|
|
70
|
+
* @param options - topK cap and optional namespace (selects DB file location)
|
|
71
|
+
*/
|
|
72
|
+
export async function retrieveRelevantFacts(projectRoot, scope, keywords, { topK = DEFAULT_TOP_K, namespace, } = {}) {
|
|
73
|
+
await ensureFactsDir(projectRoot, namespace);
|
|
74
|
+
const store = new FactStore(factsDbPath(projectRoot, namespace));
|
|
75
|
+
try {
|
|
76
|
+
// Scope-isolated by SQL WHERE scope = ? AND t_invalidated IS NULL
|
|
77
|
+
const records = store.getActiveFacts(scope);
|
|
78
|
+
const keywordTokens = new Set(keywords.flatMap(tokenize));
|
|
79
|
+
const scored = records
|
|
80
|
+
.map((r) => ({ r, score: scoreFact(r, keywordTokens) }))
|
|
81
|
+
.filter((x) => x.score > 0); // non-matching keyword → empty (mirrors retrieve.ts:96)
|
|
82
|
+
// Sort: score DESC, then id ASC (byte-stable final tiebreak)
|
|
83
|
+
scored.sort((a, b) => b.score - a.score || // 1. token overlap (DOMINANT)
|
|
84
|
+
a.r.id.localeCompare(b.r.id));
|
|
85
|
+
return scored.slice(0, topK).map((x) => x.r);
|
|
86
|
+
}
|
|
87
|
+
finally {
|
|
88
|
+
store.close();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Render a compact planner context block from an array of FactRecord values.
|
|
93
|
+
*
|
|
94
|
+
* Mirrors serializeLessonsForPlanner (retrieve.ts:122-144):
|
|
95
|
+
* - Empty input → ""
|
|
96
|
+
* - Header line + one "- subject/predicate: value" line per fact
|
|
97
|
+
* - Hard charBudget slice (output length is GUARANTEED ≤ charBudget)
|
|
98
|
+
*
|
|
99
|
+
* @param records - Already-ranked records (from retrieveRelevantFacts)
|
|
100
|
+
* @param options - charBudget: hard character limit (default DEFAULT_CHAR_BUDGET)
|
|
101
|
+
* @returns Compact string block, at most charBudget characters
|
|
102
|
+
*/
|
|
103
|
+
export function serializeFactsForContext(records, { charBudget = DEFAULT_CHAR_BUDGET } = {}) {
|
|
104
|
+
if (records.length === 0) {
|
|
105
|
+
return "";
|
|
106
|
+
}
|
|
107
|
+
const lines = [
|
|
108
|
+
"## Project facts (durable semantic memory)",
|
|
109
|
+
"",
|
|
110
|
+
...records.map((r) => `- ${r.subject}/${r.predicate}: ${r.value}`),
|
|
111
|
+
"",
|
|
112
|
+
];
|
|
113
|
+
const block = lines.join("\n");
|
|
114
|
+
// Hard truncation to charBudget (sc-5-5 guarantee — never exceed budget)
|
|
115
|
+
return block.slice(0, charBudget);
|
|
116
|
+
}
|
|
117
|
+
//# sourceMappingURL=fact-retrieve.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fact-retrieve.js","sourceRoot":"","sources":["../../../src/orchestrator/memory/fact-retrieve.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PURE deterministic lesson hygiene: decay-based pruning + conflict quarantine.
|
|
3
|
+
*
|
|
4
|
+
* PURE — the clock is never read here; `now` is injected by the CLI handler
|
|
5
|
+
* (mirrors reconcileFact's { now } pattern at reconcile.ts:54). Operates
|
|
6
|
+
* ONLY on the fields each record carries; the CLI assembles recency input (createdAt)
|
|
7
|
+
* by reading per-lesson files and passing it in via PrunableLesson.createdAt.
|
|
8
|
+
*
|
|
9
|
+
* Conflict detection: DETERMINISTIC — two lessons sharing the same contradiction key
|
|
10
|
+
* (category root + discriminator tag) with opposing polarity markers are BOTH moved
|
|
11
|
+
* to quarantine. No LLM.
|
|
12
|
+
*
|
|
13
|
+
* Decay: lessons below `minOccurrences` that are also older than `maxAgeMs` are
|
|
14
|
+
* quarantined. Missing createdAt is treated as "maximally stale" (decays immediately
|
|
15
|
+
* when occurrences are below threshold), which is conservative and documented here
|
|
16
|
+
* as a deliberate choice — prefer to quarantine an unknown-age low-occurrence lesson
|
|
17
|
+
* rather than silently keep it forever.
|
|
18
|
+
*/
|
|
19
|
+
import type { LessonIndexRecord } from "../../state/memory.js";
|
|
20
|
+
/** Default age threshold for decay: 30 days in milliseconds. */
|
|
21
|
+
export declare const THIRTY_DAYS_MS: number;
|
|
22
|
+
/** A LessonIndexRecord enriched with CLI-derived recency proxy (ISO createdAt). */
|
|
23
|
+
export interface PrunableLesson extends LessonIndexRecord {
|
|
24
|
+
/** ISO 8601 createdAt, assembled by the CLI from the per-lesson .md file. May be absent. */
|
|
25
|
+
createdAt?: string;
|
|
26
|
+
}
|
|
27
|
+
export interface PruneOptions {
|
|
28
|
+
/** Injected ISO wall-clock — NEVER read inside this module. */
|
|
29
|
+
now: string;
|
|
30
|
+
/**
|
|
31
|
+
* Lessons with occurrences strictly below this value AND older than maxAgeMs
|
|
32
|
+
* are quarantined as decayed. Default: 2.
|
|
33
|
+
*/
|
|
34
|
+
minOccurrences?: number;
|
|
35
|
+
/**
|
|
36
|
+
* Age threshold in milliseconds. A lesson is considered stale when
|
|
37
|
+
* (now - createdAt) > maxAgeMs. Default: 30 days.
|
|
38
|
+
*/
|
|
39
|
+
maxAgeMs?: number;
|
|
40
|
+
}
|
|
41
|
+
export interface PruneResult {
|
|
42
|
+
/** Lessons retained in INDEX.md. Sorted by lessonId ASC for byte-stability. */
|
|
43
|
+
kept: PrunableLesson[];
|
|
44
|
+
/** Lessons to be moved to QUARANTINE.md. Sorted by lessonId ASC for byte-stability. */
|
|
45
|
+
quarantined: PrunableLesson[];
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Partition lessons into kept vs. quarantined.
|
|
49
|
+
*
|
|
50
|
+
* Phase 1 (conflict detection): identifies deterministically contradictory pairs.
|
|
51
|
+
* A pair is contradictory when both records share the same contradiction key
|
|
52
|
+
* (category root + discriminator tag) AND carry opposing polarity markers
|
|
53
|
+
* (one "keep"-marked, one "avoid"-marked). BOTH are quarantined.
|
|
54
|
+
*
|
|
55
|
+
* Phase 2 (decay): for remaining records, quarantines those below `minOccurrences`
|
|
56
|
+
* that are also stale (createdAt older than maxAgeMs, or createdAt absent).
|
|
57
|
+
*
|
|
58
|
+
* Both output arrays are sorted by lessonId ASC for byte-identical repeated runs.
|
|
59
|
+
*
|
|
60
|
+
* @param records - Enriched lesson records (with optional createdAt from CLI)
|
|
61
|
+
* @param opts.now - ISO 8601 wall-clock, injected — never read inside
|
|
62
|
+
* @param opts.minOccurrences - Below-threshold occurrence count (default 2)
|
|
63
|
+
* @param opts.maxAgeMs - Staleness threshold in ms (default 30 days)
|
|
64
|
+
*/
|
|
65
|
+
export declare function pruneLessons(records: PrunableLesson[], { now, minOccurrences, maxAgeMs }: PruneOptions): PruneResult;
|
|
66
|
+
//# sourceMappingURL=hygiene.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hygiene.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/memory/hygiene.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAI/D,gEAAgE;AAChE,eAAO,MAAM,cAAc,QAA2B,CAAC;AAQvD,mFAAmF;AACnF,MAAM,WAAW,cAAe,SAAQ,iBAAiB;IACvD,4FAA4F;IAC5F,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,+DAA+D;IAC/D,GAAG,EAAE,MAAM,CAAC;IACZ;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,WAAW;IAC1B,+EAA+E;IAC/E,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,uFAAuF;IACvF,WAAW,EAAE,cAAc,EAAE,CAAC;CAC/B;AAkDD;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,cAAc,EAAE,EACzB,EAAE,GAAG,EAAE,cAAkB,EAAE,QAAyB,EAAE,EAAE,YAAY,GACnE,WAAW,CAsEb"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PURE deterministic lesson hygiene: decay-based pruning + conflict quarantine.
|
|
3
|
+
*
|
|
4
|
+
* PURE — the clock is never read here; `now` is injected by the CLI handler
|
|
5
|
+
* (mirrors reconcileFact's { now } pattern at reconcile.ts:54). Operates
|
|
6
|
+
* ONLY on the fields each record carries; the CLI assembles recency input (createdAt)
|
|
7
|
+
* by reading per-lesson files and passing it in via PrunableLesson.createdAt.
|
|
8
|
+
*
|
|
9
|
+
* Conflict detection: DETERMINISTIC — two lessons sharing the same contradiction key
|
|
10
|
+
* (category root + discriminator tag) with opposing polarity markers are BOTH moved
|
|
11
|
+
* to quarantine. No LLM.
|
|
12
|
+
*
|
|
13
|
+
* Decay: lessons below `minOccurrences` that are also older than `maxAgeMs` are
|
|
14
|
+
* quarantined. Missing createdAt is treated as "maximally stale" (decays immediately
|
|
15
|
+
* when occurrences are below threshold), which is conservative and documented here
|
|
16
|
+
* as a deliberate choice — prefer to quarantine an unknown-age low-occurrence lesson
|
|
17
|
+
* rather than silently keep it forever.
|
|
18
|
+
*/
|
|
19
|
+
// ── Constants ────────────────────────────────────────────────────────────────
|
|
20
|
+
/** Default age threshold for decay: 30 days in milliseconds. */
|
|
21
|
+
export const THIRTY_DAYS_MS = 30 * 24 * 60 * 60 * 1000;
|
|
22
|
+
/** Polarity markers for conflict detection (keep vs. avoid). */
|
|
23
|
+
const KEEP_MARKERS = new Set(["keep", "stable", "pass", "trusted"]);
|
|
24
|
+
const AVOID_MARKERS = new Set(["avoid", "fragile", "fail", "untrusted"]);
|
|
25
|
+
// ── Helpers ──────────────────────────────────────────────────────────────────
|
|
26
|
+
/**
|
|
27
|
+
* Derive the category root from a category string (substring before the first ":").
|
|
28
|
+
* e.g. "eval-strategy-failure:unit-test" → "eval-strategy-failure"
|
|
29
|
+
* "sprint-rework" → "sprint-rework"
|
|
30
|
+
*/
|
|
31
|
+
function categoryRoot(category) {
|
|
32
|
+
const idx = category.indexOf(":");
|
|
33
|
+
return idx === -1 ? category : category.slice(0, idx);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Extract the first discriminator tag from a tag list.
|
|
37
|
+
* Priority: sprintId: > strategy: > verificationMethod:
|
|
38
|
+
* Falls back to the first tag present, or empty string.
|
|
39
|
+
*/
|
|
40
|
+
function discriminatorTag(tags) {
|
|
41
|
+
for (const prefix of ["sprintId:", "strategy:", "verificationMethod:"]) {
|
|
42
|
+
const found = tags.find((t) => t.startsWith(prefix));
|
|
43
|
+
if (found !== undefined)
|
|
44
|
+
return found;
|
|
45
|
+
}
|
|
46
|
+
return tags[0] ?? "";
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Extract the polarity marker from a tag list.
|
|
50
|
+
* Returns "keep", "avoid", or "neutral" based on the first matching tag.
|
|
51
|
+
*/
|
|
52
|
+
function polarityOf(tags) {
|
|
53
|
+
for (const tag of tags) {
|
|
54
|
+
const bare = tag.toLowerCase();
|
|
55
|
+
if (KEEP_MARKERS.has(bare))
|
|
56
|
+
return "keep";
|
|
57
|
+
if (AVOID_MARKERS.has(bare))
|
|
58
|
+
return "avoid";
|
|
59
|
+
}
|
|
60
|
+
return "neutral";
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Build the contradiction key for conflict detection.
|
|
64
|
+
* Two records with the same key and opposing polarity (keep vs. avoid) are conflicts.
|
|
65
|
+
*/
|
|
66
|
+
function contradictionKey(record) {
|
|
67
|
+
return `${categoryRoot(record.category)}|${discriminatorTag(record.tags)}`;
|
|
68
|
+
}
|
|
69
|
+
// ── Core ─────────────────────────────────────────────────────────────────────
|
|
70
|
+
/**
|
|
71
|
+
* Partition lessons into kept vs. quarantined.
|
|
72
|
+
*
|
|
73
|
+
* Phase 1 (conflict detection): identifies deterministically contradictory pairs.
|
|
74
|
+
* A pair is contradictory when both records share the same contradiction key
|
|
75
|
+
* (category root + discriminator tag) AND carry opposing polarity markers
|
|
76
|
+
* (one "keep"-marked, one "avoid"-marked). BOTH are quarantined.
|
|
77
|
+
*
|
|
78
|
+
* Phase 2 (decay): for remaining records, quarantines those below `minOccurrences`
|
|
79
|
+
* that are also stale (createdAt older than maxAgeMs, or createdAt absent).
|
|
80
|
+
*
|
|
81
|
+
* Both output arrays are sorted by lessonId ASC for byte-identical repeated runs.
|
|
82
|
+
*
|
|
83
|
+
* @param records - Enriched lesson records (with optional createdAt from CLI)
|
|
84
|
+
* @param opts.now - ISO 8601 wall-clock, injected — never read inside
|
|
85
|
+
* @param opts.minOccurrences - Below-threshold occurrence count (default 2)
|
|
86
|
+
* @param opts.maxAgeMs - Staleness threshold in ms (default 30 days)
|
|
87
|
+
*/
|
|
88
|
+
export function pruneLessons(records, { now, minOccurrences = 2, maxAgeMs = THIRTY_DAYS_MS }) {
|
|
89
|
+
const nowMs = Date.parse(now);
|
|
90
|
+
// ── Phase 1: conflict detection ─────────────────────────────────────────
|
|
91
|
+
// Group by contradiction key, collecting polarity.
|
|
92
|
+
const byKey = new Map();
|
|
93
|
+
for (const rec of records) {
|
|
94
|
+
const key = contradictionKey(rec);
|
|
95
|
+
let bucket = byKey.get(key);
|
|
96
|
+
if (bucket === undefined) {
|
|
97
|
+
bucket = { keeps: [], avoids: [] };
|
|
98
|
+
byKey.set(key, bucket);
|
|
99
|
+
}
|
|
100
|
+
const polarity = polarityOf(rec.tags);
|
|
101
|
+
if (polarity === "keep") {
|
|
102
|
+
bucket.keeps.push(rec);
|
|
103
|
+
}
|
|
104
|
+
else if (polarity === "avoid") {
|
|
105
|
+
bucket.avoids.push(rec);
|
|
106
|
+
}
|
|
107
|
+
// "neutral" polarity: the record is bucketed but will not trigger a conflict on its own
|
|
108
|
+
}
|
|
109
|
+
// A conflict fires when a key has BOTH keep-marked AND avoid-marked records.
|
|
110
|
+
const conflictIds = new Set();
|
|
111
|
+
for (const [, bucket] of byKey) {
|
|
112
|
+
if (bucket.keeps.length > 0 && bucket.avoids.length > 0) {
|
|
113
|
+
for (const r of [...bucket.keeps, ...bucket.avoids]) {
|
|
114
|
+
conflictIds.add(r.lessonId);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
// ── Phase 2: decay ───────────────────────────────────────────────────────
|
|
119
|
+
const decayIds = new Set();
|
|
120
|
+
for (const rec of records) {
|
|
121
|
+
if (conflictIds.has(rec.lessonId))
|
|
122
|
+
continue; // already quarantined by conflict
|
|
123
|
+
if (rec.occurrences >= minOccurrences)
|
|
124
|
+
continue; // well-established, keep
|
|
125
|
+
// Below minOccurrences: quarantine if stale (or no age info → maximally stale).
|
|
126
|
+
if (rec.createdAt === undefined) {
|
|
127
|
+
// No age info = unknown = treat as maximally stale → quarantine
|
|
128
|
+
decayIds.add(rec.lessonId);
|
|
129
|
+
continue;
|
|
130
|
+
}
|
|
131
|
+
const ageMs = nowMs - Date.parse(rec.createdAt);
|
|
132
|
+
if (ageMs > maxAgeMs) {
|
|
133
|
+
decayIds.add(rec.lessonId);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// ── Partition ────────────────────────────────────────────────────────────
|
|
137
|
+
const quarantinedSet = new Set([...conflictIds, ...decayIds]);
|
|
138
|
+
const kept = [];
|
|
139
|
+
const quarantined = [];
|
|
140
|
+
for (const rec of records) {
|
|
141
|
+
if (quarantinedSet.has(rec.lessonId)) {
|
|
142
|
+
quarantined.push(rec);
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
kept.push(rec);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
// Sort both arrays by lessonId for deterministic, byte-identical output.
|
|
149
|
+
kept.sort((a, b) => a.lessonId.localeCompare(b.lessonId));
|
|
150
|
+
quarantined.sort((a, b) => a.lessonId.localeCompare(b.lessonId));
|
|
151
|
+
return { kept, quarantined };
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=hygiene.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hygiene.js","sourceRoot":"","sources":["../../../src/orchestrator/memory/hygiene.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PURE deterministic reconcile-on-write for semantic facts.
|
|
3
|
+
*
|
|
4
|
+
* PURE — never reads the clock (now is injected), never calls createClient,
|
|
5
|
+
* no network access, no Date.now(), no side effects beyond the injected store.
|
|
6
|
+
* The injected FactJudge is the ONLY async/LLM surface and is consulted ONLY
|
|
7
|
+
* on a deterministic normalized-key collision (the ambiguity branch).
|
|
8
|
+
* Exact-match NOOP/UPDATE/ADD never touch the judge or the network.
|
|
9
|
+
*/
|
|
10
|
+
import type { FactStore, FactInput } from "../../state/facts.js";
|
|
11
|
+
import type { FactJudge } from "./fact-judge.js";
|
|
12
|
+
export type ReconcileAction = "add" | "update" | "delete" | "noop";
|
|
13
|
+
/**
|
|
14
|
+
* Reconcile an incoming fact against the active state in the store.
|
|
15
|
+
*
|
|
16
|
+
* Algorithm:
|
|
17
|
+
* 1. Query getActiveFacts(scope, subject, predicate) — exact key, active-only.
|
|
18
|
+
* 2. Exact match with SAME value → NOOP.
|
|
19
|
+
* 3. Exact match with DIFFERENT value → UPDATE:
|
|
20
|
+
* supersedeFact(old.id, now, incoming.tValid) then insertFact(incoming).
|
|
21
|
+
* 4. No exact match → AMBIGUITY check via normalizeKey over all active facts in scope.
|
|
22
|
+
* a. Collision AND judge provided → judge.resolve(incoming, candidate) → apply.
|
|
23
|
+
* b. Collision but NO judge → deterministic ADD fallback.
|
|
24
|
+
* c. No collision → ADD.
|
|
25
|
+
* 5. DELETE = supersedeFact(candidate.id, now, incoming.tValid) without inserting incoming.
|
|
26
|
+
*
|
|
27
|
+
* @param store - The FactStore to read from and write to.
|
|
28
|
+
* @param incoming - The incoming fact input (validated by insertFact on the ADD path).
|
|
29
|
+
* @param opts.judge - Optional LLM-backed judge for ambiguity resolution.
|
|
30
|
+
* @param opts.now - ISO 8601 wall-clock timestamp (record-time); NEVER read inside.
|
|
31
|
+
*/
|
|
32
|
+
export declare function reconcileFact(store: FactStore, incoming: FactInput, { judge, now }: {
|
|
33
|
+
judge?: FactJudge;
|
|
34
|
+
now: string;
|
|
35
|
+
}): Promise<ReconcileAction>;
|
|
36
|
+
/**
|
|
37
|
+
* Reconcile-then-write a fact. Wall-clock `now` is injected by the caller —
|
|
38
|
+
* this function never reads the clock (mirrors the store's purity contract).
|
|
39
|
+
*
|
|
40
|
+
* The `judge` is optional; when absent the exact-match UPDATE/NOOP paths run
|
|
41
|
+
* deterministically and ambiguous collisions fall back to ADD.
|
|
42
|
+
*/
|
|
43
|
+
export declare function writeFact(store: FactStore, incoming: FactInput, opts: {
|
|
44
|
+
judge?: FactJudge;
|
|
45
|
+
now: string;
|
|
46
|
+
}): Promise<ReconcileAction>;
|
|
47
|
+
//# sourceMappingURL=reconcile.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/memory/reconcile.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAIjD,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG,QAAQ,GAAG,QAAQ,GAAG,MAAM,CAAC;AAgBnE;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAsB,aAAa,CACjC,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,SAAS,EACnB,EAAE,KAAK,EAAE,GAAG,EAAE,EAAE;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACjD,OAAO,CAAC,eAAe,CAAC,CA8C1B;AAwCD;;;;;;GAMG;AACH,wBAAsB,SAAS,CAC7B,KAAK,EAAE,SAAS,EAChB,QAAQ,EAAE,SAAS,EACnB,IAAI,EAAE;IAAE,KAAK,CAAC,EAAE,SAAS,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GACvC,OAAO,CAAC,eAAe,CAAC,CAE1B"}
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PURE deterministic reconcile-on-write for semantic facts.
|
|
3
|
+
*
|
|
4
|
+
* PURE — never reads the clock (now is injected), never calls createClient,
|
|
5
|
+
* no network access, no Date.now(), no side effects beyond the injected store.
|
|
6
|
+
* The injected FactJudge is the ONLY async/LLM surface and is consulted ONLY
|
|
7
|
+
* on a deterministic normalized-key collision (the ambiguity branch).
|
|
8
|
+
* Exact-match NOOP/UPDATE/ADD never touch the judge or the network.
|
|
9
|
+
*/
|
|
10
|
+
// ── Helpers ──────────────────────────────────────────────────────────────
|
|
11
|
+
/**
|
|
12
|
+
* Normalize a subject+predicate pair into a collision key.
|
|
13
|
+
* Lowercase + strip non-alphanumeric characters — mirrors tokenize() in retrieve.ts:30-35.
|
|
14
|
+
* Private to this module; do not import from retrieve.ts (it is not exported).
|
|
15
|
+
*/
|
|
16
|
+
function normalizeKey(subject, predicate) {
|
|
17
|
+
const norm = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, "");
|
|
18
|
+
return `${norm(subject)}|${norm(predicate)}`;
|
|
19
|
+
}
|
|
20
|
+
// ── Core ──────────────────────────────────────────────────────────────────
|
|
21
|
+
/**
|
|
22
|
+
* Reconcile an incoming fact against the active state in the store.
|
|
23
|
+
*
|
|
24
|
+
* Algorithm:
|
|
25
|
+
* 1. Query getActiveFacts(scope, subject, predicate) — exact key, active-only.
|
|
26
|
+
* 2. Exact match with SAME value → NOOP.
|
|
27
|
+
* 3. Exact match with DIFFERENT value → UPDATE:
|
|
28
|
+
* supersedeFact(old.id, now, incoming.tValid) then insertFact(incoming).
|
|
29
|
+
* 4. No exact match → AMBIGUITY check via normalizeKey over all active facts in scope.
|
|
30
|
+
* a. Collision AND judge provided → judge.resolve(incoming, candidate) → apply.
|
|
31
|
+
* b. Collision but NO judge → deterministic ADD fallback.
|
|
32
|
+
* c. No collision → ADD.
|
|
33
|
+
* 5. DELETE = supersedeFact(candidate.id, now, incoming.tValid) without inserting incoming.
|
|
34
|
+
*
|
|
35
|
+
* @param store - The FactStore to read from and write to.
|
|
36
|
+
* @param incoming - The incoming fact input (validated by insertFact on the ADD path).
|
|
37
|
+
* @param opts.judge - Optional LLM-backed judge for ambiguity resolution.
|
|
38
|
+
* @param opts.now - ISO 8601 wall-clock timestamp (record-time); NEVER read inside.
|
|
39
|
+
*/
|
|
40
|
+
export async function reconcileFact(store, incoming, { judge, now }) {
|
|
41
|
+
// ── Step 1: Exact-key lookup (active only) ────────────────────────────
|
|
42
|
+
const exactMatches = store.getActiveFacts(incoming.scope, incoming.subject, incoming.predicate);
|
|
43
|
+
// ── Step 2: Same value → NOOP ─────────────────────────────────────────
|
|
44
|
+
if (exactMatches.length > 0) {
|
|
45
|
+
const same = exactMatches.find((r) => r.value === incoming.value);
|
|
46
|
+
if (same !== undefined) {
|
|
47
|
+
return "noop";
|
|
48
|
+
}
|
|
49
|
+
// ── Step 3: Different value → UPDATE (supersede) ─────────────────────
|
|
50
|
+
// Supersede every matching active row (normally only one, but be safe).
|
|
51
|
+
for (const old of exactMatches) {
|
|
52
|
+
// Set BOTH t_invalid (world-time = incoming tValid) AND t_invalidated (record-time = now)
|
|
53
|
+
store.supersedeFact(old.id, now, incoming.tValid);
|
|
54
|
+
}
|
|
55
|
+
store.insertFact(incoming);
|
|
56
|
+
return "update";
|
|
57
|
+
}
|
|
58
|
+
// ── Step 4: No exact match → Ambiguity check ─────────────────────────
|
|
59
|
+
const incomingKey = normalizeKey(incoming.subject, incoming.predicate);
|
|
60
|
+
const allActive = store.getActiveFacts(incoming.scope);
|
|
61
|
+
const candidate = allActive.find((r) => normalizeKey(r.subject, r.predicate) === incomingKey);
|
|
62
|
+
if (candidate !== undefined) {
|
|
63
|
+
// ── Step 4a: Collision AND judge → delegate ───────────────────────
|
|
64
|
+
if (judge !== undefined) {
|
|
65
|
+
const action = await judge.resolve(incoming, candidate);
|
|
66
|
+
return applyJudgeDecision(store, incoming, candidate, action, now);
|
|
67
|
+
}
|
|
68
|
+
// ── Step 4b: Collision but NO judge → deterministic ADD fallback ──
|
|
69
|
+
store.insertFact(incoming);
|
|
70
|
+
return "add";
|
|
71
|
+
}
|
|
72
|
+
// ── Step 4c: No collision → ADD ───────────────────────────────────────
|
|
73
|
+
store.insertFact(incoming);
|
|
74
|
+
return "add";
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Apply a judge's decision to the store.
|
|
78
|
+
* DELETE = supersede candidate only; no insertFact.
|
|
79
|
+
*/
|
|
80
|
+
function applyJudgeDecision(store, incoming, candidate, action, now) {
|
|
81
|
+
switch (action) {
|
|
82
|
+
case "add":
|
|
83
|
+
store.insertFact(incoming);
|
|
84
|
+
return "add";
|
|
85
|
+
case "update":
|
|
86
|
+
store.supersedeFact(candidate.id, now, incoming.tValid);
|
|
87
|
+
store.insertFact(incoming);
|
|
88
|
+
return "update";
|
|
89
|
+
case "delete":
|
|
90
|
+
store.supersedeFact(candidate.id, now, incoming.tValid);
|
|
91
|
+
return "delete";
|
|
92
|
+
case "noop":
|
|
93
|
+
return "noop";
|
|
94
|
+
default: {
|
|
95
|
+
// Exhaustive check — if judge returns unknown action, fall back to ADD
|
|
96
|
+
store.insertFact(incoming);
|
|
97
|
+
return "add";
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
// ── writeFact ─────────────────────────────────────────────────────────────
|
|
102
|
+
//
|
|
103
|
+
// Thin wrapper that calls reconcileFact. Lives here (not in facts.ts) to
|
|
104
|
+
// avoid a state→orchestrator import cycle at runtime.
|
|
105
|
+
// The CLI and future consumers import writeFact from this module.
|
|
106
|
+
/**
|
|
107
|
+
* Reconcile-then-write a fact. Wall-clock `now` is injected by the caller —
|
|
108
|
+
* this function never reads the clock (mirrors the store's purity contract).
|
|
109
|
+
*
|
|
110
|
+
* The `judge` is optional; when absent the exact-match UPDATE/NOOP paths run
|
|
111
|
+
* deterministically and ambiguous collisions fall back to ADD.
|
|
112
|
+
*/
|
|
113
|
+
export async function writeFact(store, incoming, opts) {
|
|
114
|
+
return reconcileFact(store, incoming, opts);
|
|
115
|
+
}
|
|
116
|
+
//# sourceMappingURL=reconcile.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reconcile.js","sourceRoot":"","sources":["../../../src/orchestrator/memory/reconcile.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,8 +12,9 @@ import type { LessonIndexRecord } from "../../state/memory.js";
|
|
|
12
12
|
* to the given `keywords`.
|
|
13
13
|
*
|
|
14
14
|
* Scoring is deterministic token-overlap between the lowercased keyword tokens
|
|
15
|
-
* and each record's tags + category + summarySnippet.
|
|
16
|
-
*
|
|
15
|
+
* and each record's tags + category + summarySnippet. When overlap scores tie,
|
|
16
|
+
* records with higher occurrences rank above lower-occurrence records.
|
|
17
|
+
* Final tiebreak is by lessonId ASC (lexicographic) for byte-stable output.
|
|
17
18
|
*
|
|
18
19
|
* A non-matching keyword set (no overlap with any record) yields an empty result.
|
|
19
20
|
*
|
|
@@ -22,9 +23,10 @@ import type { LessonIndexRecord } from "../../state/memory.js";
|
|
|
22
23
|
* @param options - topK cap (default 5) and optional charBudget (ignored here; passed to serializer)
|
|
23
24
|
* @returns At most topK records sorted by relevance (descending), then lessonId (ascending)
|
|
24
25
|
*/
|
|
25
|
-
export declare function retrieveRelevantLessons(projectRoot: string, keywords: string[], { topK, charBudget: _charBudget, }?: {
|
|
26
|
+
export declare function retrieveRelevantLessons(projectRoot: string, keywords: string[], { topK, charBudget: _charBudget, namespace, }?: {
|
|
26
27
|
topK?: number;
|
|
27
28
|
charBudget?: number;
|
|
29
|
+
namespace?: string;
|
|
28
30
|
}): Promise<LessonIndexRecord[]>;
|
|
29
31
|
/**
|
|
30
32
|
* Render a compact planner context block from an array of LessonIndexRecord values.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retrieve.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/memory/retrieve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAsD/D
|
|
1
|
+
{"version":3,"file":"retrieve.d.ts","sourceRoot":"","sources":["../../../src/orchestrator/memory/retrieve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,uBAAuB,CAAC;AAsD/D;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,uBAAuB,CAC3C,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,EAAE,EAClB,EACE,IAAoB,EACpB,UAAU,EAAE,WAAW,EACvB,SAAS,GACV,GAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,MAAM,CAAA;CAAO,GACjE,OAAO,CAAC,iBAAiB,EAAE,CAAC,CAkB9B;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,0BAA0B,CACxC,OAAO,EAAE,iBAAiB,EAAE,EAC5B,EAAE,UAAgC,EAAE,GAAE;IAAE,UAAU,CAAC,EAAE,MAAM,CAAA;CAAO,GACjE,MAAM,CAmBR"}
|
|
@@ -52,8 +52,9 @@ function scoreRecord(record, keywordTokens) {
|
|
|
52
52
|
* to the given `keywords`.
|
|
53
53
|
*
|
|
54
54
|
* Scoring is deterministic token-overlap between the lowercased keyword tokens
|
|
55
|
-
* and each record's tags + category + summarySnippet.
|
|
56
|
-
*
|
|
55
|
+
* and each record's tags + category + summarySnippet. When overlap scores tie,
|
|
56
|
+
* records with higher occurrences rank above lower-occurrence records.
|
|
57
|
+
* Final tiebreak is by lessonId ASC (lexicographic) for byte-stable output.
|
|
57
58
|
*
|
|
58
59
|
* A non-matching keyword set (no overlap with any record) yields an empty result.
|
|
59
60
|
*
|
|
@@ -62,14 +63,16 @@ function scoreRecord(record, keywordTokens) {
|
|
|
62
63
|
* @param options - topK cap (default 5) and optional charBudget (ignored here; passed to serializer)
|
|
63
64
|
* @returns At most topK records sorted by relevance (descending), then lessonId (ascending)
|
|
64
65
|
*/
|
|
65
|
-
export async function retrieveRelevantLessons(projectRoot, keywords, { topK = DEFAULT_TOP_K, charBudget: _charBudget, } = {}) {
|
|
66
|
-
const records = await loadLessonIndex(projectRoot, { limit: INDEX_LOAD_LIMIT });
|
|
66
|
+
export async function retrieveRelevantLessons(projectRoot, keywords, { topK = DEFAULT_TOP_K, charBudget: _charBudget, namespace, } = {}) {
|
|
67
|
+
const records = await loadLessonIndex(projectRoot, { limit: INDEX_LOAD_LIMIT }, namespace);
|
|
67
68
|
const keywordTokens = new Set(keywords.flatMap(tokenize));
|
|
68
69
|
const scored = records
|
|
69
70
|
.map((r) => ({ r, score: scoreRecord(r, keywordTokens) }))
|
|
70
71
|
.filter((x) => x.score > 0); // C1: non-matching keyword -> empty
|
|
71
|
-
// Sort: score DESC, then lessonId ASC
|
|
72
|
-
scored.sort((a, b) => b.score - a.score ||
|
|
72
|
+
// Sort: score DESC, then occurrences DESC (tiebreak booster), then lessonId ASC (byte-stable)
|
|
73
|
+
scored.sort((a, b) => b.score - a.score || // 1. token overlap (DOMINANT)
|
|
74
|
+
b.r.occurrences - a.r.occurrences || // 2. higher occurrences win on ties (sc-3-2)
|
|
75
|
+
a.r.lessonId.localeCompare(b.r.lessonId));
|
|
73
76
|
return scored.slice(0, topK).map((x) => x.r);
|
|
74
77
|
}
|
|
75
78
|
/**
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"retrieve.js","sourceRoot":"","sources":["../../../src/orchestrator/memory/retrieve.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAGxD,uEAAuE;AAEvE,2CAA2C;AAC3C,MAAM,aAAa,GAAG,CAAC,CAAC;AAExB,iEAAiE;AACjE,MAAM,mBAAmB,GAAG,IAAI,CAAC;AAEjC,4EAA4E;AAC5E,MAAM,gBAAgB,GAAG,GAAG,CAAC;AAE7B,wEAAwE;AAExE;;;GAGG;AACH,SAAS,QAAQ,CAAC,CAAS;IACzB,OAAO,CAAC;SACL,WAAW,EAAE;SACb,KAAK,CAAC,YAAY,CAAC;SACnB,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACjC,CAAC;AAED;;;;GAIG;AACH,SAAS,WAAW,CAClB,MAAyB,EACzB,aAA0B;IAE1B,IAAI,aAAa,CAAC,IAAI,KAAK,CAAC;QAAE,OAAO,CAAC,CAAC;IAEvC,MAAM,YAAY,GAAG,IAAI,GAAG,CAAS;QACnC,GAAG,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC;QAC5B,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC;QAChC,GAAG,QAAQ,CAAC,MAAM,CAAC,cAAc,CAAC;KACnC,CAAC,CAAC;IAEH,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,KAAK,MAAM,KAAK,IAAI,aAAa,EAAE,CAAC;QAClC,IAAI,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC;YAC5B,KAAK,EAAE,CAAC;QACV,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,wEAAwE;AAExE
|
|
1
|
+
{"version":3,"file":"retrieve.js","sourceRoot":"","sources":["../../../src/orchestrator/memory/retrieve.ts"],"names":[],"mappings":"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"}
|