@evomap/evolver-core 2.0.0-beta.2 → 2.0.0-beta.22
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/assets/gep/genes.jsonl +5 -5
- package/dist/algo/bans.d.ts +10 -1
- package/dist/algo/bans.js +56 -6
- package/dist/algo/candidateAssembly.d.ts +25 -2
- package/dist/algo/candidateAssembly.js +171 -26
- package/dist/algo/capabilityCandidates.js +10 -0
- package/dist/algo/conversationSniffer.js +25 -8
- package/dist/algo/cycleEngine.d.ts +63 -5
- package/dist/algo/cycleEngine.js +320 -42
- package/dist/algo/cycleFailureClassifier.d.ts +1 -1
- package/dist/algo/evolutionEvent.d.ts +26 -0
- package/dist/algo/evolutionEvent.js +32 -0
- package/dist/algo/exploration.d.ts +7 -0
- package/dist/algo/exploration.js +16 -3
- package/dist/algo/geneHealth.d.ts +36 -3
- package/dist/algo/geneHealth.js +47 -4
- package/dist/algo/geneIntake.d.ts +46 -1
- package/dist/algo/geneIntake.js +121 -10
- package/dist/algo/geneSelection.d.ts +126 -6
- package/dist/algo/geneSelection.js +479 -36
- package/dist/algo/index.d.ts +6 -1
- package/dist/algo/index.js +6 -1
- package/dist/algo/kautoProjection.d.ts +41 -0
- package/dist/algo/kautoProjection.js +95 -0
- package/dist/algo/kautoValidator.d.ts +68 -0
- package/dist/algo/kautoValidator.js +256 -0
- package/dist/algo/memoryGraph.d.ts +62 -0
- package/dist/algo/memoryGraph.js +86 -0
- package/dist/algo/orchestrator.d.ts +17 -1
- package/dist/algo/orchestrator.js +30 -4
- package/dist/algo/publishEligibility.d.ts +34 -0
- package/dist/algo/publishEligibility.js +52 -0
- package/dist/algo/solidify.d.ts +11 -2
- package/dist/algo/solidify.js +37 -7
- package/dist/algo/ucb1.d.ts +53 -0
- package/dist/algo/ucb1.js +156 -0
- package/dist/assetrepair/hubRejection.d.ts +12 -0
- package/dist/assetrepair/hubRejection.js +109 -0
- package/dist/assetrepair/index.d.ts +2 -0
- package/dist/assetrepair/index.js +2 -0
- package/dist/assetrepair/repair.d.ts +33 -0
- package/dist/assetrepair/repair.js +155 -0
- package/dist/assetstore/assetSidecarRecords.d.ts +28 -0
- package/dist/assetstore/assetSidecarRecords.js +384 -0
- package/dist/assetstore/assetSidecarRecovery.d.ts +48 -0
- package/dist/assetstore/assetSidecarRecovery.js +288 -0
- package/dist/assetstore/assetStoreHealth.d.ts +81 -0
- package/dist/assetstore/assetStoreHealth.js +319 -0
- package/dist/assetstore/assetStoreLayout.d.ts +2 -0
- package/dist/assetstore/assetStoreLayout.js +6 -0
- package/dist/assetstore/assetStoreStorage.d.ts +42 -0
- package/dist/assetstore/assetStoreStorage.js +336 -0
- package/dist/assetstore/assetSyncLedger.d.ts +91 -1
- package/dist/assetstore/assetSyncLedger.js +718 -59
- package/dist/assetstore/foreignJsonlSource.d.ts +48 -0
- package/dist/assetstore/foreignJsonlSource.js +150 -0
- package/dist/assetstore/index.d.ts +5 -0
- package/dist/assetstore/index.js +5 -0
- package/dist/assetstore/learningHistory.js +3 -3
- package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
- package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
- package/dist/assetstore/localJsonl.d.ts +12 -1
- package/dist/assetstore/localJsonl.js +251 -42
- package/dist/assetstore/provenance.d.ts +93 -4
- package/dist/assetstore/provenance.js +368 -84
- package/dist/assetstore/provider.d.ts +63 -0
- package/dist/assetstore/provider.js +97 -6
- package/dist/assetstore/reviewFilter.d.ts +19 -1
- package/dist/assetstore/reviewFilter.js +39 -1
- package/dist/assetstore/reviewLedger.d.ts +8 -2
- package/dist/assetstore/reviewLedger.js +71 -45
- package/dist/assetstore/unionReadStore.d.ts +25 -0
- package/dist/assetstore/unionReadStore.js +119 -0
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +4 -3
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +4 -3
- package/dist/benchmark/index.d.ts +3 -1
- package/dist/benchmark/index.js +3 -1
- package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
- package/dist/benchmark/selectionFlatAbstention.js +481 -0
- package/dist/benchmark/triggerShift.d.ts +62 -0
- package/dist/benchmark/triggerShift.js +106 -0
- package/dist/bootstrap/envFingerprint.d.ts +9 -0
- package/dist/bootstrap/envFingerprint.js +5 -0
- package/dist/bootstrap/index.d.ts +3 -1
- package/dist/bootstrap/index.js +3 -1
- package/dist/bootstrap/lifecycleBootstrap.d.ts +111 -0
- package/dist/bootstrap/lifecycleBootstrap.js +433 -0
- package/dist/bootstrap/v1EnvCompat.d.ts +113 -0
- package/dist/bootstrap/v1EnvCompat.js +303 -0
- package/dist/events/eventArchive.d.ts +2 -0
- package/dist/events/eventArchive.js +13 -3
- package/dist/events/eventSchema.d.ts +7 -7
- package/dist/events/eventStore.d.ts +2 -0
- package/dist/events/eventStore.js +5 -1
- package/dist/events/ingest.d.ts +2 -1
- package/dist/events/ingest.js +15 -0
- package/dist/events/paths.d.ts +12 -10
- package/dist/events/paths.js +24 -20
- package/dist/events/public.d.ts +2 -2
- package/dist/events/public.js +2 -2
- package/dist/events/reports.d.ts +2 -0
- package/dist/events/reports.js +4 -0
- package/dist/exec/autoExec.d.ts +68 -4
- package/dist/exec/autoExec.js +392 -33
- package/dist/exec/autonomousCycle.d.ts +31 -4
- package/dist/exec/autonomousCycle.js +71 -13
- package/dist/exec/claudeBridge.d.ts +80 -15
- package/dist/exec/claudeBridge.js +845 -76
- package/dist/exec/executionBinding.d.ts +414 -0
- package/dist/exec/executionBinding.js +588 -0
- package/dist/exec/index.d.ts +1 -0
- package/dist/exec/index.js +1 -0
- package/dist/exec/openPrRegistry.d.ts +8 -2
- package/dist/exec/openPrRegistry.js +32 -22
- package/dist/exec/prompt.js +14 -1
- package/dist/exec/proofOfWork.d.ts +1 -1
- package/dist/exec/proofOfWork.js +2 -2
- package/dist/exec/runnerRegistry.d.ts +153 -36
- package/dist/exec/runnerRegistry.js +848 -65
- package/dist/exec/selfPr.js +1 -7
- package/dist/feedback/envelope.d.ts +61 -0
- package/dist/feedback/envelope.js +168 -0
- package/dist/feedback/index.d.ts +1 -0
- package/dist/feedback/index.js +1 -0
- package/dist/hooks/hooks.js +1 -0
- package/dist/hub/assetCallLog.d.ts +35 -1
- package/dist/hub/assetCallLog.js +124 -1
- package/dist/hub/bindings.d.ts +8 -1
- package/dist/hub/bindings.js +29 -8
- package/dist/hub/capability.d.ts +130 -4
- package/dist/hub/conversationDistiller.d.ts +19 -0
- package/dist/hub/conversationDistiller.js +115 -37
- package/dist/hub/fake.d.ts +3 -2
- package/dist/hub/fake.js +2 -1
- package/dist/hub/index.d.ts +1 -0
- package/dist/hub/index.js +1 -0
- package/dist/hub/questionGenerator.d.ts +5 -1
- package/dist/hub/questionGenerator.js +8 -6
- package/dist/hub/recipeCompose.d.ts +27 -0
- package/dist/hub/recipeCompose.js +90 -0
- package/dist/hub/sanitize.js +122 -7
- package/dist/index.d.ts +6 -1
- package/dist/index.js +7 -1
- package/dist/issueReporter/index.d.ts +156 -0
- package/dist/issueReporter/index.js +1688 -0
- package/dist/mailbox/dispatch.d.ts +1 -1
- package/dist/mailbox/dispatch.js +22 -6
- package/dist/mailbox/envelope.d.ts +7 -1
- package/dist/mailbox/envelope.js +9 -2
- package/dist/mailbox/ipcServer.d.ts +12 -2
- package/dist/mailbox/ipcServer.js +183 -13
- package/dist/mailbox/store.d.ts +89 -3
- package/dist/mailbox/store.js +895 -41
- package/dist/modelCompatibility.d.ts +164 -0
- package/dist/modelCompatibility.js +309 -0
- package/dist/observers/valueDigestObserver.d.ts +9 -0
- package/dist/observers/valueDigestObserver.js +35 -2
- package/dist/ops/cleanup.js +1 -1
- package/dist/ops/evolutionGraphProjection.d.ts +20 -0
- package/dist/ops/evolutionGraphProjection.js +315 -0
- package/dist/ops/index.d.ts +2 -1
- package/dist/ops/index.js +2 -1
- package/dist/ops/savingsCore.js +1 -2
- package/dist/ops/selfUpdate.d.ts +18 -1
- package/dist/ops/selfUpdate.js +88 -23
- package/dist/ops/valueOutreach.d.ts +3 -1
- package/dist/ops/valueOutreach.js +5 -1
- package/dist/personality/schema.d.ts +24 -24
- package/dist/schema/evolutionGraph.d.ts +784 -0
- package/dist/schema/evolutionGraph.js +187 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/proofOfWork.d.ts +125 -6
- package/dist/schema/proofOfWork.js +102 -4
- package/dist/schema/signal.d.ts +3 -3
- package/dist/schema/signal.js +1 -1
- package/dist/shadow/shadowHub.js +1 -0
- package/dist/signals/curriculum.d.ts +55 -0
- package/dist/signals/curriculum.js +202 -0
- package/dist/signals/cycleHistoryFromEvents.js +17 -8
- package/dist/signals/expand.d.ts +15 -1
- package/dist/signals/expand.js +169 -1
- package/dist/signals/extractor.d.ts +2 -2
- package/dist/signals/extractor.js +31 -6
- package/dist/signals/index.d.ts +4 -1
- package/dist/signals/index.js +4 -1
- package/dist/signals/metaSignals.d.ts +4 -0
- package/dist/signals/metaSignals.js +42 -0
- package/dist/signals/scopeVocabulary.d.ts +75 -0
- package/dist/signals/scopeVocabulary.js +91 -0
- package/dist/signals/signalGate.js +1 -1
- package/dist/signals/taskDomain.d.ts +22 -0
- package/dist/signals/taskDomain.js +43 -0
- package/dist/strategy/constraintAblation.d.ts +64 -0
- package/dist/strategy/constraintAblation.js +2820 -0
- package/dist/strategy/constraintAblationPredicates.d.ts +31 -0
- package/dist/strategy/constraintAblationPredicates.js +339 -0
- package/dist/strategy/index.d.ts +2 -1
- package/dist/strategy/index.js +2 -1
- package/dist/trace/index.d.ts +3 -1
- package/dist/trace/index.js +3 -1
- package/dist/trace/learningTrace.d.ts +216 -0
- package/dist/trace/learningTrace.js +298 -0
- package/dist/trace/proxyTurns.d.ts +31 -0
- package/dist/trace/proxyTurns.js +137 -0
- package/dist/trace/trajectory.d.ts +8 -0
- package/dist/trace/trajectory.js +14 -2
- package/dist/util/fetchPort.d.ts +1 -0
- package/dist/util/fetchPort.js +11 -0
- package/dist/util/fileLock.d.ts +113 -7
- package/dist/util/fileLock.js +1035 -80
- package/dist/util/index.d.ts +3 -1
- package/dist/util/index.js +2 -1
- package/dist/verify/index.d.ts +2 -1
- package/dist/verify/index.js +1 -1
- package/dist/verify/sandboxRunner.d.ts +30 -0
- package/dist/verify/sandboxRunner.js +370 -27
- package/dist/verify/sandboxedValidation.d.ts +16 -2
- package/dist/verify/sandboxedValidation.js +200 -22
- package/dist/verify/validation.d.ts +35 -4
- package/dist/verify/validation.js +164 -16
- package/dist/wire/geneHints.d.ts +88 -16
- package/dist/wire/geneHints.js +124 -15
- package/dist/wire/index.d.ts +8 -3
- package/dist/wire/index.js +2 -2
- package/dist/wire/schemaGate.d.ts +21 -0
- package/dist/wire/schemaGate.js +119 -10
- package/dist/workflow/dsl.d.ts +24 -3
- package/dist/workflow/dsl.js +4 -0
- package/dist/workflow/engine.d.ts +5 -1
- package/dist/workflow/engine.js +3 -0
- package/dist/workflow/index.d.ts +3 -1
- package/dist/workflow/index.js +3 -1
- package/dist/workflow/runtime.d.ts +110 -0
- package/dist/workflow/runtime.js +1298 -0
- package/dist/workflow/stateStore.d.ts +172 -0
- package/dist/workflow/stateStore.js +1044 -0
- package/package.json +12 -4
|
@@ -27,6 +27,9 @@ function isEmptyCycle(r) {
|
|
|
27
27
|
function isFailed(r) {
|
|
28
28
|
return r.outcome?.status === 'failed';
|
|
29
29
|
}
|
|
30
|
+
function isSuccess(r) {
|
|
31
|
+
return r.outcome?.status === 'success';
|
|
32
|
+
}
|
|
30
33
|
/**
|
|
31
34
|
* Derive the v1 history counters from a normalized cycle-record window (newest LAST), mirroring v1
|
|
32
35
|
* analyzeRecentHistory. Pure + deterministic. Use {@link cycleRecordsFromEvents} to adapt the v2 event log.
|
|
@@ -37,6 +40,8 @@ export function deriveCycleHistory(records) {
|
|
|
37
40
|
emptyCycleCount: 0,
|
|
38
41
|
consecutiveEmptyCycles: 0,
|
|
39
42
|
consecutiveFailureCount: 0,
|
|
43
|
+
consecutiveSuccessCount: 0,
|
|
44
|
+
successCycleCount: 0,
|
|
40
45
|
recentFailureRatio: 0,
|
|
41
46
|
geneFreq: {},
|
|
42
47
|
};
|
|
@@ -90,11 +95,32 @@ export function deriveCycleHistory(records) {
|
|
|
90
95
|
if (isFailed(r))
|
|
91
96
|
recentFailureCount++;
|
|
92
97
|
}
|
|
98
|
+
// Consecutive successes at the tail (stable success plateau detection). Empty cycles are zero-blast
|
|
99
|
+
// no-ops: like the failure streak, they break the success streak instead of extending it.
|
|
100
|
+
let consecutiveSuccessCount = 0;
|
|
101
|
+
for (let i = recent.length - 1; i >= 0; i--) {
|
|
102
|
+
if (isEmptyCycle(recent[i]))
|
|
103
|
+
break;
|
|
104
|
+
if (isSuccess(recent[i]))
|
|
105
|
+
consecutiveSuccessCount++;
|
|
106
|
+
else
|
|
107
|
+
break;
|
|
108
|
+
}
|
|
109
|
+
// Successful cycles within the frequency window (non-empty successes, same caliber as recentFailureRatio).
|
|
110
|
+
let successCycleCount = 0;
|
|
111
|
+
for (const r of tail) {
|
|
112
|
+
if (isEmptyCycle(r))
|
|
113
|
+
continue;
|
|
114
|
+
if (isSuccess(r))
|
|
115
|
+
successCycleCount++;
|
|
116
|
+
}
|
|
93
117
|
return {
|
|
94
118
|
consecutiveRepairCount,
|
|
95
119
|
emptyCycleCount,
|
|
96
120
|
consecutiveEmptyCycles,
|
|
97
121
|
consecutiveFailureCount,
|
|
122
|
+
consecutiveSuccessCount,
|
|
123
|
+
successCycleCount,
|
|
98
124
|
recentFailureRatio: nonEmptyCycleCount > 0 ? recentFailureCount / nonEmptyCycleCount : 0,
|
|
99
125
|
geneFreq,
|
|
100
126
|
};
|
|
@@ -153,6 +179,22 @@ export function computeMetaSignals(history) {
|
|
|
153
179
|
signals.push('high_failure_ratio');
|
|
154
180
|
signals.push('force_innovation_after_repair_loop');
|
|
155
181
|
}
|
|
182
|
+
// Stable success plateau: 5+ consecutive successes → system is in a stable success plateau.
|
|
183
|
+
// This is the SUCCESS counterpart of failure_loop_detected: instead of signaling "stuck failing",
|
|
184
|
+
// it signals "consistently succeeding" — the loop should distill WHAT WORKED into reusable genes.
|
|
185
|
+
if (history.consecutiveSuccessCount >= 5) {
|
|
186
|
+
signals.push('stable_success_plateau');
|
|
187
|
+
}
|
|
188
|
+
// Issue already resolved: 3+ consecutive successes after a prior failure context suggests
|
|
189
|
+
// the problem is solved — downstream can skip redundant repair attempts.
|
|
190
|
+
if (history.consecutiveSuccessCount >= 3 && history.recentFailureRatio > 0) {
|
|
191
|
+
signals.push('issue_already_resolved');
|
|
192
|
+
}
|
|
193
|
+
// Self-healed requires the current tail to be successful. Window totals alone would mislabel
|
|
194
|
+
// [success, success, failed] as recovered even though the latest outcome is a failure.
|
|
195
|
+
if (history.consecutiveSuccessCount > 0 && history.successCycleCount >= 2 && history.recentFailureRatio > 0 && history.recentFailureRatio < 0.5) {
|
|
196
|
+
signals.push('openclaw_self_healed');
|
|
197
|
+
}
|
|
156
198
|
// De-dup while preserving first-seen order.
|
|
157
199
|
return [...new Set(signals)];
|
|
158
200
|
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import type { AssetStoreProvider, AssetRecord } from '../assetstore/provider.js';
|
|
2
|
+
/** A signal observed in the store, with how many assets declare it. */
|
|
3
|
+
export interface ScopeVocabularyEntry {
|
|
4
|
+
signal: string;
|
|
5
|
+
/** Number of Gene assets whose signals_match declares this signal (hard facets counted by their bare tag). */
|
|
6
|
+
assetCount: number;
|
|
7
|
+
}
|
|
8
|
+
export interface ScopeVocabulary {
|
|
9
|
+
/** Observed signals, most-declared first. */
|
|
10
|
+
entries: ScopeVocabularyEntry[];
|
|
11
|
+
/** Convenience set of the bare signal strings. */
|
|
12
|
+
signals: Set<string>;
|
|
13
|
+
}
|
|
14
|
+
/** Strip a `required:` facet prefix to get the bare signal a facet gates on. */
|
|
15
|
+
export declare function bareSignal(tag: string): string;
|
|
16
|
+
/**
|
|
17
|
+
* Build the scope vocabulary from a set of Gene asset records already in hand. Hard facets contribute their BARE
|
|
18
|
+
* signal, because `required:rounding-v2` and `rounding-v2` refer to the same scope dimension — one gates on it,
|
|
19
|
+
* one hints at it. Factored out of {@link deriveScopeVocabulary} so a caller that ALREADY holds the gene records
|
|
20
|
+
* (e.g. the distiller, which lists existing genes for dedup anyway) can build the same vocabulary without a second
|
|
21
|
+
* store round-trip, and so the prompt-injection path and the post-hoc resolve path share ONE derivation.
|
|
22
|
+
*/
|
|
23
|
+
export declare function scopeVocabularyFromRecords(records: readonly AssetRecord[]): ScopeVocabulary;
|
|
24
|
+
/**
|
|
25
|
+
* Derive the scope vocabulary from the Gene assets in a store. Thin async wrapper over
|
|
26
|
+
* {@link scopeVocabularyFromRecords} that fetches the records first.
|
|
27
|
+
*/
|
|
28
|
+
export declare function deriveScopeVocabulary(store: AssetStoreProvider, limit?: number): Promise<ScopeVocabulary>;
|
|
29
|
+
export type ScopeResolution =
|
|
30
|
+
/** The proposed tag names a signal that exists; usable as-is. */
|
|
31
|
+
{
|
|
32
|
+
status: 'exact';
|
|
33
|
+
proposed: string;
|
|
34
|
+
resolved: string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The proposed tag does not exist, but exactly ONE observed signal is an unambiguous refinement of it — the
|
|
38
|
+
* autonomous-scope failure mode, where `v2` was proposed and `rounding-v2` is what exists. `resolved` is the
|
|
39
|
+
* real signal; a caller may adopt it, or surface it for review.
|
|
40
|
+
*/
|
|
41
|
+
| {
|
|
42
|
+
status: 'resolved';
|
|
43
|
+
proposed: string;
|
|
44
|
+
resolved: string;
|
|
45
|
+
reason: string;
|
|
46
|
+
}
|
|
47
|
+
/** Several observed signals match equally well; resolving would be a guess, so we refuse to pick. */
|
|
48
|
+
| {
|
|
49
|
+
status: 'ambiguous';
|
|
50
|
+
proposed: string;
|
|
51
|
+
candidates: string[];
|
|
52
|
+
}
|
|
53
|
+
/** Nothing in the store resembles the tag. Not an error: a genuinely new scope looks like this. */
|
|
54
|
+
| {
|
|
55
|
+
status: 'unknown';
|
|
56
|
+
proposed: string;
|
|
57
|
+
};
|
|
58
|
+
/**
|
|
59
|
+
* Resolve a proposed scope tag against an observed vocabulary.
|
|
60
|
+
*
|
|
61
|
+
* Matching is deliberately conservative. Beyond an exact hit we accept only ONE relation: an observed signal that
|
|
62
|
+
* ends with `-<proposed>` or `_<proposed>` (or begins with `<proposed>-`/`<proposed>_`), i.e. the proposal is a
|
|
63
|
+
* bare qualifier and the real signal is that qualifier scoped to a domain. That is exactly the `v2` →
|
|
64
|
+
* `rounding-v2` shape. We do NOT do fuzzy/edit-distance matching: silently rewriting a scope key on a weak
|
|
65
|
+
* similarity signal would be a governance hazard far worse than an unresolved tag, since scope decides what gets
|
|
66
|
+
* injected into an agent's context. Multiple candidates yield `ambiguous` rather than an arbitrary pick.
|
|
67
|
+
*/
|
|
68
|
+
export declare function resolveScopeTag(proposed: string, vocab: ScopeVocabulary): ScopeResolution;
|
|
69
|
+
/**
|
|
70
|
+
* Render the vocabulary as a compact, promptable list. This is what makes the vocabulary \emph{discoverable} to a
|
|
71
|
+
* distiller: it can be shown the signals that exist before being asked to choose a facet, instead of guessing.
|
|
72
|
+
* Bounded by `max` so a large store cannot blow a prompt budget; the most-declared signals come first, and the
|
|
73
|
+
* count is reported so a reader can tell a load-bearing scope from a one-off tag.
|
|
74
|
+
*/
|
|
75
|
+
export declare function renderScopeVocabulary(vocab: ScopeVocabulary, max?: number): string;
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
const REQUIRED_PREFIX = 'required:';
|
|
2
|
+
/** Strip a `required:` facet prefix to get the bare signal a facet gates on. */
|
|
3
|
+
export function bareSignal(tag) {
|
|
4
|
+
const t = String(tag).trim();
|
|
5
|
+
return t.startsWith(REQUIRED_PREFIX) ? t.slice(REQUIRED_PREFIX.length).trim() : t;
|
|
6
|
+
}
|
|
7
|
+
function asStrings(v) {
|
|
8
|
+
return Array.isArray(v) ? v.filter((x) => typeof x === 'string') : [];
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Build the scope vocabulary from a set of Gene asset records already in hand. Hard facets contribute their BARE
|
|
12
|
+
* signal, because `required:rounding-v2` and `rounding-v2` refer to the same scope dimension — one gates on it,
|
|
13
|
+
* one hints at it. Factored out of {@link deriveScopeVocabulary} so a caller that ALREADY holds the gene records
|
|
14
|
+
* (e.g. the distiller, which lists existing genes for dedup anyway) can build the same vocabulary without a second
|
|
15
|
+
* store round-trip, and so the prompt-injection path and the post-hoc resolve path share ONE derivation.
|
|
16
|
+
*/
|
|
17
|
+
export function scopeVocabularyFromRecords(records) {
|
|
18
|
+
const counts = new Map();
|
|
19
|
+
for (const g of records) {
|
|
20
|
+
// One asset counts once per distinct signal, so a repeated tag within one asset cannot inflate the count.
|
|
21
|
+
const seen = new Set();
|
|
22
|
+
for (const raw of asStrings(g['signals_match'])) {
|
|
23
|
+
const s = bareSignal(raw);
|
|
24
|
+
if (!s || seen.has(s))
|
|
25
|
+
continue;
|
|
26
|
+
seen.add(s);
|
|
27
|
+
counts.set(s, (counts.get(s) ?? 0) + 1);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
const entries = [...counts.entries()]
|
|
31
|
+
.map(([signal, assetCount]) => ({ signal, assetCount }))
|
|
32
|
+
.sort((a, b) => b.assetCount - a.assetCount || a.signal.localeCompare(b.signal));
|
|
33
|
+
return { entries, signals: new Set(entries.map((e) => e.signal)) };
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Derive the scope vocabulary from the Gene assets in a store. Thin async wrapper over
|
|
37
|
+
* {@link scopeVocabularyFromRecords} that fetches the records first.
|
|
38
|
+
*/
|
|
39
|
+
export async function deriveScopeVocabulary(store, limit = 500) {
|
|
40
|
+
return scopeVocabularyFromRecords(await store.list('Gene', limit));
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Resolve a proposed scope tag against an observed vocabulary.
|
|
44
|
+
*
|
|
45
|
+
* Matching is deliberately conservative. Beyond an exact hit we accept only ONE relation: an observed signal that
|
|
46
|
+
* ends with `-<proposed>` or `_<proposed>` (or begins with `<proposed>-`/`<proposed>_`), i.e. the proposal is a
|
|
47
|
+
* bare qualifier and the real signal is that qualifier scoped to a domain. That is exactly the `v2` →
|
|
48
|
+
* `rounding-v2` shape. We do NOT do fuzzy/edit-distance matching: silently rewriting a scope key on a weak
|
|
49
|
+
* similarity signal would be a governance hazard far worse than an unresolved tag, since scope decides what gets
|
|
50
|
+
* injected into an agent's context. Multiple candidates yield `ambiguous` rather than an arbitrary pick.
|
|
51
|
+
*/
|
|
52
|
+
export function resolveScopeTag(proposed, vocab) {
|
|
53
|
+
const bare = bareSignal(proposed);
|
|
54
|
+
if (!bare)
|
|
55
|
+
return { status: 'unknown', proposed };
|
|
56
|
+
if (vocab.signals.has(bare))
|
|
57
|
+
return { status: 'exact', proposed, resolved: bare };
|
|
58
|
+
const lower = bare.toLowerCase();
|
|
59
|
+
const candidates = [...vocab.signals].filter((s) => {
|
|
60
|
+
const sl = s.toLowerCase();
|
|
61
|
+
if (sl === lower)
|
|
62
|
+
return true;
|
|
63
|
+
return sl.endsWith(`-${lower}`) || sl.endsWith(`_${lower}`)
|
|
64
|
+
|| sl.startsWith(`${lower}-`) || sl.startsWith(`${lower}_`);
|
|
65
|
+
});
|
|
66
|
+
if (candidates.length === 1) {
|
|
67
|
+
return {
|
|
68
|
+
status: 'resolved',
|
|
69
|
+
proposed,
|
|
70
|
+
resolved: candidates[0],
|
|
71
|
+
reason: `proposed '${bare}' is a bare qualifier; the store's vocabulary declares '${candidates[0]}'`,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (candidates.length > 1)
|
|
75
|
+
return { status: 'ambiguous', proposed, candidates: candidates.sort() };
|
|
76
|
+
return { status: 'unknown', proposed };
|
|
77
|
+
}
|
|
78
|
+
/**
|
|
79
|
+
* Render the vocabulary as a compact, promptable list. This is what makes the vocabulary \emph{discoverable} to a
|
|
80
|
+
* distiller: it can be shown the signals that exist before being asked to choose a facet, instead of guessing.
|
|
81
|
+
* Bounded by `max` so a large store cannot blow a prompt budget; the most-declared signals come first, and the
|
|
82
|
+
* count is reported so a reader can tell a load-bearing scope from a one-off tag.
|
|
83
|
+
*/
|
|
84
|
+
export function renderScopeVocabulary(vocab, max = 40) {
|
|
85
|
+
if (vocab.entries.length === 0)
|
|
86
|
+
return '(no signals observed in this store yet)';
|
|
87
|
+
const shown = vocab.entries.slice(0, max);
|
|
88
|
+
const body = shown.map((e) => `${e.signal} (${e.assetCount})`).join(', ');
|
|
89
|
+
const omitted = vocab.entries.length - shown.length;
|
|
90
|
+
return omitted > 0 ? `${body}, ... and ${omitted} more` : body;
|
|
91
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { normalizeText } from '../signatures/signatures.js';
|
|
3
|
-
const STRENGTH_WEIGHT = { strong: 1.0, agent: 0.8, weak: 0.4 };
|
|
3
|
+
const STRENGTH_WEIGHT = { strong: 1.0, agent: 0.8, success: 0.7, weak: 0.4 };
|
|
4
4
|
function signatureOf(s) {
|
|
5
5
|
return createHash('sha1').update(`${s.kind}\x1f${normalizeText(s.text)}`).digest('hex').slice(0, 16);
|
|
6
6
|
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export declare const TASK_DOMAIN_SIGNAL_PREFIX: "task_domain:";
|
|
2
|
+
export type TaskDomainResolution = {
|
|
3
|
+
status: 'absent';
|
|
4
|
+
} | {
|
|
5
|
+
status: 'resolved';
|
|
6
|
+
slug: string;
|
|
7
|
+
} | {
|
|
8
|
+
status: 'ambiguous';
|
|
9
|
+
} | {
|
|
10
|
+
status: 'invalid';
|
|
11
|
+
};
|
|
12
|
+
/** Identify the namespace even when the value is malformed, so it cannot leak into generic matching. */
|
|
13
|
+
export declare function isTaskDomainSignal(raw: string): boolean;
|
|
14
|
+
/** Remove task-domain tokens from generic matching while preserving their original wire representation elsewhere. */
|
|
15
|
+
export declare function withoutTaskDomainSignals(signals: readonly string[]): string[];
|
|
16
|
+
/**
|
|
17
|
+
* Resolve one canonical task domain from signal tokens. Parsing is order-independent and fail-closed:
|
|
18
|
+
* malformed tokens invalidate the whole dimension, and distinct valid slugs are ambiguous.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveTaskDomainSignals(signals: readonly string[]): TaskDomainResolution;
|
|
21
|
+
/** Emit a canonical wire token. Callers must supply an already-normalized lowercase slug. */
|
|
22
|
+
export declare function taskDomainSignal(slug: string): string;
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
export const TASK_DOMAIN_SIGNAL_PREFIX = 'task_domain:';
|
|
2
|
+
const TASK_DOMAIN_SLUG_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
|
|
3
|
+
/** Identify the namespace even when the value is malformed, so it cannot leak into generic matching. */
|
|
4
|
+
export function isTaskDomainSignal(raw) {
|
|
5
|
+
return raw.trim().toLowerCase().startsWith(TASK_DOMAIN_SIGNAL_PREFIX);
|
|
6
|
+
}
|
|
7
|
+
/** Remove task-domain tokens from generic matching while preserving their original wire representation elsewhere. */
|
|
8
|
+
export function withoutTaskDomainSignals(signals) {
|
|
9
|
+
return signals.filter((signal) => !isTaskDomainSignal(signal));
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Resolve one canonical task domain from signal tokens. Parsing is order-independent and fail-closed:
|
|
13
|
+
* malformed tokens invalidate the whole dimension, and distinct valid slugs are ambiguous.
|
|
14
|
+
*/
|
|
15
|
+
export function resolveTaskDomainSignals(signals) {
|
|
16
|
+
const slugs = new Set();
|
|
17
|
+
let found = false;
|
|
18
|
+
for (const raw of signals) {
|
|
19
|
+
const signal = raw.trim();
|
|
20
|
+
const lower = signal.toLowerCase();
|
|
21
|
+
if (!lower.startsWith(TASK_DOMAIN_SIGNAL_PREFIX))
|
|
22
|
+
continue;
|
|
23
|
+
found = true;
|
|
24
|
+
if (signal !== raw)
|
|
25
|
+
return { status: 'invalid' };
|
|
26
|
+
const slug = lower.slice(TASK_DOMAIN_SIGNAL_PREFIX.length);
|
|
27
|
+
if (!TASK_DOMAIN_SLUG_RE.test(slug))
|
|
28
|
+
return { status: 'invalid' };
|
|
29
|
+
slugs.add(slug);
|
|
30
|
+
}
|
|
31
|
+
if (!found)
|
|
32
|
+
return { status: 'absent' };
|
|
33
|
+
if (slugs.size !== 1)
|
|
34
|
+
return { status: 'ambiguous' };
|
|
35
|
+
return { status: 'resolved', slug: slugs.values().next().value };
|
|
36
|
+
}
|
|
37
|
+
/** Emit a canonical wire token. Callers must supply an already-normalized lowercase slug. */
|
|
38
|
+
export function taskDomainSignal(slug) {
|
|
39
|
+
if (!TASK_DOMAIN_SLUG_RE.test(slug)) {
|
|
40
|
+
throw new Error(`invalid task_domain slug: ${slug}`);
|
|
41
|
+
}
|
|
42
|
+
return `${TASK_DOMAIN_SIGNAL_PREFIX}${slug}`;
|
|
43
|
+
}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
export type ConstraintKind = 'must' | 'must_not';
|
|
2
|
+
export type ConstraintTraceSource = 'plan' | 'task' | 'trace';
|
|
3
|
+
export type ConstraintSeverity = 'low' | 'medium' | 'high';
|
|
4
|
+
export type SensitiveClass = 'credential' | 'email' | 'filesystem_path';
|
|
5
|
+
export type TaskSuccessStatus = 'success' | 'failure' | 'unknown';
|
|
6
|
+
export type TaskSuccessSource = 'oracle' | 'task_status' | 'manual' | 'trace';
|
|
7
|
+
export type SensitivitySuccessComparison = 'success_constraint_sensitive' | 'success_constraint_insensitive' | 'failure_constraint_sensitive' | 'failure_constraint_insensitive' | 'unknown_success';
|
|
8
|
+
export interface ConstraintTrace {
|
|
9
|
+
source: ConstraintTraceSource;
|
|
10
|
+
text: string;
|
|
11
|
+
traceId?: string;
|
|
12
|
+
}
|
|
13
|
+
export interface ExtractedConstraint {
|
|
14
|
+
id: string;
|
|
15
|
+
kind: ConstraintKind;
|
|
16
|
+
textHash: string;
|
|
17
|
+
redactedText: string;
|
|
18
|
+
source: ConstraintTraceSource;
|
|
19
|
+
traceId?: string;
|
|
20
|
+
sensitiveClasses: SensitiveClass[];
|
|
21
|
+
}
|
|
22
|
+
export interface ConstraintAblatedPrompt {
|
|
23
|
+
originalPromptHash: string;
|
|
24
|
+
ablatedPromptHash: string;
|
|
25
|
+
removedConstraintIds: string[];
|
|
26
|
+
redactedPreview?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface ConstraintViolation {
|
|
29
|
+
constraintId: string;
|
|
30
|
+
kind: ConstraintKind;
|
|
31
|
+
severity: ConstraintSeverity;
|
|
32
|
+
evidenceHash: string;
|
|
33
|
+
matchedTerms: string[];
|
|
34
|
+
}
|
|
35
|
+
export interface TaskSuccessLabel {
|
|
36
|
+
status: TaskSuccessStatus;
|
|
37
|
+
source: TaskSuccessSource;
|
|
38
|
+
}
|
|
39
|
+
export interface ConstraintAblationScore {
|
|
40
|
+
source: 'constraint_ablation_replay';
|
|
41
|
+
sensitivity: number;
|
|
42
|
+
ablationCount: number;
|
|
43
|
+
baselineViolationCount: number;
|
|
44
|
+
ablatedViolationCount: number;
|
|
45
|
+
mustViolationCount: number;
|
|
46
|
+
mustNotViolationCount: number;
|
|
47
|
+
taskSuccess: TaskSuccessLabel;
|
|
48
|
+
comparison: SensitivitySuccessComparison;
|
|
49
|
+
}
|
|
50
|
+
export interface ConstraintAblationScoreInput {
|
|
51
|
+
baselineViolations: readonly ConstraintViolation[];
|
|
52
|
+
ablatedViolations: readonly ConstraintViolation[];
|
|
53
|
+
removedConstraintIds: readonly string[];
|
|
54
|
+
ablationCount: number;
|
|
55
|
+
taskSuccess: TaskSuccessLabel;
|
|
56
|
+
sensitivityThreshold?: number;
|
|
57
|
+
}
|
|
58
|
+
export declare function redactConstraintText(text: string): string;
|
|
59
|
+
export declare function extractConstraints(traces: readonly ConstraintTrace[]): ExtractedConstraint[];
|
|
60
|
+
export declare function buildConstraintAblatedPrompts(prompt: string, constraints: readonly ExtractedConstraint[], opts?: {
|
|
61
|
+
includeRedactedPreview?: boolean;
|
|
62
|
+
}): ConstraintAblatedPrompt[];
|
|
63
|
+
export declare function detectConstraintViolations(output: string, constraints: readonly ExtractedConstraint[]): ConstraintViolation[];
|
|
64
|
+
export declare function computeConstraintAblationScore(input: ConstraintAblationScoreInput): ConstraintAblationScore;
|