pi-fabric 0.78.0 → 0.79.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/README.md +1 -0
- package/dist/chunks/chunk-5IFANSBG.js +1046 -0
- package/dist/chunks/chunk-5IFANSBG.js.map +7 -0
- package/dist/chunks/{chunk-DYEOFQ64.js → chunk-5OVYFN4A.js} +2 -2
- package/dist/chunks/{chunk-E3V22XGU.js → chunk-6GRNSFDJ.js} +2 -1
- package/dist/chunks/chunk-6GRNSFDJ.js.map +7 -0
- package/dist/chunks/{chunk-DUIHQAIZ.js → chunk-ESE4P267.js} +2 -2
- package/dist/chunks/chunk-HDTW3CML.js +273 -0
- package/dist/chunks/chunk-HDTW3CML.js.map +7 -0
- package/dist/chunks/{chunk-3QPTSKDG.js → chunk-NXTORPRY.js} +46 -13
- package/dist/chunks/chunk-NXTORPRY.js.map +7 -0
- package/dist/chunks/chunk-OTB7OOQS.js +84 -0
- package/dist/chunks/chunk-OTB7OOQS.js.map +7 -0
- package/dist/chunks/{chunk-PFF3X7OO.js → chunk-R32YJVKM.js} +2 -2
- package/dist/chunks/{chunk-2AF5ANEB.js → chunk-UKZ452XN.js} +4 -4
- package/dist/chunks/{chunk-2E3CAABA.js → chunk-XWRRKI63.js} +11 -4
- package/dist/chunks/chunk-XWRRKI63.js.map +7 -0
- package/dist/chunks/{fabric-runtime-state-H2NCRJWK.js → fabric-runtime-state-YKNG3IDT.js} +59 -162
- package/dist/chunks/fabric-runtime-state-YKNG3IDT.js.map +7 -0
- package/dist/chunks/{settings-FUL3U3N4.js → settings-OXHVJMMW.js} +3 -3
- package/dist/commands/fabric.d.ts.map +1 -1
- package/dist/config.d.ts +4 -0
- package/dist/config.d.ts.map +1 -1
- package/dist/core/action-registry.d.ts.map +1 -1
- package/dist/core/action-registry.js +2 -1
- package/dist/core/file-lock.d.ts +11 -0
- package/dist/core/file-lock.d.ts.map +1 -0
- package/dist/entropy/active.d.ts +7 -0
- package/dist/entropy/active.d.ts.map +1 -0
- package/dist/entropy/compiled-store.d.ts +13 -0
- package/dist/entropy/compiled-store.d.ts.map +1 -0
- package/dist/entropy/compiled-surface.d.ts +43 -0
- package/dist/entropy/compiled-surface.d.ts.map +1 -0
- package/dist/entropy/compiler.d.ts +23 -0
- package/dist/entropy/compiler.d.ts.map +1 -0
- package/dist/entropy/corpus.d.ts +9 -0
- package/dist/entropy/corpus.d.ts.map +1 -0
- package/dist/entropy/fingerprint.d.ts +9 -0
- package/dist/entropy/fingerprint.d.ts.map +1 -0
- package/dist/entropy/index.d.ts +11 -0
- package/dist/entropy/index.d.ts.map +1 -0
- package/dist/entropy/index.js +91 -0
- package/dist/entropy/index.js.map +7 -0
- package/dist/entropy/meter.d.ts +9 -0
- package/dist/entropy/meter.d.ts.map +1 -0
- package/dist/entropy/passes.d.ts +12 -0
- package/dist/entropy/passes.d.ts.map +1 -0
- package/dist/entropy/sessions.d.ts +32 -0
- package/dist/entropy/sessions.d.ts.map +1 -0
- package/dist/entropy/surface.d.ts +29 -0
- package/dist/entropy/surface.d.ts.map +1 -0
- package/dist/entropy/types.d.ts +151 -0
- package/dist/entropy/types.d.ts.map +1 -0
- package/dist/fabric-runtime-state.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +264 -14
- package/dist/index.js.map +3 -3
- package/dist/memory/discovery.d.ts +2 -0
- package/dist/memory/discovery.d.ts.map +1 -1
- package/dist/memory/discovery.js +5 -3
- package/dist/providers/memory-provider.js +2 -2
- package/dist/repairs/store.d.ts.map +1 -1
- package/dist/residency/host.js +4 -4
- package/dist/residency/pi-entry.js +4 -4
- package/docs/certification.md +2 -1
- package/docs/configuration.md +2 -0
- package/docs/entropy.md +259 -0
- package/docs/interface.md +1 -0
- package/docs/repairs.md +2 -0
- package/package.json +2 -1
- package/dist/chunks/chunk-2E3CAABA.js.map +0 -7
- package/dist/chunks/chunk-3QPTSKDG.js.map +0 -7
- package/dist/chunks/chunk-E3V22XGU.js.map +0 -7
- package/dist/chunks/fabric-runtime-state-H2NCRJWK.js.map +0 -7
- /package/dist/chunks/{chunk-DYEOFQ64.js.map → chunk-5OVYFN4A.js.map} +0 -0
- /package/dist/chunks/{chunk-DUIHQAIZ.js.map → chunk-ESE4P267.js.map} +0 -0
- /package/dist/chunks/{chunk-PFF3X7OO.js.map → chunk-R32YJVKM.js.map} +0 -0
- /package/dist/chunks/{chunk-2AF5ANEB.js.map → chunk-UKZ452XN.js.map} +0 -0
- /package/dist/chunks/{settings-FUL3U3N4.js.map → settings-OXHVJMMW.js.map} +0 -0
|
@@ -0,0 +1,1046 @@
|
|
|
1
|
+
import {
|
|
2
|
+
COMPILED_SURFACE_VERSION,
|
|
3
|
+
MAX_COMPILED_SURFACE_PROPOSALS,
|
|
4
|
+
applyCompiledSurface,
|
|
5
|
+
replaySuccessfulCalls,
|
|
6
|
+
schemaDigest
|
|
7
|
+
} from "./chunk-OTB7OOQS.js";
|
|
8
|
+
import {
|
|
9
|
+
sessionDirForCwd
|
|
10
|
+
} from "./chunk-6GRNSFDJ.js";
|
|
11
|
+
import {
|
|
12
|
+
stableJsonHash
|
|
13
|
+
} from "./chunk-CIJPRYAP.js";
|
|
14
|
+
import {
|
|
15
|
+
FABRIC_EXECUTION_TRACE_KIND,
|
|
16
|
+
readFabricExecutionTraceV1
|
|
17
|
+
} from "./chunk-IDTQ2B5W.js";
|
|
18
|
+
|
|
19
|
+
// src/entropy/types.ts
|
|
20
|
+
var ENTROPY_METRIC_VERSION = 2;
|
|
21
|
+
var ENTROPY_WEIGHTS = {
|
|
22
|
+
shape: 1,
|
|
23
|
+
failureStage: 1,
|
|
24
|
+
churn: 4,
|
|
25
|
+
navigation: 4,
|
|
26
|
+
flow: 1,
|
|
27
|
+
lexicon: 2,
|
|
28
|
+
staticFreedom: 0.25
|
|
29
|
+
};
|
|
30
|
+
var ENTROPY_INVOCATION_STAGES = ["resolve", "prepare", "validate"];
|
|
31
|
+
|
|
32
|
+
// src/entropy/fingerprint.ts
|
|
33
|
+
var SIGNATURE_MAX_KEYS = 32;
|
|
34
|
+
var SIGNATURE_MAX_DEPTH = 3;
|
|
35
|
+
var ENUM_SATURATION_BITS = 6;
|
|
36
|
+
var isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
37
|
+
var roundMetric = (value) => {
|
|
38
|
+
const rounded = Math.round(value * 1e6) / 1e6;
|
|
39
|
+
return rounded === 0 ? 0 : rounded;
|
|
40
|
+
};
|
|
41
|
+
var compareCodeUnits = (left, right) => left < right ? -1 : left > right ? 1 : 0;
|
|
42
|
+
var typeTag = (value, depth) => {
|
|
43
|
+
if (value === null) return "null";
|
|
44
|
+
if (Array.isArray(value)) return "arr";
|
|
45
|
+
switch (typeof value) {
|
|
46
|
+
case "string":
|
|
47
|
+
return "str";
|
|
48
|
+
case "number":
|
|
49
|
+
return "num";
|
|
50
|
+
case "boolean":
|
|
51
|
+
return "bool";
|
|
52
|
+
case "object": {
|
|
53
|
+
if (depth >= SIGNATURE_MAX_DEPTH) return "obj";
|
|
54
|
+
try {
|
|
55
|
+
const record = value;
|
|
56
|
+
const keys = Object.keys(record).sort();
|
|
57
|
+
const head = keys.slice(0, SIGNATURE_MAX_KEYS).map((key) => `${key}:${typeTag(record[key], depth + 1)}`);
|
|
58
|
+
if (keys.length > SIGNATURE_MAX_KEYS) {
|
|
59
|
+
head.push(`\u2026+${keys.length - SIGNATURE_MAX_KEYS}`);
|
|
60
|
+
}
|
|
61
|
+
return `{${head.join(",")}}`;
|
|
62
|
+
} catch {
|
|
63
|
+
return "obj";
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
default:
|
|
67
|
+
return "other";
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
var shapeSignature = (args) => {
|
|
71
|
+
const keys = Object.keys(args).sort();
|
|
72
|
+
const head = keys.slice(0, SIGNATURE_MAX_KEYS).map((key) => `${key}:${typeTag(args[key], 1)}`);
|
|
73
|
+
if (keys.length > SIGNATURE_MAX_KEYS) {
|
|
74
|
+
head.push(`\u2026+${keys.length - SIGNATURE_MAX_KEYS}`);
|
|
75
|
+
}
|
|
76
|
+
return `(${head.join(",")})`;
|
|
77
|
+
};
|
|
78
|
+
var signatureDistance = (left, right) => {
|
|
79
|
+
if (left === right) return 0;
|
|
80
|
+
const bound = Math.max(left.length, right.length);
|
|
81
|
+
if (bound === 0) return 0;
|
|
82
|
+
let previous = [];
|
|
83
|
+
for (let index = 0; index <= right.length; index++) {
|
|
84
|
+
previous.push(index);
|
|
85
|
+
}
|
|
86
|
+
for (let index = 1; index <= left.length; index++) {
|
|
87
|
+
const current = [index];
|
|
88
|
+
for (let inner = 1; inner <= right.length; inner++) {
|
|
89
|
+
const substitution = previous[inner - 1] + (left[index - 1] === right[inner - 1] ? 0 : 1);
|
|
90
|
+
const insertion = current[inner - 1] + 1;
|
|
91
|
+
const deletion = previous[inner] + 1;
|
|
92
|
+
current.push(Math.min(substitution, insertion, deletion));
|
|
93
|
+
}
|
|
94
|
+
previous = current;
|
|
95
|
+
}
|
|
96
|
+
return roundMetric(previous[right.length] / bound);
|
|
97
|
+
};
|
|
98
|
+
var shannonEntropyBits = (counts) => {
|
|
99
|
+
const total = counts.reduce((sum, value) => sum + value, 0);
|
|
100
|
+
if (total <= 0) return 0;
|
|
101
|
+
let bits = 0;
|
|
102
|
+
for (const count of counts) {
|
|
103
|
+
if (count <= 0) continue;
|
|
104
|
+
const probability = count / total;
|
|
105
|
+
bits += probability * Math.log2(probability);
|
|
106
|
+
}
|
|
107
|
+
return roundMetric(-bits);
|
|
108
|
+
};
|
|
109
|
+
var schemaFreedom = (schema, depth) => {
|
|
110
|
+
if (depth > SIGNATURE_MAX_DEPTH) return 0.5;
|
|
111
|
+
if (!isPlainRecord(schema)) return 0.5;
|
|
112
|
+
const union = Array.isArray(schema.anyOf) ? schema.anyOf : Array.isArray(schema.oneOf) ? schema.oneOf : void 0;
|
|
113
|
+
if (union) {
|
|
114
|
+
const scores = union.map((branch) => schemaFreedom(branch, depth + 1));
|
|
115
|
+
const widest = scores.length > 0 ? Math.max(...scores) : 0;
|
|
116
|
+
return roundMetric(widest + 0.1 * Math.max(0, union.length - 1));
|
|
117
|
+
}
|
|
118
|
+
if (Array.isArray(schema.enum)) {
|
|
119
|
+
return roundMetric(
|
|
120
|
+
Math.min(1, Math.log2(Math.max(1, schema.enum.length)) / ENUM_SATURATION_BITS)
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
if ("const" in schema) return 0;
|
|
124
|
+
const type = schema.type;
|
|
125
|
+
if (typeof type !== "string") return 0.75;
|
|
126
|
+
if (type === "boolean") return 0.1;
|
|
127
|
+
if (type === "number" || type === "integer") return 0.5;
|
|
128
|
+
if (type === "string") return 1;
|
|
129
|
+
if (type === "array") return roundMetric(0.5 + 0.5 * schemaFreedom(schema.items, depth + 1));
|
|
130
|
+
if (type === "object") {
|
|
131
|
+
const properties = schema.properties;
|
|
132
|
+
if (!isPlainRecord(properties)) return 1;
|
|
133
|
+
const keys = Object.keys(properties).sort();
|
|
134
|
+
if (keys.length === 0) return 1;
|
|
135
|
+
const required = Array.isArray(schema.required) ? schema.required.filter((entry) => typeof entry === "string") : [];
|
|
136
|
+
let score = 0;
|
|
137
|
+
for (const key of keys) {
|
|
138
|
+
score += schemaFreedom(properties[key], depth + 1);
|
|
139
|
+
if (!required.includes(key)) score += 0.25;
|
|
140
|
+
}
|
|
141
|
+
if (schema.additionalProperties !== false) score += 0.5;
|
|
142
|
+
return roundMetric(score);
|
|
143
|
+
}
|
|
144
|
+
return 0.75;
|
|
145
|
+
};
|
|
146
|
+
var staticFreedomFromSchema = (schema) => schemaFreedom(schema, 0);
|
|
147
|
+
var trendFromScores = (scores) => {
|
|
148
|
+
const count = scores.length;
|
|
149
|
+
const first = count > 0 ? scores[0] : void 0;
|
|
150
|
+
const last = count > 0 ? scores[count - 1] : void 0;
|
|
151
|
+
let slope = 0;
|
|
152
|
+
if (count >= 2) {
|
|
153
|
+
const meanX = (count - 1) / 2;
|
|
154
|
+
const meanY = scores.reduce((sum, value) => sum + value, 0) / count;
|
|
155
|
+
let numerator = 0;
|
|
156
|
+
let denominator = 0;
|
|
157
|
+
for (let index = 0; index < count; index++) {
|
|
158
|
+
numerator += (index - meanX) * (scores[index] - meanY);
|
|
159
|
+
denominator += (index - meanX) ** 2;
|
|
160
|
+
}
|
|
161
|
+
slope = denominator > 0 ? numerator / denominator : 0;
|
|
162
|
+
}
|
|
163
|
+
return {
|
|
164
|
+
count,
|
|
165
|
+
...first !== void 0 ? { first: roundMetric(first) } : {},
|
|
166
|
+
...last !== void 0 ? { last: roundMetric(last) } : {},
|
|
167
|
+
slopePerStep: roundMetric(slope)
|
|
168
|
+
};
|
|
169
|
+
};
|
|
170
|
+
|
|
171
|
+
// src/entropy/meter.ts
|
|
172
|
+
var DISCOVERY_PREFIX = "fabric.discovery.";
|
|
173
|
+
var WORKFLOW_PREFIX = "fabric.workflow.";
|
|
174
|
+
var DEFAULT_TASK_KEY = "(none)";
|
|
175
|
+
var TOP_SIGNATURES = 8;
|
|
176
|
+
var measureOnce = (input) => {
|
|
177
|
+
const surfaceByRef = /* @__PURE__ */ new Map();
|
|
178
|
+
if (input.surface) {
|
|
179
|
+
for (const action of input.surface.actions) surfaceByRef.set(action.ref, action.inputSchema);
|
|
180
|
+
}
|
|
181
|
+
const lexiconByRef = /* @__PURE__ */ new Map();
|
|
182
|
+
let lexiconRows = 0;
|
|
183
|
+
for (const row of input.repairs ?? []) {
|
|
184
|
+
lexiconByRef.set(row.ref, (lexiconByRef.get(row.ref) ?? 0) + 1);
|
|
185
|
+
lexiconRows += 1;
|
|
186
|
+
}
|
|
187
|
+
const totals = {
|
|
188
|
+
traces: input.traces.length,
|
|
189
|
+
operations: 0,
|
|
190
|
+
actionOperations: 0,
|
|
191
|
+
discoveryOperations: 0,
|
|
192
|
+
workflowOperations: 0,
|
|
193
|
+
succeeded: 0,
|
|
194
|
+
failed: 0,
|
|
195
|
+
aborted: 0,
|
|
196
|
+
timedOut: 0,
|
|
197
|
+
invocationRejections: 0,
|
|
198
|
+
invocationRejectionsPer1k: 0
|
|
199
|
+
};
|
|
200
|
+
const refs = /* @__PURE__ */ new Map();
|
|
201
|
+
const taskSequences = /* @__PURE__ */ new Map();
|
|
202
|
+
let churnPairs = 0;
|
|
203
|
+
let churnSum = 0;
|
|
204
|
+
const accumulator = (ref) => {
|
|
205
|
+
const existing = refs.get(ref);
|
|
206
|
+
if (existing) return existing;
|
|
207
|
+
const created = {
|
|
208
|
+
calls: 0,
|
|
209
|
+
succeeded: 0,
|
|
210
|
+
failed: 0,
|
|
211
|
+
signatures: /* @__PURE__ */ new Map(),
|
|
212
|
+
stages: /* @__PURE__ */ new Map(),
|
|
213
|
+
churnPairs: 0,
|
|
214
|
+
churnSum: 0
|
|
215
|
+
};
|
|
216
|
+
refs.set(ref, created);
|
|
217
|
+
return created;
|
|
218
|
+
};
|
|
219
|
+
for (const sourceTrace of input.traces) {
|
|
220
|
+
const taskKey = sourceTrace.taskKey ?? DEFAULT_TASK_KEY;
|
|
221
|
+
const sequence = [];
|
|
222
|
+
const pendingFailed = /* @__PURE__ */ new Map();
|
|
223
|
+
for (const operation of sourceTrace.operations) {
|
|
224
|
+
totals.operations += 1;
|
|
225
|
+
if (operation.ref.startsWith(DISCOVERY_PREFIX)) {
|
|
226
|
+
totals.discoveryOperations += 1;
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (operation.ref.startsWith(WORKFLOW_PREFIX)) {
|
|
230
|
+
totals.workflowOperations += 1;
|
|
231
|
+
continue;
|
|
232
|
+
}
|
|
233
|
+
totals.actionOperations += 1;
|
|
234
|
+
sequence.push(operation.ref);
|
|
235
|
+
const acc = accumulator(operation.ref);
|
|
236
|
+
acc.calls += 1;
|
|
237
|
+
if (operation.outcome === "succeeded") {
|
|
238
|
+
totals.succeeded += 1;
|
|
239
|
+
acc.succeeded += 1;
|
|
240
|
+
} else if (operation.outcome === "failed") {
|
|
241
|
+
totals.failed += 1;
|
|
242
|
+
acc.failed += 1;
|
|
243
|
+
} else if (operation.outcome === "aborted") {
|
|
244
|
+
totals.aborted += 1;
|
|
245
|
+
} else {
|
|
246
|
+
totals.timedOut += 1;
|
|
247
|
+
}
|
|
248
|
+
const signature = shapeSignature(operation.args);
|
|
249
|
+
acc.signatures.set(signature, (acc.signatures.get(signature) ?? 0) + 1);
|
|
250
|
+
if (operation.outcome === "failed") {
|
|
251
|
+
const stage = operation.failureStage ?? "unknown";
|
|
252
|
+
acc.stages.set(stage, (acc.stages.get(stage) ?? 0) + 1);
|
|
253
|
+
if (ENTROPY_INVOCATION_STAGES.includes(stage)) totals.invocationRejections += 1;
|
|
254
|
+
}
|
|
255
|
+
const pending = pendingFailed.get(operation.ref);
|
|
256
|
+
if (pending !== void 0) {
|
|
257
|
+
const distance = signatureDistance(pending, signature);
|
|
258
|
+
acc.churnPairs += 1;
|
|
259
|
+
acc.churnSum += distance;
|
|
260
|
+
churnPairs += 1;
|
|
261
|
+
churnSum += distance;
|
|
262
|
+
pendingFailed.delete(operation.ref);
|
|
263
|
+
}
|
|
264
|
+
if (operation.outcome === "failed") pendingFailed.set(operation.ref, signature);
|
|
265
|
+
}
|
|
266
|
+
if (sequence.length > 0) {
|
|
267
|
+
const key = sequence.join("\u2192");
|
|
268
|
+
const perTask = taskSequences.get(taskKey) ?? /* @__PURE__ */ new Map();
|
|
269
|
+
perTask.set(key, (perTask.get(key) ?? 0) + 1);
|
|
270
|
+
taskSequences.set(taskKey, perTask);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
const refEntries = [...refs.entries()].sort(([left], [right]) => compareCodeUnits(left, right));
|
|
274
|
+
const refReports = [];
|
|
275
|
+
let staticFreedomTotal = 0;
|
|
276
|
+
let staticWeighted = 0;
|
|
277
|
+
let shapeWeighted = 0;
|
|
278
|
+
let failureStageWeighted = 0;
|
|
279
|
+
let scoreSum = 0;
|
|
280
|
+
for (const [ref, acc] of refEntries) {
|
|
281
|
+
const shapeEntropyBits = shannonEntropyBits([...acc.signatures.values()]);
|
|
282
|
+
const failureStageEntropyBits = shannonEntropyBits([...acc.stages.values()]);
|
|
283
|
+
const refChurnRate = acc.churnPairs > 0 ? roundMetric(acc.churnSum / acc.churnPairs) : 0;
|
|
284
|
+
const lexicon = lexiconByRef.get(ref) ?? 0;
|
|
285
|
+
const staticFreedom = surfaceByRef.has(ref) ? staticFreedomFromSchema(surfaceByRef.get(ref)) : 0;
|
|
286
|
+
staticFreedomTotal += staticFreedom;
|
|
287
|
+
staticWeighted += ENTROPY_WEIGHTS.staticFreedom * staticFreedom;
|
|
288
|
+
shapeWeighted += shapeEntropyBits * acc.calls;
|
|
289
|
+
failureStageWeighted += failureStageEntropyBits * acc.failed;
|
|
290
|
+
const score2 = roundMetric(
|
|
291
|
+
ENTROPY_WEIGHTS.shape * shapeEntropyBits + ENTROPY_WEIGHTS.failureStage * failureStageEntropyBits + ENTROPY_WEIGHTS.churn * refChurnRate + ENTROPY_WEIGHTS.lexicon * lexicon + ENTROPY_WEIGHTS.staticFreedom * staticFreedom
|
|
292
|
+
);
|
|
293
|
+
scoreSum += score2;
|
|
294
|
+
const shapeSignatures = [...acc.signatures.entries()].map(([signature, count]) => ({ signature, count })).sort(
|
|
295
|
+
(left, right) => right.count - left.count || compareCodeUnits(left.signature, right.signature)
|
|
296
|
+
).slice(0, TOP_SIGNATURES);
|
|
297
|
+
refReports.push({
|
|
298
|
+
ref,
|
|
299
|
+
calls: acc.calls,
|
|
300
|
+
succeeded: acc.succeeded,
|
|
301
|
+
failed: acc.failed,
|
|
302
|
+
shapeSignatures,
|
|
303
|
+
shapeEntropyBits,
|
|
304
|
+
failureStageEntropyBits,
|
|
305
|
+
churnRate: refChurnRate,
|
|
306
|
+
lexiconRows: lexicon,
|
|
307
|
+
staticFreedom,
|
|
308
|
+
score: score2
|
|
309
|
+
});
|
|
310
|
+
}
|
|
311
|
+
const navigationRatio = totals.actionOperations > 0 ? roundMetric(totals.discoveryOperations / totals.actionOperations) : 0;
|
|
312
|
+
let flowWeighted = 0;
|
|
313
|
+
let flowOccurrences = 0;
|
|
314
|
+
for (const perTask of taskSequences.values()) {
|
|
315
|
+
const counts = [...perTask.values()];
|
|
316
|
+
const occurrences = counts.reduce((sum, value) => sum + value, 0);
|
|
317
|
+
flowWeighted += shannonEntropyBits(counts) * occurrences;
|
|
318
|
+
flowOccurrences += occurrences;
|
|
319
|
+
}
|
|
320
|
+
const flowEntropyBits = flowOccurrences > 0 ? roundMetric(flowWeighted / flowOccurrences) : 0;
|
|
321
|
+
totals.invocationRejectionsPer1k = totals.actionOperations > 0 ? roundMetric(totals.invocationRejections / totals.actionOperations * 1e3) : 0;
|
|
322
|
+
const score = roundMetric(
|
|
323
|
+
(scoreSum + ENTROPY_WEIGHTS.navigation * navigationRatio + ENTROPY_WEIGHTS.flow * flowEntropyBits) / Math.max(1, totals.succeeded)
|
|
324
|
+
);
|
|
325
|
+
const staticScore = roundMetric(staticWeighted / Math.max(1, totals.succeeded));
|
|
326
|
+
const behavioralScore = roundMetric(score - staticScore);
|
|
327
|
+
const sortedRefs = [...refReports].sort(
|
|
328
|
+
(left, right) => right.score - left.score || compareCodeUnits(left.ref, right.ref)
|
|
329
|
+
);
|
|
330
|
+
return {
|
|
331
|
+
metricVersion: ENTROPY_METRIC_VERSION,
|
|
332
|
+
catalogDigest: input.catalogDigest ?? "",
|
|
333
|
+
totals,
|
|
334
|
+
shapeEntropyBits: totals.actionOperations > 0 ? roundMetric(shapeWeighted / totals.actionOperations) : 0,
|
|
335
|
+
failureStageEntropyBits: totals.failed > 0 ? roundMetric(failureStageWeighted / totals.failed) : 0,
|
|
336
|
+
churnRate: churnPairs > 0 ? roundMetric(churnSum / churnPairs) : 0,
|
|
337
|
+
navigationRatio,
|
|
338
|
+
flowEntropyBits,
|
|
339
|
+
lexiconRows,
|
|
340
|
+
staticFreedom: roundMetric(staticFreedomTotal),
|
|
341
|
+
staticScore,
|
|
342
|
+
behavioralScore,
|
|
343
|
+
score,
|
|
344
|
+
refs: sortedRefs
|
|
345
|
+
};
|
|
346
|
+
};
|
|
347
|
+
var measureEntropy = (input) => {
|
|
348
|
+
const report = measureOnce(input);
|
|
349
|
+
const groups = /* @__PURE__ */ new Map();
|
|
350
|
+
for (const sourceTrace of input.traces) {
|
|
351
|
+
if (!sourceTrace.model) continue;
|
|
352
|
+
const group = groups.get(sourceTrace.model) ?? [];
|
|
353
|
+
group.push(sourceTrace);
|
|
354
|
+
groups.set(sourceTrace.model, group);
|
|
355
|
+
}
|
|
356
|
+
const byModel = [...groups.entries()].sort(([left], [right]) => compareCodeUnits(left, right)).map(([model, traces]) => {
|
|
357
|
+
const scoped = measureOnce({ ...input, traces });
|
|
358
|
+
return {
|
|
359
|
+
model,
|
|
360
|
+
operations: scoped.totals.operations,
|
|
361
|
+
actionOperations: scoped.totals.actionOperations,
|
|
362
|
+
succeeded: scoped.totals.succeeded,
|
|
363
|
+
invocationRejections: scoped.totals.invocationRejections,
|
|
364
|
+
invocationRejectionsPer1k: scoped.totals.invocationRejectionsPer1k,
|
|
365
|
+
behavioralScore: scoped.behavioralScore
|
|
366
|
+
};
|
|
367
|
+
});
|
|
368
|
+
return { ...report, byModel };
|
|
369
|
+
};
|
|
370
|
+
|
|
371
|
+
// src/entropy/passes.ts
|
|
372
|
+
var ENUM_MIN_OBSERVATIONS = 8;
|
|
373
|
+
var ENUM_MAX_DISTINCT = 8;
|
|
374
|
+
var ENUM_MIN_TOP_SHARE = 0.5;
|
|
375
|
+
var SPLIT_MIN_SHAPE_ENTROPY_BITS = 1;
|
|
376
|
+
var SPLIT_MIN_CLUSTER_CALLS = 2;
|
|
377
|
+
var FUSE_MIN_SEQUENCE_LENGTH = 3;
|
|
378
|
+
var FUSE_MAX_SEQUENCE_LENGTH = 6;
|
|
379
|
+
var FUSE_MIN_OCCURRENCES = 2;
|
|
380
|
+
var QUARANTINE_MIN_CALLS = 3;
|
|
381
|
+
var QUARANTINE_MIN_STAGE_ENTROPY_BITS = 1;
|
|
382
|
+
var MAX_PROPOSALS_PER_KIND = 8;
|
|
383
|
+
var isPlainRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
384
|
+
var valueKey = (value) => `${typeof value}:${String(value)}`;
|
|
385
|
+
var schemaProperties = (schema) => {
|
|
386
|
+
if (!isPlainRecord2(schema)) return void 0;
|
|
387
|
+
const properties = schema.properties;
|
|
388
|
+
return isPlainRecord2(properties) ? properties : void 0;
|
|
389
|
+
};
|
|
390
|
+
var enumKeys = (schema) => {
|
|
391
|
+
if (!isPlainRecord2(schema) || !Array.isArray(schema.enum)) return void 0;
|
|
392
|
+
const keys = /* @__PURE__ */ new Set();
|
|
393
|
+
for (const entry of schema.enum) {
|
|
394
|
+
if (typeof entry === "string" || typeof entry === "number" || typeof entry === "boolean") {
|
|
395
|
+
keys.add(valueKey(entry));
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
return keys;
|
|
399
|
+
};
|
|
400
|
+
var contains = (outer, inner) => {
|
|
401
|
+
if (outer.length < inner.length) return false;
|
|
402
|
+
for (let start = 0; start + inner.length <= outer.length; start++) {
|
|
403
|
+
let matches = true;
|
|
404
|
+
for (let index = 0; index < inner.length; index++) {
|
|
405
|
+
if (outer[start + index] !== inner[index]) {
|
|
406
|
+
matches = false;
|
|
407
|
+
break;
|
|
408
|
+
}
|
|
409
|
+
}
|
|
410
|
+
if (matches) return true;
|
|
411
|
+
}
|
|
412
|
+
return false;
|
|
413
|
+
};
|
|
414
|
+
var proposeEntropyReductions = (input) => {
|
|
415
|
+
const calledRefs = /* @__PURE__ */ new Map();
|
|
416
|
+
for (const ref of input.report.refs) calledRefs.set(ref.ref, ref);
|
|
417
|
+
const surfaceByRef = /* @__PURE__ */ new Map();
|
|
418
|
+
const surfaceRefs = /* @__PURE__ */ new Set();
|
|
419
|
+
if (input.surface) {
|
|
420
|
+
for (const action of input.surface.actions) {
|
|
421
|
+
surfaceByRef.set(action.ref, action.inputSchema);
|
|
422
|
+
surfaceRefs.add(action.ref);
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
const proposals = [];
|
|
426
|
+
const observations = /* @__PURE__ */ new Map();
|
|
427
|
+
const observeValue = (ref, key, value) => {
|
|
428
|
+
const id = `${ref}\0${key}`;
|
|
429
|
+
const entry = observations.get(id) ?? {
|
|
430
|
+
ref,
|
|
431
|
+
key,
|
|
432
|
+
counts: /* @__PURE__ */ new Map(),
|
|
433
|
+
total: 0
|
|
434
|
+
};
|
|
435
|
+
const valueId = valueKey(value);
|
|
436
|
+
const existing = entry.counts.get(valueId);
|
|
437
|
+
entry.counts.set(valueId, { value, count: (existing?.count ?? 0) + 1 });
|
|
438
|
+
entry.total += 1;
|
|
439
|
+
observations.set(id, entry);
|
|
440
|
+
};
|
|
441
|
+
if (input.valueObservations) {
|
|
442
|
+
for (const observation of input.valueObservations) {
|
|
443
|
+
observeValue(observation.ref, observation.key, observation.value);
|
|
444
|
+
}
|
|
445
|
+
} else {
|
|
446
|
+
for (const sourceTrace of input.traces) {
|
|
447
|
+
for (const operation of sourceTrace.operations) {
|
|
448
|
+
if (operation.ref.startsWith("fabric.")) continue;
|
|
449
|
+
for (const [key, value] of Object.entries(operation.args)) {
|
|
450
|
+
if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
|
|
451
|
+
continue;
|
|
452
|
+
}
|
|
453
|
+
observeValue(operation.ref, key, value);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
const enumCandidates = [...observations.values()].filter((entry) => entry.total >= ENUM_MIN_OBSERVATIONS).filter((entry) => entry.counts.size >= 2 && entry.counts.size <= ENUM_MAX_DISTINCT).map((entry) => {
|
|
459
|
+
const ranked = [...entry.counts.values()].sort(
|
|
460
|
+
(left, right) => right.count - left.count || compareCodeUnits(valueKey(left.value), valueKey(right.value))
|
|
461
|
+
);
|
|
462
|
+
return { entry, ranked, topShare: roundMetric(ranked[0].count / entry.total) };
|
|
463
|
+
}).filter((candidate) => candidate.topShare >= ENUM_MIN_TOP_SHARE).filter((candidate) => {
|
|
464
|
+
if (!input.surface) return true;
|
|
465
|
+
const properties = schemaProperties(surfaceByRef.get(candidate.entry.ref));
|
|
466
|
+
const target = properties ? properties[candidate.entry.key] : void 0;
|
|
467
|
+
const existing = enumKeys(target);
|
|
468
|
+
if (!existing) return true;
|
|
469
|
+
return [...candidate.entry.counts.keys()].some((observed) => !existing.has(observed));
|
|
470
|
+
}).sort(
|
|
471
|
+
(left, right) => compareCodeUnits(`${left.entry.ref}\0${left.entry.key}`, `${right.entry.ref}\0${right.entry.key}`)
|
|
472
|
+
).slice(0, MAX_PROPOSALS_PER_KIND);
|
|
473
|
+
for (const candidate of enumCandidates) {
|
|
474
|
+
proposals.push({
|
|
475
|
+
kind: "enum-tighten",
|
|
476
|
+
ref: candidate.entry.ref,
|
|
477
|
+
key: candidate.entry.key,
|
|
478
|
+
values: candidate.ranked.map((item) => item.value),
|
|
479
|
+
calls: candidate.entry.total,
|
|
480
|
+
distinct: candidate.entry.counts.size,
|
|
481
|
+
topShare: candidate.topShare
|
|
482
|
+
});
|
|
483
|
+
}
|
|
484
|
+
const repairRows = [...input.repairs ?? []].sort(
|
|
485
|
+
(left, right) => compareCodeUnits(left.ref, right.ref) || compareCodeUnits(left.from, right.from) || compareCodeUnits(left.to, right.to)
|
|
486
|
+
);
|
|
487
|
+
for (const row of repairRows) {
|
|
488
|
+
if (!calledRefs.has(row.ref)) continue;
|
|
489
|
+
if (input.surface) {
|
|
490
|
+
if (row.kind === "keyAlias") {
|
|
491
|
+
const properties = schemaProperties(surfaceByRef.get(row.ref));
|
|
492
|
+
if (properties && row.from in properties) continue;
|
|
493
|
+
} else {
|
|
494
|
+
const provider = row.ref.split(".")[0] ?? row.ref;
|
|
495
|
+
if (surfaceRefs.has(`${provider}.${row.from}`)) continue;
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
proposals.push({
|
|
499
|
+
kind: "modal-rename",
|
|
500
|
+
level: row.kind === "keyAlias" ? "key" : "action",
|
|
501
|
+
ref: row.ref,
|
|
502
|
+
from: row.from,
|
|
503
|
+
to: row.to,
|
|
504
|
+
note: row.kind === "keyAlias" ? "the spilled spelling dominates observed calls; rename the declared key and retire the repair row" : "the spilled action name dominates observed calls; rename the declared action and retire the repair row"
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
const overloadRefs = new Set(
|
|
508
|
+
input.report.refs.filter((ref) => ref.shapeEntropyBits >= SPLIT_MIN_SHAPE_ENTROPY_BITS).map((ref) => ref.ref)
|
|
509
|
+
);
|
|
510
|
+
if (overloadRefs.size > 0) {
|
|
511
|
+
const keySets = /* @__PURE__ */ new Map();
|
|
512
|
+
for (const sourceTrace of input.traces) {
|
|
513
|
+
for (const operation of sourceTrace.operations) {
|
|
514
|
+
if (!overloadRefs.has(operation.ref)) continue;
|
|
515
|
+
const keys = Object.keys(operation.args).sort();
|
|
516
|
+
const id = keys.join("\0");
|
|
517
|
+
const perRef = keySets.get(operation.ref) ?? /* @__PURE__ */ new Map();
|
|
518
|
+
const cluster = perRef.get(id) ?? { keys, calls: 0 };
|
|
519
|
+
cluster.calls += 1;
|
|
520
|
+
perRef.set(id, cluster);
|
|
521
|
+
keySets.set(operation.ref, perRef);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
let overloadCount = 0;
|
|
525
|
+
for (const [ref, perRef] of [...keySets.entries()].sort(
|
|
526
|
+
([left], [right]) => compareCodeUnits(left, right)
|
|
527
|
+
)) {
|
|
528
|
+
if (overloadCount >= MAX_PROPOSALS_PER_KIND) break;
|
|
529
|
+
const clusters = [...perRef.values()].filter((cluster) => cluster.calls >= SPLIT_MIN_CLUSTER_CALLS).sort(
|
|
530
|
+
(left, right) => right.calls - left.calls || compareCodeUnits(left.keys.join(","), right.keys.join(","))
|
|
531
|
+
);
|
|
532
|
+
const disjoint = [];
|
|
533
|
+
for (const cluster of clusters) {
|
|
534
|
+
const keys = new Set(cluster.keys);
|
|
535
|
+
if (disjoint.every((candidate) => candidate.keys.every((key) => !keys.has(key)))) {
|
|
536
|
+
disjoint.push(cluster);
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
if (disjoint.length >= 2) {
|
|
540
|
+
overloadCount += 1;
|
|
541
|
+
const report = calledRefs.get(ref);
|
|
542
|
+
proposals.push({
|
|
543
|
+
kind: "overload-split",
|
|
544
|
+
ref,
|
|
545
|
+
shapeEntropyBits: report?.shapeEntropyBits ?? 0,
|
|
546
|
+
clusters: disjoint.slice(0, MAX_PROPOSALS_PER_KIND).map((cluster) => ({
|
|
547
|
+
keys: cluster.keys,
|
|
548
|
+
calls: cluster.calls
|
|
549
|
+
}))
|
|
550
|
+
});
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
const sequenceCounts = /* @__PURE__ */ new Map();
|
|
555
|
+
for (const sourceTrace of input.traces) {
|
|
556
|
+
const refs = sourceTrace.operations.filter((operation) => !operation.ref.startsWith("fabric.")).map((operation) => operation.ref);
|
|
557
|
+
for (let length = FUSE_MIN_SEQUENCE_LENGTH; length <= Math.min(FUSE_MAX_SEQUENCE_LENGTH, refs.length); length++) {
|
|
558
|
+
for (let start = 0; start + length <= refs.length; start++) {
|
|
559
|
+
const sequence = refs.slice(start, start + length);
|
|
560
|
+
if (new Set(sequence).size < 2) continue;
|
|
561
|
+
const id = sequence.join("\u2192");
|
|
562
|
+
const entry = sequenceCounts.get(id) ?? { sequence, occurrences: 0 };
|
|
563
|
+
entry.occurrences += 1;
|
|
564
|
+
sequenceCounts.set(id, entry);
|
|
565
|
+
}
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
const fuseCandidates = [...sequenceCounts.values()].filter((entry) => entry.occurrences >= FUSE_MIN_OCCURRENCES).sort(
|
|
569
|
+
(left, right) => right.sequence.length - left.sequence.length || right.occurrences - left.occurrences || compareCodeUnits(left.sequence.join("\u2192"), right.sequence.join("\u2192"))
|
|
570
|
+
);
|
|
571
|
+
const selected = [];
|
|
572
|
+
for (const candidate of fuseCandidates) {
|
|
573
|
+
if (selected.length >= MAX_PROPOSALS_PER_KIND) break;
|
|
574
|
+
const redundant = selected.some(
|
|
575
|
+
(chosen) => contains(candidate.sequence, chosen.sequence) || contains(chosen.sequence, candidate.sequence)
|
|
576
|
+
);
|
|
577
|
+
if (redundant) continue;
|
|
578
|
+
selected.push(candidate);
|
|
579
|
+
}
|
|
580
|
+
for (const candidate of selected) {
|
|
581
|
+
proposals.push({
|
|
582
|
+
kind: "sequence-fuse",
|
|
583
|
+
sequence: candidate.sequence,
|
|
584
|
+
occurrences: candidate.occurrences
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
const quarantineCandidates = input.report.refs.filter(
|
|
588
|
+
(ref) => ref.calls >= QUARANTINE_MIN_CALLS && ref.failed > ref.succeeded && ref.failureStageEntropyBits >= QUARANTINE_MIN_STAGE_ENTROPY_BITS
|
|
589
|
+
).filter((ref) => !input.surface || surfaceRefs.has(ref.ref)).slice(0, MAX_PROPOSALS_PER_KIND);
|
|
590
|
+
for (const ref of quarantineCandidates) {
|
|
591
|
+
proposals.push({
|
|
592
|
+
kind: "noise-quarantine",
|
|
593
|
+
ref: ref.ref,
|
|
594
|
+
calls: ref.calls,
|
|
595
|
+
succeeded: ref.succeeded,
|
|
596
|
+
failed: ref.failed,
|
|
597
|
+
failureStageEntropyBits: ref.failureStageEntropyBits
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
return proposals;
|
|
601
|
+
};
|
|
602
|
+
var deepCloneJson = (value) => value === void 0 ? {} : JSON.parse(JSON.stringify(value));
|
|
603
|
+
var applyProposalsToSurface = (surface, proposals) => {
|
|
604
|
+
const actions = surface.actions.map((action) => ({
|
|
605
|
+
ref: action.ref,
|
|
606
|
+
inputSchema: deepCloneJson(action.inputSchema)
|
|
607
|
+
}));
|
|
608
|
+
const quarantined = /* @__PURE__ */ new Set();
|
|
609
|
+
for (const proposal of proposals) {
|
|
610
|
+
if (proposal.kind === "noise-quarantine") {
|
|
611
|
+
quarantined.add(proposal.ref);
|
|
612
|
+
continue;
|
|
613
|
+
}
|
|
614
|
+
if (proposal.kind === "modal-rename" && proposal.level === "action") {
|
|
615
|
+
const provider = proposal.ref.split(".")[0] ?? proposal.ref;
|
|
616
|
+
const renamed = `${provider}.${proposal.from}`;
|
|
617
|
+
for (const action of actions) {
|
|
618
|
+
if (action.ref === proposal.ref) action.ref = renamed;
|
|
619
|
+
}
|
|
620
|
+
continue;
|
|
621
|
+
}
|
|
622
|
+
if (proposal.kind === "enum-tighten" || proposal.kind === "modal-rename" && proposal.level === "key") {
|
|
623
|
+
const action = actions.find((candidate) => candidate.ref === proposal.ref);
|
|
624
|
+
if (!action || !isPlainRecord2(action.inputSchema)) continue;
|
|
625
|
+
const schema = action.inputSchema;
|
|
626
|
+
const properties = schemaProperties(schema);
|
|
627
|
+
if (!properties) continue;
|
|
628
|
+
if (proposal.kind === "enum-tighten") {
|
|
629
|
+
if (!isPlainRecord2(properties[proposal.key])) continue;
|
|
630
|
+
properties[proposal.key] = {
|
|
631
|
+
...properties[proposal.key],
|
|
632
|
+
enum: [...proposal.values]
|
|
633
|
+
};
|
|
634
|
+
continue;
|
|
635
|
+
}
|
|
636
|
+
if (!(proposal.to in properties) || proposal.from in properties) continue;
|
|
637
|
+
properties[proposal.from] = properties[proposal.to];
|
|
638
|
+
delete properties[proposal.to];
|
|
639
|
+
if (Array.isArray(schema.required)) {
|
|
640
|
+
schema.required = schema.required.map((key) => key === proposal.to ? proposal.from : key);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
}
|
|
644
|
+
const survivors = actions.filter((action) => !quarantined.has(action.ref)).sort((left, right) => compareCodeUnits(left.ref, right.ref));
|
|
645
|
+
return { version: 1, actions: survivors };
|
|
646
|
+
};
|
|
647
|
+
var evaluateGate = (before, after) => {
|
|
648
|
+
const reasons = [];
|
|
649
|
+
if (after.score > before.score) {
|
|
650
|
+
reasons.push(`score increased ${before.score} \u2192 ${after.score}`);
|
|
651
|
+
}
|
|
652
|
+
if (after.totals.succeeded < before.totals.succeeded) {
|
|
653
|
+
reasons.push(
|
|
654
|
+
`successful calls dropped ${before.totals.succeeded} \u2192 ${after.totals.succeeded}`
|
|
655
|
+
);
|
|
656
|
+
}
|
|
657
|
+
return {
|
|
658
|
+
passed: reasons.length === 0,
|
|
659
|
+
beforeScore: before.score,
|
|
660
|
+
afterScore: after.score,
|
|
661
|
+
delta: roundMetric(after.score - before.score),
|
|
662
|
+
reasons
|
|
663
|
+
};
|
|
664
|
+
};
|
|
665
|
+
|
|
666
|
+
// src/entropy/surface.ts
|
|
667
|
+
var SURFACE_LIST_LIMIT = 1e3;
|
|
668
|
+
var surfaceInvocationContext = (input) => ({
|
|
669
|
+
cwd: input.cwd,
|
|
670
|
+
signal: void 0,
|
|
671
|
+
parentToolCallId: "fabric-entropy-surface",
|
|
672
|
+
nestedToolCallId: "fabric-entropy-surface",
|
|
673
|
+
extensionContext: input.extensionContext,
|
|
674
|
+
update: () => {
|
|
675
|
+
}
|
|
676
|
+
});
|
|
677
|
+
var liveSurfaceSnapshot = async (input) => {
|
|
678
|
+
const actions = await input.registry.list(
|
|
679
|
+
{ limit: SURFACE_LIST_LIMIT },
|
|
680
|
+
surfaceInvocationContext(input)
|
|
681
|
+
);
|
|
682
|
+
return {
|
|
683
|
+
version: 1,
|
|
684
|
+
actions: actions.map((action) => ({ ref: action.ref, inputSchema: action.inputSchema })).sort((left, right) => compareCodeUnits(left.ref, right.ref))
|
|
685
|
+
};
|
|
686
|
+
};
|
|
687
|
+
var entropySurfaceHash = (surface) => stableJsonHash(surface);
|
|
688
|
+
var surfaceFreedomReport = (surface) => {
|
|
689
|
+
const actions = surface.actions.map((action) => ({ ref: action.ref, freedom: staticFreedomFromSchema(action.inputSchema) })).sort((left, right) => right.freedom - left.freedom || compareCodeUnits(left.ref, right.ref));
|
|
690
|
+
const total = roundMetric(actions.reduce((sum, entry) => sum + entry.freedom, 0));
|
|
691
|
+
return {
|
|
692
|
+
actions,
|
|
693
|
+
total,
|
|
694
|
+
mean: actions.length > 0 ? roundMetric(total / actions.length) : 0
|
|
695
|
+
};
|
|
696
|
+
};
|
|
697
|
+
|
|
698
|
+
// src/entropy/compiler.ts
|
|
699
|
+
var AUTO_APPLY_PROPOSAL_KINDS = ["enum-tighten", "noise-quarantine"];
|
|
700
|
+
var proposalDetail = (proposal) => {
|
|
701
|
+
if (proposal.kind === "enum-tighten") {
|
|
702
|
+
return { ref: proposal.ref, detail: `${proposal.key}: ${proposal.values.length} observed values` };
|
|
703
|
+
}
|
|
704
|
+
if (proposal.kind === "noise-quarantine") {
|
|
705
|
+
return { ref: proposal.ref, detail: `${proposal.failed} failed vs ${proposal.succeeded} succeeded` };
|
|
706
|
+
}
|
|
707
|
+
return { ref: "n/a", detail: "review-only" };
|
|
708
|
+
};
|
|
709
|
+
var touchedRefs = (before, after) => {
|
|
710
|
+
const beforeByRef = new Map(before.actions.map((action) => [action.ref, action.inputSchema]));
|
|
711
|
+
const touched = /* @__PURE__ */ new Set();
|
|
712
|
+
for (const action of after.actions) {
|
|
713
|
+
if (stableJsonHash(beforeByRef.get(action.ref)) !== stableJsonHash(action.inputSchema)) {
|
|
714
|
+
touched.add(action.ref);
|
|
715
|
+
}
|
|
716
|
+
}
|
|
717
|
+
for (const ref of beforeByRef.keys()) {
|
|
718
|
+
if (!after.actions.some((action) => action.ref === ref)) touched.add(ref);
|
|
719
|
+
}
|
|
720
|
+
return touched;
|
|
721
|
+
};
|
|
722
|
+
var artifactFromCandidate = (live, candidate, proposals, gate, evidenceDigest, previous) => {
|
|
723
|
+
const liveByRef = new Map(live.actions.map((action) => [action.ref, action.inputSchema]));
|
|
724
|
+
const actions = candidate.actions.filter(
|
|
725
|
+
(action) => stableJsonHash(action.inputSchema) !== stableJsonHash(liveByRef.get(action.ref))
|
|
726
|
+
).map((action) => ({
|
|
727
|
+
ref: action.ref,
|
|
728
|
+
inputSchema: action.inputSchema,
|
|
729
|
+
baseSchemaDigest: schemaDigest(liveByRef.get(action.ref))
|
|
730
|
+
}));
|
|
731
|
+
const candidateRefs = new Set(candidate.actions.map((action) => action.ref));
|
|
732
|
+
const quarantined = live.actions.filter((action) => !candidateRefs.has(action.ref)).map((action) => ({ ref: action.ref, baseSchemaDigest: schemaDigest(action.inputSchema) }));
|
|
733
|
+
const applied = [...previous?.applied ?? []];
|
|
734
|
+
const seen = new Set(applied.map((entry) => `${entry.kind}:${entry.ref}:${entry.detail}`));
|
|
735
|
+
for (const proposal of proposals) {
|
|
736
|
+
const { ref, detail } = proposalDetail(proposal);
|
|
737
|
+
const identity = `${proposal.kind}:${ref}:${detail}`;
|
|
738
|
+
if (seen.has(identity)) continue;
|
|
739
|
+
seen.add(identity);
|
|
740
|
+
applied.push({ kind: proposal.kind, ref, detail });
|
|
741
|
+
}
|
|
742
|
+
return {
|
|
743
|
+
version: COMPILED_SURFACE_VERSION,
|
|
744
|
+
metricVersion: ENTROPY_METRIC_VERSION,
|
|
745
|
+
actions,
|
|
746
|
+
quarantined,
|
|
747
|
+
applied: applied.slice(0, MAX_COMPILED_SURFACE_PROPOSALS),
|
|
748
|
+
gate,
|
|
749
|
+
evidenceDigest
|
|
750
|
+
};
|
|
751
|
+
};
|
|
752
|
+
var compileEntropySurface = (input) => {
|
|
753
|
+
const effective = applyCompiledSurface(input.surface, input.artifact);
|
|
754
|
+
const report = measureEntropy({
|
|
755
|
+
traces: input.traces,
|
|
756
|
+
surface: effective,
|
|
757
|
+
...input.repairs ? { repairs: input.repairs } : {},
|
|
758
|
+
catalogDigest: input.catalogDigest ?? entropySurfaceHash(effective)
|
|
759
|
+
});
|
|
760
|
+
const proposals = proposeEntropyReductions({
|
|
761
|
+
report,
|
|
762
|
+
traces: input.traces,
|
|
763
|
+
surface: effective,
|
|
764
|
+
...input.repairs ? { repairs: input.repairs } : {},
|
|
765
|
+
...input.valueObservations ? { valueObservations: input.valueObservations } : {}
|
|
766
|
+
});
|
|
767
|
+
const auto = proposals.filter(
|
|
768
|
+
(proposal) => AUTO_APPLY_PROPOSAL_KINDS.includes(proposal.kind)
|
|
769
|
+
);
|
|
770
|
+
if (auto.length === 0) {
|
|
771
|
+
return {
|
|
772
|
+
status: "converged",
|
|
773
|
+
...input.artifact ? { artifact: input.artifact } : {},
|
|
774
|
+
report,
|
|
775
|
+
proposals
|
|
776
|
+
};
|
|
777
|
+
}
|
|
778
|
+
const candidate = applyProposalsToSurface(effective, auto);
|
|
779
|
+
const after = measureEntropy({
|
|
780
|
+
traces: input.traces,
|
|
781
|
+
surface: candidate,
|
|
782
|
+
...input.repairs ? { repairs: input.repairs } : {},
|
|
783
|
+
catalogDigest: entropySurfaceHash(candidate)
|
|
784
|
+
});
|
|
785
|
+
const scoreGate = evaluateGate(report, after);
|
|
786
|
+
const violations = replaySuccessfulCalls(
|
|
787
|
+
candidate,
|
|
788
|
+
input.traces,
|
|
789
|
+
touchedRefs(effective, candidate)
|
|
790
|
+
);
|
|
791
|
+
const reasons = [
|
|
792
|
+
...scoreGate.reasons,
|
|
793
|
+
...violations.map((violation) => `${violation.ref}: ${violation.reason}`)
|
|
794
|
+
];
|
|
795
|
+
const gate = {
|
|
796
|
+
passed: scoreGate.passed && violations.length === 0,
|
|
797
|
+
beforeScore: report.score,
|
|
798
|
+
afterScore: after.score,
|
|
799
|
+
delta: scoreGate.delta,
|
|
800
|
+
reasons: scoreGate.passed && violations.length === 0 ? [] : reasons
|
|
801
|
+
};
|
|
802
|
+
if (gate.passed) {
|
|
803
|
+
const evidenceDigest = stableJsonHash({
|
|
804
|
+
traces: input.traces.length,
|
|
805
|
+
operations: report.totals.operations,
|
|
806
|
+
succeeded: report.totals.succeeded,
|
|
807
|
+
failed: report.totals.failed,
|
|
808
|
+
surface: entropySurfaceHash(effective),
|
|
809
|
+
repairs: input.repairs?.length ?? 0
|
|
810
|
+
});
|
|
811
|
+
return {
|
|
812
|
+
status: "compiled",
|
|
813
|
+
artifact: artifactFromCandidate(
|
|
814
|
+
input.surface,
|
|
815
|
+
candidate,
|
|
816
|
+
auto,
|
|
817
|
+
{ passed: true, beforeScore: report.score, afterScore: after.score, reasons: [] },
|
|
818
|
+
evidenceDigest,
|
|
819
|
+
input.artifact
|
|
820
|
+
),
|
|
821
|
+
report,
|
|
822
|
+
after,
|
|
823
|
+
proposals,
|
|
824
|
+
gate
|
|
825
|
+
};
|
|
826
|
+
}
|
|
827
|
+
return {
|
|
828
|
+
status: "rejected",
|
|
829
|
+
...input.artifact ? { artifact: input.artifact } : {},
|
|
830
|
+
report,
|
|
831
|
+
after,
|
|
832
|
+
proposals,
|
|
833
|
+
gate
|
|
834
|
+
};
|
|
835
|
+
};
|
|
836
|
+
|
|
837
|
+
// src/entropy/corpus.ts
|
|
838
|
+
var DEFAULT_TASK_KEY2 = "(none)";
|
|
839
|
+
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
840
|
+
var entropyTraceFromFabricTrace = (trace, model) => ({
|
|
841
|
+
operations: trace.operations.map((operation) => ({
|
|
842
|
+
ref: operation.ref,
|
|
843
|
+
args: operation.args,
|
|
844
|
+
outcome: operation.outcome,
|
|
845
|
+
...operation.failureStage !== void 0 ? { failureStage: operation.failureStage } : {}
|
|
846
|
+
})),
|
|
847
|
+
taskKey: trace.phases[0] ?? DEFAULT_TASK_KEY2,
|
|
848
|
+
...model ? { model } : {}
|
|
849
|
+
});
|
|
850
|
+
var MODEL_CHANGE_FILTER = '"type":"model_change"';
|
|
851
|
+
var ASSISTANT_FILTER = '"role":"assistant"';
|
|
852
|
+
var modelFromRecord = (parsed) => {
|
|
853
|
+
if (parsed.type === "model_change") {
|
|
854
|
+
const provider2 = typeof parsed.provider === "string" ? parsed.provider : "";
|
|
855
|
+
const modelId2 = typeof parsed.modelId === "string" ? parsed.modelId : "";
|
|
856
|
+
return provider2 && modelId2 ? `${provider2}/${modelId2}` : void 0;
|
|
857
|
+
}
|
|
858
|
+
const message = isRecord(parsed.message) ? parsed.message : void 0;
|
|
859
|
+
if (!message || message.role !== "assistant") return void 0;
|
|
860
|
+
const provider = typeof message.provider === "string" ? message.provider : "";
|
|
861
|
+
const modelId = typeof message.model === "string" ? message.model : "";
|
|
862
|
+
return provider && modelId ? `${provider}/${modelId}` : void 0;
|
|
863
|
+
};
|
|
864
|
+
var sessionRecords = (lines) => {
|
|
865
|
+
const records = [];
|
|
866
|
+
let currentModel;
|
|
867
|
+
for (const line of lines) {
|
|
868
|
+
const trimmed = line.trim();
|
|
869
|
+
if (trimmed === "") continue;
|
|
870
|
+
const traceLine = trimmed.includes(FABRIC_EXECUTION_TRACE_KIND);
|
|
871
|
+
const modelLine = !traceLine && (trimmed.includes(MODEL_CHANGE_FILTER) || trimmed.includes(ASSISTANT_FILTER));
|
|
872
|
+
if (!traceLine && !modelLine) continue;
|
|
873
|
+
let parsed;
|
|
874
|
+
try {
|
|
875
|
+
parsed = JSON.parse(trimmed);
|
|
876
|
+
} catch {
|
|
877
|
+
continue;
|
|
878
|
+
}
|
|
879
|
+
if (!isRecord(parsed)) continue;
|
|
880
|
+
if (modelLine) {
|
|
881
|
+
const model = modelFromRecord(parsed);
|
|
882
|
+
if (model) currentModel = model;
|
|
883
|
+
continue;
|
|
884
|
+
}
|
|
885
|
+
const details = isRecord(parsed.details) ? parsed.details : isRecord(parsed.message) && isRecord(parsed.message.details) ? parsed.message.details : void 0;
|
|
886
|
+
if (isRecord(details)) {
|
|
887
|
+
records.push({ details, ...currentModel ? { model: currentModel } : {} });
|
|
888
|
+
}
|
|
889
|
+
}
|
|
890
|
+
return records;
|
|
891
|
+
};
|
|
892
|
+
var entropyTracesFromSessionJsonl = (lines) => {
|
|
893
|
+
const traces = [];
|
|
894
|
+
for (const record of sessionRecords(lines)) {
|
|
895
|
+
const trace = readFabricExecutionTraceV1(record.details.trace);
|
|
896
|
+
if (trace) traces.push(entropyTraceFromFabricTrace(trace, record.model));
|
|
897
|
+
}
|
|
898
|
+
return traces;
|
|
899
|
+
};
|
|
900
|
+
var entropyValueObservationsFromSessionJsonl = (lines) => {
|
|
901
|
+
const observations = [];
|
|
902
|
+
for (const record of sessionRecords(lines)) {
|
|
903
|
+
const details = record.details;
|
|
904
|
+
if (!Array.isArray(details.audits)) continue;
|
|
905
|
+
for (const audit of details.audits) {
|
|
906
|
+
if (!isRecord(audit)) continue;
|
|
907
|
+
const ref = typeof audit.ref === "string" ? audit.ref : void 0;
|
|
908
|
+
const args = isRecord(audit.args) ? audit.args : void 0;
|
|
909
|
+
if (!ref || !args) continue;
|
|
910
|
+
for (const [key, value] of Object.entries(args)) {
|
|
911
|
+
if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
|
|
912
|
+
observations.push({ ref, key, value });
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
return observations;
|
|
918
|
+
};
|
|
919
|
+
var entropyRepairRows = (repairs) => repairs.map(
|
|
920
|
+
(repair) => repair.kind === "keyAlias" ? { kind: "keyAlias", ref: repair.ref, from: repair.from, to: repair.to } : {
|
|
921
|
+
kind: "actionAlias",
|
|
922
|
+
ref: `${repair.provider}.${repair.to}`,
|
|
923
|
+
from: repair.from,
|
|
924
|
+
to: repair.to
|
|
925
|
+
}
|
|
926
|
+
);
|
|
927
|
+
var entropyReportHash = (report) => stableJsonHash(report);
|
|
928
|
+
|
|
929
|
+
// src/entropy/sessions.ts
|
|
930
|
+
import fs from "node:fs";
|
|
931
|
+
import path from "node:path";
|
|
932
|
+
var DEFAULT_SESSION_WINDOW = 8;
|
|
933
|
+
var projectSessionFiles = (agentDir, cwd, limit = DEFAULT_SESSION_WINDOW) => {
|
|
934
|
+
const dir = sessionDirForCwd(cwd, agentDir);
|
|
935
|
+
let entries = [];
|
|
936
|
+
try {
|
|
937
|
+
entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
938
|
+
} catch {
|
|
939
|
+
return [];
|
|
940
|
+
}
|
|
941
|
+
const stamped = entries.filter((entry) => entry.isFile() && entry.name.endsWith(".jsonl")).map((entry) => {
|
|
942
|
+
const file = path.join(dir, entry.name);
|
|
943
|
+
let mtime = 0;
|
|
944
|
+
try {
|
|
945
|
+
mtime = fs.statSync(file).mtimeMs;
|
|
946
|
+
} catch {
|
|
947
|
+
mtime = 0;
|
|
948
|
+
}
|
|
949
|
+
return { file, mtime };
|
|
950
|
+
});
|
|
951
|
+
stamped.sort(
|
|
952
|
+
(left, right) => right.mtime - left.mtime || (left.file < right.file ? -1 : left.file > right.file ? 1 : 0)
|
|
953
|
+
);
|
|
954
|
+
return stamped.slice(0, Math.max(1, limit)).map((entry) => entry.file);
|
|
955
|
+
};
|
|
956
|
+
var measureSessionCorpus = (input) => {
|
|
957
|
+
const sessions = [];
|
|
958
|
+
for (const file of input.files) {
|
|
959
|
+
let text;
|
|
960
|
+
try {
|
|
961
|
+
text = fs.readFileSync(file, "utf8");
|
|
962
|
+
} catch {
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
const traces = entropyTracesFromSessionJsonl(text.split("\n"));
|
|
966
|
+
if (traces.length === 0) continue;
|
|
967
|
+
const report = measureEntropy({
|
|
968
|
+
traces,
|
|
969
|
+
...input.surface ? { surface: input.surface } : {},
|
|
970
|
+
...input.catalogDigest !== void 0 ? { catalogDigest: input.catalogDigest } : {}
|
|
971
|
+
});
|
|
972
|
+
if (report.totals.operations === 0) continue;
|
|
973
|
+
sessions.push({ file, operations: report.totals.operations, report });
|
|
974
|
+
}
|
|
975
|
+
const chronological = [...sessions].reverse();
|
|
976
|
+
const modelScores = /* @__PURE__ */ new Map();
|
|
977
|
+
for (const session of chronological) {
|
|
978
|
+
for (const model of session.report.byModel) {
|
|
979
|
+
const entry = modelScores.get(model.model) ?? { behavioral: [], latest: model };
|
|
980
|
+
entry.behavioral.push(model.behavioralScore);
|
|
981
|
+
entry.latest = model;
|
|
982
|
+
modelScores.set(model.model, entry);
|
|
983
|
+
}
|
|
984
|
+
}
|
|
985
|
+
const models = [...modelScores.entries()].sort(([left], [right]) => compareCodeUnits(left, right)).map(([model, entry]) => ({
|
|
986
|
+
model,
|
|
987
|
+
sessions: entry.behavioral.length,
|
|
988
|
+
latestBehavioralScore: entry.latest.behavioralScore,
|
|
989
|
+
slopePerSession: trendFromScores(entry.behavioral).slopePerStep,
|
|
990
|
+
latestRejectionsPer1k: entry.latest.invocationRejectionsPer1k
|
|
991
|
+
}));
|
|
992
|
+
return {
|
|
993
|
+
files: input.files,
|
|
994
|
+
sessions,
|
|
995
|
+
...sessions.length > 0 ? { latest: sessions[0].report } : {},
|
|
996
|
+
trend: trendFromScores(chronological.map((session) => session.report.score)),
|
|
997
|
+
models
|
|
998
|
+
};
|
|
999
|
+
};
|
|
1000
|
+
var sessionWindowEvidence = (files) => {
|
|
1001
|
+
const traces = [];
|
|
1002
|
+
const valueObservations = [];
|
|
1003
|
+
for (const file of files) {
|
|
1004
|
+
let text;
|
|
1005
|
+
try {
|
|
1006
|
+
text = fs.readFileSync(file, "utf8");
|
|
1007
|
+
} catch {
|
|
1008
|
+
continue;
|
|
1009
|
+
}
|
|
1010
|
+
const lines = text.split("\n");
|
|
1011
|
+
traces.push(...entropyTracesFromSessionJsonl(lines));
|
|
1012
|
+
valueObservations.push(...entropyValueObservationsFromSessionJsonl(lines));
|
|
1013
|
+
}
|
|
1014
|
+
return { traces, valueObservations };
|
|
1015
|
+
};
|
|
1016
|
+
|
|
1017
|
+
export {
|
|
1018
|
+
ENTROPY_METRIC_VERSION,
|
|
1019
|
+
ENTROPY_WEIGHTS,
|
|
1020
|
+
ENTROPY_INVOCATION_STAGES,
|
|
1021
|
+
roundMetric,
|
|
1022
|
+
compareCodeUnits,
|
|
1023
|
+
shapeSignature,
|
|
1024
|
+
signatureDistance,
|
|
1025
|
+
shannonEntropyBits,
|
|
1026
|
+
staticFreedomFromSchema,
|
|
1027
|
+
trendFromScores,
|
|
1028
|
+
liveSurfaceSnapshot,
|
|
1029
|
+
entropySurfaceHash,
|
|
1030
|
+
surfaceFreedomReport,
|
|
1031
|
+
entropyTraceFromFabricTrace,
|
|
1032
|
+
entropyTracesFromSessionJsonl,
|
|
1033
|
+
entropyValueObservationsFromSessionJsonl,
|
|
1034
|
+
entropyRepairRows,
|
|
1035
|
+
entropyReportHash,
|
|
1036
|
+
measureEntropy,
|
|
1037
|
+
projectSessionFiles,
|
|
1038
|
+
measureSessionCorpus,
|
|
1039
|
+
sessionWindowEvidence,
|
|
1040
|
+
proposeEntropyReductions,
|
|
1041
|
+
applyProposalsToSurface,
|
|
1042
|
+
evaluateGate,
|
|
1043
|
+
AUTO_APPLY_PROPOSAL_KINDS,
|
|
1044
|
+
compileEntropySurface
|
|
1045
|
+
};
|
|
1046
|
+
//# sourceMappingURL=chunk-5IFANSBG.js.map
|