agent-bober 0.17.1 → 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/plugin.json +1 -1
- package/CHANGELOG.md +15 -0
- package/README.md +158 -5
- 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/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/index.js +12 -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,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RefusalDetector — pure, synchronous, 0-LLM non-emergency content-policy refusal.
|
|
3
|
+
*
|
|
4
|
+
* Classifies prescription / specific-dosing / individualized-treatment-plan requests.
|
|
5
|
+
* Conservative phrase matching: accept FALSE-NEGATIVES (fall through to 'none'),
|
|
6
|
+
* NEVER false-positive into giving medical advice. Pattern set is versioned for audit.
|
|
7
|
+
*
|
|
8
|
+
* NO async. NO fs. NO network. NO LLM import. Identical input => identical output.
|
|
9
|
+
*/
|
|
10
|
+
// ── Version constant ─────────────────────────────────────────────────
|
|
11
|
+
/** Versioned pattern-set identifier recorded in the audit log. */
|
|
12
|
+
export const REFUSAL_PATTERNSET_VERSION = "refusal-2026.06.17";
|
|
13
|
+
// ── Canned refuse reason strings (NEVER model-generated) ─────────────
|
|
14
|
+
// Decline + see-a-licensed-clinician. DISTINCT from 911/988 escalations —
|
|
15
|
+
// no "911", "988", or "emergency" so refuse text is byte-distinct from escalation text.
|
|
16
|
+
const REFUSE_PRESCRIPTION = "I can't provide a prescription or recommend a specific prescription medication. " +
|
|
17
|
+
"Please consult a licensed clinician who can evaluate you and prescribe appropriately.";
|
|
18
|
+
const REFUSE_DOSING = "I can't provide a specific medication dose or dosing schedule for you. " +
|
|
19
|
+
"Please consult a licensed clinician or pharmacist for personalised dosing guidance.";
|
|
20
|
+
const REFUSE_TREATMENT_PLAN = "I can't create an individualised treatment or care plan. " +
|
|
21
|
+
"Please consult a licensed clinician who can evaluate your situation and develop a plan for you.";
|
|
22
|
+
/**
|
|
23
|
+
* Fixed canned reason strings per refusal category.
|
|
24
|
+
* Exported so callers can assert byte-equality (proving non-model-generation).
|
|
25
|
+
*/
|
|
26
|
+
export const REFUSAL_REASONS = {
|
|
27
|
+
prescription: REFUSE_PRESCRIPTION,
|
|
28
|
+
"specific-dosing": REFUSE_DOSING,
|
|
29
|
+
"individualized-treatment-plan": REFUSE_TREATMENT_PLAN,
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Conservative rule list. Order: prescription → specific-dosing → treatment-plan.
|
|
33
|
+
* Accept false-negatives freely; NEVER fire on benign informational prompts.
|
|
34
|
+
*
|
|
35
|
+
* bober: phrase-include matching for now; upgrade to word-boundary regex or
|
|
36
|
+
* a trie if the pattern count grows to > 50 rules.
|
|
37
|
+
*/
|
|
38
|
+
const RULES = [
|
|
39
|
+
// ── prescription ────────────────────────────────────────────────────
|
|
40
|
+
{
|
|
41
|
+
ruleId: "rx-request-modal",
|
|
42
|
+
category: "prescription",
|
|
43
|
+
test: (n) => n.includes("can you prescribe") || n.includes("could you prescribe"),
|
|
44
|
+
},
|
|
45
|
+
{
|
|
46
|
+
ruleId: "rx-request-write",
|
|
47
|
+
category: "prescription",
|
|
48
|
+
test: (n) => n.includes("write me a prescription") ||
|
|
49
|
+
n.includes("get me a prescription") ||
|
|
50
|
+
n.includes("prescription for me"),
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
ruleId: "rx-request-direct",
|
|
54
|
+
category: "prescription",
|
|
55
|
+
test: (n) => n.includes("prescribe me") ||
|
|
56
|
+
(n.includes("prescribe") && (n.includes(" me ") || n.includes(" me?") || n.includes(" i "))),
|
|
57
|
+
},
|
|
58
|
+
// ── specific-dosing ──────────────────────────────────────────────────
|
|
59
|
+
{
|
|
60
|
+
ruleId: "dose-quantity-mg",
|
|
61
|
+
category: "specific-dosing",
|
|
62
|
+
test: (n) => n.includes("how many mg") || n.includes("how many milligrams"),
|
|
63
|
+
},
|
|
64
|
+
{
|
|
65
|
+
ruleId: "dose-query-what",
|
|
66
|
+
category: "specific-dosing",
|
|
67
|
+
test: (n) => n.includes("what dose") || n.includes("what dosage"),
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
ruleId: "dose-query-take-mg",
|
|
71
|
+
category: "specific-dosing",
|
|
72
|
+
test: (n) => /should i take\s+\d/.test(n) ||
|
|
73
|
+
(n.includes("should i take") && (n.includes("mg") || n.includes("milligram"))),
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
ruleId: "dose-query-how-much-take",
|
|
77
|
+
category: "specific-dosing",
|
|
78
|
+
test: (n) => n.includes("how much") && n.includes("should i take"),
|
|
79
|
+
},
|
|
80
|
+
// ── individualized-treatment-plan ────────────────────────────────────
|
|
81
|
+
{
|
|
82
|
+
ruleId: "txplan-for-me",
|
|
83
|
+
category: "individualized-treatment-plan",
|
|
84
|
+
test: (n) => n.includes("treatment plan for me") || n.includes("my treatment plan"),
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
ruleId: "txplan-what-to-do",
|
|
88
|
+
category: "individualized-treatment-plan",
|
|
89
|
+
test: (n) => n.includes("what should i do to treat my") || n.includes("how do i treat my"),
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
ruleId: "txplan-personalized",
|
|
93
|
+
category: "individualized-treatment-plan",
|
|
94
|
+
test: (n) => n.includes("personalized treatment") ||
|
|
95
|
+
n.includes("personalised treatment") ||
|
|
96
|
+
n.includes("personal treatment plan"),
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
ruleId: "txplan-care-plan",
|
|
100
|
+
category: "individualized-treatment-plan",
|
|
101
|
+
test: (n) => n.includes("care plan for my") || n.includes("care plan for me"),
|
|
102
|
+
},
|
|
103
|
+
];
|
|
104
|
+
// ── RefusalDetector ──────────────────────────────────────────────────
|
|
105
|
+
/**
|
|
106
|
+
* Detects non-emergency content-policy refusal phrases in a medical prompt.
|
|
107
|
+
*
|
|
108
|
+
* PURE + SYNCHRONOUS. No async, no fs, no network, no LLM import.
|
|
109
|
+
* Identical input => identical RefusalMatch output.
|
|
110
|
+
*
|
|
111
|
+
* bober: conservative phrase matching; novel or indirect phrasing may return
|
|
112
|
+
* 'none' and fall through to normal SOP handling (accepted risk; false-negative
|
|
113
|
+
* is safer than false-positive into refused advice).
|
|
114
|
+
*/
|
|
115
|
+
export class RefusalDetector {
|
|
116
|
+
patternsetVersion = REFUSAL_PATTERNSET_VERSION;
|
|
117
|
+
/**
|
|
118
|
+
* Returns the first matching RefusalMatch, or { category: 'none' } if no
|
|
119
|
+
* rule fires. Evaluation order is prescription → specific-dosing → treatment-plan.
|
|
120
|
+
*/
|
|
121
|
+
detect(prompt) {
|
|
122
|
+
const norm = prompt.toLowerCase().trim();
|
|
123
|
+
for (const rule of RULES) {
|
|
124
|
+
if (rule.test(norm)) {
|
|
125
|
+
return { category: rule.category, ruleId: rule.ruleId };
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return { category: "none" };
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=refusal.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"refusal.js","sourceRoot":"","sources":["../../src/medical/refusal.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/** GroundingCritic — fail-closed faithfulness+completeness judge for medical synthesis output. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import type { LLMClient } from "../../providers/types.js";
|
|
4
|
+
import type { Passage } from "./medline-source.js";
|
|
5
|
+
export declare const GROUNDING_PARSE_MAX_RETRIES = 1;
|
|
6
|
+
export declare const GROUNDING_MAX_LLM_CALLS: number;
|
|
7
|
+
export declare const GroundingVerdictSchema: z.ZodObject<{
|
|
8
|
+
verdict: z.ZodEnum<["approve", "reject"]>;
|
|
9
|
+
feedback: z.ZodString;
|
|
10
|
+
}, "strip", z.ZodTypeAny, {
|
|
11
|
+
feedback: string;
|
|
12
|
+
verdict: "approve" | "reject";
|
|
13
|
+
}, {
|
|
14
|
+
feedback: string;
|
|
15
|
+
verdict: "approve" | "reject";
|
|
16
|
+
}>;
|
|
17
|
+
export type GroundingVerdict = z.infer<typeof GroundingVerdictSchema>;
|
|
18
|
+
export type ValidateGroundingResult = {
|
|
19
|
+
ok: true;
|
|
20
|
+
verdict: GroundingVerdict;
|
|
21
|
+
} | {
|
|
22
|
+
ok: false;
|
|
23
|
+
error: string;
|
|
24
|
+
};
|
|
25
|
+
export declare function validateGroundingVerdict(rawText: string): ValidateGroundingResult;
|
|
26
|
+
export declare function buildGroundingSystemPrompt(question: string, answerBody: string, passages: Passage[]): string;
|
|
27
|
+
export declare function getGroundingVerdict(input: {
|
|
28
|
+
llm: LLMClient;
|
|
29
|
+
model: string;
|
|
30
|
+
question: string;
|
|
31
|
+
answerBody: string;
|
|
32
|
+
passages: Passage[];
|
|
33
|
+
}): Promise<GroundingVerdict>;
|
|
34
|
+
//# sourceMappingURL=grounding-critic.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grounding-critic.d.ts","sourceRoot":"","sources":["../../../src/medical/retrieval/grounding-critic.ts"],"names":[],"mappings":"AAAA,kGAAkG;AAClG,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,SAAS,EAAW,MAAM,0BAA0B,CAAC;AACnE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAC;AAInD,eAAO,MAAM,2BAA2B,IAAI,CAAC;AAC7C,eAAO,MAAM,uBAAuB,QAAkC,CAAC;AAkBvE,eAAO,MAAM,sBAAsB;;;;;;;;;EAGjC,CAAC;AAEH,MAAM,MAAM,gBAAgB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,sBAAsB,CAAC,CAAC;AAEtE,MAAM,MAAM,uBAAuB,GAC/B;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,OAAO,EAAE,gBAAgB,CAAA;CAAE,GACvC;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAIjC,wBAAgB,wBAAwB,CAAC,OAAO,EAAE,MAAM,GAAG,uBAAuB,CAgDjF;AAYD,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,MAAM,EAChB,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,OAAO,EAAE,GAClB,MAAM,CAeR;AAmDD,wBAAsB,mBAAmB,CAAC,KAAK,EAAE;IAC/C,GAAG,EAAE,SAAS,CAAC;IACf,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,OAAO,EAAE,CAAC;CACrB,GAAG,OAAO,CAAC,gBAAgB,CAAC,CA+B5B"}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/** GroundingCritic — fail-closed faithfulness+completeness judge for medical synthesis output. */
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
// ── Constants ────────────────────────────────────────────────────────
|
|
4
|
+
export const GROUNDING_PARSE_MAX_RETRIES = 1;
|
|
5
|
+
export const GROUNDING_MAX_LLM_CALLS = 1 + GROUNDING_PARSE_MAX_RETRIES;
|
|
6
|
+
// ── Prompts ──────────────────────────────────────────────────────────
|
|
7
|
+
const GROUNDING_COERCION_INSTRUCTION = `Your previous response was not a valid grounding verdict.
|
|
8
|
+
Output ONLY a single JSON object (no prose, no markdown fences, no tool calls) with EXACTLY this shape:
|
|
9
|
+
{
|
|
10
|
+
"verdict": "approve" | "reject",
|
|
11
|
+
"feedback": "<concise feedback; empty string if approving>"
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
Rules:
|
|
15
|
+
- "verdict" must be exactly "approve" or "reject".
|
|
16
|
+
- "feedback" must be a string.
|
|
17
|
+
- Output the JSON object and nothing else.`;
|
|
18
|
+
// ── Schema + types ───────────────────────────────────────────────────
|
|
19
|
+
export const GroundingVerdictSchema = z.object({
|
|
20
|
+
verdict: z.enum(["approve", "reject"]),
|
|
21
|
+
feedback: z.string(),
|
|
22
|
+
});
|
|
23
|
+
// ── validateGroundingVerdict (NEVER throws) ──────────────────────────
|
|
24
|
+
export function validateGroundingVerdict(rawText) {
|
|
25
|
+
let parsed;
|
|
26
|
+
// Try direct parse first
|
|
27
|
+
try {
|
|
28
|
+
parsed = JSON.parse(rawText.trim());
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Try extracting JSON from markdown code fences
|
|
32
|
+
const fenceMatch = /```(?:json)?\s*\n?([\s\S]*?)\n?\s*```/.exec(rawText);
|
|
33
|
+
if (fenceMatch) {
|
|
34
|
+
try {
|
|
35
|
+
parsed = JSON.parse(fenceMatch[1].trim());
|
|
36
|
+
}
|
|
37
|
+
catch {
|
|
38
|
+
// Fall through
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Try finding the first { ... } block
|
|
42
|
+
if (!parsed) {
|
|
43
|
+
const braceStart = rawText.indexOf("{");
|
|
44
|
+
const braceEnd = rawText.lastIndexOf("}");
|
|
45
|
+
if (braceStart !== -1 && braceEnd > braceStart) {
|
|
46
|
+
try {
|
|
47
|
+
parsed = JSON.parse(rawText.slice(braceStart, braceEnd + 1));
|
|
48
|
+
}
|
|
49
|
+
catch {
|
|
50
|
+
return {
|
|
51
|
+
ok: false,
|
|
52
|
+
error: `No valid JSON object found in response. Raw: ${rawText.slice(0, 200)}`,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
return {
|
|
58
|
+
ok: false,
|
|
59
|
+
error: `No JSON object found in response. Raw: ${rawText.slice(0, 200)}`,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
const result = GroundingVerdictSchema.safeParse(parsed);
|
|
65
|
+
if (!result.success) {
|
|
66
|
+
const issues = result.error.issues
|
|
67
|
+
.map((i) => ` - ${i.path.join(".")}: ${i.message}`)
|
|
68
|
+
.join("\n");
|
|
69
|
+
return { ok: false, error: issues };
|
|
70
|
+
}
|
|
71
|
+
return { ok: true, verdict: result.data };
|
|
72
|
+
}
|
|
73
|
+
// ── Helpers ──────────────────────────────────────────────────────────
|
|
74
|
+
function formatPassageBlock(passages) {
|
|
75
|
+
return passages
|
|
76
|
+
.map((p, i) => `[${i + 1}] ${p.title}\n${p.text}\nSource: ${p.url}`)
|
|
77
|
+
.join("\n\n");
|
|
78
|
+
}
|
|
79
|
+
// ── buildGroundingSystemPrompt ────────────────────────────────────────
|
|
80
|
+
export function buildGroundingSystemPrompt(question, answerBody, passages) {
|
|
81
|
+
const passageBlock = formatPassageBlock(passages);
|
|
82
|
+
return (`You are an independent reviewer. The answer below was produced by another assistant ` +
|
|
83
|
+
`from ONLY the retrieved passages listed here. ` +
|
|
84
|
+
`Approve ONLY if EVERY claim in the answer is directly supported by a cited passage ` +
|
|
85
|
+
`AND the answer addresses the core of the question. ` +
|
|
86
|
+
`Reject if any claim is unsupported (faithfulness) OR the answer omits the central part ` +
|
|
87
|
+
`of the question (completeness). ` +
|
|
88
|
+
`Output ONLY {"verdict":"approve"|"reject","feedback":"..."}\n\n` +
|
|
89
|
+
`Question: ${question}\n\n` +
|
|
90
|
+
`Answer to review:\n${answerBody}\n\n` +
|
|
91
|
+
`Retrieved passages:\n${passageBlock}`);
|
|
92
|
+
}
|
|
93
|
+
// ── Internal: one grounding-critic call ─────────────────────────────
|
|
94
|
+
async function callGroundingCritic(input) {
|
|
95
|
+
const { llm, model, question, answerBody, passages, priorText, formattedError } = input;
|
|
96
|
+
// Fresh message array — NEVER extends the prior synthesis conversation (LOCK1)
|
|
97
|
+
const firstUserContent = `Review this answer as an independent reviewer.\n\n` +
|
|
98
|
+
`Question: ${question}\n\n` +
|
|
99
|
+
`Answer to review:\n${answerBody}\n\n` +
|
|
100
|
+
`Cited passages:\n${formatPassageBlock(passages)}`;
|
|
101
|
+
let messages;
|
|
102
|
+
if (priorText !== undefined && formattedError !== undefined) {
|
|
103
|
+
// Coercion retry: 3-message shape [user, assistant, user]
|
|
104
|
+
messages = [
|
|
105
|
+
{ role: "user", content: firstUserContent },
|
|
106
|
+
{ role: "assistant", content: priorText },
|
|
107
|
+
{
|
|
108
|
+
role: "user",
|
|
109
|
+
content: `${GROUNDING_COERCION_INSTRUCTION}\n\nPrevious validation error:\n${formattedError}`,
|
|
110
|
+
},
|
|
111
|
+
];
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
// First turn: single user message
|
|
115
|
+
messages = [{ role: "user", content: firstUserContent }];
|
|
116
|
+
}
|
|
117
|
+
const response = await llm.chat({
|
|
118
|
+
model,
|
|
119
|
+
system: buildGroundingSystemPrompt(question, answerBody, passages),
|
|
120
|
+
messages,
|
|
121
|
+
jsonObjectMode: true,
|
|
122
|
+
});
|
|
123
|
+
return response.text;
|
|
124
|
+
}
|
|
125
|
+
// ── getGroundingVerdict (FAIL-CLOSED on parse exhaustion) ────────────
|
|
126
|
+
export async function getGroundingVerdict(input) {
|
|
127
|
+
const { llm, model, question, answerBody, passages } = input;
|
|
128
|
+
const maxAttempts = GROUNDING_MAX_LLM_CALLS;
|
|
129
|
+
let lastError = "Unknown error";
|
|
130
|
+
let priorText;
|
|
131
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
132
|
+
const rawText = await callGroundingCritic({
|
|
133
|
+
llm,
|
|
134
|
+
model,
|
|
135
|
+
question,
|
|
136
|
+
answerBody,
|
|
137
|
+
passages,
|
|
138
|
+
priorText: attempt > 0 ? priorText : undefined,
|
|
139
|
+
formattedError: attempt > 0 ? lastError : undefined,
|
|
140
|
+
});
|
|
141
|
+
const validated = validateGroundingVerdict(rawText);
|
|
142
|
+
if (validated.ok) {
|
|
143
|
+
return validated.verdict;
|
|
144
|
+
}
|
|
145
|
+
lastError = validated.error;
|
|
146
|
+
priorText = rawText;
|
|
147
|
+
}
|
|
148
|
+
// FAIL-CLOSED inversion of critic-deep.ts:199-201 (which returns approve on exhaustion).
|
|
149
|
+
// bober: reject on parse exhaustion; this is intentional for medical safety — Sprint 2
|
|
150
|
+
// maps a reject to re-synthesis / abstain rather than letting an unverified answer through.
|
|
151
|
+
return { verdict: "reject", feedback: "<unparseable critic output>" };
|
|
152
|
+
}
|
|
153
|
+
//# sourceMappingURL=grounding-critic.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"grounding-critic.js","sourceRoot":"","sources":["../../../src/medical/retrieval/grounding-critic.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/** LiteratureRetriever — checks the literature-retrieval axis; returns {disabled} sync when off (ADR-6). */
|
|
2
|
+
import type { EgressGuard } from "../egress.js";
|
|
3
|
+
import { MedlineSource, type RetrievalOutcome } from "./medline-source.js";
|
|
4
|
+
import type { LLMClient } from "../../providers/types.js";
|
|
5
|
+
import type { MedicalAnswer, CriticVerdict } from "../types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Orchestrates literature retrieval behind the literature-retrieval egress gate.
|
|
8
|
+
*
|
|
9
|
+
* SYNCHRONOUS short-circuit: if the axis is off, return {disabled} — NO network
|
|
10
|
+
* attempt and NO MedlineSource method is called. This is the zero-egress proof.
|
|
11
|
+
*/
|
|
12
|
+
export declare class LiteratureRetriever {
|
|
13
|
+
private readonly egress;
|
|
14
|
+
private readonly source;
|
|
15
|
+
constructor(egress: EgressGuard, source?: MedlineSource);
|
|
16
|
+
/**
|
|
17
|
+
* Retrieve literature passages for the given query.
|
|
18
|
+
*
|
|
19
|
+
* When literature-retrieval is OFF: returns { kind: "disabled" } IMMEDIATELY,
|
|
20
|
+
* before MedlineSource is consulted — guarantees zero outbound bytes.
|
|
21
|
+
*
|
|
22
|
+
* When literature-retrieval is ON: delegates to MedlineSource.fetchPassages.
|
|
23
|
+
* Any throw from the source is caught and maps to abstain{source-error} (belt-and-braces;
|
|
24
|
+
* MedlineSource.fetchPassages itself is already fail-closed).
|
|
25
|
+
*/
|
|
26
|
+
retrieve(query: string): Promise<RetrievalOutcome>;
|
|
27
|
+
}
|
|
28
|
+
/** Return type for synthesizeGrounded — widens MedicalAnswer with the critic gate verdict (Sprint 3). */
|
|
29
|
+
export interface GroundedResult {
|
|
30
|
+
answer: MedicalAnswer;
|
|
31
|
+
verdict: CriticVerdict;
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* synthesize — single LLM call that grounds the answer in retrieved passages.
|
|
35
|
+
*
|
|
36
|
+
* Rules (non-negotiable, enforced here):
|
|
37
|
+
* - disabled/abstain outcome => abstained MedicalAnswer, citations: [], no clinical assertion.
|
|
38
|
+
* - grounded outcome => one llm.chat call; if the model abstains or returns empty => abstained.
|
|
39
|
+
* - Every non-abstained answer carries >= 1 citation derived from the passages.
|
|
40
|
+
* - LLM error (e.g. Ollama unreachable) => abstained + "model unavailable" footer, NO cloud fallback.
|
|
41
|
+
* - Never emits an uncited clinical claim.
|
|
42
|
+
*
|
|
43
|
+
* @param query - The original user question.
|
|
44
|
+
* @param outcome - The RetrievalOutcome from LiteratureRetriever.retrieve.
|
|
45
|
+
* @param llm - Injectable LLMClient (tests pass {chat: vi.fn()}).
|
|
46
|
+
* @param footer - DisclaimerComposer footer string included in every answer.
|
|
47
|
+
* @param model - Model identifier to use for synthesis (default: SYNTHESIS_MODEL).
|
|
48
|
+
*/
|
|
49
|
+
export declare function synthesize(query: string, outcome: RetrievalOutcome, llm: LLMClient, footer: string, model?: string): Promise<MedicalAnswer>;
|
|
50
|
+
/**
|
|
51
|
+
* Worst-case LLM call budget for the grounded gate:
|
|
52
|
+
* 1 synth + GROUNDING_MAX_LLM_CALLS critic + 1 re-synth + GROUNDING_MAX_LLM_CALLS re-critic
|
|
53
|
+
* Computed from the imported constant so it tracks future changes to the critic budget.
|
|
54
|
+
*/
|
|
55
|
+
export declare const GROUNDED_GATE_MAX_LLM_CALLS: number;
|
|
56
|
+
/**
|
|
57
|
+
* synthesizeGrounded — fail-closed gate: synthesize → critic → one re-synth → abstain.
|
|
58
|
+
*
|
|
59
|
+
* For non-grounded outcomes, delegates to synthesize (disabled/abstain handled there).
|
|
60
|
+
* For grounded outcomes:
|
|
61
|
+
* 1. synthesize → if abstained, return it
|
|
62
|
+
* 2. getGroundingVerdict → if approve, return answer
|
|
63
|
+
* 3. synthesizeWithFeedback (one re-synth with critic feedback)
|
|
64
|
+
* 4. getGroundingVerdict again → if approve, return re-synth answer
|
|
65
|
+
* 5. else → return abstainAnswer (fail-closed)
|
|
66
|
+
*
|
|
67
|
+
* Any thrown error from synthesize or the critic maps to abstainAnswer (fail-closed).
|
|
68
|
+
* Bounded by GROUNDED_GATE_MAX_LLM_CALLS (= 6 worst case today).
|
|
69
|
+
*
|
|
70
|
+
* Returns { answer, verdict } where verdict is one of:
|
|
71
|
+
* 'approve' — gate returned an approved answer
|
|
72
|
+
* 'reject-abstained' — gate abstained after a critic reject (second reject)
|
|
73
|
+
* 'error-abstained' — gate abstained due to a thrown error or model unavailable
|
|
74
|
+
*
|
|
75
|
+
* @param model - Model identifier to use (default: SYNTHESIS_MODEL for back-compat).
|
|
76
|
+
*/
|
|
77
|
+
export declare function synthesizeGrounded(query: string, outcome: RetrievalOutcome, llm: LLMClient, footer: string, model?: string): Promise<GroundedResult>;
|
|
78
|
+
//# sourceMappingURL=literature.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"literature.d.ts","sourceRoot":"","sources":["../../../src/medical/retrieval/literature.ts"],"names":[],"mappings":"AAAA,4GAA4G;AAC5G,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,KAAK,gBAAgB,EAAgB,MAAM,qBAAqB,CAAC;AACzF,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,0BAA0B,CAAC;AAC1D,OAAO,KAAK,EAAE,aAAa,EAAY,aAAa,EAAE,MAAM,aAAa,CAAC;AAK1E;;;;;GAKG;AACH,qBAAa,mBAAmB;IAE5B,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;gBADN,MAAM,EAAE,WAAW,EACnB,MAAM,gBAA4B;IAGrD;;;;;;;;;OASG;IACG,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,gBAAgB,CAAC;CAWzD;AAQD,yGAAyG;AACzG,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,aAAa,CAAC;IACtB,OAAO,EAAE,aAAa,CAAC;CACxB;AAqCD;;;;;;;;;;;;;;;GAeG;AACH,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAwB,GAC9B,OAAO,CAAC,aAAa,CAAC,CA0ExB;AAID;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,QACmB,CAAC;AA0D5D;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,gBAAgB,EACzB,GAAG,EAAE,SAAS,EACd,MAAM,EAAE,MAAM,EACd,KAAK,GAAE,MAAwB,GAC9B,OAAO,CAAC,cAAc,CAAC,CAyDzB"}
|