@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.
Files changed (240) hide show
  1. package/assets/gep/genes.jsonl +5 -5
  2. package/dist/algo/bans.d.ts +10 -1
  3. package/dist/algo/bans.js +56 -6
  4. package/dist/algo/candidateAssembly.d.ts +25 -2
  5. package/dist/algo/candidateAssembly.js +171 -26
  6. package/dist/algo/capabilityCandidates.js +10 -0
  7. package/dist/algo/conversationSniffer.js +25 -8
  8. package/dist/algo/cycleEngine.d.ts +63 -5
  9. package/dist/algo/cycleEngine.js +320 -42
  10. package/dist/algo/cycleFailureClassifier.d.ts +1 -1
  11. package/dist/algo/evolutionEvent.d.ts +26 -0
  12. package/dist/algo/evolutionEvent.js +32 -0
  13. package/dist/algo/exploration.d.ts +7 -0
  14. package/dist/algo/exploration.js +16 -3
  15. package/dist/algo/geneHealth.d.ts +36 -3
  16. package/dist/algo/geneHealth.js +47 -4
  17. package/dist/algo/geneIntake.d.ts +46 -1
  18. package/dist/algo/geneIntake.js +121 -10
  19. package/dist/algo/geneSelection.d.ts +126 -6
  20. package/dist/algo/geneSelection.js +479 -36
  21. package/dist/algo/index.d.ts +6 -1
  22. package/dist/algo/index.js +6 -1
  23. package/dist/algo/kautoProjection.d.ts +41 -0
  24. package/dist/algo/kautoProjection.js +95 -0
  25. package/dist/algo/kautoValidator.d.ts +68 -0
  26. package/dist/algo/kautoValidator.js +256 -0
  27. package/dist/algo/memoryGraph.d.ts +62 -0
  28. package/dist/algo/memoryGraph.js +86 -0
  29. package/dist/algo/orchestrator.d.ts +17 -1
  30. package/dist/algo/orchestrator.js +30 -4
  31. package/dist/algo/publishEligibility.d.ts +34 -0
  32. package/dist/algo/publishEligibility.js +52 -0
  33. package/dist/algo/solidify.d.ts +11 -2
  34. package/dist/algo/solidify.js +37 -7
  35. package/dist/algo/ucb1.d.ts +53 -0
  36. package/dist/algo/ucb1.js +156 -0
  37. package/dist/assetrepair/hubRejection.d.ts +12 -0
  38. package/dist/assetrepair/hubRejection.js +109 -0
  39. package/dist/assetrepair/index.d.ts +2 -0
  40. package/dist/assetrepair/index.js +2 -0
  41. package/dist/assetrepair/repair.d.ts +33 -0
  42. package/dist/assetrepair/repair.js +155 -0
  43. package/dist/assetstore/assetSidecarRecords.d.ts +28 -0
  44. package/dist/assetstore/assetSidecarRecords.js +384 -0
  45. package/dist/assetstore/assetSidecarRecovery.d.ts +48 -0
  46. package/dist/assetstore/assetSidecarRecovery.js +288 -0
  47. package/dist/assetstore/assetStoreHealth.d.ts +81 -0
  48. package/dist/assetstore/assetStoreHealth.js +319 -0
  49. package/dist/assetstore/assetStoreLayout.d.ts +2 -0
  50. package/dist/assetstore/assetStoreLayout.js +6 -0
  51. package/dist/assetstore/assetStoreStorage.d.ts +42 -0
  52. package/dist/assetstore/assetStoreStorage.js +336 -0
  53. package/dist/assetstore/assetSyncLedger.d.ts +91 -1
  54. package/dist/assetstore/assetSyncLedger.js +718 -59
  55. package/dist/assetstore/foreignJsonlSource.d.ts +48 -0
  56. package/dist/assetstore/foreignJsonlSource.js +150 -0
  57. package/dist/assetstore/index.d.ts +5 -0
  58. package/dist/assetstore/index.js +5 -0
  59. package/dist/assetstore/learningHistory.js +3 -3
  60. package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
  61. package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
  62. package/dist/assetstore/localJsonl.d.ts +12 -1
  63. package/dist/assetstore/localJsonl.js +251 -42
  64. package/dist/assetstore/provenance.d.ts +93 -4
  65. package/dist/assetstore/provenance.js +368 -84
  66. package/dist/assetstore/provider.d.ts +63 -0
  67. package/dist/assetstore/provider.js +97 -6
  68. package/dist/assetstore/reviewFilter.d.ts +19 -1
  69. package/dist/assetstore/reviewFilter.js +39 -1
  70. package/dist/assetstore/reviewLedger.d.ts +8 -2
  71. package/dist/assetstore/reviewLedger.js +71 -45
  72. package/dist/assetstore/unionReadStore.d.ts +25 -0
  73. package/dist/assetstore/unionReadStore.js +119 -0
  74. package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
  75. package/dist/benchmark/antiGeneBenchmark.js +4 -3
  76. package/dist/benchmark/antiGeneRollout.d.ts +2 -0
  77. package/dist/benchmark/antiGeneRollout.js +4 -3
  78. package/dist/benchmark/index.d.ts +3 -1
  79. package/dist/benchmark/index.js +3 -1
  80. package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
  81. package/dist/benchmark/selectionFlatAbstention.js +481 -0
  82. package/dist/benchmark/triggerShift.d.ts +62 -0
  83. package/dist/benchmark/triggerShift.js +106 -0
  84. package/dist/bootstrap/envFingerprint.d.ts +9 -0
  85. package/dist/bootstrap/envFingerprint.js +5 -0
  86. package/dist/bootstrap/index.d.ts +3 -1
  87. package/dist/bootstrap/index.js +3 -1
  88. package/dist/bootstrap/lifecycleBootstrap.d.ts +111 -0
  89. package/dist/bootstrap/lifecycleBootstrap.js +433 -0
  90. package/dist/bootstrap/v1EnvCompat.d.ts +113 -0
  91. package/dist/bootstrap/v1EnvCompat.js +303 -0
  92. package/dist/events/eventArchive.d.ts +2 -0
  93. package/dist/events/eventArchive.js +13 -3
  94. package/dist/events/eventSchema.d.ts +7 -7
  95. package/dist/events/eventStore.d.ts +2 -0
  96. package/dist/events/eventStore.js +5 -1
  97. package/dist/events/ingest.d.ts +2 -1
  98. package/dist/events/ingest.js +15 -0
  99. package/dist/events/paths.d.ts +12 -10
  100. package/dist/events/paths.js +24 -20
  101. package/dist/events/public.d.ts +2 -2
  102. package/dist/events/public.js +2 -2
  103. package/dist/events/reports.d.ts +2 -0
  104. package/dist/events/reports.js +4 -0
  105. package/dist/exec/autoExec.d.ts +68 -4
  106. package/dist/exec/autoExec.js +392 -33
  107. package/dist/exec/autonomousCycle.d.ts +31 -4
  108. package/dist/exec/autonomousCycle.js +71 -13
  109. package/dist/exec/claudeBridge.d.ts +80 -15
  110. package/dist/exec/claudeBridge.js +845 -76
  111. package/dist/exec/executionBinding.d.ts +414 -0
  112. package/dist/exec/executionBinding.js +588 -0
  113. package/dist/exec/index.d.ts +1 -0
  114. package/dist/exec/index.js +1 -0
  115. package/dist/exec/openPrRegistry.d.ts +8 -2
  116. package/dist/exec/openPrRegistry.js +32 -22
  117. package/dist/exec/prompt.js +14 -1
  118. package/dist/exec/proofOfWork.d.ts +1 -1
  119. package/dist/exec/proofOfWork.js +2 -2
  120. package/dist/exec/runnerRegistry.d.ts +153 -36
  121. package/dist/exec/runnerRegistry.js +848 -65
  122. package/dist/exec/selfPr.js +1 -7
  123. package/dist/feedback/envelope.d.ts +61 -0
  124. package/dist/feedback/envelope.js +168 -0
  125. package/dist/feedback/index.d.ts +1 -0
  126. package/dist/feedback/index.js +1 -0
  127. package/dist/hooks/hooks.js +1 -0
  128. package/dist/hub/assetCallLog.d.ts +35 -1
  129. package/dist/hub/assetCallLog.js +124 -1
  130. package/dist/hub/bindings.d.ts +8 -1
  131. package/dist/hub/bindings.js +29 -8
  132. package/dist/hub/capability.d.ts +130 -4
  133. package/dist/hub/conversationDistiller.d.ts +19 -0
  134. package/dist/hub/conversationDistiller.js +115 -37
  135. package/dist/hub/fake.d.ts +3 -2
  136. package/dist/hub/fake.js +2 -1
  137. package/dist/hub/index.d.ts +1 -0
  138. package/dist/hub/index.js +1 -0
  139. package/dist/hub/questionGenerator.d.ts +5 -1
  140. package/dist/hub/questionGenerator.js +8 -6
  141. package/dist/hub/recipeCompose.d.ts +27 -0
  142. package/dist/hub/recipeCompose.js +90 -0
  143. package/dist/hub/sanitize.js +122 -7
  144. package/dist/index.d.ts +6 -1
  145. package/dist/index.js +7 -1
  146. package/dist/issueReporter/index.d.ts +156 -0
  147. package/dist/issueReporter/index.js +1688 -0
  148. package/dist/mailbox/dispatch.d.ts +1 -1
  149. package/dist/mailbox/dispatch.js +22 -6
  150. package/dist/mailbox/envelope.d.ts +7 -1
  151. package/dist/mailbox/envelope.js +9 -2
  152. package/dist/mailbox/ipcServer.d.ts +12 -2
  153. package/dist/mailbox/ipcServer.js +183 -13
  154. package/dist/mailbox/store.d.ts +89 -3
  155. package/dist/mailbox/store.js +895 -41
  156. package/dist/modelCompatibility.d.ts +164 -0
  157. package/dist/modelCompatibility.js +309 -0
  158. package/dist/observers/valueDigestObserver.d.ts +9 -0
  159. package/dist/observers/valueDigestObserver.js +35 -2
  160. package/dist/ops/cleanup.js +1 -1
  161. package/dist/ops/evolutionGraphProjection.d.ts +20 -0
  162. package/dist/ops/evolutionGraphProjection.js +315 -0
  163. package/dist/ops/index.d.ts +2 -1
  164. package/dist/ops/index.js +2 -1
  165. package/dist/ops/savingsCore.js +1 -2
  166. package/dist/ops/selfUpdate.d.ts +18 -1
  167. package/dist/ops/selfUpdate.js +88 -23
  168. package/dist/ops/valueOutreach.d.ts +3 -1
  169. package/dist/ops/valueOutreach.js +5 -1
  170. package/dist/personality/schema.d.ts +24 -24
  171. package/dist/schema/evolutionGraph.d.ts +784 -0
  172. package/dist/schema/evolutionGraph.js +187 -0
  173. package/dist/schema/index.d.ts +1 -0
  174. package/dist/schema/index.js +1 -0
  175. package/dist/schema/proofOfWork.d.ts +125 -6
  176. package/dist/schema/proofOfWork.js +102 -4
  177. package/dist/schema/signal.d.ts +3 -3
  178. package/dist/schema/signal.js +1 -1
  179. package/dist/shadow/shadowHub.js +1 -0
  180. package/dist/signals/curriculum.d.ts +55 -0
  181. package/dist/signals/curriculum.js +202 -0
  182. package/dist/signals/cycleHistoryFromEvents.js +17 -8
  183. package/dist/signals/expand.d.ts +15 -1
  184. package/dist/signals/expand.js +169 -1
  185. package/dist/signals/extractor.d.ts +2 -2
  186. package/dist/signals/extractor.js +31 -6
  187. package/dist/signals/index.d.ts +4 -1
  188. package/dist/signals/index.js +4 -1
  189. package/dist/signals/metaSignals.d.ts +4 -0
  190. package/dist/signals/metaSignals.js +42 -0
  191. package/dist/signals/scopeVocabulary.d.ts +75 -0
  192. package/dist/signals/scopeVocabulary.js +91 -0
  193. package/dist/signals/signalGate.js +1 -1
  194. package/dist/signals/taskDomain.d.ts +22 -0
  195. package/dist/signals/taskDomain.js +43 -0
  196. package/dist/strategy/constraintAblation.d.ts +64 -0
  197. package/dist/strategy/constraintAblation.js +2820 -0
  198. package/dist/strategy/constraintAblationPredicates.d.ts +31 -0
  199. package/dist/strategy/constraintAblationPredicates.js +339 -0
  200. package/dist/strategy/index.d.ts +2 -1
  201. package/dist/strategy/index.js +2 -1
  202. package/dist/trace/index.d.ts +3 -1
  203. package/dist/trace/index.js +3 -1
  204. package/dist/trace/learningTrace.d.ts +216 -0
  205. package/dist/trace/learningTrace.js +298 -0
  206. package/dist/trace/proxyTurns.d.ts +31 -0
  207. package/dist/trace/proxyTurns.js +137 -0
  208. package/dist/trace/trajectory.d.ts +8 -0
  209. package/dist/trace/trajectory.js +14 -2
  210. package/dist/util/fetchPort.d.ts +1 -0
  211. package/dist/util/fetchPort.js +11 -0
  212. package/dist/util/fileLock.d.ts +113 -7
  213. package/dist/util/fileLock.js +1035 -80
  214. package/dist/util/index.d.ts +3 -1
  215. package/dist/util/index.js +2 -1
  216. package/dist/verify/index.d.ts +2 -1
  217. package/dist/verify/index.js +1 -1
  218. package/dist/verify/sandboxRunner.d.ts +30 -0
  219. package/dist/verify/sandboxRunner.js +370 -27
  220. package/dist/verify/sandboxedValidation.d.ts +16 -2
  221. package/dist/verify/sandboxedValidation.js +200 -22
  222. package/dist/verify/validation.d.ts +35 -4
  223. package/dist/verify/validation.js +164 -16
  224. package/dist/wire/geneHints.d.ts +88 -16
  225. package/dist/wire/geneHints.js +124 -15
  226. package/dist/wire/index.d.ts +8 -3
  227. package/dist/wire/index.js +2 -2
  228. package/dist/wire/schemaGate.d.ts +21 -0
  229. package/dist/wire/schemaGate.js +119 -10
  230. package/dist/workflow/dsl.d.ts +24 -3
  231. package/dist/workflow/dsl.js +4 -0
  232. package/dist/workflow/engine.d.ts +5 -1
  233. package/dist/workflow/engine.js +3 -0
  234. package/dist/workflow/index.d.ts +3 -1
  235. package/dist/workflow/index.js +3 -1
  236. package/dist/workflow/runtime.d.ts +110 -0
  237. package/dist/workflow/runtime.js +1298 -0
  238. package/dist/workflow/stateStore.d.ts +172 -0
  239. package/dist/workflow/stateStore.js +1044 -0
  240. package/package.json +12 -4
