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,73 @@
|
|
|
1
|
+
/** ConsentGate — fail-closed first-run consent (Phase 6, Sprint 2). */
|
|
2
|
+
import { readFile, writeFile } from "node:fs/promises";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { ensureDir } from "../utils/fs.js";
|
|
5
|
+
// ── ConsentGate ─────────────────────────────────────────────────────
|
|
6
|
+
/**
|
|
7
|
+
* Manages consent persistence at .bober/medical/consent.json.
|
|
8
|
+
*
|
|
9
|
+
* Fail-closed: a missing or corrupt consent.json returns false/undefined —
|
|
10
|
+
* the engine refuses rather than proceeding without consent.
|
|
11
|
+
*
|
|
12
|
+
* Depends on AuditLog to record a 'consent' entry on recordConsent().
|
|
13
|
+
*
|
|
14
|
+
* bober: JSON file sidecar; if multi-session concurrent access is needed,
|
|
15
|
+
* migrate to the SQLite FactStore (see src/state/facts.ts).
|
|
16
|
+
*/
|
|
17
|
+
export class ConsentGate {
|
|
18
|
+
projectRoot;
|
|
19
|
+
audit;
|
|
20
|
+
constructor(projectRoot, audit) {
|
|
21
|
+
this.projectRoot = projectRoot;
|
|
22
|
+
this.audit = audit;
|
|
23
|
+
}
|
|
24
|
+
// ── Path ──────────────────────────────────────────────────────────
|
|
25
|
+
path() {
|
|
26
|
+
return join(this.projectRoot, ".bober", "medical", "consent.json");
|
|
27
|
+
}
|
|
28
|
+
// ── Read ──────────────────────────────────────────────────────────
|
|
29
|
+
/**
|
|
30
|
+
* Fail-closed: missing or corrupt file returns false. Never throws.
|
|
31
|
+
* Reads .bober/medical/consent.json via node:fs/promises.
|
|
32
|
+
*/
|
|
33
|
+
async hasConsent() {
|
|
34
|
+
return (await this.current()) !== undefined;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Returns the parsed ConsentRecord, or undefined if missing/corrupt.
|
|
38
|
+
* Validates required fields — a partial/corrupt record returns undefined.
|
|
39
|
+
*/
|
|
40
|
+
async current() {
|
|
41
|
+
try {
|
|
42
|
+
const data = JSON.parse(await readFile(this.path(), "utf-8"));
|
|
43
|
+
// Validate required fields for fail-closed behaviour on partial records.
|
|
44
|
+
if (typeof data.consentVersion !== "string" ||
|
|
45
|
+
typeof data.acceptedAtIso !== "string" ||
|
|
46
|
+
typeof data.rulesetVersion !== "string" ||
|
|
47
|
+
typeof data.disclaimerVersion !== "string") {
|
|
48
|
+
return undefined;
|
|
49
|
+
}
|
|
50
|
+
return data;
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
return undefined;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
// ── Write ─────────────────────────────────────────────────────────
|
|
57
|
+
/**
|
|
58
|
+
* Persist a ConsentRecord (mode 0600) AND append a 'consent' audit entry.
|
|
59
|
+
*
|
|
60
|
+
* @param record - The consent record to persist.
|
|
61
|
+
* @param nowIso - INJECTED ISO 8601 timestamp — never wall-clock.
|
|
62
|
+
*/
|
|
63
|
+
async recordConsent(record, nowIso) {
|
|
64
|
+
await ensureDir(join(this.projectRoot, ".bober", "medical"));
|
|
65
|
+
await writeFile(this.path(), JSON.stringify(record, null, 2) + "\n", { encoding: "utf-8", mode: 0o600 });
|
|
66
|
+
await this.audit.append({
|
|
67
|
+
tIso: nowIso,
|
|
68
|
+
event: "consent",
|
|
69
|
+
rulesetVersion: record.rulesetVersion,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=consent.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"consent.js","sourceRoot":"","sources":["../../src/medical/consent.ts"],"names":[],"mappings":"AAAA,uEAAuE;AACvE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACvD,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAI3C,uEAAuE;AAEvE;;;;;;;;;;GAUG;AACH,MAAM,OAAO,WAAW;IAEH;IACA;IAFnB,YACmB,WAAmB,EACnB,KAAe;QADf,gBAAW,GAAX,WAAW,CAAQ;QACnB,UAAK,GAAL,KAAK,CAAU;IAC/B,CAAC;IAEJ,qEAAqE;IAE7D,IAAI;QACV,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,cAAc,CAAC,CAAC;IACrE,CAAC;IAED,qEAAqE;IAErE;;;OAGG;IACH,KAAK,CAAC,UAAU;QACd,OAAO,CAAC,MAAM,IAAI,CAAC,OAAO,EAAE,CAAC,KAAK,SAAS,CAAC;IAC9C,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,OAAO;QACX,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CACrB,MAAM,QAAQ,CAAC,IAAI,CAAC,IAAI,EAAE,EAAE,OAAO,CAAC,CACpB,CAAC;YACnB,yEAAyE;YACzE,IACE,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ;gBACvC,OAAO,IAAI,CAAC,aAAa,KAAK,QAAQ;gBACtC,OAAO,IAAI,CAAC,cAAc,KAAK,QAAQ;gBACvC,OAAO,IAAI,CAAC,iBAAiB,KAAK,QAAQ,EAC1C,CAAC;gBACD,OAAO,SAAS,CAAC;YACnB,CAAC;YACD,OAAO,IAAI,CAAC;QACd,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,SAAS,CAAC;QACnB,CAAC;IACH,CAAC;IAED,qEAAqE;IAErE;;;;;OAKG;IACH,KAAK,CAAC,aAAa,CAAC,MAAqB,EAAE,MAAc;QACvD,MAAM,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC;QAC7D,MAAM,SAAS,CACb,IAAI,CAAC,IAAI,EAAE,EACX,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,EACtC,EAAE,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,CACnC,CAAC;QACF,MAAM,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC;YACtB,IAAI,EAAE,MAAM;YACZ,KAAK,EAAE,SAAS;YAChB,cAAc,EAAE,MAAM,CAAC,cAAc;SACtC,CAAC,CAAC;IACL,CAAC;CACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/** DisclaimerComposer — versioned per-response wellness footer (Phase 6, Sprint 2). */
|
|
2
|
+
/**
|
|
3
|
+
* Produces a versioned per-response wellness disclaimer footer.
|
|
4
|
+
* Pure — no I/O, no side effects.
|
|
5
|
+
*/
|
|
6
|
+
export declare class DisclaimerComposer {
|
|
7
|
+
readonly disclaimerVersion = "1.0.0";
|
|
8
|
+
/** Returns a non-empty footer string carrying the disclaimerVersion. */
|
|
9
|
+
footer(): string;
|
|
10
|
+
}
|
|
11
|
+
//# sourceMappingURL=disclaimer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disclaimer.d.ts","sourceRoot":"","sources":["../../src/medical/disclaimer.ts"],"names":[],"mappings":"AAAA,uFAAuF;AAcvF;;;GAGG;AACH,qBAAa,kBAAkB;IAC7B,QAAQ,CAAC,iBAAiB,WAAsB;IAEhD,wEAAwE;IACxE,MAAM,IAAI,MAAM;CAGjB"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/** DisclaimerComposer — versioned per-response wellness footer (Phase 6, Sprint 2). */
|
|
2
|
+
// ── DisclaimerComposer ──────────────────────────────────────────────
|
|
3
|
+
const DISCLAIMER_VERSION = "1.0.0";
|
|
4
|
+
/**
|
|
5
|
+
* General-wellness disclaimer text. Non-diagnostic, non-treatment.
|
|
6
|
+
* Consistent with FFDCA §201(h) / GW-safe-harbor posture.
|
|
7
|
+
*/
|
|
8
|
+
const FOOTER_TEXT = "General wellness information only — not medical advice, diagnosis, or treatment. " +
|
|
9
|
+
"Consult a licensed professional. In an emergency call your local emergency number.";
|
|
10
|
+
/**
|
|
11
|
+
* Produces a versioned per-response wellness disclaimer footer.
|
|
12
|
+
* Pure — no I/O, no side effects.
|
|
13
|
+
*/
|
|
14
|
+
export class DisclaimerComposer {
|
|
15
|
+
disclaimerVersion = DISCLAIMER_VERSION;
|
|
16
|
+
/** Returns a non-empty footer string carrying the disclaimerVersion. */
|
|
17
|
+
footer() {
|
|
18
|
+
return `${FOOTER_TEXT} [disclaimer v${this.disclaimerVersion}]`;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
//# sourceMappingURL=disclaimer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"disclaimer.js","sourceRoot":"","sources":["../../src/medical/disclaimer.ts"],"names":[],"mappings":"AAAA,uFAAuF;AAEvF,uEAAuE;AAEvE,MAAM,kBAAkB,GAAG,OAAO,CAAC;AAEnC;;;GAGG;AACH,MAAM,WAAW,GACf,mFAAmF;IACnF,oFAAoF,CAAC;AAEvF;;;GAGG;AACH,MAAM,OAAO,kBAAkB;IACpB,iBAAiB,GAAG,kBAAkB,CAAC;IAEhD,wEAAwE;IACxE,MAAM;QACJ,OAAO,GAAG,WAAW,iBAAiB,IAAI,CAAC,iBAAiB,GAAG,CAAC;IAClE,CAAC;CACF"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** EgressGuard — three independently opt-in egress axes, all default false (Phase 6, Sprint 6; ADR-6). */
|
|
2
|
+
import type { BoberConfig } from "../config/schema.js";
|
|
3
|
+
/** The three independent egress axes. All default FALSE (code-enforced zero-egress, ADR-6). */
|
|
4
|
+
export type EgressAxis = "cloud-inference" | "literature-retrieval" | "device-connection";
|
|
5
|
+
/**
|
|
6
|
+
* Guards outbound egress for the medical pipeline.
|
|
7
|
+
*
|
|
8
|
+
* Three axes operate INDEPENDENTLY — enabling one does NOT enable the others.
|
|
9
|
+
* All default false when absent from config. assertAllowed throws if the axis
|
|
10
|
+
* is not opted in, providing a hard code-enforced barrier.
|
|
11
|
+
*
|
|
12
|
+
* bober: plain decision object; no network import here; swap for ABAC policy if
|
|
13
|
+
* per-user granularity is needed.
|
|
14
|
+
*/
|
|
15
|
+
export declare class EgressGuard {
|
|
16
|
+
private readonly cloudInference;
|
|
17
|
+
private readonly literatureRetrieval;
|
|
18
|
+
private readonly deviceConnection;
|
|
19
|
+
constructor(cloudInference: boolean, literatureRetrieval: boolean, deviceConnection?: boolean);
|
|
20
|
+
/** Build from BoberConfig medical section; all axes default false when absent. */
|
|
21
|
+
static fromConfig(config: BoberConfig): EgressGuard;
|
|
22
|
+
/** Returns true only when the axis has been explicitly opted in via config. */
|
|
23
|
+
isAllowed(axis: EgressAxis): boolean;
|
|
24
|
+
/**
|
|
25
|
+
* Throws an Error when the axis is not enabled.
|
|
26
|
+
* Returns void (not throws) when the axis is allowed.
|
|
27
|
+
*/
|
|
28
|
+
assertAllowed(axis: EgressAxis): void;
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=egress.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"egress.d.ts","sourceRoot":"","sources":["../../src/medical/egress.ts"],"names":[],"mappings":"AAAA,0GAA0G;AAC1G,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AAEvD,+FAA+F;AAC/F,MAAM,MAAM,UAAU,GAAG,iBAAiB,GAAG,sBAAsB,GAAG,mBAAmB,CAAC;AAE1F;;;;;;;;;GASG;AACH,qBAAa,WAAW;IAEpB,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,mBAAmB;IACpC,OAAO,CAAC,QAAQ,CAAC,gBAAgB;gBAFhB,cAAc,EAAE,OAAO,EACvB,mBAAmB,EAAE,OAAO,EAC5B,gBAAgB,GAAE,OAAe;IAGpD,kFAAkF;IAClF,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,WAAW,GAAG,WAAW;IASnD,+EAA+E;IAC/E,SAAS,CAAC,IAAI,EAAE,UAAU,GAAG,OAAO;IAepC;;;OAGG;IACH,aAAa,CAAC,IAAI,EAAE,UAAU,GAAG,IAAI;CAKtC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Guards outbound egress for the medical pipeline.
|
|
3
|
+
*
|
|
4
|
+
* Three axes operate INDEPENDENTLY — enabling one does NOT enable the others.
|
|
5
|
+
* All default false when absent from config. assertAllowed throws if the axis
|
|
6
|
+
* is not opted in, providing a hard code-enforced barrier.
|
|
7
|
+
*
|
|
8
|
+
* bober: plain decision object; no network import here; swap for ABAC policy if
|
|
9
|
+
* per-user granularity is needed.
|
|
10
|
+
*/
|
|
11
|
+
export class EgressGuard {
|
|
12
|
+
cloudInference;
|
|
13
|
+
literatureRetrieval;
|
|
14
|
+
deviceConnection;
|
|
15
|
+
constructor(cloudInference, literatureRetrieval, deviceConnection = false) {
|
|
16
|
+
this.cloudInference = cloudInference;
|
|
17
|
+
this.literatureRetrieval = literatureRetrieval;
|
|
18
|
+
this.deviceConnection = deviceConnection;
|
|
19
|
+
}
|
|
20
|
+
/** Build from BoberConfig medical section; all axes default false when absent. */
|
|
21
|
+
static fromConfig(config) {
|
|
22
|
+
const med = config.medical;
|
|
23
|
+
return new EgressGuard(med?.egress?.cloudInference ?? false, med?.egress?.literatureRetrieval ?? false, med?.egress?.deviceConnection ?? false);
|
|
24
|
+
}
|
|
25
|
+
/** Returns true only when the axis has been explicitly opted in via config. */
|
|
26
|
+
isAllowed(axis) {
|
|
27
|
+
switch (axis) {
|
|
28
|
+
case "cloud-inference":
|
|
29
|
+
return this.cloudInference;
|
|
30
|
+
case "literature-retrieval":
|
|
31
|
+
return this.literatureRetrieval;
|
|
32
|
+
case "device-connection":
|
|
33
|
+
return this.deviceConnection;
|
|
34
|
+
default: {
|
|
35
|
+
const _exhaustive = axis; // compile error if an EgressAxis value is unhandled
|
|
36
|
+
return _exhaustive;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Throws an Error when the axis is not enabled.
|
|
42
|
+
* Returns void (not throws) when the axis is allowed.
|
|
43
|
+
*/
|
|
44
|
+
assertAllowed(axis) {
|
|
45
|
+
if (!this.isAllowed(axis)) {
|
|
46
|
+
throw new Error(`Egress axis '${axis}' not enabled`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=egress.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"egress.js","sourceRoot":"","sources":["../../src/medical/egress.ts"],"names":[],"mappings":"AAMA;;;;;;;;;GASG;AACH,MAAM,OAAO,WAAW;IAEH;IACA;IACA;IAHnB,YACmB,cAAuB,EACvB,mBAA4B,EAC5B,mBAA4B,KAAK;QAFjC,mBAAc,GAAd,cAAc,CAAS;QACvB,wBAAmB,GAAnB,mBAAmB,CAAS;QAC5B,qBAAgB,GAAhB,gBAAgB,CAAiB;IACjD,CAAC;IAEJ,kFAAkF;IAClF,MAAM,CAAC,UAAU,CAAC,MAAmB;QACnC,MAAM,GAAG,GAAG,MAAM,CAAC,OAAO,CAAC;QAC3B,OAAO,IAAI,WAAW,CACpB,GAAG,EAAE,MAAM,EAAE,cAAc,IAAI,KAAK,EACpC,GAAG,EAAE,MAAM,EAAE,mBAAmB,IAAI,KAAK,EACzC,GAAG,EAAE,MAAM,EAAE,gBAAgB,IAAI,KAAK,CACvC,CAAC;IACJ,CAAC;IAED,+EAA+E;IAC/E,SAAS,CAAC,IAAgB;QACxB,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,iBAAiB;gBACpB,OAAO,IAAI,CAAC,cAAc,CAAC;YAC7B,KAAK,sBAAsB;gBACzB,OAAO,IAAI,CAAC,mBAAmB,CAAC;YAClC,KAAK,mBAAmB;gBACtB,OAAO,IAAI,CAAC,gBAAgB,CAAC;YAC/B,OAAO,CAAC,CAAC,CAAC;gBACR,MAAM,WAAW,GAAU,IAAI,CAAC,CAAC,oDAAoD;gBACrF,OAAO,WAAW,CAAC;YACrB,CAAC;QACH,CAAC;IACH,CAAC;IAED;;;OAGG;IACH,aAAa,CAAC,IAAgB;QAC5B,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;YAC1B,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,eAAe,CAAC,CAAC;QACvD,CAAC;IACH,CAAC;CACF"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* MedicalSopEngine — Full SOP wired (Phase 6, Sprint 6).
|
|
3
|
+
*
|
|
4
|
+
* Implements PipelineEngine with pipelineShape 'medical-sop'.
|
|
5
|
+
* Full ordered SOP:
|
|
6
|
+
* Gate 1 (consent) → fail-closed, ZERO downstream on refuse
|
|
7
|
+
* Gate 2 (red-flag) → 0-LLM short-circuit on emergency match
|
|
8
|
+
* (3) NumericsQueryLayer — deterministic compute, NO LLM
|
|
9
|
+
* (4) FactStore.getActiveFacts — active medications (ADR-7)
|
|
10
|
+
* Gate 3 (EgressGuard) → literature-retrieval axis, default false
|
|
11
|
+
* (5) LiteratureRetriever.retrieve — {disabled} sync when axis off → ABSTAIN
|
|
12
|
+
* (6) DisclaimerComposer.footer
|
|
13
|
+
* (7) AuditLog.append("answer" | "abstain")
|
|
14
|
+
* (8) return PipelineResult & { medicalAnswer }
|
|
15
|
+
*
|
|
16
|
+
* Zero-arg constructor preserved for selector.ts:126.
|
|
17
|
+
* All timestamps injected via opts.now — never the wall clock.
|
|
18
|
+
* No LLM calls, no SDK imports. No network import.
|
|
19
|
+
*/
|
|
20
|
+
import type { BoberConfig } from "../config/schema.js";
|
|
21
|
+
import type { PipelineResult } from "../orchestrator/pipeline.js";
|
|
22
|
+
import type { PipelineEngine, PipelineEngineName } from "../orchestrator/workflow/engine.js";
|
|
23
|
+
import { AuditLog } from "./audit.js";
|
|
24
|
+
import { ConsentGate } from "./consent.js";
|
|
25
|
+
import { DisclaimerComposer } from "./disclaimer.js";
|
|
26
|
+
import { EgressGuard } from "./egress.js";
|
|
27
|
+
import { LiteratureRetriever } from "./retrieval/literature.js";
|
|
28
|
+
import { HealthDataStore } from "./health-store.js";
|
|
29
|
+
import { FactStore } from "../state/facts.js";
|
|
30
|
+
import type { GuardrailSet } from "./types.js";
|
|
31
|
+
import type { LLMClient } from "../providers/types.js";
|
|
32
|
+
/**
|
|
33
|
+
* Optional deps injected by tests.
|
|
34
|
+
* Production code leaves this undefined; run() constructs real instances.
|
|
35
|
+
* bober: simple optional-deps seam; swap for a DI container if the dep graph grows.
|
|
36
|
+
*/
|
|
37
|
+
export interface MedicalSopDeps {
|
|
38
|
+
auditLog?: AuditLog;
|
|
39
|
+
consentGate?: ConsentGate;
|
|
40
|
+
disclaimer?: DisclaimerComposer;
|
|
41
|
+
/** Inject the real MedicalGuardrails (or a test fake). Sprint 3. */
|
|
42
|
+
guardrails?: GuardrailSet;
|
|
43
|
+
/** Spy/fake LLMClient — asserted NEVER called on red-flag short-circuit (carry-forward S2 fix). */
|
|
44
|
+
llmClient?: LLMClient;
|
|
45
|
+
/** Spy/fake numerics layer — asserted NEVER called on red-flag short-circuit. */
|
|
46
|
+
numerics?: () => unknown;
|
|
47
|
+
/** Injected EgressGuard for tests. Production builds from config. */
|
|
48
|
+
egress?: EgressGuard;
|
|
49
|
+
/** Injected LiteratureRetriever for tests. Production uses EgressGuard from config. */
|
|
50
|
+
literature?: LiteratureRetriever;
|
|
51
|
+
/** Injected FactStore for medications (tests pass :memory:). */
|
|
52
|
+
facts?: FactStore;
|
|
53
|
+
/** Injected HealthDataStore for numerics (tests pass :memory:). */
|
|
54
|
+
healthStore?: HealthDataStore;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Medical-SOP pipeline engine.
|
|
58
|
+
*
|
|
59
|
+
* Zero-arg constructor is preserved so that src/orchestrator/workflow/selector.ts
|
|
60
|
+
* can call `new MedicalSopEngine()` without changes (Sprint 1 contract).
|
|
61
|
+
* Fakes are injected via the optional `deps` constructor argument.
|
|
62
|
+
*/
|
|
63
|
+
export declare class MedicalSopEngine implements PipelineEngine {
|
|
64
|
+
private readonly deps?;
|
|
65
|
+
readonly name: PipelineEngineName;
|
|
66
|
+
constructor(deps?: MedicalSopDeps | undefined);
|
|
67
|
+
run(userPrompt: string, projectRoot: string, config: BoberConfig, // was _config — now consumed (EgressGuard.fromConfig)
|
|
68
|
+
opts?: {
|
|
69
|
+
runId?: string;
|
|
70
|
+
now?: string;
|
|
71
|
+
}): Promise<PipelineResult>;
|
|
72
|
+
}
|
|
73
|
+
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/medical/engine.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AACH,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,qBAAqB,CAAC;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,KAAK,EAAE,cAAc,EAAE,kBAAkB,EAAE,MAAM,oCAAoC,CAAC;AAE7F,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,EAAE,kBAAkB,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAsB,MAAM,2BAA2B,CAAC;AAEpF,OAAO,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAE,SAAS,EAAe,MAAM,mBAAmB,CAAC;AAE3D,OAAO,KAAK,EAAE,YAAY,EAA+E,MAAM,YAAY,CAAC;AAC5H,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,uBAAuB,CAAC;AAOvD;;;;GAIG;AACH,MAAM,WAAW,cAAc;IAC7B,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,WAAW,CAAC,EAAE,WAAW,CAAC;IAC1B,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,oEAAoE;IACpE,UAAU,CAAC,EAAE,YAAY,CAAC;IAC1B,mGAAmG;IACnG,SAAS,CAAC,EAAE,SAAS,CAAC;IACtB,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,OAAO,CAAC;IAEzB,qEAAqE;IACrE,MAAM,CAAC,EAAE,WAAW,CAAC;IACrB,uFAAuF;IACvF,UAAU,CAAC,EAAE,mBAAmB,CAAC;IACjC,gEAAgE;IAChE,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,mEAAmE;IACnE,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAsHD;;;;;;GAMG;AACH,qBAAa,gBAAiB,YAAW,cAAc;IAGzC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC;IAFlC,QAAQ,CAAC,IAAI,EAAE,kBAAkB,CAAiB;gBAErB,IAAI,CAAC,EAAE,cAAc,YAAA;IAE5C,GAAG,CACP,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,MAAM,EACnB,MAAM,EAAE,WAAW,EAAW,sDAAsD;IACpF,IAAI,CAAC,EAAE;QAAE,KAAK,CAAC,EAAE,MAAM,CAAC;QAAC,GAAG,CAAC,EAAE,MAAM,CAAA;KAAE,GACtC,OAAO,CAAC,cAAc,CAAC;CA6O3B"}
|
|
@@ -0,0 +1,333 @@
|
|
|
1
|
+
import { createSpec } from "../contracts/spec.js";
|
|
2
|
+
import { AuditLog } from "./audit.js";
|
|
3
|
+
import { ConsentGate } from "./consent.js";
|
|
4
|
+
import { DisclaimerComposer } from "./disclaimer.js";
|
|
5
|
+
import { MedicalGuardrails } from "./guardrails.js";
|
|
6
|
+
import { EgressGuard } from "./egress.js";
|
|
7
|
+
import { LiteratureRetriever, synthesizeGrounded } from "./retrieval/literature.js";
|
|
8
|
+
import { NumericsQueryLayer } from "./numerics.js";
|
|
9
|
+
import { HealthDataStore } from "./health-store.js";
|
|
10
|
+
import { FactStore, factsDbPath } from "../state/facts.js";
|
|
11
|
+
import { buildMedicalInferenceClient } from "./inference.js";
|
|
12
|
+
import { join } from "node:path";
|
|
13
|
+
// ── Canned messages ─────────────────────────────────────────────────
|
|
14
|
+
const CONSENT_REQUIRED_MSG = "Consent is required before this assistant can respond to health-related questions. " +
|
|
15
|
+
"Please provide consent to continue.";
|
|
16
|
+
// ── Small deterministic helpers (no LLM, no network, no async) ──────
|
|
17
|
+
/**
|
|
18
|
+
* Returns true when the prompt appears to be a numeric health query.
|
|
19
|
+
* Detects common aggregation keywords. Purely local string match — no LLM.
|
|
20
|
+
*
|
|
21
|
+
* bober: minimal NL detector; extend regex for broader coverage when needed.
|
|
22
|
+
*/
|
|
23
|
+
function isNumericQuestion(prompt) {
|
|
24
|
+
return /\b(average|mean|min|max|minimum|maximum|latest|last|delta|slope|trend|percentile|zscore|z-score)\b/i.test(prompt);
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Derive a MetricWindow from the prompt and the current ISO timestamp.
|
|
28
|
+
* Maps common health metric keywords to metric names; defaults to a 7-day window.
|
|
29
|
+
*
|
|
30
|
+
* bober: minimal NL→window mapping; full parse is out of scope (S4 proved correctness).
|
|
31
|
+
*/
|
|
32
|
+
function deriveWindow(prompt, nowIso) {
|
|
33
|
+
const p = prompt.toLowerCase();
|
|
34
|
+
let metric = "heart_rate"; // default
|
|
35
|
+
if (p.includes("blood pressure") || p.includes("bp"))
|
|
36
|
+
metric = "blood_pressure";
|
|
37
|
+
else if (p.includes("glucose") || p.includes("sugar"))
|
|
38
|
+
metric = "glucose";
|
|
39
|
+
else if (p.includes("weight"))
|
|
40
|
+
metric = "weight";
|
|
41
|
+
else if (p.includes("steps"))
|
|
42
|
+
metric = "steps";
|
|
43
|
+
else if (p.includes("sleep"))
|
|
44
|
+
metric = "sleep_hours";
|
|
45
|
+
else if (p.includes("heart rate") || p.includes("resting heart"))
|
|
46
|
+
metric = "heart_rate";
|
|
47
|
+
// Default to 7-day look-back from now
|
|
48
|
+
const toDate = new Date(nowIso);
|
|
49
|
+
const fromDate = new Date(toDate);
|
|
50
|
+
fromDate.setDate(fromDate.getDate() - 7);
|
|
51
|
+
return {
|
|
52
|
+
metric,
|
|
53
|
+
fromIso: fromDate.toISOString(),
|
|
54
|
+
toIso: nowIso,
|
|
55
|
+
unit: metric === "heart_rate" ? "bpm" : "",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Derive the NumericPrimitive from the prompt.
|
|
60
|
+
* Defaults to "mean" when no specific primitive is detected.
|
|
61
|
+
*/
|
|
62
|
+
function derivePrimitive(prompt) {
|
|
63
|
+
const p = prompt.toLowerCase();
|
|
64
|
+
if (p.includes("average") || p.includes("mean"))
|
|
65
|
+
return "mean";
|
|
66
|
+
if (p.includes("minimum") || p.includes("min"))
|
|
67
|
+
return "min";
|
|
68
|
+
if (p.includes("maximum") || p.includes("max"))
|
|
69
|
+
return "max";
|
|
70
|
+
if (p.includes("latest") || p.includes("last"))
|
|
71
|
+
return "latest";
|
|
72
|
+
if (p.includes("delta"))
|
|
73
|
+
return "delta";
|
|
74
|
+
if (p.includes("slope") || p.includes("trend"))
|
|
75
|
+
return "slope";
|
|
76
|
+
if (p.includes("percentile"))
|
|
77
|
+
return "percentile";
|
|
78
|
+
if (p.includes("zscore") || p.includes("z-score"))
|
|
79
|
+
return "zscore";
|
|
80
|
+
return "mean";
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Compose the answer body from numeric result, active medications, and retrieval outcome.
|
|
84
|
+
* Pure text composition — no LLM, no network.
|
|
85
|
+
*/
|
|
86
|
+
function composeBody(numericResult, activeMeds, outcome) {
|
|
87
|
+
const parts = [];
|
|
88
|
+
// Numeric result
|
|
89
|
+
if (numericResult !== null) {
|
|
90
|
+
if (numericResult.sampleCount === 0 || numericResult.value === null) {
|
|
91
|
+
parts.push(`No ${numericResult.primitive} data found for ${numericResult.unit || "the requested metric"} in the given window.`);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
parts.push(`${numericResult.primitive.charAt(0).toUpperCase() + numericResult.primitive.slice(1)}: ${numericResult.value} ${numericResult.unit} (${numericResult.sampleCount} samples).`);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// Active medications from FactStore
|
|
98
|
+
if (activeMeds.length > 0) {
|
|
99
|
+
const medList = activeMeds.map((f) => f.value).join(", ");
|
|
100
|
+
parts.push(`Current medications (from records): ${medList}.`);
|
|
101
|
+
}
|
|
102
|
+
// Retrieval outcome
|
|
103
|
+
if (outcome.kind === "disabled" || outcome.kind === "abstain") {
|
|
104
|
+
parts.push("Literature retrieval is not enabled. " +
|
|
105
|
+
"For evidence-based guidance, please consult a licensed healthcare professional.");
|
|
106
|
+
}
|
|
107
|
+
if (parts.length === 0) {
|
|
108
|
+
parts.push("I can provide general wellness information. " +
|
|
109
|
+
"For specific medical advice, please consult a licensed healthcare professional.");
|
|
110
|
+
}
|
|
111
|
+
return parts.join(" ");
|
|
112
|
+
}
|
|
113
|
+
// ── MedicalSopEngine ────────────────────────────────────────────────
|
|
114
|
+
/**
|
|
115
|
+
* Medical-SOP pipeline engine.
|
|
116
|
+
*
|
|
117
|
+
* Zero-arg constructor is preserved so that src/orchestrator/workflow/selector.ts
|
|
118
|
+
* can call `new MedicalSopEngine()` without changes (Sprint 1 contract).
|
|
119
|
+
* Fakes are injected via the optional `deps` constructor argument.
|
|
120
|
+
*/
|
|
121
|
+
export class MedicalSopEngine {
|
|
122
|
+
deps;
|
|
123
|
+
name = "medical-sop";
|
|
124
|
+
constructor(deps) {
|
|
125
|
+
this.deps = deps;
|
|
126
|
+
}
|
|
127
|
+
async run(userPrompt, projectRoot, config, // was _config — now consumed (EgressGuard.fromConfig)
|
|
128
|
+
opts) {
|
|
129
|
+
// All timestamps come from the injected `now` — never the wall clock.
|
|
130
|
+
// bober: fallback ISO for ad-hoc manual runs only; tests must always inject `now`.
|
|
131
|
+
const now = opts?.now ?? new Date().toISOString();
|
|
132
|
+
// ── Construct real instances when not injected ────────────────────
|
|
133
|
+
const auditLog = this.deps?.auditLog ?? new AuditLog(projectRoot);
|
|
134
|
+
const consentGate = this.deps?.consentGate ?? new ConsentGate(projectRoot, auditLog);
|
|
135
|
+
const disclaimer = this.deps?.disclaimer ?? new DisclaimerComposer();
|
|
136
|
+
const guardrails = this.deps?.guardrails ?? new MedicalGuardrails();
|
|
137
|
+
const footer = disclaimer.footer();
|
|
138
|
+
// ── Gate 1: Consent (fail-closed) ────────────────────────────────
|
|
139
|
+
const hasConsent = await consentGate.hasConsent();
|
|
140
|
+
if (!hasConsent) {
|
|
141
|
+
// Refuse — ZERO downstream calls (no numerics, no LLM, no retrieval).
|
|
142
|
+
await auditLog.append({
|
|
143
|
+
tIso: now,
|
|
144
|
+
event: "refuse",
|
|
145
|
+
ruleId: "consent-required",
|
|
146
|
+
});
|
|
147
|
+
const refuseAnswer = {
|
|
148
|
+
body: CONSENT_REQUIRED_MSG,
|
|
149
|
+
abstained: false,
|
|
150
|
+
citations: [],
|
|
151
|
+
disclaimerFooter: footer,
|
|
152
|
+
shortCircuit: true,
|
|
153
|
+
};
|
|
154
|
+
const spec = createSpec("Medical SOP — consent refused", "Consent gate refused the request before any downstream processing.", []);
|
|
155
|
+
return {
|
|
156
|
+
success: false,
|
|
157
|
+
spec,
|
|
158
|
+
completedSprints: [],
|
|
159
|
+
failedSprints: [],
|
|
160
|
+
duration: 0,
|
|
161
|
+
// Surface the MedicalAnswer in the spec description for downstream callers.
|
|
162
|
+
medicalAnswer: refuseAnswer,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
// ── Gate 2: Red-flag short-circuit (0 LLM, 0 numerics) ──────────
|
|
166
|
+
// Runs immediately after consent. A red-flag match returns a canned escalation
|
|
167
|
+
// and reaches NO downstream work (no numerics, no retrieval, no LLM).
|
|
168
|
+
const verdict = guardrails.evaluate(userPrompt, {});
|
|
169
|
+
if (verdict.kind === "short-circuit") {
|
|
170
|
+
await auditLog.append({
|
|
171
|
+
tIso: now,
|
|
172
|
+
event: "short-circuit",
|
|
173
|
+
ruleId: verdict.rule,
|
|
174
|
+
rulesetVersion: guardrails.rulesetVersion,
|
|
175
|
+
patternsetVersion: "patternsetVersion" in guardrails
|
|
176
|
+
? guardrails.patternsetVersion
|
|
177
|
+
: undefined,
|
|
178
|
+
});
|
|
179
|
+
const scAnswer = {
|
|
180
|
+
body: verdict.cannedResponse,
|
|
181
|
+
abstained: false,
|
|
182
|
+
citations: [],
|
|
183
|
+
disclaimerFooter: footer,
|
|
184
|
+
shortCircuit: true,
|
|
185
|
+
};
|
|
186
|
+
const scSpec = createSpec("Medical SOP — red-flag short-circuit", "Red-flag gate escalated; no numerics/LLM reached.", []);
|
|
187
|
+
return {
|
|
188
|
+
success: true,
|
|
189
|
+
spec: scSpec,
|
|
190
|
+
completedSprints: [],
|
|
191
|
+
failedSprints: [],
|
|
192
|
+
duration: 0,
|
|
193
|
+
medicalAnswer: scAnswer,
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
// Gate 2b: Content-policy refusal (prescription / dosing / treatment-plan).
|
|
197
|
+
// Only reached when verdict.kind !== "short-circuit" (red-flag wins by early return above).
|
|
198
|
+
if (verdict.kind === "refuse") {
|
|
199
|
+
await auditLog.append({
|
|
200
|
+
tIso: now,
|
|
201
|
+
event: "refuse",
|
|
202
|
+
ruleId: verdict.rule,
|
|
203
|
+
rulesetVersion: guardrails.rulesetVersion,
|
|
204
|
+
patternsetVersion: "refusalPatternsetVersion" in guardrails
|
|
205
|
+
? guardrails.refusalPatternsetVersion
|
|
206
|
+
: undefined,
|
|
207
|
+
});
|
|
208
|
+
const refuseAnswer = {
|
|
209
|
+
body: verdict.reason,
|
|
210
|
+
abstained: false,
|
|
211
|
+
citations: [],
|
|
212
|
+
disclaimerFooter: footer,
|
|
213
|
+
shortCircuit: true,
|
|
214
|
+
};
|
|
215
|
+
const refuseSpec = createSpec("Medical SOP — content-policy refusal", "Refusal gate declined a prescription/dosing/treatment-plan request; no numerics/LLM reached.", []);
|
|
216
|
+
return {
|
|
217
|
+
success: true,
|
|
218
|
+
spec: refuseSpec,
|
|
219
|
+
completedSprints: [],
|
|
220
|
+
failedSprints: [],
|
|
221
|
+
duration: 0,
|
|
222
|
+
medicalAnswer: refuseAnswer,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
// verdict.kind === "allow" → proceed to the full SOP (S6).
|
|
226
|
+
const consentRecord = await consentGate.current();
|
|
227
|
+
const rulesetVersion = consentRecord?.rulesetVersion;
|
|
228
|
+
// ── (3) Numerics (deterministic compute, NO LLM) ──────────────────
|
|
229
|
+
// Derive a minimal MetricWindow from the prompt (full NL parse is out of scope; S4 proved correctness).
|
|
230
|
+
// numericsSpy injection: when deps.numerics is provided (tests), the engine MUST call it so the
|
|
231
|
+
// spy assertions are real (carry-forward A).
|
|
232
|
+
let numericResult = null;
|
|
233
|
+
if (isNumericQuestion(userPrompt)) {
|
|
234
|
+
if (this.deps?.numerics) {
|
|
235
|
+
this.deps.numerics(); // exercise the injected spy (carry-forward A)
|
|
236
|
+
}
|
|
237
|
+
if (this.deps?.healthStore) {
|
|
238
|
+
// Use injected store (tests).
|
|
239
|
+
const numerics = new NumericsQueryLayer(this.deps.healthStore);
|
|
240
|
+
const window = deriveWindow(userPrompt, now);
|
|
241
|
+
const primitive = derivePrimitive(userPrompt);
|
|
242
|
+
numericResult = numerics.getMetric(window, primitive); // sampleCount 0 ⇒ abstain
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
// Production: try to open the health DB; gracefully abstain if directory missing.
|
|
246
|
+
const healthDbPath = join(projectRoot, ".bober", "medical", "health.db");
|
|
247
|
+
try {
|
|
248
|
+
const healthStore = new HealthDataStore(healthDbPath);
|
|
249
|
+
const numerics = new NumericsQueryLayer(healthStore);
|
|
250
|
+
const window = deriveWindow(userPrompt, now);
|
|
251
|
+
const primitive = derivePrimitive(userPrompt);
|
|
252
|
+
numericResult = numerics.getMetric(window, primitive);
|
|
253
|
+
healthStore.close();
|
|
254
|
+
}
|
|
255
|
+
catch {
|
|
256
|
+
// Directory not yet initialized — no observations; abstain.
|
|
257
|
+
numericResult = null;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
// ── (4) Medications via FactStore.getActiveFacts (ADR-7) ──────────
|
|
262
|
+
// NEVER HealthDataStore. Medication-list state lives only in FactStore (ADR-7).
|
|
263
|
+
// In production the directory may not yet exist (first run) — open gracefully.
|
|
264
|
+
let activeMeds;
|
|
265
|
+
if (this.deps?.facts) {
|
|
266
|
+
activeMeds = this.deps.facts.getActiveFacts("medical", "patient", "takes-medication");
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
const dbPath = factsDbPath(projectRoot, "medical");
|
|
270
|
+
try {
|
|
271
|
+
const facts = new FactStore(dbPath);
|
|
272
|
+
activeMeds = facts.getActiveFacts("medical", "patient", "takes-medication");
|
|
273
|
+
facts.close();
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
// Directory not yet created (first run without CLI init) — no medications on file.
|
|
277
|
+
activeMeds = [];
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
// ── GATE 3 + (5) Literature egress gate ───────────────────────────
|
|
281
|
+
const egress = this.deps?.egress ?? EgressGuard.fromConfig(config);
|
|
282
|
+
const literature = this.deps?.literature ?? new LiteratureRetriever(egress);
|
|
283
|
+
const outcome = await literature.retrieve(userPrompt); // {disabled} sync when axis off → NO network
|
|
284
|
+
// ── (6)+(7)+(8) Compose answer + audit + return ───────────────────
|
|
285
|
+
// Answered from compute when: numeric result with data (sampleCount > 0).
|
|
286
|
+
// Grounded synthesis when: literature axis ON + grounded passages (LLM call happens here only).
|
|
287
|
+
// Abstained when: no grounded literature AND no numeric data.
|
|
288
|
+
// A numeric question answered purely from local compute is an "answer" event (sc-6-8).
|
|
289
|
+
const hasNumericAnswer = numericResult !== null && numericResult.sampleCount > 0;
|
|
290
|
+
let answer;
|
|
291
|
+
let criticVerdict;
|
|
292
|
+
if (outcome.kind === "grounded" && !hasNumericAnswer) {
|
|
293
|
+
// ── Grounded synthesis path ────────────────────────────────────
|
|
294
|
+
// Obtain the LLMClient lazily — only on this path (numeric/disabled/red-flag paths
|
|
295
|
+
// get zero LLM construction, preserving sc-7-8 and existing engine.test.ts assertions).
|
|
296
|
+
// deps.llmClient wins for tests; production uses buildMedicalInferenceClient (fail-closed to local).
|
|
297
|
+
const { client: llmClient, model: synthModel } = this.deps?.llmClient
|
|
298
|
+
? { client: this.deps.llmClient, model: "llama3" }
|
|
299
|
+
: buildMedicalInferenceClient(config, egress);
|
|
300
|
+
const groundedResult = await synthesizeGrounded(userPrompt, outcome, llmClient, footer, synthModel);
|
|
301
|
+
answer = groundedResult.answer;
|
|
302
|
+
criticVerdict = groundedResult.verdict;
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
// ── Numeric / disabled / abstain path ─────────────────────────
|
|
306
|
+
// composeBody is pure text composition — no LLM, no network.
|
|
307
|
+
const abstained = !hasNumericAnswer;
|
|
308
|
+
answer = {
|
|
309
|
+
body: composeBody(numericResult, activeMeds, outcome),
|
|
310
|
+
abstained,
|
|
311
|
+
citations: [],
|
|
312
|
+
disclaimerFooter: footer,
|
|
313
|
+
shortCircuit: false,
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
await auditLog.append({
|
|
317
|
+
tIso: now,
|
|
318
|
+
event: answer.abstained ? "abstain" : "answer",
|
|
319
|
+
rulesetVersion,
|
|
320
|
+
...(criticVerdict ? { criticVerdict } : {}),
|
|
321
|
+
});
|
|
322
|
+
const spec = createSpec("Medical SOP", "Full local SOP turn.", []);
|
|
323
|
+
return {
|
|
324
|
+
success: true,
|
|
325
|
+
spec,
|
|
326
|
+
completedSprints: [],
|
|
327
|
+
failedSprints: [],
|
|
328
|
+
duration: 0,
|
|
329
|
+
medicalAnswer: answer,
|
|
330
|
+
};
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
//# sourceMappingURL=engine.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"engine.js","sourceRoot":"","sources":["../../src/medical/engine.ts"],"names":[],"mappings":"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"}
|