@evomap/evolver-core 2.0.0-beta.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/dist/algo/antiDistill.d.ts +35 -0
- package/dist/algo/antiDistill.js +167 -0
- package/dist/algo/bans.d.ts +6 -0
- package/dist/algo/bans.js +33 -0
- package/dist/algo/candidateAssembly.d.ts +65 -0
- package/dist/algo/candidateAssembly.js +196 -0
- package/dist/algo/capabilityCandidates.d.ts +38 -0
- package/dist/algo/capabilityCandidates.js +131 -0
- package/dist/algo/confidence.d.ts +106 -0
- package/dist/algo/confidence.js +196 -0
- package/dist/algo/cycleEngine.d.ts +143 -0
- package/dist/algo/cycleEngine.js +417 -0
- package/dist/algo/cycleFailureClassifier.d.ts +52 -0
- package/dist/algo/cycleFailureClassifier.js +104 -0
- package/dist/algo/epigenetics.d.ts +14 -0
- package/dist/algo/epigenetics.js +41 -0
- package/dist/algo/evolutionEvent.d.ts +23 -0
- package/dist/algo/evolutionEvent.js +24 -0
- package/dist/algo/exploration.d.ts +34 -0
- package/dist/algo/exploration.js +55 -0
- package/dist/algo/geneHealth.d.ts +24 -0
- package/dist/algo/geneHealth.js +16 -0
- package/dist/algo/geneIntake.d.ts +37 -0
- package/dist/algo/geneIntake.js +100 -0
- package/dist/algo/genePromotion.d.ts +48 -0
- package/dist/algo/genePromotion.js +53 -0
- package/dist/algo/geneSelection.d.ts +128 -0
- package/dist/algo/geneSelection.js +222 -0
- package/dist/algo/index.d.ts +18 -0
- package/dist/algo/index.js +18 -0
- package/dist/algo/mutation.d.ts +12 -0
- package/dist/algo/mutation.js +22 -0
- package/dist/algo/orchestrator.d.ts +65 -0
- package/dist/algo/orchestrator.js +63 -0
- package/dist/algo/solidify.d.ts +43 -0
- package/dist/algo/solidify.js +63 -0
- package/dist/algo/strategyPresets.d.ts +32 -0
- package/dist/algo/strategyPresets.js +61 -0
- package/dist/assetstore/index.d.ts +8 -0
- package/dist/assetstore/index.js +8 -0
- package/dist/assetstore/learningHistory.d.ts +25 -0
- package/dist/assetstore/learningHistory.js +38 -0
- package/dist/assetstore/localJsonl.d.ts +34 -0
- package/dist/assetstore/localJsonl.js +182 -0
- package/dist/assetstore/pendingSignals.d.ts +22 -0
- package/dist/assetstore/pendingSignals.js +171 -0
- package/dist/assetstore/provenance.d.ts +39 -0
- package/dist/assetstore/provenance.js +109 -0
- package/dist/assetstore/provider.d.ts +48 -0
- package/dist/assetstore/provider.js +36 -0
- package/dist/assetstore/remoteStub.d.ts +32 -0
- package/dist/assetstore/remoteStub.js +36 -0
- package/dist/assetstore/reviewFilter.d.ts +20 -0
- package/dist/assetstore/reviewFilter.js +44 -0
- package/dist/assetstore/reviewLedger.d.ts +55 -0
- package/dist/assetstore/reviewLedger.js +121 -0
- package/dist/benchmark/antiGeneBenchmark.d.ts +79 -0
- package/dist/benchmark/antiGeneBenchmark.js +241 -0
- package/dist/benchmark/antiGeneRollout.d.ts +80 -0
- package/dist/benchmark/antiGeneRollout.js +256 -0
- package/dist/benchmark/benchmark.d.ts +32 -0
- package/dist/benchmark/benchmark.js +26 -0
- package/dist/benchmark/evolutionThesisSolver.d.ts +25 -0
- package/dist/benchmark/evolutionThesisSolver.js +41 -0
- package/dist/benchmark/index.d.ts +5 -0
- package/dist/benchmark/index.js +5 -0
- package/dist/benchmark/thesis.d.ts +85 -0
- package/dist/benchmark/thesis.js +150 -0
- package/dist/bootstrap/envFingerprint.d.ts +45 -0
- package/dist/bootstrap/envFingerprint.js +72 -0
- package/dist/bootstrap/index.d.ts +2 -0
- package/dist/bootstrap/index.js +2 -0
- package/dist/bootstrap/selfEvolve.d.ts +36 -0
- package/dist/bootstrap/selfEvolve.js +26 -0
- package/dist/cycle/cycleTimeline.d.ts +35 -0
- package/dist/cycle/cycleTimeline.js +74 -0
- package/dist/cycle/index.d.ts +2 -0
- package/dist/cycle/index.js +2 -0
- package/dist/cycle/stateMachine.d.ts +6 -0
- package/dist/cycle/stateMachine.js +25 -0
- package/dist/daemon/cycleContext.d.ts +12 -0
- package/dist/daemon/cycleContext.js +13 -0
- package/dist/daemon/drain.d.ts +11 -0
- package/dist/daemon/drain.js +16 -0
- package/dist/daemon/guards.d.ts +87 -0
- package/dist/daemon/guards.js +142 -0
- package/dist/daemon/idempotency.d.ts +11 -0
- package/dist/daemon/idempotency.js +36 -0
- package/dist/daemon/idleScheduler.d.ts +55 -0
- package/dist/daemon/idleScheduler.js +181 -0
- package/dist/daemon/index.d.ts +8 -0
- package/dist/daemon/index.js +8 -0
- package/dist/daemon/stepRunner.d.ts +12 -0
- package/dist/daemon/stepRunner.js +31 -0
- package/dist/daemon/unhandledRejectionWindow.d.ts +21 -0
- package/dist/daemon/unhandledRejectionWindow.js +52 -0
- package/dist/daemon/watchdog.d.ts +11 -0
- package/dist/daemon/watchdog.js +14 -0
- package/dist/events/eventSchema.d.ts +132 -0
- package/dist/events/eventSchema.js +30 -0
- package/dist/events/eventStore.d.ts +32 -0
- package/dist/events/eventStore.js +137 -0
- package/dist/events/ingest.d.ts +34 -0
- package/dist/events/ingest.js +61 -0
- package/dist/events/paths.d.ts +22 -0
- package/dist/events/paths.js +44 -0
- package/dist/events/projectors.d.ts +9 -0
- package/dist/events/projectors.js +11 -0
- package/dist/events/public.d.ts +14 -0
- package/dist/events/public.js +8 -0
- package/dist/events/replayer.d.ts +26 -0
- package/dist/events/replayer.js +67 -0
- package/dist/events/reports.d.ts +90 -0
- package/dist/events/reports.js +244 -0
- package/dist/events/retention.d.ts +70 -0
- package/dist/events/retention.js +177 -0
- package/dist/events/sink.d.ts +5 -0
- package/dist/events/sink.js +1 -0
- package/dist/exec/autoExec.d.ts +137 -0
- package/dist/exec/autoExec.js +221 -0
- package/dist/exec/autonomousCycle.d.ts +48 -0
- package/dist/exec/autonomousCycle.js +78 -0
- package/dist/exec/claudeBridge.d.ts +112 -0
- package/dist/exec/claudeBridge.js +219 -0
- package/dist/exec/index.d.ts +9 -0
- package/dist/exec/index.js +11 -0
- package/dist/exec/openPrRegistry.d.ts +60 -0
- package/dist/exec/openPrRegistry.js +130 -0
- package/dist/exec/policy/blastRadius.d.ts +31 -0
- package/dist/exec/policy/blastRadius.js +67 -0
- package/dist/exec/policy/constraints.d.ts +26 -0
- package/dist/exec/policy/constraints.js +62 -0
- package/dist/exec/policy/destructive.d.ts +45 -0
- package/dist/exec/policy/destructive.js +108 -0
- package/dist/exec/policy/failureMode.d.ts +33 -0
- package/dist/exec/policy/failureMode.js +49 -0
- package/dist/exec/policy/index.d.ts +24 -0
- package/dist/exec/policy/index.js +30 -0
- package/dist/exec/policy/protectedPaths.d.ts +17 -0
- package/dist/exec/policy/protectedPaths.js +65 -0
- package/dist/exec/policyCheck.d.ts +2 -0
- package/dist/exec/policyCheck.js +7 -0
- package/dist/exec/prompt.d.ts +34 -0
- package/dist/exec/prompt.js +108 -0
- package/dist/exec/proofOfWork.d.ts +9 -0
- package/dist/exec/proofOfWork.js +11 -0
- package/dist/exec/runnerRegistry.d.ts +123 -0
- package/dist/exec/runnerRegistry.js +237 -0
- package/dist/exec/selfPr.d.ts +83 -0
- package/dist/exec/selfPr.js +104 -0
- package/dist/exec/selfPrObfuscation.d.ts +21 -0
- package/dist/exec/selfPrObfuscation.js +85 -0
- package/dist/hooks/hooks.d.ts +117 -0
- package/dist/hooks/hooks.js +89 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hub/assetCallLog.d.ts +43 -0
- package/dist/hub/assetCallLog.js +72 -0
- package/dist/hub/bindings.d.ts +28 -0
- package/dist/hub/bindings.js +90 -0
- package/dist/hub/capability.d.ts +278 -0
- package/dist/hub/capability.js +1 -0
- package/dist/hub/conversationDistiller.d.ts +88 -0
- package/dist/hub/conversationDistiller.js +264 -0
- package/dist/hub/fake.d.ts +45 -0
- package/dist/hub/fake.js +69 -0
- package/dist/hub/hubReview.d.ts +91 -0
- package/dist/hub/hubReview.js +106 -0
- package/dist/hub/index.d.ts +10 -0
- package/dist/hub/index.js +10 -0
- package/dist/hub/ingest.d.ts +15 -0
- package/dist/hub/ingest.js +15 -0
- package/dist/hub/questionGenerator.d.ts +59 -0
- package/dist/hub/questionGenerator.js +406 -0
- package/dist/hub/reuseDecision.d.ts +87 -0
- package/dist/hub/reuseDecision.js +127 -0
- package/dist/hub/sanitize.d.ts +52 -0
- package/dist/hub/sanitize.js +321 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +27 -0
- package/dist/mailbox/catalog.d.ts +17 -0
- package/dist/mailbox/catalog.js +53 -0
- package/dist/mailbox/daemon.d.ts +48 -0
- package/dist/mailbox/daemon.js +61 -0
- package/dist/mailbox/dispatch.d.ts +46 -0
- package/dist/mailbox/dispatch.js +62 -0
- package/dist/mailbox/envelope.d.ts +43 -0
- package/dist/mailbox/envelope.js +52 -0
- package/dist/mailbox/index.d.ts +8 -0
- package/dist/mailbox/index.js +8 -0
- package/dist/mailbox/ipcServer.d.ts +46 -0
- package/dist/mailbox/ipcServer.js +168 -0
- package/dist/mailbox/requestReply.d.ts +40 -0
- package/dist/mailbox/requestReply.js +77 -0
- package/dist/mailbox/store.d.ts +74 -0
- package/dist/mailbox/store.js +308 -0
- package/dist/mailbox/wake.d.ts +45 -0
- package/dist/mailbox/wake.js +60 -0
- package/dist/material/boundary.d.ts +7 -0
- package/dist/material/boundary.js +14 -0
- package/dist/material/consumer.d.ts +21 -0
- package/dist/material/consumer.js +52 -0
- package/dist/material/emit.d.ts +55 -0
- package/dist/material/emit.js +52 -0
- package/dist/material/factory.d.ts +27 -0
- package/dist/material/factory.js +25 -0
- package/dist/material/index.d.ts +8 -0
- package/dist/material/index.js +8 -0
- package/dist/material/materialStore.d.ts +24 -0
- package/dist/material/materialStore.js +76 -0
- package/dist/material/sampling.d.ts +21 -0
- package/dist/material/sampling.js +39 -0
- package/dist/material/sources.d.ts +25 -0
- package/dist/material/sources.js +33 -0
- package/dist/material/watermark.d.ts +21 -0
- package/dist/material/watermark.js +76 -0
- package/dist/observers/cursorRewriteObserver.d.ts +46 -0
- package/dist/observers/cursorRewriteObserver.js +103 -0
- package/dist/observers/distillObserver.d.ts +31 -0
- package/dist/observers/distillObserver.js +93 -0
- package/dist/observers/index.d.ts +5 -0
- package/dist/observers/index.js +5 -0
- package/dist/observers/observerBus.d.ts +44 -0
- package/dist/observers/observerBus.js +73 -0
- package/dist/observers/reflectionObserver.d.ts +31 -0
- package/dist/observers/reflectionObserver.js +124 -0
- package/dist/observers/valueDigestObserver.d.ts +50 -0
- package/dist/observers/valueDigestObserver.js +70 -0
- package/dist/ops/cleanup.d.ts +23 -0
- package/dist/ops/cleanup.js +68 -0
- package/dist/ops/health.d.ts +37 -0
- package/dist/ops/health.js +96 -0
- package/dist/ops/index.d.ts +7 -0
- package/dist/ops/index.js +7 -0
- package/dist/ops/recall.d.ts +65 -0
- package/dist/ops/recall.js +152 -0
- package/dist/ops/reuseOutcomes.d.ts +68 -0
- package/dist/ops/reuseOutcomes.js +126 -0
- package/dist/ops/savingsCore.d.ts +47 -0
- package/dist/ops/savingsCore.js +112 -0
- package/dist/ops/selfUpdate.d.ts +106 -0
- package/dist/ops/selfUpdate.js +294 -0
- package/dist/ops/valueLedger.d.ts +205 -0
- package/dist/ops/valueLedger.js +238 -0
- package/dist/ops/valueOutreach.d.ts +70 -0
- package/dist/ops/valueOutreach.js +244 -0
- package/dist/personality/drift.d.ts +45 -0
- package/dist/personality/drift.js +106 -0
- package/dist/personality/events.d.ts +25 -0
- package/dist/personality/events.js +37 -0
- package/dist/personality/evolveOps.d.ts +20 -0
- package/dist/personality/evolveOps.js +93 -0
- package/dist/personality/index.d.ts +11 -0
- package/dist/personality/index.js +11 -0
- package/dist/personality/mutate.d.ts +21 -0
- package/dist/personality/mutate.js +39 -0
- package/dist/personality/pivot.d.ts +23 -0
- package/dist/personality/pivot.js +22 -0
- package/dist/personality/prompt.d.ts +6 -0
- package/dist/personality/prompt.js +62 -0
- package/dist/personality/riskGate.d.ts +42 -0
- package/dist/personality/riskGate.js +80 -0
- package/dist/personality/schema.d.ts +255 -0
- package/dist/personality/schema.js +119 -0
- package/dist/personality/select.d.ts +46 -0
- package/dist/personality/select.js +71 -0
- package/dist/personality/stats.d.ts +46 -0
- package/dist/personality/stats.js +82 -0
- package/dist/personality/store.d.ts +35 -0
- package/dist/personality/store.js +92 -0
- package/dist/schema/common.d.ts +28 -0
- package/dist/schema/common.js +17 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/material.d.ts +201 -0
- package/dist/schema/material.js +50 -0
- package/dist/schema/problem.d.ts +160 -0
- package/dist/schema/problem.js +45 -0
- package/dist/schema/proofOfWork.d.ts +97 -0
- package/dist/schema/proofOfWork.js +9 -0
- package/dist/schema/signal.d.ts +41 -0
- package/dist/schema/signal.js +19 -0
- package/dist/shadow/index.d.ts +6 -0
- package/dist/shadow/index.js +6 -0
- package/dist/shadow/jsonlSink.d.ts +17 -0
- package/dist/shadow/jsonlSink.js +27 -0
- package/dist/shadow/redact.d.ts +11 -0
- package/dist/shadow/redact.js +18 -0
- package/dist/shadow/shadowHub.d.ts +12 -0
- package/dist/shadow/shadowHub.js +42 -0
- package/dist/shadow/shadowMailbox.d.ts +17 -0
- package/dist/shadow/shadowMailbox.js +25 -0
- package/dist/shadow/shadowStore.d.ts +18 -0
- package/dist/shadow/shadowStore.js +55 -0
- package/dist/shadow/sink.d.ts +37 -0
- package/dist/shadow/sink.js +18 -0
- package/dist/signals/cycleHistoryFromEvents.d.ts +11 -0
- package/dist/signals/cycleHistoryFromEvents.js +96 -0
- package/dist/signals/expand.d.ts +34 -0
- package/dist/signals/expand.js +125 -0
- package/dist/signals/extractor.d.ts +25 -0
- package/dist/signals/extractor.js +57 -0
- package/dist/signals/index.d.ts +6 -0
- package/dist/signals/index.js +6 -0
- package/dist/signals/metaSignals.d.ts +57 -0
- package/dist/signals/metaSignals.js +158 -0
- package/dist/signals/signalGate.d.ts +15 -0
- package/dist/signals/signalGate.js +40 -0
- package/dist/signals/traceSignals.d.ts +44 -0
- package/dist/signals/traceSignals.js +123 -0
- package/dist/signatures/classifier.d.ts +16 -0
- package/dist/signatures/classifier.js +29 -0
- package/dist/signatures/index.d.ts +2 -0
- package/dist/signatures/index.js +2 -0
- package/dist/signatures/signatures.d.ts +22 -0
- package/dist/signatures/signatures.js +42 -0
- package/dist/strategy/experiment.d.ts +48 -0
- package/dist/strategy/experiment.js +63 -0
- package/dist/strategy/index.d.ts +2 -0
- package/dist/strategy/index.js +2 -0
- package/dist/strategy/strategyPoint.d.ts +30 -0
- package/dist/strategy/strategyPoint.js +23 -0
- package/dist/trace/index.d.ts +2 -0
- package/dist/trace/index.js +2 -0
- package/dist/trace/trajectory.d.ts +82 -0
- package/dist/trace/trajectory.js +117 -0
- package/dist/trace/trajectoryExport.d.ts +237 -0
- package/dist/trace/trajectoryExport.js +1818 -0
- package/dist/trigger/budget.d.ts +16 -0
- package/dist/trigger/budget.js +20 -0
- package/dist/trigger/engine.d.ts +12 -0
- package/dist/trigger/engine.js +23 -0
- package/dist/trigger/index.d.ts +6 -0
- package/dist/trigger/index.js +6 -0
- package/dist/trigger/problemBuilder.d.ts +24 -0
- package/dist/trigger/problemBuilder.js +32 -0
- package/dist/trigger/trigger.d.ts +22 -0
- package/dist/trigger/trigger.js +27 -0
- package/dist/trigger/valueModel.d.ts +26 -0
- package/dist/trigger/valueModel.js +25 -0
- package/dist/trigger/wfq.d.ts +13 -0
- package/dist/trigger/wfq.js +25 -0
- package/dist/util/fileLock.d.ts +24 -0
- package/dist/util/fileLock.js +227 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.js +2 -0
- package/dist/util/version.d.ts +12 -0
- package/dist/util/version.js +51 -0
- package/dist/verify/index.d.ts +4 -0
- package/dist/verify/index.js +4 -0
- package/dist/verify/rollback.d.ts +22 -0
- package/dist/verify/rollback.js +33 -0
- package/dist/verify/sandboxRunner.d.ts +39 -0
- package/dist/verify/sandboxRunner.js +116 -0
- package/dist/verify/sandboxedValidation.d.ts +35 -0
- package/dist/verify/sandboxedValidation.js +56 -0
- package/dist/verify/validation.d.ts +41 -0
- package/dist/verify/validation.js +85 -0
- package/dist/wire/canonicalize.golden.d.ts +23 -0
- package/dist/wire/canonicalize.golden.js +46 -0
- package/dist/wire/geneHints.d.ts +64 -0
- package/dist/wire/geneHints.js +100 -0
- package/dist/wire/index.d.ts +91 -0
- package/dist/wire/index.js +3 -0
- package/dist/wire/schemaGate.d.ts +11 -0
- package/dist/wire/schemaGate.js +49 -0
- package/dist/workflow/dsl.d.ts +54 -0
- package/dist/workflow/dsl.js +15 -0
- package/dist/workflow/engine.d.ts +30 -0
- package/dist/workflow/engine.js +94 -0
- package/dist/workflow/index.d.ts +2 -0
- package/dist/workflow/index.js +2 -0
- package/package.json +29 -0
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/** A minimal session turn shape (decoupled from the runtime-adapter NormalizedTurn so core needs no adapter import). */
|
|
2
|
+
export interface RecallTurn {
|
|
3
|
+
/** 'assistant' = the agent's own output — the only place "the agent applied the strategy" can show. */
|
|
4
|
+
role: string;
|
|
5
|
+
text: string;
|
|
6
|
+
}
|
|
7
|
+
/** The distinctive content of an injected gene that recall is checked against. */
|
|
8
|
+
export interface GeneRecallInput {
|
|
9
|
+
geneId: string;
|
|
10
|
+
/** The gene's learned strategy steps — the strongest recall evidence when their distinctive terms reappear. */
|
|
11
|
+
strategy?: readonly string[];
|
|
12
|
+
/** The gene's one-line summary (secondary evidence). */
|
|
13
|
+
summary?: string;
|
|
14
|
+
}
|
|
15
|
+
export type RecallVerdict = 'used' | 'unused' | 'unknown';
|
|
16
|
+
/** root_events type for an OBSERVED recall verdict (#274): derived from a session transcript (not agent self-report),
|
|
17
|
+
* so the experience loop learns which injected/fetched genes were actually applied. Distinct from the reuse-outcome
|
|
18
|
+
* events (which record whether a reused gene WORKED) — recall records whether it was USED at all. */
|
|
19
|
+
export declare const VALUE_RECALL_EVENT = "value.recall";
|
|
20
|
+
/** Payload of a `value.recall` root_event: one gene's observed recall verdict for a session. */
|
|
21
|
+
export interface RecallEventPayload {
|
|
22
|
+
geneId: string;
|
|
23
|
+
recalled: RecallVerdict;
|
|
24
|
+
/** Fraction of the gene's distinctive terms found in the agent's output, in [0,1]. */
|
|
25
|
+
score: number;
|
|
26
|
+
/** The session this verdict was observed in (transcript-derived), for idempotency + attribution. */
|
|
27
|
+
sessionId?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface GeneRecallResult {
|
|
30
|
+
geneId: string;
|
|
31
|
+
/** used = the agent's output carries the gene's distinctive terms; unused = injected but ~no overlap;
|
|
32
|
+
* unknown = the gene has no distinctive content OR the session has no agent turns to judge against. */
|
|
33
|
+
recalled: RecallVerdict;
|
|
34
|
+
/** Fraction of the gene's distinctive terms that appear in the agent's output, in [0,1]. */
|
|
35
|
+
score: number;
|
|
36
|
+
/** The distinctive terms that were found in the agent's output (for explainability / audit). */
|
|
37
|
+
matched: string[];
|
|
38
|
+
}
|
|
39
|
+
export interface RecallOptions {
|
|
40
|
+
/** Minimum score to call a gene `used`. Default 0.3 — a third of the distinctive terms reappearing is a
|
|
41
|
+
* deliberate, conservative bar (above incidental single-word coincidence, below demanding verbatim echo). */
|
|
42
|
+
threshold?: number;
|
|
43
|
+
/** Drop terms at or below this length before matching (kills articles/operators that overlap by chance). Default 3. */
|
|
44
|
+
minTermLength?: number;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Decide whether an injected gene was recalled by the agent in a session. Pure: looks only at the gene's
|
|
48
|
+
* distinctive terms (strategy + summary) and whether they reappear in the agent's OWN turns. We check assistant
|
|
49
|
+
* turns specifically — the strategy text was injected into the prompt (user side), so finding it echoed in the
|
|
50
|
+
* agent's output is the evidence that the agent actually carried it out, not just that we put it there.
|
|
51
|
+
*/
|
|
52
|
+
export declare function verifyGeneRecall(gene: GeneRecallInput, turns: readonly RecallTurn[], opts?: RecallOptions): GeneRecallResult;
|
|
53
|
+
/** Verify a batch of injected genes against one session's turns (the inject-attribution → recall closure). */
|
|
54
|
+
export declare function verifyInjectedGenes(genes: readonly GeneRecallInput[], turns: readonly RecallTurn[], opts?: RecallOptions): GeneRecallResult[];
|
|
55
|
+
/** Operator-facing rollup of a batch recall check. `pruneCandidates` are genes judged 'unused' (injected but the
|
|
56
|
+
* agent did not echo their strategy) — the ones that keep paying prompt budget for no observed effect. Never
|
|
57
|
+
* includes 'unknown' (no agent output / no distinctive terms — not judgeable, so not a prune signal). */
|
|
58
|
+
export interface RecallSummary {
|
|
59
|
+
total: number;
|
|
60
|
+
used: number;
|
|
61
|
+
unused: number;
|
|
62
|
+
unknown: number;
|
|
63
|
+
pruneCandidates: string[];
|
|
64
|
+
}
|
|
65
|
+
export declare function summarizeRecall(results: readonly GeneRecallResult[]): RecallSummary;
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
// Recall verification (v1 gap: recallVerifier) — the missing other half of the inject attribution rail.
|
|
2
|
+
//
|
|
3
|
+
// The `value.inject` event records WHICH genes were injected into a SessionStart prompt, but the ledger is
|
|
4
|
+
// "attribution-only": it never checks whether the agent ACTUALLY used the injected gene, so an injected-but-
|
|
5
|
+
// ignored gene looks the same as an injected-and-applied one. That makes inject a permanently weak signal and
|
|
6
|
+
// lets dead genes ride along, taxing the prompt budget for nothing.
|
|
7
|
+
//
|
|
8
|
+
// This is the verification primitive: given a gene's distinctive content and the session's turns, decide
|
|
9
|
+
// whether the gene was RECALLED (its strategy shows up in the agent's OWN output, i.e. the agent acted on it).
|
|
10
|
+
// It is PURE and deterministic — no live agent, no fs/clock — so it is fully unit-testable with fixtures, and it
|
|
11
|
+
// reads only data that already exists (the gene record + the session transcript turns). Wiring it onto the
|
|
12
|
+
// experience loop (emit a `value.recall` enrichment, prune never-recalled genes) is a separate, later slice; this
|
|
13
|
+
// module is just the decision function so the heuristic can be reviewed and tested in isolation first.
|
|
14
|
+
/** root_events type for an OBSERVED recall verdict (#274): derived from a session transcript (not agent self-report),
|
|
15
|
+
* so the experience loop learns which injected/fetched genes were actually applied. Distinct from the reuse-outcome
|
|
16
|
+
* events (which record whether a reused gene WORKED) — recall records whether it was USED at all. */
|
|
17
|
+
export const VALUE_RECALL_EVENT = 'value.recall';
|
|
18
|
+
// Common English + Chinese-pipeline filler that would create spurious overlap. Intentionally small: the goal is
|
|
19
|
+
// to remove obvious noise, not to do real NLP. Anything not filtered is fine — it only raises the matching bar.
|
|
20
|
+
const STOPWORDS = new Set([
|
|
21
|
+
'the', 'and', 'for', 'with', 'that', 'this', 'then', 'from', 'into', 'your', 'you', 'use', 'using', 'used',
|
|
22
|
+
'when', 'will', 'should', 'must', 'have', 'has', 'are', 'was', 'were', 'not', 'but', 'all', 'any', 'can',
|
|
23
|
+
'add', 'set', 'get', 'run', 'fix', 'via', 'per', 'its', 'it', 'a', 'an', 'to', 'of', 'in', 'on', 'or', 'is',
|
|
24
|
+
]);
|
|
25
|
+
// Light suffix stemmer so an inflected paraphrase still counts as recall ("cleared"→"clear", "retried"→"retry",
|
|
26
|
+
// "retrying"→"retry", "cookies"→"cookie"). Deliberately tiny — over-merging only widens matches slightly and the
|
|
27
|
+
// score threshold absorbs it; the alternative (exact match) misses the common case where the agent re-tenses the
|
|
28
|
+
// strategy verbs, which is exactly what "the agent applied it" looks like.
|
|
29
|
+
function stem(t) {
|
|
30
|
+
if (t.length > 4 && t.endsWith('ied'))
|
|
31
|
+
return `${t.slice(0, -3)}y`; // tried/retried → try/retry (verb form)
|
|
32
|
+
if (t.length > 5 && t.endsWith('ing'))
|
|
33
|
+
return t.slice(0, -3); // retrying → retry
|
|
34
|
+
if (t.length > 4 && t.endsWith('ed'))
|
|
35
|
+
return t.slice(0, -2); // cleared → clear
|
|
36
|
+
// Plural -s: strip only the trailing 's' for a normal plural (cookies → cookie, sessions → session). 'es' is two
|
|
37
|
+
// real letters ONLY after a sibilant (boxes → box, matches → match); elsewhere stripping 'es' over-strips
|
|
38
|
+
// (cookies → cooki) and false-marks a plural paraphrase as unused (Bugbot), so gate the -es strip on a sibilant.
|
|
39
|
+
// NOTE: a y→ies plural (retries → retry) is NOT handled — a tiny stemmer can't tell it from cookie+s; the verb
|
|
40
|
+
// forms (retried/retrying) cover that gene, and the score threshold absorbs the occasional plural-noun miss.
|
|
41
|
+
if (t.length > 4 && /(?:s|x|z|ch|sh)es$/.test(t))
|
|
42
|
+
return t.slice(0, -2);
|
|
43
|
+
// Plural -s, but NOT a singular lemma ending in 's': -ss (process/class), -us (focus/status/bonus), -is
|
|
44
|
+
// (analysis/basis/crisis) are singular and must keep their 's', else gene "focus" stems to "focu" while the
|
|
45
|
+
// agent's "focused" stems to "focus" and a real recall is missed (Bugbot; NB its "process" example was wrong —
|
|
46
|
+
// process ends -ss, already excluded). Rare -us/-is plurals (menus/skis) lose a strip; acceptably uncommon.
|
|
47
|
+
if (t.length > 4 && t.endsWith('s') && !/(?:ss|us|is)$/.test(t))
|
|
48
|
+
return t.slice(0, -1);
|
|
49
|
+
return t;
|
|
50
|
+
}
|
|
51
|
+
// A doubled-consonant -ing (committing→committ) hides the base verb (commit). We CANNOT destructively undouble in
|
|
52
|
+
// stem() — "calling"→"call" already matches and undoubling would wrongly break it to "cal" (regressing
|
|
53
|
+
// call/press/pass/fall/fill, verified). So instead, ONLY on the agent side, ADD the undoubled form as an EXTRA
|
|
54
|
+
// accepted variant: "committing" contributes {committ, commit} — "committ" is kept and "commit" is gained, nothing
|
|
55
|
+
// is lost (Bugbot -ing finding; note retrying→retry was never wrong). The variant is added by distinctiveTerms only
|
|
56
|
+
// when it clears minLen (so "calling" keeps "call" but the sub-minLen "cal" is dropped — see there).
|
|
57
|
+
function ingUndoubledVariant(raw, stemmed) {
|
|
58
|
+
if (raw.length <= 5 || !raw.endsWith('ing') || stemmed.length < 3)
|
|
59
|
+
return null;
|
|
60
|
+
const last = stemmed[stemmed.length - 1];
|
|
61
|
+
if (last === stemmed[stemmed.length - 2] && /[bcdfghjklmnpqrstvwxz]/.test(last))
|
|
62
|
+
return stemmed.slice(0, -1);
|
|
63
|
+
return null;
|
|
64
|
+
}
|
|
65
|
+
// Undouble an already-stemmed token (committ→commit, wrapp→wrap) for MATCH-TIME comparison only. The agent-side
|
|
66
|
+
// expansion above handles "agent gerund vs gene base"; this handles the mirror — a GENE gerund (committing→committ)
|
|
67
|
+
// vs an agent BASE (commit) — without adding a second gene term (which would inflate the recall denominator).
|
|
68
|
+
// "commit" base stems stay single-consonant so they return null; a genuine doubled base like "call"→"cal" only ever
|
|
69
|
+
// produces a variant the agent never emits, so it cannot create a false match.
|
|
70
|
+
function undoubledStem(s) {
|
|
71
|
+
if (s.length < 4)
|
|
72
|
+
return null;
|
|
73
|
+
const last = s[s.length - 1];
|
|
74
|
+
if (last === s[s.length - 2] && /[bcdfghjklmnpqrstvwxz]/.test(last))
|
|
75
|
+
return s.slice(0, -1);
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Lowercase + split on non-word-ish boundaries → distinctive STEMMED terms (stopwords + short tokens dropped,
|
|
80
|
+
* deduped). With expandIng, also emits the undoubled -ing variant (agent side only) so a doubled-consonant
|
|
81
|
+
* gerund still matches the base-verb gene term, without the destructive undouble that would regress call/press.
|
|
82
|
+
*/
|
|
83
|
+
function distinctiveTerms(text, minLen, expandIng = false) {
|
|
84
|
+
const out = new Set();
|
|
85
|
+
for (const raw of text.toLowerCase().split(/[^a-z0-9_]+/)) {
|
|
86
|
+
if (raw.length <= minLen)
|
|
87
|
+
continue;
|
|
88
|
+
if (STOPWORDS.has(raw))
|
|
89
|
+
continue;
|
|
90
|
+
const s = stem(raw);
|
|
91
|
+
// A stem that collapses onto a stopword (fixing→fix, adding→add) is not distinctive, and the bare stopword
|
|
92
|
+
// (fix/add) is dropped on the OTHER side (both stopword AND <=minLen), so keeping it here scores a phantom
|
|
93
|
+
// miss for one side only. Drop stopword stems so the gene and agent sides agree (Bugbot).
|
|
94
|
+
if (STOPWORDS.has(s))
|
|
95
|
+
continue;
|
|
96
|
+
out.add(s);
|
|
97
|
+
if (expandIng) {
|
|
98
|
+
const u = ingUndoubledVariant(raw, s);
|
|
99
|
+
// Keep the undouble variant only when it is itself a distinctive term: length > minLen and not a stopword.
|
|
100
|
+
// A <=minLen undouble (calling→cal, running→run) is intentionally dropped — gene terms are never that short
|
|
101
|
+
// for a doubled-consonant base (the gene side yields "commit"/"wrap", not "run"), so it could match nothing.
|
|
102
|
+
if (u && u.length > minLen && !STOPWORDS.has(u))
|
|
103
|
+
out.add(u);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
return out;
|
|
107
|
+
}
|
|
108
|
+
/**
|
|
109
|
+
* Decide whether an injected gene was recalled by the agent in a session. Pure: looks only at the gene's
|
|
110
|
+
* distinctive terms (strategy + summary) and whether they reappear in the agent's OWN turns. We check assistant
|
|
111
|
+
* turns specifically — the strategy text was injected into the prompt (user side), so finding it echoed in the
|
|
112
|
+
* agent's output is the evidence that the agent actually carried it out, not just that we put it there.
|
|
113
|
+
*/
|
|
114
|
+
export function verifyGeneRecall(gene, turns, opts = {}) {
|
|
115
|
+
const threshold = opts.threshold ?? 0.3;
|
|
116
|
+
const minLen = opts.minTermLength ?? 3;
|
|
117
|
+
const geneText = [...(gene.strategy ?? []), gene.summary ?? ''].join(' ');
|
|
118
|
+
const terms = distinctiveTerms(geneText, minLen);
|
|
119
|
+
const agentTurns = turns.filter((t) => t.role === 'assistant');
|
|
120
|
+
// Cannot judge → unknown (never a false 'unused'): EITHER the gene has no distinctive content to look for, OR
|
|
121
|
+
// there is no agent output at all to judge against. Gate on the PRESENCE of agent turns, not on whether they
|
|
122
|
+
// yielded distinctive terms (Bugbot): an agent turn that exists but stems to nothing (stopword-only, or only
|
|
123
|
+
// dropped short tokens like "401 ok") IS judgeable — the agent produced output that does not carry the gene,
|
|
124
|
+
// i.e. 'unused', not 'unknown'. Conflating the two blurs the signal the downstream value.recall / pruning needs.
|
|
125
|
+
if (terms.size === 0 || agentTurns.length === 0) {
|
|
126
|
+
return { geneId: gene.geneId, recalled: 'unknown', score: 0, matched: [] };
|
|
127
|
+
}
|
|
128
|
+
const agentTerms = distinctiveTerms(agentTurns.map((t) => t.text).join(' \n '), minLen, true); // expandIng: agent side
|
|
129
|
+
// A gene term matches when the agent set has it directly, OR has its undoubled base — so a gene gerund
|
|
130
|
+
// ("committing"→"committ") still matches an agent base ("commit"). One match per gene term, denominator unchanged.
|
|
131
|
+
const matched = [...terms].filter((t) => {
|
|
132
|
+
if (agentTerms.has(t))
|
|
133
|
+
return true;
|
|
134
|
+
const u = undoubledStem(t);
|
|
135
|
+
return u !== null && agentTerms.has(u);
|
|
136
|
+
});
|
|
137
|
+
const score = matched.length / terms.size;
|
|
138
|
+
return { geneId: gene.geneId, recalled: score >= threshold ? 'used' : 'unused', score, matched };
|
|
139
|
+
}
|
|
140
|
+
/** Verify a batch of injected genes against one session's turns (the inject-attribution → recall closure). */
|
|
141
|
+
export function verifyInjectedGenes(genes, turns, opts) {
|
|
142
|
+
return genes.map((g) => verifyGeneRecall(g, turns, opts));
|
|
143
|
+
}
|
|
144
|
+
export function summarizeRecall(results) {
|
|
145
|
+
return {
|
|
146
|
+
total: results.length,
|
|
147
|
+
used: results.filter((r) => r.recalled === 'used').length,
|
|
148
|
+
unused: results.filter((r) => r.recalled === 'unused').length,
|
|
149
|
+
unknown: results.filter((r) => r.recalled === 'unknown').length,
|
|
150
|
+
pruneCandidates: results.filter((r) => r.recalled === 'unused').map((r) => r.geneId),
|
|
151
|
+
};
|
|
152
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/** root_events type appended for a NON-success MCP reuse-result — the negative half of the keep/prune signal. */
|
|
2
|
+
export declare const VALUE_REUSE_OUTCOME_EVENT = "value.reuse_outcome";
|
|
3
|
+
/** The negative reuse outcomes an agent can self-report (success is recorded as `value.reuse_hit`, not here). */
|
|
4
|
+
export type ReuseNegativeOutcome = 'failed' | 'mismatched' | 'stale' | 'unsafe';
|
|
5
|
+
/** Payload of a `value.reuse_outcome` root_event. */
|
|
6
|
+
export interface ReuseOutcomePayload {
|
|
7
|
+
assetId: string;
|
|
8
|
+
cycleId: string;
|
|
9
|
+
outcome: ReuseNegativeOutcome;
|
|
10
|
+
}
|
|
11
|
+
/** A minimal root_event view this module reads (matches the events module's ReportEvent shape; declared locally so
|
|
12
|
+
* ops stays decoupled from the events module, mirroring valueLedger's LedgerRootEvent). */
|
|
13
|
+
export interface ReuseOutcomeEvent {
|
|
14
|
+
type: string;
|
|
15
|
+
payload?: Record<string, unknown>;
|
|
16
|
+
}
|
|
17
|
+
export interface GeneReuseOutcome {
|
|
18
|
+
assetId: string;
|
|
19
|
+
/** Successful reuses (from `value.reuse_hit`). */
|
|
20
|
+
success: number;
|
|
21
|
+
/** Non-success reuses (from `value.reuse_outcome`). */
|
|
22
|
+
negative: number;
|
|
23
|
+
/** Per-outcome breakdown of the negatives. */
|
|
24
|
+
byOutcome: Record<ReuseNegativeOutcome, number>;
|
|
25
|
+
}
|
|
26
|
+
export interface ReuseOutcomeSummary {
|
|
27
|
+
/** Total reuse signals considered (success + negative). */
|
|
28
|
+
total: number;
|
|
29
|
+
/** Per-gene rollup, sorted by negative desc then assetId. */
|
|
30
|
+
perGene: GeneReuseOutcome[];
|
|
31
|
+
/** Genes reused but NEVER successfully (negative>0 AND success===0) — the strongest cross-runtime prune signal.
|
|
32
|
+
* Mirrors summarizeRecall.pruneCandidates, but sourced from reuse outcomes across every runtime. */
|
|
33
|
+
pruneCandidates: string[];
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Roll reuse signals up per gene from root_events: SUCCESS from `value.reuse_hit`, the negative verdicts from
|
|
37
|
+
* `value.reuse_outcome`. Pure; deterministic ordering. Unknown/garbled payloads are skipped, never counted.
|
|
38
|
+
*/
|
|
39
|
+
export declare function summarizeReuseOutcomes(events: readonly ReuseOutcomeEvent[]): ReuseOutcomeSummary;
|
|
40
|
+
/** Raw reuse tallies for one gene-id, kept as COUNTS (not a pre-collapsed sentiment) so a consumer that knows a
|
|
41
|
+
* gene's multiple ids can COMBINE them before computing sentiment (#268 Bugbot: events for one gene can split
|
|
42
|
+
* across its logical id and its content asset_id — averaging two sentiments would skew; summing counts is right). */
|
|
43
|
+
export interface ReuseCounts {
|
|
44
|
+
success: number;
|
|
45
|
+
negative: number;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Map a reuse-outcome summary to per-id reuse COUNTS (#268 phase 1) — the input to a SOFT selection re-order.
|
|
49
|
+
* Keyed by the id the events recorded (which may be a logical geneId OR a content asset_id), so the store-aware
|
|
50
|
+
* consumer (candidateAssembly) can fold all of a gene's ids together via `reuseSentiment` before ranking. Only ids
|
|
51
|
+
* with at least one reuse signal are included. Pure; the cycle injects it via RunCycleOptions.reuseOutcomes.
|
|
52
|
+
*/
|
|
53
|
+
export declare function reuseCountsFromSummary(summary: ReuseOutcomeSummary): Map<string, ReuseCounts>;
|
|
54
|
+
/** Net reuse sentiment in [-1, 1] from combined counts: (success − negative) / (success + negative). all-success
|
|
55
|
+
* → +1, all-negative → −1, balanced → 0. Zero total → 0. The WEIGHT/clamping that bounds its selection influence
|
|
56
|
+
* lives in geneSelection (it can never override the hard trust/review/ban gates, which run before scoring). */
|
|
57
|
+
export declare function reuseSentiment(counts: ReuseCounts): number;
|
|
58
|
+
/** Weight of an OBSERVED recall verdict (#274 slice 3) relative to a reported reuse outcome. < 1 because a
|
|
59
|
+
* transcript-observed `used`/`unused` is weaker evidence than an agent (or daemon) explicitly reporting that a
|
|
60
|
+
* reuse WORKED or FAILED — being applied/ignored is softer than working/breaking. */
|
|
61
|
+
export declare const RECALL_WEIGHT = 0.5;
|
|
62
|
+
/**
|
|
63
|
+
* Per-gene reuse COUNTS contributed by OBSERVED `value.recall` events (#274 slice 3): `used` → success, `unused` →
|
|
64
|
+
* negative, each weighted RECALL_WEIGHT (weaker than a reported reuse outcome); `unknown` is ignored. Keyed by the
|
|
65
|
+
* recall payload's geneId. Pure. Merged into the reuse-outcome counts upstream so recall SOFTLY re-orders selection
|
|
66
|
+
* — it never reaches the quarantine path (summarizeReuseOutcomes reads only the reuse-outcome events, not recall).
|
|
67
|
+
*/
|
|
68
|
+
export declare function recallCountsFromEvents(events: readonly ReuseOutcomeEvent[]): Map<string, ReuseCounts>;
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// Reuse-outcome accounting (#268 slice C) — the cross-runtime keep/prune SIGNAL, derived purely from root_events.
|
|
2
|
+
//
|
|
3
|
+
// Slice A/B made `evolver_asset_reuse_result` work in every mode and emit a `value.reuse_hit` on SUCCESS (the $
|
|
4
|
+
// rail). This module records the OTHER outcomes too and rolls the whole distribution up per gene, so the question
|
|
5
|
+
// "is this gene worth keeping?" can be answered from EVERY runtime's reuse (MCP-native agents included), not only
|
|
6
|
+
// from the hook/daemon transcript-recall path. It is the foreign-agent analogue of summarizeRecall.
|
|
7
|
+
//
|
|
8
|
+
// PURE + read-only: a function over already-emitted events. It does NOT actuate (it never mutates gene health,
|
|
9
|
+
// selection weight, or the store) — wiring this signal into selection/health is a separate, explicitly-gated step
|
|
10
|
+
// (it touches the keep/prune actuator). This module only makes the signal visible and accountable.
|
|
11
|
+
//
|
|
12
|
+
// Two event sources (decoupled by design): a SUCCESS reuse credits the $ ledger via `value.reuse_hit` (slice A/B),
|
|
13
|
+
// so success is counted from THAT; the non-success verdicts (failed/mismatched/stale/unsafe) are recorded as
|
|
14
|
+
// `value.reuse_outcome`. Reading both yields the full per-gene success-vs-negative picture without a redundant
|
|
15
|
+
// second event on the success path.
|
|
16
|
+
import { VALUE_REUSE_HIT_EVENT } from './valueLedger.js';
|
|
17
|
+
import { VALUE_RECALL_EVENT } from './recall.js';
|
|
18
|
+
/** root_events type appended for a NON-success MCP reuse-result — the negative half of the keep/prune signal. */
|
|
19
|
+
export const VALUE_REUSE_OUTCOME_EVENT = 'value.reuse_outcome';
|
|
20
|
+
const NEG = ['failed', 'mismatched', 'stale', 'unsafe'];
|
|
21
|
+
function assetIdOf(e) {
|
|
22
|
+
const id = e.payload?.['assetId'];
|
|
23
|
+
return typeof id === 'string' && id.length > 0 ? id : null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Roll reuse signals up per gene from root_events: SUCCESS from `value.reuse_hit`, the negative verdicts from
|
|
27
|
+
* `value.reuse_outcome`. Pure; deterministic ordering. Unknown/garbled payloads are skipped, never counted.
|
|
28
|
+
*/
|
|
29
|
+
export function summarizeReuseOutcomes(events) {
|
|
30
|
+
const agg = new Map();
|
|
31
|
+
const get = (assetId) => {
|
|
32
|
+
let g = agg.get(assetId);
|
|
33
|
+
if (!g) {
|
|
34
|
+
g = { assetId, success: 0, negative: 0, byOutcome: { failed: 0, mismatched: 0, stale: 0, unsafe: 0 } };
|
|
35
|
+
agg.set(assetId, g);
|
|
36
|
+
}
|
|
37
|
+
return g;
|
|
38
|
+
};
|
|
39
|
+
let total = 0;
|
|
40
|
+
for (const e of events) {
|
|
41
|
+
if (e.type === VALUE_REUSE_HIT_EVENT) {
|
|
42
|
+
const assetId = assetIdOf(e);
|
|
43
|
+
if (!assetId)
|
|
44
|
+
continue;
|
|
45
|
+
get(assetId).success += 1;
|
|
46
|
+
total += 1;
|
|
47
|
+
}
|
|
48
|
+
else if (e.type === VALUE_REUSE_OUTCOME_EVENT) {
|
|
49
|
+
const assetId = assetIdOf(e);
|
|
50
|
+
const outcome = e.payload?.['outcome'];
|
|
51
|
+
if (!assetId || typeof outcome !== 'string' || !NEG.includes(outcome))
|
|
52
|
+
continue;
|
|
53
|
+
const g = get(assetId);
|
|
54
|
+
g.negative += 1;
|
|
55
|
+
g.byOutcome[outcome] += 1;
|
|
56
|
+
total += 1;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
const perGene = [...agg.values()].sort((a, b) => b.negative - a.negative || (a.assetId < b.assetId ? -1 : a.assetId > b.assetId ? 1 : 0));
|
|
60
|
+
const pruneCandidates = perGene.filter((g) => g.negative > 0 && g.success === 0).map((g) => g.assetId);
|
|
61
|
+
return { total, perGene, pruneCandidates };
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Map a reuse-outcome summary to per-id reuse COUNTS (#268 phase 1) — the input to a SOFT selection re-order.
|
|
65
|
+
* Keyed by the id the events recorded (which may be a logical geneId OR a content asset_id), so the store-aware
|
|
66
|
+
* consumer (candidateAssembly) can fold all of a gene's ids together via `reuseSentiment` before ranking. Only ids
|
|
67
|
+
* with at least one reuse signal are included. Pure; the cycle injects it via RunCycleOptions.reuseOutcomes.
|
|
68
|
+
*/
|
|
69
|
+
export function reuseCountsFromSummary(summary) {
|
|
70
|
+
const out = new Map();
|
|
71
|
+
for (const g of summary.perGene) {
|
|
72
|
+
if (g.success + g.negative <= 0)
|
|
73
|
+
continue;
|
|
74
|
+
out.set(g.assetId, { success: g.success, negative: g.negative });
|
|
75
|
+
}
|
|
76
|
+
return out;
|
|
77
|
+
}
|
|
78
|
+
/** Net reuse sentiment in [-1, 1] from combined counts: (success − negative) / (success + negative). all-success
|
|
79
|
+
* → +1, all-negative → −1, balanced → 0. Zero total → 0. The WEIGHT/clamping that bounds its selection influence
|
|
80
|
+
* lives in geneSelection (it can never override the hard trust/review/ban gates, which run before scoring). */
|
|
81
|
+
export function reuseSentiment(counts) {
|
|
82
|
+
const denom = counts.success + counts.negative;
|
|
83
|
+
return denom > 0 ? (counts.success - counts.negative) / denom : 0;
|
|
84
|
+
}
|
|
85
|
+
/** Weight of an OBSERVED recall verdict (#274 slice 3) relative to a reported reuse outcome. < 1 because a
|
|
86
|
+
* transcript-observed `used`/`unused` is weaker evidence than an agent (or daemon) explicitly reporting that a
|
|
87
|
+
* reuse WORKED or FAILED — being applied/ignored is softer than working/breaking. */
|
|
88
|
+
export const RECALL_WEIGHT = 0.5;
|
|
89
|
+
/**
|
|
90
|
+
* Per-gene reuse COUNTS contributed by OBSERVED `value.recall` events (#274 slice 3): `used` → success, `unused` →
|
|
91
|
+
* negative, each weighted RECALL_WEIGHT (weaker than a reported reuse outcome); `unknown` is ignored. Keyed by the
|
|
92
|
+
* recall payload's geneId. Pure. Merged into the reuse-outcome counts upstream so recall SOFTLY re-orders selection
|
|
93
|
+
* — it never reaches the quarantine path (summarizeReuseOutcomes reads only the reuse-outcome events, not recall).
|
|
94
|
+
*/
|
|
95
|
+
export function recallCountsFromEvents(events) {
|
|
96
|
+
// Collapse to ONE verdict per (session, gene) first: within a session `used` is terminal and WINS over an earlier
|
|
97
|
+
// provisional `unused`, so a within-session upgrade is not double-counted (it would otherwise cancel to neutral —
|
|
98
|
+
// #274 Bugbot). Then accumulate ACROSS sessions, so a gene reused (un)successfully in many sessions adds up.
|
|
99
|
+
const perSession = new Map(); // key: `${sessionId}|${geneId}` (per-session collapse)
|
|
100
|
+
const geneOf = new Map();
|
|
101
|
+
for (const e of events) {
|
|
102
|
+
if (e.type !== VALUE_RECALL_EVENT)
|
|
103
|
+
continue;
|
|
104
|
+
const id = e.payload?.['geneId'];
|
|
105
|
+
const recalled = e.payload?.['recalled'];
|
|
106
|
+
if (typeof id !== 'string' || (recalled !== 'used' && recalled !== 'unused'))
|
|
107
|
+
continue;
|
|
108
|
+
const sid = typeof e.payload?.['sessionId'] === 'string' ? e.payload['sessionId'] : '';
|
|
109
|
+
const key = `${sid}|${id}`;
|
|
110
|
+
if (perSession.get(key) === 'used')
|
|
111
|
+
continue; // terminal for this session
|
|
112
|
+
perSession.set(key, recalled === 'used' ? 'used' : 'unused');
|
|
113
|
+
geneOf.set(key, id);
|
|
114
|
+
}
|
|
115
|
+
const out = new Map();
|
|
116
|
+
for (const [key, verdict] of perSession) {
|
|
117
|
+
const id = geneOf.get(key);
|
|
118
|
+
const c = out.get(id) ?? { success: 0, negative: 0 };
|
|
119
|
+
if (verdict === 'used')
|
|
120
|
+
c.success += RECALL_WEIGHT;
|
|
121
|
+
else
|
|
122
|
+
c.negative += RECALL_WEIGHT;
|
|
123
|
+
out.set(id, c);
|
|
124
|
+
}
|
|
125
|
+
return out;
|
|
126
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
export declare const SAVINGS_SPEC_VERSION = "0.3.0";
|
|
2
|
+
export declare const ENTROPY_EVENT_TOKENS_EST: Readonly<{
|
|
3
|
+
dedup_quarantine: 12000;
|
|
4
|
+
dedup_warning: 3600;
|
|
5
|
+
hub_search_hit: 8000;
|
|
6
|
+
hub_search_miss: 0;
|
|
7
|
+
fetch_reuse: 4000;
|
|
8
|
+
}>;
|
|
9
|
+
export declare const FETCH_USAGE_TOKENS_EST: Readonly<{
|
|
10
|
+
Gene: 1500;
|
|
11
|
+
Capsule: 3500;
|
|
12
|
+
EvolutionEvent: 0;
|
|
13
|
+
}>;
|
|
14
|
+
export declare const REUSE_ESTIMATOR: Readonly<{
|
|
15
|
+
derive_base_tokens: 120000;
|
|
16
|
+
tokens_per_changed_line: 800;
|
|
17
|
+
derive_cap_tokens: 600000;
|
|
18
|
+
typical_changed_lines: 75;
|
|
19
|
+
reference_saving_fraction: 0.4;
|
|
20
|
+
}>;
|
|
21
|
+
export declare const USD_PER_M_TOKENS_BLENDED = 9;
|
|
22
|
+
export declare const CACHE_READ_SAVED_USD_PER_M_TOKENS: Readonly<{
|
|
23
|
+
anthropic: 2.7;
|
|
24
|
+
}>;
|
|
25
|
+
export declare const SAVINGS_BASIS_PRECEDENCE: readonly string[];
|
|
26
|
+
export declare function measuredSavings(rawTokens: number, optimizedTokens: number): {
|
|
27
|
+
tokens_saved: number;
|
|
28
|
+
savings_pct: number;
|
|
29
|
+
};
|
|
30
|
+
export declare function rolloutFoldPct(nAvgRollouts: number): number;
|
|
31
|
+
export interface EntropyEvent {
|
|
32
|
+
type: string;
|
|
33
|
+
count?: number;
|
|
34
|
+
tokensEstSaved?: number | null;
|
|
35
|
+
}
|
|
36
|
+
export declare function entropyTotal(events: readonly EntropyEvent[]): {
|
|
37
|
+
total_tokens_saved: number;
|
|
38
|
+
total_events: number;
|
|
39
|
+
};
|
|
40
|
+
export declare function fetchUsageEstimate(byType: Record<string, number>): number;
|
|
41
|
+
export declare function reuseEstimate(blastRadiusLines: number | null | undefined, mode?: string): {
|
|
42
|
+
tokens_saved: number;
|
|
43
|
+
basis: 'estimated_blast_radius' | 'estimated_default';
|
|
44
|
+
};
|
|
45
|
+
export declare function hitRatePct(hits: number, misses: number): number;
|
|
46
|
+
export declare function usdSaved(tokens: number): number;
|
|
47
|
+
export declare function cacheSavedUsd(provider: string, cacheReadTokens: number): number;
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// savings-core — token-savings formula, TS implementation for evolver-v2.
|
|
2
|
+
//
|
|
3
|
+
// Single source of truth: EvoMap/savings-core (spec v0.3.0). The constants and formula semantics
|
|
4
|
+
// here MUST match the repo-root vendored copies (conformance/savings-core/constants.json) and
|
|
5
|
+
// reproduce conformance/savings-core/golden-vectors.json bit-for-bit — savingsCore.test.ts is the
|
|
6
|
+
// gate (it reads the vendored files at runtime, so this module stays free of cross-rootDir JSON
|
|
7
|
+
// imports), and savings-core's daily drift-check locks the vendored copies to upstream. To change a
|
|
8
|
+
// coefficient: change savings-core first, regenerate vectors there, bump spec_version, then
|
|
9
|
+
// re-vendor here AND in evomap-hub / evomap-private / evox-online-deck / evomap-desktop / evox /
|
|
10
|
+
// evolver-private-dev / evolver-v2-enterprise-dev.
|
|
11
|
+
//
|
|
12
|
+
// Core purity contract holds: no I/O, no Date.now/Math.random — pure functions over numbers.
|
|
13
|
+
export const SAVINGS_SPEC_VERSION = '0.3.0';
|
|
14
|
+
// E1 — per-event estimated tokens saved.
|
|
15
|
+
export const ENTROPY_EVENT_TOKENS_EST = Object.freeze({
|
|
16
|
+
dedup_quarantine: 12_000,
|
|
17
|
+
dedup_warning: 3_600,
|
|
18
|
+
hub_search_hit: 8_000,
|
|
19
|
+
hub_search_miss: 0,
|
|
20
|
+
fetch_reuse: 4_000,
|
|
21
|
+
});
|
|
22
|
+
// E2 — node-level fetch-usage estimates, per asset type.
|
|
23
|
+
export const FETCH_USAGE_TOKENS_EST = Object.freeze({
|
|
24
|
+
Gene: 1_500,
|
|
25
|
+
Capsule: 3_500,
|
|
26
|
+
EvolutionEvent: 0,
|
|
27
|
+
});
|
|
28
|
+
// E3 — blast-radius reuse estimator parameters (anchor: 120000 + 75×800 = the legacy 180k blanket).
|
|
29
|
+
export const REUSE_ESTIMATOR = Object.freeze({
|
|
30
|
+
derive_base_tokens: 120_000,
|
|
31
|
+
tokens_per_changed_line: 800,
|
|
32
|
+
derive_cap_tokens: 600_000,
|
|
33
|
+
typical_changed_lines: 75,
|
|
34
|
+
reference_saving_fraction: 0.4,
|
|
35
|
+
});
|
|
36
|
+
// U1 — blended $/1M tokens across input+output.
|
|
37
|
+
export const USD_PER_M_TOKENS_BLENDED = 9.0;
|
|
38
|
+
// C1 — $ saved per 1M prompt-cache-read tokens, per provider.
|
|
39
|
+
export const CACHE_READ_SAVED_USD_PER_M_TOKENS = Object.freeze({
|
|
40
|
+
anthropic: 2.7,
|
|
41
|
+
});
|
|
42
|
+
export const SAVINGS_BASIS_PRECEDENCE = Object.freeze(['measured', 'cost_index', 'estimator']);
|
|
43
|
+
const round2 = (x) => Math.round((x + Number.EPSILON) * 100) / 100;
|
|
44
|
+
const round4 = (x) => Math.round((x + Number.EPSILON) * 10000) / 10000;
|
|
45
|
+
const coerce = (x) => Math.max(0, Math.round(Number(x) || 0));
|
|
46
|
+
// R1 — measured savings from a raw/optimized token pair (preferred basis).
|
|
47
|
+
export function measuredSavings(rawTokens, optimizedTokens) {
|
|
48
|
+
const raw = coerce(rawTokens);
|
|
49
|
+
const optimized = coerce(optimizedTokens);
|
|
50
|
+
const saved = Math.max(0, raw - optimized);
|
|
51
|
+
const rate = raw > 0 ? Math.max(0, 1 - optimized / raw) : 0;
|
|
52
|
+
return { tokens_saved: saved, savings_pct: round2(rate * 100) };
|
|
53
|
+
}
|
|
54
|
+
// R2 — rollout-folding rate.
|
|
55
|
+
export function rolloutFoldPct(nAvgRollouts) {
|
|
56
|
+
const n = Number(nAvgRollouts) || 0;
|
|
57
|
+
return n >= 1 ? round2((1 - 1 / n) * 100) : 0;
|
|
58
|
+
}
|
|
59
|
+
// E1 — aggregate estimated savings over entropy events (unknown types skipped).
|
|
60
|
+
export function entropyTotal(events) {
|
|
61
|
+
let tokens = 0;
|
|
62
|
+
let count = 0;
|
|
63
|
+
for (const e of events ?? []) {
|
|
64
|
+
let per;
|
|
65
|
+
if (e.tokensEstSaved !== undefined && e.tokensEstSaved !== null) {
|
|
66
|
+
per = coerce(e.tokensEstSaved);
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
const coeff = ENTROPY_EVENT_TOKENS_EST[e.type];
|
|
70
|
+
if (coeff === undefined)
|
|
71
|
+
continue;
|
|
72
|
+
per = coeff;
|
|
73
|
+
}
|
|
74
|
+
const n = coerce(e.count ?? 1);
|
|
75
|
+
tokens += per * n;
|
|
76
|
+
count += n;
|
|
77
|
+
}
|
|
78
|
+
return { total_tokens_saved: tokens, total_events: count };
|
|
79
|
+
}
|
|
80
|
+
// E2 — node fetch-usage estimate; unknown asset types contribute 0.
|
|
81
|
+
export function fetchUsageEstimate(byType) {
|
|
82
|
+
let sum = 0;
|
|
83
|
+
for (const [type, n] of Object.entries(byType ?? {})) {
|
|
84
|
+
sum += (FETCH_USAGE_TOKENS_EST[type] ?? 0) * coerce(n);
|
|
85
|
+
}
|
|
86
|
+
return sum;
|
|
87
|
+
}
|
|
88
|
+
// E3 — blast-radius reuse estimator (the `estimator` basis fallback).
|
|
89
|
+
export function reuseEstimate(blastRadiusLines, mode) {
|
|
90
|
+
const n = Number(blastRadiusLines);
|
|
91
|
+
const valid = Number.isFinite(n) && n > 0;
|
|
92
|
+
const basis = valid ? 'estimated_blast_radius' : 'estimated_default';
|
|
93
|
+
const lines = valid ? n : REUSE_ESTIMATOR.typical_changed_lines;
|
|
94
|
+
const full = Math.min(REUSE_ESTIMATOR.derive_base_tokens + lines * REUSE_ESTIMATOR.tokens_per_changed_line, REUSE_ESTIMATOR.derive_cap_tokens);
|
|
95
|
+
const tokens = Math.round(mode === 'reference' ? full * REUSE_ESTIMATOR.reference_saving_fraction : full);
|
|
96
|
+
return { tokens_saved: tokens, basis };
|
|
97
|
+
}
|
|
98
|
+
// H1 — search hit rate as a 2-dp percentage.
|
|
99
|
+
export function hitRatePct(hits, misses) {
|
|
100
|
+
const h = coerce(hits);
|
|
101
|
+
const m = coerce(misses);
|
|
102
|
+
return h + m > 0 ? round2((h / (h + m)) * 100) : 0;
|
|
103
|
+
}
|
|
104
|
+
// U1 — USD value of saved tokens at the blended rate.
|
|
105
|
+
export function usdSaved(tokens) {
|
|
106
|
+
return round2((coerce(tokens) / 1_000_000) * USD_PER_M_TOKENS_BLENDED);
|
|
107
|
+
}
|
|
108
|
+
// C1 — prompt-cache savings in USD (4 dp); unknown providers contribute 0.
|
|
109
|
+
export function cacheSavedUsd(provider, cacheReadTokens) {
|
|
110
|
+
const rate = CACHE_READ_SAVED_USD_PER_M_TOKENS[provider] ?? 0;
|
|
111
|
+
return round4((coerce(cacheReadTokens) / 1_000_000) * rate);
|
|
112
|
+
}
|