@@ -6,15 +6,9 @@
6
6
  // 3. leak scan of the diff before any push 6. cooldown + diff dedup
7
7
  // evaluateSelfPr is PURE (decision only); the `gh` call is an injected GhRunner seam so a test never creates a
8
8
  // real PR. Nothing here runs unless the caller has wired enabled:true AND a non-empty allowedRoots.
9
- import { resolve as resolvePath, sep } from 'node:path';
9
+ import { isWithinRoot } from './claudeBridge.js';
10
10
  import { defaultReadManifest, isObfuscatedFile } from './selfPrObfuscation.js';
11
11
  export const DEFAULT_SELF_PR_GATES = { minScore: 0.9, minStreak: 2, maxFiles: 5, maxLines: 200, cooldownMs: 24 * 60 * 60 * 1000 };
12
- /** Whether `child` is the same as or nested under `root` (both resolved absolute). */
13
- function isWithinRoot(child, root) {
14
- const c = resolvePath(child);
15
- const r = resolvePath(root);
16
- return c === r || c.startsWith(r.endsWith(sep) ? r : r + sep);
17
- }
18
12
  /**
19
13
  * Decide whether a self-PR may be created. Pure — combines every gate and returns the FIRST failing reason, so
20
14
  * the default (disabled, empty allowlist) is always a clean refusal that runs nothing.
@@ -0,0 +1,61 @@
1
+ export declare const PRIORITY_AXES: readonly ["task_success", "user_preference", "quality", "safety", "cost", "latency", "other"];
2
+ export declare const LABELS: readonly ["positive", "negative", "mixed", "neutral"];
3
+ export declare const ATTENTION_LEVELS: readonly ["full", "limited", "skimmed", "unknown"];
4
+ export declare const EVIDENCE_KINDS: readonly ["evolution_event", "evolution_outcome", "user_override", "review", "turn", "external"];
5
+ export type FeedbackPriorityAxis = typeof PRIORITY_AXES[number];
6
+ export type FeedbackLabel = typeof LABELS[number];
7
+ export type EvaluatorAttentionLevel = typeof ATTENTION_LEVELS[number];
8
+ export type FeedbackEvidenceKind = typeof EVIDENCE_KINDS[number];
9
+ export interface EvaluatorAttention {
10
+ level: EvaluatorAttentionLevel;
11
+ observed_items?: number;
12
+ elapsed_ms?: number;
13
+ }
14
+ export interface FeedbackEvidenceRef {
15
+ kind: FeedbackEvidenceKind;
16
+ id: string;
17
+ summary?: string;
18
+ }
19
+ export interface FeedbackEnvelope {
20
+ priority_axis: FeedbackPriorityAxis;
21
+ label: FeedbackLabel;
22
+ scalar: number;
23
+ indecision: boolean;
24
+ conflict: boolean;
25
+ evaluator_attention: EvaluatorAttention;
26
+ evidence_ref: FeedbackEvidenceRef;
27
+ uncertainty: number;
28
+ }
29
+ export interface FeedbackAggregate {
30
+ dominant_label: 'positive' | 'negative' | null;
31
+ uncertainty: number;
32
+ sample_count: number;
33
+ }
34
+ export interface FeedbackEnvelopeInput {
35
+ priority_axis?: unknown;
36
+ priorityAxis?: unknown;
37
+ scalar?: unknown;
38
+ indecision?: unknown;
39
+ conflict?: unknown;
40
+ evaluator_attention?: unknown;
41
+ evaluatorAttention?: unknown;
42
+ evidence_ref?: unknown;
43
+ evidenceRef?: unknown;
44
+ }
45
+ export interface FeedbackOutcomeLike {
46
+ score?: unknown;
47
+ user_override?: unknown;
48
+ }
49
+ export declare function clamp01(value: unknown): number;
50
+ export declare function labelFromScalar(value: unknown): FeedbackLabel;
51
+ export declare function normalizeAttention(input: unknown): EvaluatorAttention;
52
+ export declare function evidenceRef(kind: unknown, id: unknown, options?: {
53
+ summary?: unknown;
54
+ }): FeedbackEvidenceRef;
55
+ export declare function normalizeEvidenceRef(input: unknown): FeedbackEvidenceRef;
56
+ export declare function envelopeUncertainty(scalar: unknown, attentionLevel: EvaluatorAttentionLevel, indecision: boolean, conflict: boolean): number;
57
+ export declare function fromScalarFeedback(options?: FeedbackEnvelopeInput | null): FeedbackEnvelope;
58
+ export declare function fromOutcomeScalar(outcome: unknown, options?: Omit<FeedbackEnvelopeInput, 'scalar'>): FeedbackEnvelope | null;
59
+ export declare function withConflict(envelope: FeedbackEnvelope): FeedbackEnvelope;
60
+ export declare function withIndecision(envelope: FeedbackEnvelope): FeedbackEnvelope;
61
+ export declare function aggregateFeedbackEnvelopes(envelopes: readonly FeedbackEnvelope[] | null | undefined): FeedbackAggregate;
@@ -0,0 +1,168 @@
1
+ export const PRIORITY_AXES = Object.freeze([
2
+ 'task_success',
3
+ 'user_preference',
4
+ 'quality',
5
+ 'safety',
6
+ 'cost',
7
+ 'latency',
8
+ 'other',
9
+ ]);
10
+ export const LABELS = Object.freeze(['positive', 'negative', 'mixed', 'neutral']);
11
+ export const ATTENTION_LEVELS = Object.freeze(['full', 'limited', 'skimmed', 'unknown']);
12
+ export const EVIDENCE_KINDS = Object.freeze([
13
+ 'evolution_event',
14
+ 'evolution_outcome',
15
+ 'user_override',
16
+ 'review',
17
+ 'turn',
18
+ 'external',
19
+ ]);
20
+ export function clamp01(value) {
21
+ const n = Number(value);
22
+ if (!Number.isFinite(n))
23
+ return 0.5;
24
+ if (n < 0)
25
+ return 0;
26
+ if (n > 1)
27
+ return 1;
28
+ return n;
29
+ }
30
+ export function labelFromScalar(value) {
31
+ const scalar = clamp01(value);
32
+ if (scalar >= 0.6)
33
+ return 'positive';
34
+ if (scalar <= 0.4)
35
+ return 'negative';
36
+ return 'mixed';
37
+ }
38
+ export function normalizeAttention(input) {
39
+ const record = asRecord(input);
40
+ if (!record)
41
+ return { level: 'unknown' };
42
+ const attention = {
43
+ level: enumValue(record['level'], ATTENTION_LEVELS, 'unknown'),
44
+ };
45
+ const observedItems = nonNegativeInteger(record['observed_items']);
46
+ const elapsedMs = nonNegativeInteger(record['elapsed_ms']);
47
+ if (observedItems !== null)
48
+ attention.observed_items = observedItems;
49
+ if (elapsedMs !== null)
50
+ attention.elapsed_ms = elapsedMs;
51
+ return attention;
52
+ }
53
+ export function evidenceRef(kind, id, options = {}) {
54
+ const ref = {
55
+ kind: enumValue(kind, EVIDENCE_KINDS, 'external'),
56
+ id: String(id ?? '').trim() || 'unknown',
57
+ };
58
+ if (typeof options.summary === 'string' && options.summary.trim()) {
59
+ ref.summary = options.summary.trim();
60
+ }
61
+ return ref;
62
+ }
63
+ export function normalizeEvidenceRef(input) {
64
+ const record = asRecord(input);
65
+ if (!record)
66
+ return evidenceRef('external', 'unknown');
67
+ return evidenceRef(record['kind'], record['id'], { summary: record['summary'] });
68
+ }
69
+ export function envelopeUncertainty(scalar, attentionLevel, indecision, conflict) {
70
+ const normalizedScalar = clamp01(scalar);
71
+ const ambiguity = 1 - Math.abs(normalizedScalar - 0.5) * 2;
72
+ const attentionPenalty = {
73
+ full: 0,
74
+ limited: 0.15,
75
+ skimmed: 0.30,
76
+ unknown: 0.20,
77
+ };
78
+ return clamp01(0.10
79
+ + 0.30 * ambiguity
80
+ + attentionPenalty[attentionLevel]
81
+ + (indecision ? 0.25 : 0)
82
+ + (conflict ? 0.35 : 0));
83
+ }
84
+ export function fromScalarFeedback(options = {}) {
85
+ const input = options ?? {};
86
+ const scalar = clamp01(input.scalar);
87
+ const label = labelFromScalar(scalar);
88
+ const indecision = Boolean(input.indecision) || label === 'mixed';
89
+ const conflict = Boolean(input.conflict);
90
+ const evaluatorAttention = normalizeAttention(input.evaluator_attention ?? input.evaluatorAttention);
91
+ const priorityAxis = enumValue(input.priority_axis ?? input.priorityAxis, PRIORITY_AXES, 'task_success');
92
+ const evidence = input.evidence_ref ?? input.evidenceRef ?? evidenceRef('external', 'unknown');
93
+ return {
94
+ priority_axis: priorityAxis,
95
+ label,
96
+ scalar,
97
+ indecision,
98
+ conflict,
99
+ evaluator_attention: evaluatorAttention,
100
+ evidence_ref: normalizeEvidenceRef(evidence),
101
+ uncertainty: envelopeUncertainty(scalar, evaluatorAttention.level, indecision, conflict),
102
+ };
103
+ }
104
+ export function fromOutcomeScalar(outcome, options = {}) {
105
+ const record = asRecord(outcome);
106
+ if (!record)
107
+ return null;
108
+ const scalar = record['user_override'] ?? record['score'];
109
+ if (scalar === null || scalar === undefined)
110
+ return null;
111
+ return fromScalarFeedback({ ...options, scalar });
112
+ }
113
+ export function withConflict(envelope) {
114
+ const evaluatorAttention = normalizeAttention(envelope.evaluator_attention);
115
+ return {
116
+ ...envelope,
117
+ conflict: true,
118
+ evaluator_attention: evaluatorAttention,
119
+ uncertainty: envelopeUncertainty(envelope.scalar, evaluatorAttention.level, envelope.indecision, true),
120
+ };
121
+ }
122
+ export function withIndecision(envelope) {
123
+ const evaluatorAttention = normalizeAttention(envelope.evaluator_attention);
124
+ return {
125
+ ...envelope,
126
+ indecision: true,
127
+ evaluator_attention: evaluatorAttention,
128
+ uncertainty: envelopeUncertainty(envelope.scalar, evaluatorAttention.level, true, envelope.conflict),
129
+ };
130
+ }
131
+ export function aggregateFeedbackEnvelopes(envelopes) {
132
+ const list = Array.isArray(envelopes) ? envelopes.filter(Boolean) : [];
133
+ if (list.length === 0)
134
+ return { dominant_label: null, uncertainty: 1, sample_count: 0 };
135
+ const sampleCount = list.length;
136
+ const meanUncertainty = list.reduce((sum, envelope) => sum + clamp01(envelope.uncertainty), 0) / sampleCount;
137
+ const hasPositive = list.some((envelope) => envelope.label === 'positive');
138
+ const hasNegative = list.some((envelope) => envelope.label === 'negative');
139
+ const hasConflict = list.some((envelope) => envelope.conflict) || (hasPositive && hasNegative);
140
+ const hasIndecision = list.some((envelope) => envelope.indecision || envelope.label === 'mixed' || envelope.label === 'neutral');
141
+ const hasLowAttention = list.some((envelope) => normalizeAttention(envelope.evaluator_attention).level !== 'full');
142
+ const uncertainty = clamp01(meanUncertainty
143
+ + (hasConflict ? 0.25 : 0)
144
+ + (hasIndecision ? 0.10 : 0)
145
+ + (hasLowAttention ? 0.10 : 0));
146
+ let dominantLabel = null;
147
+ if (!hasConflict && !hasLowAttention && uncertainty < 0.5) {
148
+ if (hasPositive)
149
+ dominantLabel = 'positive';
150
+ else if (hasNegative)
151
+ dominantLabel = 'negative';
152
+ }
153
+ return { dominant_label: dominantLabel, uncertainty, sample_count: sampleCount };
154
+ }
155
+ function enumValue(value, allowed, fallback) {
156
+ return typeof value === 'string' && allowed.includes(value)
157
+ ? value
158
+ : fallback;
159
+ }
160
+ function nonNegativeInteger(value) {
161
+ const n = Number(value);
162
+ return Number.isFinite(n) ? Math.max(0, Math.floor(n)) : null;
163
+ }
164
+ function asRecord(value) {
165
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
166
+ ? value
167
+ : null;
168
+ }
@@ -0,0 +1 @@
1
+ export * from './envelope.js';
@@ -0,0 +1 @@
1
+ export * from './envelope.js';
@@ -69,6 +69,7 @@ export function composeSessionStartWithRecap(base, recapCtx, estimateTokensOrOpt
69
69
  export function signalsFromToolUse(event) {
70
70
  const turn = {
71
71
  toolName: event.toolName,
72
+ ...(event.toolResult ? { toolResult: event.toolResult } : {}),
72
73
  ...(event.isError && event.toolResult ? { errorMessage: event.toolResult } : {}),
73
74
  ...(event.text ? { text: event.text } : {}),
74
75
  isMeta: false,
@@ -1,4 +1,5 @@
1
- export type AssetCallAction = 'hub_search_hit' | 'hub_search_miss' | 'asset_reuse' | 'asset_reference' | 'asset_publish' | 'asset_publish_skip' | 'hub_review_submitted' | 'hub_review_rejected' | 'hub_review_failed';
1
+ export type AssetCallAction = 'hub_search_hit' | 'hub_search_miss' | 'asset_reuse' | 'asset_reference' | 'asset_publish' | 'asset_publish_skip' | 'asset_inject' | 'asset_inject_shadow' | 'hub_review_submitted' | 'hub_review_rejected' | 'hub_review_failed';
2
+ export type TokensSavedBasis = 'measured' | 'cost_index' | 'estimated_blast_radius' | 'estimated_default';
2
3
  export interface AssetCallEntry {
3
4
  run_id?: string | null;
4
5
  action: AssetCallAction;
@@ -10,6 +11,9 @@ export interface AssetCallEntry {
10
11
  mode?: 'direct' | 'reference';
11
12
  signals?: readonly string[];
12
13
  reason?: string;
14
+ tokens_saved?: number | null;
15
+ tokens_saved_basis?: TokensSavedBasis | null;
16
+ tokens_spent?: number | null;
13
17
  extra?: Record<string, unknown>;
14
18
  }
15
19
  export interface AssetCallRecord extends AssetCallEntry {
@@ -30,6 +34,32 @@ export interface CallLogSummary {
30
34
  by_action: Record<string, number>;
31
35
  entries: AssetCallRecord[];
32
36
  }
37
+ export interface AssetReuseAttribution {
38
+ asset_id: string;
39
+ source_node_id: string | null;
40
+ chain_id: string | null;
41
+ reuse: number;
42
+ reference: number;
43
+ tokens_saved: number;
44
+ }
45
+ export interface ReuseAttributionSummary {
46
+ total_reuse: number;
47
+ total_reference: number;
48
+ total_tokens_saved: number;
49
+ by_asset: AssetReuseAttribution[];
50
+ }
51
+ export interface ReuseSavingsMetric {
52
+ tokens_saved: number;
53
+ tokens_saved_basis: TokensSavedBasis;
54
+ }
55
+ /** Measured derivation cost carried by an asset (or by a Hub wrapper's payload). */
56
+ export declare function assetDerivationTokenCost(asset: unknown): number | undefined;
57
+ /**
58
+ * Attribute savings without inventing a measured value: asset telemetry wins, then this node's publish-cost
59
+ * index, then the savings-core blast/default estimator. Reference reuse applies the same fractional discount
60
+ * to measured and estimated costs.
61
+ */
62
+ export declare function reuseSavingsForAsset(asset: unknown, mode: 'direct' | 'reference', indexedTokenCost?: unknown): ReuseSavingsMetric;
33
63
  export declare class AssetCallLog {
34
64
  private readonly path;
35
65
  private readonly now;
@@ -40,4 +70,8 @@ export declare class AssetCallLog {
40
70
  read(opts?: ReadOpts): AssetCallRecord[];
41
71
  /** Totals + per-action counts (for CLI / observability). */
42
72
  summarize(opts?: ReadOpts): CallLogSummary;
73
+ /** Local-only attribution rollup over reuse/reference audit rows. */
74
+ reuseAttributionSummary(opts?: ReadOpts): ReuseAttributionSummary;
75
+ /** Later valid publish rows win; malformed/non-positive costs never erase a prior measurement. */
76
+ assetCostIndex(opts?: ReadOpts): Record<string, number>;
43
77
  }
@@ -5,6 +5,69 @@
5
5
  // or fails an evolution. The log path is injected so it's testable without a real home dir.
6
6
  import { appendFileSync, existsSync, mkdirSync, readFileSync } from 'node:fs';
7
7
  import { dirname } from 'node:path';
8
+ import { REUSE_ESTIMATOR, reuseEstimate } from '../ops/savingsCore.js';
9
+ function objectRecord(value) {
10
+ return value !== null && typeof value === 'object' && !Array.isArray(value)
11
+ ? value
12
+ : undefined;
13
+ }
14
+ function nonEmptyString(value) {
15
+ return typeof value === 'string' && value.length > 0 ? value : undefined;
16
+ }
17
+ function positiveNumber(value, coerceLegacy = false) {
18
+ const number = typeof value === 'number' ? value : coerceLegacy ? Number(value) : Number.NaN;
19
+ return Number.isFinite(number) && number > 0 ? number : undefined;
20
+ }
21
+ function nestedAssetRecord(asset) {
22
+ const direct = objectRecord(asset);
23
+ return { ...(direct ? { direct } : {}), ...(objectRecord(direct?.['payload']) ? { payload: objectRecord(direct?.['payload']) } : {}) };
24
+ }
25
+ /** Measured derivation cost carried by an asset (or by a Hub wrapper's payload). */
26
+ export function assetDerivationTokenCost(asset) {
27
+ const records = nestedAssetRecord(asset);
28
+ for (const record of [records.direct, records.payload]) {
29
+ const derivation = objectRecord(record?.['derivation_tokens']);
30
+ const total = positiveNumber(derivation?.['total_tokens']);
31
+ if (total !== undefined)
32
+ return total;
33
+ }
34
+ return undefined;
35
+ }
36
+ function assetBlastRadiusLines(asset) {
37
+ const records = nestedAssetRecord(asset);
38
+ for (const record of [records.direct, records.payload]) {
39
+ const blast = objectRecord(record?.['blast_radius']);
40
+ const lines = positiveNumber(blast?.['lines']);
41
+ if (lines !== undefined)
42
+ return lines;
43
+ }
44
+ return undefined;
45
+ }
46
+ /**
47
+ * Attribute savings without inventing a measured value: asset telemetry wins, then this node's publish-cost
48
+ * index, then the savings-core blast/default estimator. Reference reuse applies the same fractional discount
49
+ * to measured and estimated costs.
50
+ */
51
+ export function reuseSavingsForAsset(asset, mode, indexedTokenCost) {
52
+ const measured = assetDerivationTokenCost(asset);
53
+ const indexed = measured === undefined ? positiveNumber(indexedTokenCost) : undefined;
54
+ const cost = measured ?? indexed;
55
+ if (cost !== undefined) {
56
+ return {
57
+ tokens_saved: Math.round(mode === 'reference' ? cost * REUSE_ESTIMATOR.reference_saving_fraction : cost),
58
+ tokens_saved_basis: measured !== undefined ? 'measured' : 'cost_index',
59
+ };
60
+ }
61
+ const estimated = reuseEstimate(assetBlastRadiusLines(asset), mode);
62
+ return { tokens_saved: estimated.tokens_saved, tokens_saved_basis: estimated.basis };
63
+ }
64
+ function isAssetCallRecord(value) {
65
+ const record = objectRecord(value);
66
+ return record !== undefined
67
+ && typeof record['timestamp'] === 'string'
68
+ && typeof record['action'] === 'string'
69
+ && record['action'].length > 0;
70
+ }
8
71
  export class AssetCallLog {
9
72
  path;
10
73
  now;
@@ -33,7 +96,9 @@ export class AssetCallLog {
33
96
  if (!line)
34
97
  continue;
35
98
  try {
36
- entries.push(JSON.parse(line));
99
+ const parsed = JSON.parse(line);
100
+ if (isAssetCallRecord(parsed))
101
+ entries.push(parsed);
37
102
  }
38
103
  catch { /* skip corrupt */ }
39
104
  }
@@ -69,4 +134,62 @@ export class AssetCallLog {
69
134
  }
70
135
  return { total_entries: entries.length, unique_assets: assets.size, unique_runs: runs.size, by_action: byAction, entries };
71
136
  }
137
+ /** Local-only attribution rollup over reuse/reference audit rows. */
138
+ reuseAttributionSummary(opts = {}) {
139
+ const entries = this.read(opts).filter((entry) => entry.action === 'asset_reuse' || entry.action === 'asset_reference');
140
+ const byAsset = new Map();
141
+ let totalTokensSaved = 0;
142
+ let totalReuse = 0;
143
+ let totalReference = 0;
144
+ for (const entry of entries) {
145
+ const id = nonEmptyString(entry.asset_id) ?? '(unknown)';
146
+ let aggregate = byAsset.get(id);
147
+ if (!aggregate) {
148
+ aggregate = {
149
+ asset_id: id,
150
+ source_node_id: nonEmptyString(entry.source_node_id) ?? null,
151
+ chain_id: nonEmptyString(entry.chain_id) ?? null,
152
+ reuse: 0,
153
+ reference: 0,
154
+ tokens_saved: 0,
155
+ };
156
+ byAsset.set(id, aggregate);
157
+ }
158
+ if (entry.action === 'asset_reuse') {
159
+ aggregate.reuse += 1;
160
+ totalReuse += 1;
161
+ }
162
+ else {
163
+ aggregate.reference += 1;
164
+ totalReference += 1;
165
+ }
166
+ const tokensSaved = positiveNumber(entry['tokens_saved'], true);
167
+ if (tokensSaved !== undefined) {
168
+ aggregate.tokens_saved += tokensSaved;
169
+ totalTokensSaved += tokensSaved;
170
+ }
171
+ aggregate.source_node_id ??= nonEmptyString(entry.source_node_id) ?? null;
172
+ aggregate.chain_id ??= nonEmptyString(entry.chain_id) ?? null;
173
+ }
174
+ const byAssetRows = [...byAsset.values()].sort((left, right) => (right.reuse + right.reference) - (left.reuse + left.reference));
175
+ return {
176
+ total_reuse: totalReuse,
177
+ total_reference: totalReference,
178
+ total_tokens_saved: totalTokensSaved,
179
+ by_asset: byAssetRows,
180
+ };
181
+ }
182
+ /** Later valid publish rows win; malformed/non-positive costs never erase a prior measurement. */
183
+ assetCostIndex(opts = {}) {
184
+ const costs = new Map();
185
+ for (const entry of this.read(opts)) {
186
+ if (entry.action !== 'asset_publish')
187
+ continue;
188
+ const assetId = nonEmptyString(entry.asset_id);
189
+ const tokensSpent = positiveNumber(entry['tokens_spent'], true);
190
+ if (assetId && tokensSpent !== undefined)
191
+ costs.set(assetId, tokensSpent);
192
+ }
193
+ return Object.fromEntries(costs);
194
+ }
72
195
  }
