@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,406 @@
|
|
|
1
|
+
import { REDACTED, redactString } from './sanitize.js';
|
|
2
|
+
export const QUESTION_INTERVAL_MS = 30 * 60 * 1000;
|
|
3
|
+
export const URGENT_QUESTION_INTERVAL_MS = 5 * 60 * 1000;
|
|
4
|
+
export const EXPLORATION_QUESTION_INTERVAL_MS = 6 * 60 * 60 * 1000;
|
|
5
|
+
export const MAX_QUESTIONS_PER_CYCLE = 3;
|
|
6
|
+
export const MAX_URGENT_QUESTIONS = 2;
|
|
7
|
+
export const URGENT_QUESTION_RUNTIME_WIRING_STATUS = {
|
|
8
|
+
status: 'deferred',
|
|
9
|
+
reason: 'urgent_question_runtime_wiring_not_connected',
|
|
10
|
+
};
|
|
11
|
+
const INFRA_ERROR_RE = /\b(401|403|429|500|502|503|504|529)\b|invalid[\s_-]?api[\s_-]?key|authentication[\s_-]?error|unauthorized|permission[\s_-]?denied|rate[\s_-]?limit|too[\s_-]?many[\s_-]?requests|overloaded[\s_-]?error|ECONNRESET|ETIMEDOUT|ENOTFOUND|EAI_AGAIN|EPIPE|fetch[\s_-]?failed|network[\s_-]?error|connection[\s_-]?refused|context[\s_-]?length|token[\s_-]?limit|(?:context|input)[\s_-]?window[\s_-]?exceeded|maximum[\s_-]?context[\s_-]?length/i;
|
|
12
|
+
const PROBLEM_SIGNALS = [
|
|
13
|
+
'recurring_error', 'high_failure_ratio', 'capability_gap', 'unsupported_input_type',
|
|
14
|
+
'evolution_saturation', 'force_steady_state', 'consecutive_failure_streak',
|
|
15
|
+
'user_feature_request', 'perf_bottleneck', 'hub_search_miss_with_problem',
|
|
16
|
+
'repair_loop_detected', 'force_innovation_after_repair_loop',
|
|
17
|
+
'plateau_pivot_required', 'plateau_pivot_suggested',
|
|
18
|
+
];
|
|
19
|
+
const EXPLORE_STOPWORDS = new Set([
|
|
20
|
+
'the', 'and', 'for', 'that', 'this', 'with', 'from', 'have', 'what', 'your',
|
|
21
|
+
'agent', 'about', 'into', 'then', 'than', 'they', 'them', 'their', 'there',
|
|
22
|
+
'here', 'will', 'would', 'could', 'should', 'been', 'were', 'using', 'used',
|
|
23
|
+
'cycle', 'evolution', 'error', 'errors', 'failed', 'failure', 'null', 'undefined',
|
|
24
|
+
'true', 'false', 'console', 'return', 'function', 'const', 'value', 'result',
|
|
25
|
+
]);
|
|
26
|
+
const SENSITIVE_TOPIC_RE = /secret|token|api[_-]?key|password|passwd|credential|bearer|authorization|cookie|session[_-]?id|private[_-]?key|oauth|refresh[_-]?token/i;
|
|
27
|
+
const LONG_OPAQUE_RE = /^[a-z0-9_-]{32,}$/i;
|
|
28
|
+
function isInfraError(text) {
|
|
29
|
+
if (!text)
|
|
30
|
+
return false;
|
|
31
|
+
return INFRA_ERROR_RE.test(text);
|
|
32
|
+
}
|
|
33
|
+
function normalizeState(state) {
|
|
34
|
+
return {
|
|
35
|
+
lastAskedAt: state?.lastAskedAt ?? null,
|
|
36
|
+
lastUrgentAt: state?.lastUrgentAt ?? null,
|
|
37
|
+
lastExploreAt: state?.lastExploreAt ?? null,
|
|
38
|
+
recentQuestions: [...(state?.recentQuestions ?? [])],
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
function questionState(state) {
|
|
42
|
+
return {
|
|
43
|
+
lastAskedAt: state.lastAskedAt,
|
|
44
|
+
lastUrgentAt: state.lastUrgentAt,
|
|
45
|
+
lastExploreAt: state.lastExploreAt,
|
|
46
|
+
recentQuestions: [...state.recentQuestions],
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function parseIsoMs(value) {
|
|
50
|
+
if (!value)
|
|
51
|
+
return null;
|
|
52
|
+
const n = Date.parse(value);
|
|
53
|
+
return Number.isFinite(n) ? n : null;
|
|
54
|
+
}
|
|
55
|
+
function tooSoon(lastAt, intervalMs, now) {
|
|
56
|
+
const ts = parseIsoMs(lastAt);
|
|
57
|
+
return ts !== null && now - ts < intervalMs;
|
|
58
|
+
}
|
|
59
|
+
function defaultEnv() {
|
|
60
|
+
return typeof process === 'undefined' ? {} : process.env;
|
|
61
|
+
}
|
|
62
|
+
function isDuplicate(question, recentQuestions) {
|
|
63
|
+
const qLower = question.toLowerCase();
|
|
64
|
+
const qWords = new Set(qLower.split(/\s+/).filter((w) => w.length > 2));
|
|
65
|
+
for (const prevQuestion of recentQuestions) {
|
|
66
|
+
const prev = String(prevQuestion || '').toLowerCase();
|
|
67
|
+
if (prev === qLower)
|
|
68
|
+
return true;
|
|
69
|
+
const pWords = new Set(prev.split(/\s+/).filter((w) => w.length > 2));
|
|
70
|
+
if (qWords.size === 0 || pWords.size === 0)
|
|
71
|
+
continue;
|
|
72
|
+
let overlap = 0;
|
|
73
|
+
for (const w of qWords)
|
|
74
|
+
if (pWords.has(w))
|
|
75
|
+
overlap += 1;
|
|
76
|
+
if (overlap / Math.max(qWords.size, pWords.size) > 0.7)
|
|
77
|
+
return true;
|
|
78
|
+
}
|
|
79
|
+
return false;
|
|
80
|
+
}
|
|
81
|
+
function extractErrorContext(transcript, maxLen = 150) {
|
|
82
|
+
const line = transcript.split('\n').find((l) => /error|exception|failed|cannot|not supported|unsupported|not implemented/i.test(l));
|
|
83
|
+
return line ? line.replace(/\s+/g, ' ').trim().slice(0, maxLen) : '';
|
|
84
|
+
}
|
|
85
|
+
function hasPublicContext(text) {
|
|
86
|
+
return /[a-z0-9]/i.test(text.replaceAll(REDACTED, ''));
|
|
87
|
+
}
|
|
88
|
+
function safeQuestionContext(text, maxLen) {
|
|
89
|
+
const normalized = String(text ?? '').replace(/\s+/g, ' ').trim();
|
|
90
|
+
if (!normalized)
|
|
91
|
+
return '';
|
|
92
|
+
return redactString(normalized).replace(/\s+/g, ' ').trim().slice(0, maxLen);
|
|
93
|
+
}
|
|
94
|
+
function safeProblemContext(text, maxLen) {
|
|
95
|
+
const context = safeQuestionContext(text, maxLen);
|
|
96
|
+
if (!context || !hasPublicContext(context) || isInfraError(context))
|
|
97
|
+
return '';
|
|
98
|
+
return context;
|
|
99
|
+
}
|
|
100
|
+
function safeTopicToken(token) {
|
|
101
|
+
if (EXPLORE_STOPWORDS.has(token))
|
|
102
|
+
return false;
|
|
103
|
+
if (SENSITIVE_TOPIC_RE.test(token))
|
|
104
|
+
return false;
|
|
105
|
+
if (LONG_OPAQUE_RE.test(token))
|
|
106
|
+
return false;
|
|
107
|
+
return true;
|
|
108
|
+
}
|
|
109
|
+
export function extractTopicKeywords(transcript, memory, max = 5) {
|
|
110
|
+
const text = `${String(transcript ?? '')} ${String(memory ?? '')}`.toLowerCase();
|
|
111
|
+
const words = text.match(/[a-z][a-z0-9_-]{4,}/g) ?? [];
|
|
112
|
+
const freq = new Map();
|
|
113
|
+
for (const word of words) {
|
|
114
|
+
if (!safeTopicToken(word))
|
|
115
|
+
continue;
|
|
116
|
+
freq.set(word, (freq.get(word) ?? 0) + 1);
|
|
117
|
+
}
|
|
118
|
+
return [...freq.entries()]
|
|
119
|
+
.filter(([, count]) => count >= 2)
|
|
120
|
+
.sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0]))
|
|
121
|
+
.map(([word]) => word)
|
|
122
|
+
.slice(0, max);
|
|
123
|
+
}
|
|
124
|
+
function safeGeneId(id) {
|
|
125
|
+
if (!/^[a-z0-9_.:-]{3,80}$/i.test(id))
|
|
126
|
+
return false;
|
|
127
|
+
return !SENSITIVE_TOPIC_RE.test(id);
|
|
128
|
+
}
|
|
129
|
+
function extractRecentGeneIds(recentEvents, count = 5) {
|
|
130
|
+
const ids = [];
|
|
131
|
+
for (const event of (recentEvents ?? []).slice(-count)) {
|
|
132
|
+
const genes = event.genes_used ?? event.genesUsed ?? [];
|
|
133
|
+
const first = genes[0];
|
|
134
|
+
if (typeof first === 'string' && safeGeneId(first))
|
|
135
|
+
ids.push(first);
|
|
136
|
+
}
|
|
137
|
+
return [...new Set(ids)].slice(0, count);
|
|
138
|
+
}
|
|
139
|
+
function safeSignal(signal) {
|
|
140
|
+
if (!/^[a-z][a-z0-9_]{3,40}$/i.test(signal))
|
|
141
|
+
return false;
|
|
142
|
+
if (SENSITIVE_TOPIC_RE.test(signal))
|
|
143
|
+
return false;
|
|
144
|
+
if (signal.startsWith('errsig') || signal.startsWith('ban_gene') || signal.startsWith('recurring_'))
|
|
145
|
+
return false;
|
|
146
|
+
return !PROBLEM_SIGNALS.some((p) => signal === p || signal.startsWith(p));
|
|
147
|
+
}
|
|
148
|
+
function buildExplorationCandidate(signals, recentEvents, transcript, memory) {
|
|
149
|
+
const topicSignals = signals.map(String).filter(safeSignal).slice(0, 4);
|
|
150
|
+
const keywords = extractTopicKeywords(transcript, memory, 5);
|
|
151
|
+
const genes = extractRecentGeneIds(recentEvents, 5);
|
|
152
|
+
if (keywords.length === 0 && genes.length === 0)
|
|
153
|
+
return null;
|
|
154
|
+
const focus = keywords.length > 0 ? keywords.slice(0, 5).join(', ') : genes.join(', ');
|
|
155
|
+
return {
|
|
156
|
+
question: `A productively-running agent working on ${focus} wants to extend its capabilities. What reusable patterns, automation genes, or complementary tools in this area would be most valuable to build next, and what adjacent high-value problems is the ecosystem not yet solving well here?`,
|
|
157
|
+
amount: 0,
|
|
158
|
+
signals: ['capability_frontier', 'exploration', 'proactive_curiosity', ...keywords.slice(0, 2), ...topicSignals.slice(0, 2)],
|
|
159
|
+
priority: 0,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
function buildStandardCandidates(signals, recentEvents, transcript) {
|
|
163
|
+
const candidates = [];
|
|
164
|
+
const signalSet = new Set(signals);
|
|
165
|
+
if (signalSet.has('recurring_error') || signalSet.has('high_failure_ratio')) {
|
|
166
|
+
const errSig = signals.find((s) => String(s).startsWith('recurring_errsig'));
|
|
167
|
+
if (errSig) {
|
|
168
|
+
const errDetail = safeProblemContext(String(errSig).replace(/^recurring_errsig\(\d+x\):/, ''), 120);
|
|
169
|
+
if (errDetail) {
|
|
170
|
+
candidates.push({
|
|
171
|
+
question: `Recurring error in evolution cycle that auto-repair cannot resolve: ${errDetail} -- What approaches or patches have worked for similar issues?`,
|
|
172
|
+
amount: 0,
|
|
173
|
+
signals: ['recurring_error', 'auto_repair_failed'],
|
|
174
|
+
priority: 3,
|
|
175
|
+
});
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
if (signalSet.has('capability_gap') || signalSet.has('unsupported_input_type')) {
|
|
180
|
+
const gapContext = safeProblemContext(extractErrorContext(transcript, 150), 150);
|
|
181
|
+
if (gapContext) {
|
|
182
|
+
candidates.push({
|
|
183
|
+
question: `Capability gap detected in agent environment: ${gapContext} -- How can this be addressed or what alternative approaches exist?`,
|
|
184
|
+
amount: 0,
|
|
185
|
+
signals: ['capability_gap'],
|
|
186
|
+
priority: 2,
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
if (signalSet.has('evolution_saturation') || signalSet.has('force_steady_state')) {
|
|
191
|
+
const uniqueGenes = extractRecentGeneIds(recentEvents, 5);
|
|
192
|
+
candidates.push({
|
|
193
|
+
question: `Agent evolution has reached saturation after exhausting genes: [${uniqueGenes.join(', ')}]. What new evolution directions, automation patterns, or capability genes would be most valuable?`,
|
|
194
|
+
amount: 0,
|
|
195
|
+
signals: ['evolution_saturation', 'innovation_needed'],
|
|
196
|
+
priority: 1,
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
const failStreak = signals.find((s) => String(s).startsWith('consecutive_failure_streak_'));
|
|
200
|
+
if (failStreak) {
|
|
201
|
+
const streakCount = Number.parseInt(String(failStreak).replace('consecutive_failure_streak_', ''), 10) || 0;
|
|
202
|
+
if (streakCount >= 3) {
|
|
203
|
+
const failGene = signals.find((s) => String(s).startsWith('ban_gene:'));
|
|
204
|
+
const failGeneId = failGene ? safeQuestionContext(String(failGene).replace('ban_gene:', ''), 80) || 'unknown' : 'unknown';
|
|
205
|
+
candidates.push({
|
|
206
|
+
question: `Agent has failed ${streakCount} consecutive evolution cycles (last gene: ${failGeneId}). The current approach is exhausted. What alternative strategies or environmental fixes should be tried?`,
|
|
207
|
+
amount: 0,
|
|
208
|
+
signals: ['failure_streak', 'external_help_needed'],
|
|
209
|
+
priority: 3,
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (signalSet.has('user_feature_request') || signals.some((s) => String(s).startsWith('user_feature_request:'))) {
|
|
214
|
+
const featureLine = transcript.split('\n').find((l) => /\b(add|implement|create|build|i want|i need|please add)\b/i.test(l));
|
|
215
|
+
if (featureLine) {
|
|
216
|
+
const featureContext = safeQuestionContext(featureLine, 150);
|
|
217
|
+
candidates.push({
|
|
218
|
+
question: `User requested a feature that may benefit from community solutions: ${featureContext} -- Are there existing implementations or best practices for this?`,
|
|
219
|
+
amount: 0,
|
|
220
|
+
signals: ['user_feature_request', 'community_solution_sought'],
|
|
221
|
+
priority: 1,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
if (signalSet.has('perf_bottleneck')) {
|
|
226
|
+
const perfLine = transcript.split('\n').find((l) => /\b(slow|timeout|latency|bottleneck|high cpu|high memory)\b/i.test(l));
|
|
227
|
+
if (perfLine) {
|
|
228
|
+
const perfContext = safeQuestionContext(perfLine, 150);
|
|
229
|
+
candidates.push({
|
|
230
|
+
question: `Performance bottleneck detected: ${perfContext} -- What optimization strategies or architectural patterns address this?`,
|
|
231
|
+
amount: 0,
|
|
232
|
+
signals: ['perf_bottleneck', 'optimization_sought'],
|
|
233
|
+
priority: 2,
|
|
234
|
+
});
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
if (signalSet.has('hub_search_miss_with_problem')) {
|
|
238
|
+
const problemCtx = safeProblemContext(extractErrorContext(transcript, 120), 120);
|
|
239
|
+
const problemSignalList = signals
|
|
240
|
+
.filter((s) => s === 'log_error' || s === 'test_failure' || s === 'deployment_issue' || String(s).startsWith('errsig:'))
|
|
241
|
+
.map((s) => safeQuestionContext(String(s), 80))
|
|
242
|
+
.filter((s) => s && hasPublicContext(s) && !isInfraError(s))
|
|
243
|
+
.slice(0, 3);
|
|
244
|
+
if (problemCtx || problemSignalList.length > 0) {
|
|
245
|
+
candidates.push({
|
|
246
|
+
question: `No matching solution found in ecosystem for active problem (signals: ${problemSignalList.join(', ')}). Context: ${problemCtx || 'complex multi-signal issue'} -- What strategies, patterns, or tools address this class of problem?`,
|
|
247
|
+
amount: 0,
|
|
248
|
+
signals: ['hub_search_miss', 'ecosystem_gap', 'solution_sought'],
|
|
249
|
+
priority: 2,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (signalSet.has('repair_loop_detected') || signalSet.has('force_innovation_after_repair_loop')) {
|
|
254
|
+
const recentGenes = extractRecentGeneIds(recentEvents, 6);
|
|
255
|
+
candidates.push({
|
|
256
|
+
question: `Agent is stuck in a repair loop (repair->fail->repair cycle) with genes: [${recentGenes.join(', ')}]. The underlying issue persists despite multiple attempts. What fundamentally different approach could break this cycle?`,
|
|
257
|
+
amount: 0,
|
|
258
|
+
signals: ['repair_loop', 'architectural_help_needed'],
|
|
259
|
+
priority: 3,
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
if (signalSet.has('plateau_pivot_required') || signalSet.has('plateau_pivot_suggested')) {
|
|
263
|
+
const severity = signalSet.has('plateau_pivot_required') ? 'severe' : 'moderate';
|
|
264
|
+
candidates.push({
|
|
265
|
+
question: `Agent evolution has plateaued (${severity} -- no improvement in recent cycles). Current gene pool and mutation strategies are exhausted. What novel approaches, architectural patterns, or paradigm shifts could restart progress?`,
|
|
266
|
+
amount: 0,
|
|
267
|
+
signals: ['evolution_plateau', 'pivot_needed'],
|
|
268
|
+
priority: severity === 'severe' ? 3 : 2,
|
|
269
|
+
});
|
|
270
|
+
}
|
|
271
|
+
return candidates;
|
|
272
|
+
}
|
|
273
|
+
function publicQuestion(q) {
|
|
274
|
+
return { question: q.question, amount: q.amount, signals: [...(q.signals ?? [])] };
|
|
275
|
+
}
|
|
276
|
+
function completeStandardGeneration(candidates, state, now) {
|
|
277
|
+
if (candidates.length === 0)
|
|
278
|
+
return { questions: [], state: questionState(state), changed: false };
|
|
279
|
+
candidates.sort((a, b) => b.priority - a.priority);
|
|
280
|
+
const filtered = [];
|
|
281
|
+
for (const candidate of candidates) {
|
|
282
|
+
if (filtered.length >= MAX_QUESTIONS_PER_CYCLE)
|
|
283
|
+
break;
|
|
284
|
+
if (!isDuplicate(candidate.question, state.recentQuestions))
|
|
285
|
+
filtered.push(candidate);
|
|
286
|
+
}
|
|
287
|
+
if (filtered.length === 0)
|
|
288
|
+
return { questions: [], state: questionState(state), changed: false };
|
|
289
|
+
const recentQuestions = [...state.recentQuestions, ...filtered.map((q) => q.question)].slice(-30);
|
|
290
|
+
const explorationSent = filtered.some((q) => q.signals?.includes('exploration'));
|
|
291
|
+
const nextState = {
|
|
292
|
+
lastAskedAt: new Date(now).toISOString(),
|
|
293
|
+
lastUrgentAt: state.lastUrgentAt,
|
|
294
|
+
lastExploreAt: explorationSent ? new Date(now).toISOString() : state.lastExploreAt,
|
|
295
|
+
recentQuestions,
|
|
296
|
+
};
|
|
297
|
+
return { questions: filtered.map(publicQuestion), state: nextState, changed: true };
|
|
298
|
+
}
|
|
299
|
+
export function generateQuestions(input = {}) {
|
|
300
|
+
const now = input.now ?? Date.now();
|
|
301
|
+
const state = normalizeState(input.state);
|
|
302
|
+
if (tooSoon(state.lastAskedAt, QUESTION_INTERVAL_MS, now))
|
|
303
|
+
return { questions: [], state: questionState(state), changed: false };
|
|
304
|
+
const signals = (input.signals ?? []).map(String);
|
|
305
|
+
const recentEvents = input.recentEvents ?? [];
|
|
306
|
+
const transcript = String(input.sessionTranscript ?? '');
|
|
307
|
+
const memory = String(input.memorySnippet ?? '');
|
|
308
|
+
const candidates = buildStandardCandidates(signals, recentEvents, transcript);
|
|
309
|
+
const env = input.env ?? defaultEnv();
|
|
310
|
+
let explorationEligible = input.explorationEnabled ?? (env['EVOLVER_EXPLORATION_QUESTIONS'] !== '0');
|
|
311
|
+
if (explorationEligible && tooSoon(state.lastExploreAt, EXPLORATION_QUESTION_INTERVAL_MS, now))
|
|
312
|
+
explorationEligible = false;
|
|
313
|
+
if (explorationEligible) {
|
|
314
|
+
const exploration = buildExplorationCandidate(signals, recentEvents, transcript, memory);
|
|
315
|
+
if (exploration)
|
|
316
|
+
candidates.push(exploration);
|
|
317
|
+
}
|
|
318
|
+
return completeStandardGeneration(candidates, state, now);
|
|
319
|
+
}
|
|
320
|
+
function buildUrgentCandidates(input) {
|
|
321
|
+
const candidates = [];
|
|
322
|
+
if (input.validationFailed) {
|
|
323
|
+
const valErrors = safeProblemContext(String(input.validationErrors ?? ''), 200);
|
|
324
|
+
const geneId = input.geneId ? safeQuestionContext(input.geneId, 80) || 'unknown' : 'unknown';
|
|
325
|
+
if (valErrors) {
|
|
326
|
+
candidates.push({
|
|
327
|
+
question: `Evolution cycle produced a patch that failed validation (gene: ${geneId}). Errors: ${valErrors} -- What is the correct approach to fix this validation failure?`,
|
|
328
|
+
amount: 0,
|
|
329
|
+
signals: ['validation_failure', 'solidify_rejected'],
|
|
330
|
+
priority: 3,
|
|
331
|
+
});
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
if (input.lowConfidence && Number.isFinite(input.confidenceScore)) {
|
|
335
|
+
const score = Math.round(Number(input.confidenceScore) * 100) / 100;
|
|
336
|
+
candidates.push({
|
|
337
|
+
question: `Evolution cycle completed with low confidence (score: ${score}, intent: ${input.intent || 'unknown'}). The change is uncertain and may not be beneficial. What higher-confidence approaches exist for this type of problem?`,
|
|
338
|
+
amount: 0,
|
|
339
|
+
signals: ['low_confidence', 'uncertain_outcome'],
|
|
340
|
+
priority: 2,
|
|
341
|
+
});
|
|
342
|
+
}
|
|
343
|
+
if (input.llmReviewRejected) {
|
|
344
|
+
const reason = safeProblemContext(String(input.llmReviewReason ?? ''), 200);
|
|
345
|
+
if (reason) {
|
|
346
|
+
candidates.push({
|
|
347
|
+
question: `Proposed code change was rejected by LLM review: ${reason} -- What alternative implementation approach would pass quality review?`,
|
|
348
|
+
amount: 0,
|
|
349
|
+
signals: ['llm_review_rejected', 'quality_concern'],
|
|
350
|
+
priority: 3,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (input.zeroBlastRadius && input.hadSignals) {
|
|
355
|
+
const attemptedSignals = (input.signals ?? [])
|
|
356
|
+
.map((s) => safeQuestionContext(String(s), 80))
|
|
357
|
+
.filter((s) => s && hasPublicContext(s))
|
|
358
|
+
.slice(0, 5)
|
|
359
|
+
.join(', ');
|
|
360
|
+
candidates.push({
|
|
361
|
+
question: `Evolution cycle targeting signals [${attemptedSignals}] produced zero blast radius (no effective changes). The approach was insufficient. What concrete implementation steps would address these signals?`,
|
|
362
|
+
amount: 0,
|
|
363
|
+
signals: ['zero_blast_radius', 'ineffective_approach'],
|
|
364
|
+
priority: 2,
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
if (input.taskCompletionFailed) {
|
|
368
|
+
const taskTitle = safeProblemContext(String(input.taskTitle ?? ''), 120);
|
|
369
|
+
const taskSignals = safeProblemContext(String(input.taskSignals ?? ''), 100);
|
|
370
|
+
if (taskTitle || taskSignals) {
|
|
371
|
+
candidates.push({
|
|
372
|
+
question: `Failed to complete claimed task: "${taskTitle || 'unknown'}" (signals: ${taskSignals || 'unknown'}). The problem exceeds current capabilities. What approaches, tools, or patterns would solve this?`,
|
|
373
|
+
amount: 0,
|
|
374
|
+
signals: ['task_completion_failed', 'help_needed'],
|
|
375
|
+
priority: 3,
|
|
376
|
+
});
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
return candidates;
|
|
380
|
+
}
|
|
381
|
+
export function generateUrgentQuestions(input = {}) {
|
|
382
|
+
const now = input.now ?? Date.now();
|
|
383
|
+
const state = normalizeState(input.state);
|
|
384
|
+
if (tooSoon(state.lastUrgentAt, URGENT_QUESTION_INTERVAL_MS, now))
|
|
385
|
+
return { questions: [], state: questionState(state), changed: false };
|
|
386
|
+
const candidates = buildUrgentCandidates(input);
|
|
387
|
+
if (candidates.length === 0)
|
|
388
|
+
return { questions: [], state: questionState(state), changed: false };
|
|
389
|
+
candidates.sort((a, b) => b.priority - a.priority);
|
|
390
|
+
const filtered = [];
|
|
391
|
+
for (const candidate of candidates) {
|
|
392
|
+
if (filtered.length >= MAX_URGENT_QUESTIONS)
|
|
393
|
+
break;
|
|
394
|
+
if (!isDuplicate(candidate.question, state.recentQuestions))
|
|
395
|
+
filtered.push(candidate);
|
|
396
|
+
}
|
|
397
|
+
if (filtered.length === 0)
|
|
398
|
+
return { questions: [], state: questionState(state), changed: false };
|
|
399
|
+
const nextState = {
|
|
400
|
+
lastAskedAt: state.lastAskedAt,
|
|
401
|
+
lastUrgentAt: new Date(now).toISOString(),
|
|
402
|
+
lastExploreAt: state.lastExploreAt,
|
|
403
|
+
recentQuestions: [...state.recentQuestions, ...filtered.map((q) => q.question)].slice(-30),
|
|
404
|
+
};
|
|
405
|
+
return { questions: filtered.map(publicQuestion), state: nextState, changed: true };
|
|
406
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/** Default reuse threshold (ported from v1 DEFAULT_MIN_REUSE_SCORE). The adapter overrides via env. */
|
|
2
|
+
export declare const DEFAULT_MIN_REUSE_SCORE = 0.72;
|
|
3
|
+
/** Streak cap to prevent unbounded score inflation (ported from v1 MAX_STREAK_CAP). */
|
|
4
|
+
export declare const MAX_STREAK_CAP = 5;
|
|
5
|
+
/** Weight of the semantic-similarity bonus when the hub provided a vector similarity (ported from v1). */
|
|
6
|
+
export declare const SEMANTIC_SIMILARITY_BONUS = 0.3;
|
|
7
|
+
/** Weight of the signal-match multiplier: how much local↔candidate signal overlap shapes the rank. */
|
|
8
|
+
export declare const SIGNAL_MATCH_FLOOR = 0.5;
|
|
9
|
+
/** Recency half-life in days: a candidate that is HALF_LIFE_DAYS old keeps half its recency weight. */
|
|
10
|
+
export declare const RECENCY_HALF_LIFE_DAYS = 90;
|
|
11
|
+
/**
|
|
12
|
+
* Hub-quality metadata for ONE search candidate (the FREE phase-1 row — metadata only, no payload yet).
|
|
13
|
+
* Hub-agnostic: any hub that can describe a reusable asset's quality fits here. No economic field exists by
|
|
14
|
+
* design (see the boundary note above). Fields are all optional so a sparse hub still ranks.
|
|
15
|
+
*/
|
|
16
|
+
export interface HubMetadata {
|
|
17
|
+
/** Content-addressed id of the candidate asset (used by the adapter to fetch the winner's payload). */
|
|
18
|
+
assetId: string;
|
|
19
|
+
/** The asset's own signals (signals_match / signals / trigger) — matched against the local signals. */
|
|
20
|
+
signalsMatch?: readonly string[];
|
|
21
|
+
/** Optional category/summary for semantic tag expansion (reuses signals/expand's matching). */
|
|
22
|
+
category?: string;
|
|
23
|
+
summary?: string;
|
|
24
|
+
/** Hub gate status. Only 'promoted' is eligible for reuse (untrusted/quarantined are skipped). */
|
|
25
|
+
status?: string;
|
|
26
|
+
/** Author/asset confidence in [0..1]-ish range. */
|
|
27
|
+
confidence?: number;
|
|
28
|
+
/** Consecutive successful reuses (capped at MAX_STREAK_CAP). */
|
|
29
|
+
successStreak?: number;
|
|
30
|
+
/** Reputation of the source (0..100, defaults to 50 when absent — neutral). */
|
|
31
|
+
reputationScore?: number;
|
|
32
|
+
/** GDI quality score (0..1-ish) when the hub provides one; multiplies the rank when present. */
|
|
33
|
+
gdiScore?: number;
|
|
34
|
+
/** Observed success rate of past reuses (0..1) when the hub provides one. */
|
|
35
|
+
successRate?: number;
|
|
36
|
+
/** How many times the asset has been reused (popularity prior). */
|
|
37
|
+
reuseCount?: number;
|
|
38
|
+
/** Vector similarity from a semantic search leg (0..1), added as a bonus when present. */
|
|
39
|
+
semanticSimilarity?: number;
|
|
40
|
+
/** Last-updated epoch ms (for recency). Absent → no recency adjustment (neutral 1.0). */
|
|
41
|
+
updatedAt?: number;
|
|
42
|
+
}
|
|
43
|
+
/** A scored, ranked candidate. Pure ranking — no economic dimension anywhere. */
|
|
44
|
+
export interface RankedCandidate {
|
|
45
|
+
assetId: string;
|
|
46
|
+
/** Final rank (higher = more worth reusing). Rounded to 3 dp for stable golden output. */
|
|
47
|
+
score: number;
|
|
48
|
+
/** Decomposition for explainability (禁黑盒): the multiplicative parts that produced `score`. */
|
|
49
|
+
reasons: {
|
|
50
|
+
quality: number;
|
|
51
|
+
signalMatch: number;
|
|
52
|
+
recency: number;
|
|
53
|
+
semanticBonus: number;
|
|
54
|
+
reuseFactor: number;
|
|
55
|
+
};
|
|
56
|
+
/** The original metadata row (carried through so the adapter can fetch the winner). */
|
|
57
|
+
metadata: HubMetadata;
|
|
58
|
+
}
|
|
59
|
+
export interface ScoreOptions {
|
|
60
|
+
/** Injected clock for recency (defaults to a neutral now if absent — keeps pure callers deterministic). */
|
|
61
|
+
now?: number;
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Rank free hub search candidates by reuse quality only. Deterministic given (signals, metadata, now).
|
|
65
|
+
* Returns a new array sorted high→low score; ties are broken by assetId for stable golden output.
|
|
66
|
+
*/
|
|
67
|
+
export declare function scoreSearchResults(localSignals: readonly string[], hubMetadata: readonly HubMetadata[], opts?: ScoreOptions): RankedCandidate[];
|
|
68
|
+
/** The reuse decision: fetch the winner, or solve fresh. */
|
|
69
|
+
export interface ReuseDecision {
|
|
70
|
+
action: 'fetch' | 'solve-fresh';
|
|
71
|
+
/** Present only when action === 'fetch': the single candidate worth paying to fetch. */
|
|
72
|
+
candidate?: RankedCandidate;
|
|
73
|
+
/** The threshold applied (for explainability / logging in the adapter). */
|
|
74
|
+
threshold: number;
|
|
75
|
+
/** How many candidates were considered (after the eligibility filter). */
|
|
76
|
+
considered: number;
|
|
77
|
+
}
|
|
78
|
+
export interface DecideOptions {
|
|
79
|
+
/** Reuse threshold (default DEFAULT_MIN_REUSE_SCORE). The adapter sources this from env. */
|
|
80
|
+
threshold?: number;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Decide whether to reuse (port of v1 pickBestMatch): among the ranked candidates, keep only eligible ones
|
|
84
|
+
* (status promoted or unset), and pick the single highest scorer that clears the threshold. Below threshold
|
|
85
|
+
* → solve-fresh. Picks AT MOST ONE — the adapter pays to fetch only this winner (or nothing).
|
|
86
|
+
*/
|
|
87
|
+
export declare function decideReuse(ranked: readonly RankedCandidate[], opts?: DecideOptions): ReuseDecision;
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// Reuse-before-solve DECISION CORE (#110) — pure, hub-agnostic ranking. Ported from v1 hubSearch.js
|
|
2
|
+
// (scoreHubResult / pickBestMatch / getMinReuseScore). Given the local problem signals and the FREE
|
|
3
|
+
// metadata of hub search candidates, this ranks "which candidate is worth getting" and decides whether
|
|
4
|
+
// reusing one is better than solving fresh. It picks AT MOST ONE candidate to fetch.
|
|
5
|
+
//
|
|
6
|
+
// HARD BOUNDARY (acceptance criterion #110): the economic-ownership rule. This file ranks reuse QUALITY and
|
|
7
|
+
// outputs "which asset is worth getting" — it never models the economic dimension of getting it. The paid
|
|
8
|
+
// pull, the economic receipt, and the cache all live in the adapter. An acceptance test greps this file for
|
|
9
|
+
// the economic vocabulary and expects zero hits — so even the comments here avoid those words deliberately.
|
|
10
|
+
//
|
|
11
|
+
// Determinism: scoring is a pure function of the inputs. Recency uses an injected `now` (no Date.now here)
|
|
12
|
+
// so golden tests are reproducible.
|
|
13
|
+
import { tagOverlapScore } from '../signals/expand.js';
|
|
14
|
+
/** Default reuse threshold (ported from v1 DEFAULT_MIN_REUSE_SCORE). The adapter overrides via env. */
|
|
15
|
+
export const DEFAULT_MIN_REUSE_SCORE = 0.72;
|
|
16
|
+
/** Streak cap to prevent unbounded score inflation (ported from v1 MAX_STREAK_CAP). */
|
|
17
|
+
export const MAX_STREAK_CAP = 5;
|
|
18
|
+
/** Weight of the semantic-similarity bonus when the hub provided a vector similarity (ported from v1). */
|
|
19
|
+
export const SEMANTIC_SIMILARITY_BONUS = 0.3;
|
|
20
|
+
/** Weight of the signal-match multiplier: how much local↔candidate signal overlap shapes the rank. */
|
|
21
|
+
export const SIGNAL_MATCH_FLOOR = 0.5;
|
|
22
|
+
/** Recency half-life in days: a candidate that is HALF_LIFE_DAYS old keeps half its recency weight. */
|
|
23
|
+
export const RECENCY_HALF_LIFE_DAYS = 90;
|
|
24
|
+
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
25
|
+
function clampStreak(raw) {
|
|
26
|
+
return Math.min(Math.max(Number.isFinite(raw) ? raw : 0, 1), MAX_STREAK_CAP);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Base hub-quality score (ported faithfully from v1 scoreHubResult):
|
|
30
|
+
* quality = confidence × cappedStreak × (reputation/100)
|
|
31
|
+
* Extended with the optional hub quality multipliers the v2 metadata can carry (gdi / successRate),
|
|
32
|
+
* each gated so an absent field is neutral (×1), never a penalty.
|
|
33
|
+
*/
|
|
34
|
+
function qualityScore(m) {
|
|
35
|
+
const confidence = Number.isFinite(Number(m.confidence)) ? Number(m.confidence) : 0;
|
|
36
|
+
const streak = clampStreak(Number(m.successStreak));
|
|
37
|
+
const reputation = Number.isFinite(Number(m.reputationScore)) ? Number(m.reputationScore) : 50;
|
|
38
|
+
let q = confidence * streak * (reputation / 100);
|
|
39
|
+
// Optional v2 hub quality metadata — multiply in only when present (absent = neutral).
|
|
40
|
+
if (Number.isFinite(Number(m.gdiScore)))
|
|
41
|
+
q *= Math.max(Number(m.gdiScore), 0);
|
|
42
|
+
if (Number.isFinite(Number(m.successRate)))
|
|
43
|
+
q *= Math.max(Number(m.successRate), 0);
|
|
44
|
+
return q;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Signal-match multiplier in [SIGNAL_MATCH_FLOOR..1]. Reuses signals/expand's three-layer matching
|
|
48
|
+
* (tagOverlapScore) so a '429' local signal still matches a candidate tagged for 'repair'. A zero overlap
|
|
49
|
+
* does not zero out the rank (the hub already recalled it as relevant) but discounts it to the floor.
|
|
50
|
+
*/
|
|
51
|
+
function signalMatchFactor(localSignals, m) {
|
|
52
|
+
const overlap = tagOverlapScore(localSignals, {
|
|
53
|
+
...(m.signalsMatch ? { signalsMatch: m.signalsMatch } : {}),
|
|
54
|
+
...(m.category ? { category: m.category } : {}),
|
|
55
|
+
...(m.summary ? { summary: m.summary } : {}),
|
|
56
|
+
});
|
|
57
|
+
return SIGNAL_MATCH_FLOOR + (1 - SIGNAL_MATCH_FLOOR) * Math.min(Math.max(overlap, 0), 1);
|
|
58
|
+
}
|
|
59
|
+
/** Exponential time-decay recency multiplier in (0..1]. Absent updatedAt or now → neutral 1.0. */
|
|
60
|
+
function recencyFactor(m, now) {
|
|
61
|
+
if (!Number.isFinite(Number(m.updatedAt)) || now === undefined)
|
|
62
|
+
return 1;
|
|
63
|
+
const ageMs = now - Number(m.updatedAt);
|
|
64
|
+
if (ageMs <= 0)
|
|
65
|
+
return 1;
|
|
66
|
+
const ageDays = ageMs / MS_PER_DAY;
|
|
67
|
+
return Math.pow(0.5, ageDays / RECENCY_HALF_LIFE_DAYS);
|
|
68
|
+
}
|
|
69
|
+
/** Popularity prior: a gently saturating multiplier from reuseCount (absent → neutral 1.0). */
|
|
70
|
+
function reuseFactor(m) {
|
|
71
|
+
const n = Number(m.reuseCount);
|
|
72
|
+
if (!Number.isFinite(n) || n <= 0)
|
|
73
|
+
return 1;
|
|
74
|
+
// log1p saturates: 0→1.0, 9→~1.23, 99→~1.46 — popularity helps but never dominates quality.
|
|
75
|
+
return 1 + Math.log1p(n) / 20;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Rank free hub search candidates by reuse quality only. Deterministic given (signals, metadata, now).
|
|
79
|
+
* Returns a new array sorted high→low score; ties are broken by assetId for stable golden output.
|
|
80
|
+
*/
|
|
81
|
+
export function scoreSearchResults(localSignals, hubMetadata, opts = {}) {
|
|
82
|
+
const now = opts.now;
|
|
83
|
+
const ranked = [];
|
|
84
|
+
for (const m of hubMetadata) {
|
|
85
|
+
if (!m || typeof m.assetId !== 'string' || m.assetId.length === 0)
|
|
86
|
+
continue;
|
|
87
|
+
const quality = qualityScore(m);
|
|
88
|
+
const signalMatch = signalMatchFactor(localSignals, m);
|
|
89
|
+
const recency = recencyFactor(m, now);
|
|
90
|
+
const reuse = reuseFactor(m);
|
|
91
|
+
const sim = Number.isFinite(Number(m.semanticSimilarity)) ? Math.max(Number(m.semanticSimilarity), 0) : 0;
|
|
92
|
+
const semanticBonus = sim * SEMANTIC_SIMILARITY_BONUS;
|
|
93
|
+
const raw = quality * signalMatch * recency * reuse + semanticBonus;
|
|
94
|
+
ranked.push({
|
|
95
|
+
assetId: m.assetId,
|
|
96
|
+
score: Math.round(raw * 1000) / 1000,
|
|
97
|
+
reasons: { quality, signalMatch, recency, semanticBonus, reuseFactor: reuse },
|
|
98
|
+
metadata: m,
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
ranked.sort((a, b) => (b.score - a.score) || (a.assetId < b.assetId ? -1 : a.assetId > b.assetId ? 1 : 0));
|
|
102
|
+
return ranked;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Decide whether to reuse (port of v1 pickBestMatch): among the ranked candidates, keep only eligible ones
|
|
106
|
+
* (status promoted or unset), and pick the single highest scorer that clears the threshold. Below threshold
|
|
107
|
+
* → solve-fresh. Picks AT MOST ONE — the adapter pays to fetch only this winner (or nothing).
|
|
108
|
+
*/
|
|
109
|
+
export function decideReuse(ranked, opts = {}) {
|
|
110
|
+
const threshold = Number.isFinite(Number(opts.threshold)) && Number(opts.threshold) > 0
|
|
111
|
+
? Number(opts.threshold)
|
|
112
|
+
: DEFAULT_MIN_REUSE_SCORE;
|
|
113
|
+
// Eligibility: an explicit non-promoted status disqualifies (untrusted/quarantined never auto-reused).
|
|
114
|
+
const eligible = ranked.filter((r) => {
|
|
115
|
+
const status = r.metadata.status;
|
|
116
|
+
return !status || status === 'promoted';
|
|
117
|
+
});
|
|
118
|
+
// Pick the max explicitly (don't rely on input order — robust to an unsorted `ranked`).
|
|
119
|
+
let winner;
|
|
120
|
+
for (const r of eligible)
|
|
121
|
+
if (!winner || r.score > winner.score)
|
|
122
|
+
winner = r;
|
|
123
|
+
if (!winner || winner.score < threshold) {
|
|
124
|
+
return { action: 'solve-fresh', threshold, considered: eligible.length };
|
|
125
|
+
}
|
|
126
|
+
return { action: 'fetch', candidate: winner, threshold, considered: eligible.length };
|
|
127
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import type { AssetRecord } from '../assetstore/provider.js';
|
|
2
|
+
export declare const REDACTED = "[REDACTED]";
|
|
3
|
+
/** Apply every redaction pattern (secrets → [REDACTED]) then anonymize local paths (~/...) to a single string. */
|
|
4
|
+
export declare function redactString(s: string): string;
|
|
5
|
+
/** Deep redaction: recursively redact string values in objects/arrays (keys untouched). Returns a copy; does not mutate the input. */
|
|
6
|
+
export declare function redactDeep<T>(v: T): T;
|
|
7
|
+
/**
|
|
8
|
+
* Redact a single asset and RECOMPUTE its asset_id (command point). When redaction changes content, the id must
|
|
9
|
+
* change with it, otherwise the published body and id disagree. computeAssetId excludes the asset_id field by
|
|
10
|
+
* default; on the rare null return we fall back to the original id.
|
|
11
|
+
*/
|
|
12
|
+
export declare function sanitizeAsset(asset: AssetRecord): AssetRecord;
|
|
13
|
+
export interface Leak {
|
|
14
|
+
type: string;
|
|
15
|
+
value: string;
|
|
16
|
+
suggestion: string;
|
|
17
|
+
}
|
|
18
|
+
/** Pattern-scan content for sensitive info. Does not mutate content; returns a structured result. */
|
|
19
|
+
export declare function scanForLeaks(content: string): {
|
|
20
|
+
found: boolean;
|
|
21
|
+
leaks: Leak[];
|
|
22
|
+
};
|
|
23
|
+
/** Reverse detection: if any process.env value (>=8 chars) appears verbatim in content, an env value was hardcoded and should be replaced with the env reference. */
|
|
24
|
+
export declare function detectEnvValueLeaks(content: string, env: Record<string, string | undefined>): Leak[];
|
|
25
|
+
/** Full leak check: pattern scan + reverse env-value detection. */
|
|
26
|
+
export declare function fullLeakCheck(content: string, env: Record<string, string | undefined>): {
|
|
27
|
+
found: boolean;
|
|
28
|
+
leaks: Leak[];
|
|
29
|
+
};
|
|
30
|
+
export type LeakCheckMode = 'strict' | 'warn' | 'off';
|
|
31
|
+
/** Read the leak-check mode from env (EVOLVER_LEAK_CHECK, default strict, matching v1). */
|
|
32
|
+
export declare function leakCheckModeFromEnv(env: Record<string, string | undefined>): LeakCheckMode;
|
|
33
|
+
export interface SanitizeBundleResult {
|
|
34
|
+
/** The redacted bundle (asset_id recomputed). Redaction always happens regardless of mode (the leak-proof floor). */
|
|
35
|
+
bundle: AssetRecord[];
|
|
36
|
+
/** strict mode + leak found → true: the chokepoint should refuse to publish (not retryable). */
|
|
37
|
+
blocked: boolean;
|
|
38
|
+
/** Leaks found (scanned on the original content, before redaction). */
|
|
39
|
+
leaks: Leak[];
|
|
40
|
+
mode: LeakCheckMode;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Pre-publish sanitize of a bundle (pure, never throws):
|
|
44
|
+
* 1) mode != off: scan the ORIGINAL (pre-redaction) content for leaks; strict + found → blocked=true (chokepoint refuses); warn → flag only.
|
|
45
|
+
* 2) REGARDLESS of mode, always deep-redact every asset + recompute asset_id (the leak-proof floor; off only skips the scan, not the redaction).
|
|
46
|
+
*/
|
|
47
|
+
export declare function sanitizeBundle(bundle: readonly AssetRecord[], opts: {
|
|
48
|
+
env: Record<string, string | undefined>;
|
|
49
|
+
mode?: LeakCheckMode;
|
|
50
|
+
}): SanitizeBundleResult;
|
|
51
|
+
/** Collapse a leak list into a one-line human-readable summary (for logs / rejection reason). */
|
|
52
|
+
export declare function summarizeLeaks(leaks: readonly Leak[]): string;
|