@@ -1,4 +1,4 @@
1
- import type { HubCapability, HubBindings } from './capability.js';
1
+ import type { HubCapability, HubBindings, PublishReceipt } from './capability.js';
2
2
  import { type LeakCheckMode } from './sanitize.js';
3
3
  /** publish 回执非 accepted → 抛此错; SyncEngine 据 terminal 决定是否重试. */
4
4
  export declare class PublishRejectedError extends Error {
@@ -19,6 +19,13 @@ export interface HubBindingsOptions {
19
19
  mode?: LeakCheckMode;
20
20
  };
21
21
  }
22
+ /**
23
+ * Internal publish-handler result. `submittedAssetIds` identifies the exact sanitized
24
+ * content sent to the Hub, even when the Hub's accepted receipt omits asset ids.
25
+ */
26
+ export interface PublishDispatchResult extends PublishReceipt {
27
+ submittedAssetIds?: string[];
28
+ }
22
29
  /**
23
30
  * 把 HubCapability 接到 core 两 seam(M6-1):
24
31
  * - asProxyHandler: Dispatcher.handlers.proxy — 按 envelope.type 路由到 hub. 抛错→store.fail()重试;
@@ -37,14 +37,18 @@ export function makeHubBindings(cap, options = {}) {
37
37
  const sanEnabled = sanCfg.enabled ?? true;
38
38
  const sanEnv = sanCfg.env ?? (typeof process !== 'undefined' ? process.env : {});
39
39
  /** Single publish chokepoint: sanitize (on by default) → strict + leak found → refuse (terminal, not retryable) → cap.publish the redacted bundle. */
40
- const publishSanitized = (bundle) => {
40
+ const publishSanitized = async (bundle, publishOptions) => {
41
41
  if (!sanEnabled)
42
- return cap.publish(bundle);
42
+ return publishOptions ? cap.publish(bundle, publishOptions) : cap.publish(bundle);
43
43
  const r = sanitizeBundle(bundle, { env: sanEnv, ...(sanCfg.mode ? { mode: sanCfg.mode } : {}) });
44
44
  if (r.blocked) {
45
45
  throw new PublishRejectedError('leak_blocked', true, `sensitive data detected before publish, refused (not retryable): ${summarizeLeaks(r.leaks)}`);
46
46
  }
47
- return cap.publish(r.bundle);
47
+ const receipt = publishOptions
48
+ ? await cap.publish(r.bundle, publishOptions)
49
+ : await cap.publish(r.bundle);
50
+ const submittedAssetIds = contentAssetIds(r.bundle);
51
+ return submittedAssetIds ? { ...receipt, submittedAssetIds } : receipt;
48
52
  };
49
53
  return {
50
54
  asProxyHandler() {
@@ -54,9 +58,10 @@ export function makeHubBindings(cap, options = {}) {
54
58
  // payload 可为 bundle {assets:[...]} 或单资产; 统一成 bundle 数组发.
55
59
  const p = e.payload;
56
60
  const bundle = Array.isArray(p.assets) ? p.assets : [p];
57
- const r = await publishSanitized(bundle);
58
- if (r.status !== 'accepted')
59
- throw new PublishRejectedError(r.status, r.terminal ?? true, r.reason);
61
+ const r = await publishSanitized(bundle, { idempotencyKey: e.idempotencyKey });
62
+ if (r.status !== 'accepted') {
63
+ throw new PublishRejectedError(r.rejection?.code ?? r.status, r.terminal ?? true, r.reason, r.rejection?.retryAfterMs);
64
+ }
60
65
  return r;
61
66
  }
62
67
  case 'task_claim': {
@@ -67,7 +72,10 @@ export function makeHubBindings(cap, options = {}) {
67
72
  const p = e.payload;
68
73
  const taskId = firstString(p.taskId, p.task_id);
69
74
  const assetId = firstString(p.assetId, p.asset_id);
70
- const claimId = firstString(p.claimId, p.claim_id) ?? taskId ?? '';
75
+ const claimId = firstString(p.claimId, p.claim_id);
76
+ if (!claimId) {
77
+ throw new PublishRejectedError('invalid_task_complete', true, 'claim_id is required because task claim handles are opaque');
78
+ }
71
79
  const context = taskId && assetId ? { taskId, assetId } : undefined;
72
80
  return cap.task.complete(claimId, p.result, context);
73
81
  }
@@ -95,6 +103,19 @@ export function makeHubBindings(cap, options = {}) {
95
103
  },
96
104
  };
97
105
  }
106
+ function contentAssetIds(bundle) {
107
+ const ids = bundle.map((asset) => asset.asset_id);
108
+ return ids.every((id) => typeof id === 'string' && /^sha256:[a-f0-9]{64}$/i.test(id))
109
+ ? ids
110
+ : undefined;
111
+ }
98
112
  function firstString(...values) {
99
- return values.find((value) => typeof value === 'string' && value.length > 0);
113
+ for (const value of values) {
114
+ if (typeof value !== 'string')
115
+ continue;
116
+ const trimmed = value.trim();
117
+ if (trimmed)
118
+ return trimmed;
119
+ }
120
+ return undefined;
100
121
  }