@replayci/replay 0.1.9 → 0.1.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +999 -26
- package/dist/index.d.cts +127 -6
- package/dist/index.d.ts +127 -6
- package/dist/index.js +984 -11
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -2370,7 +2370,10 @@ function normalizeInlineContract(input) {
|
|
|
2370
2370
|
...source.transitions != null ? { transitions: source.transitions } : {},
|
|
2371
2371
|
...source.gate != null ? { gate: source.gate } : {},
|
|
2372
2372
|
...source.evidence_class != null ? { evidence_class: source.evidence_class } : {},
|
|
2373
|
-
...source.commit_requirement != null ? { commit_requirement: source.commit_requirement } : {}
|
|
2373
|
+
...source.commit_requirement != null ? { commit_requirement: source.commit_requirement } : {},
|
|
2374
|
+
...typeof source.schema_derived === "boolean" ? { schema_derived: source.schema_derived } : {},
|
|
2375
|
+
...Array.isArray(source.schema_derived_exclude) ? { schema_derived_exclude: source.schema_derived_exclude } : {},
|
|
2376
|
+
...Array.isArray(source.binds) ? { binds: source.binds } : {}
|
|
2374
2377
|
};
|
|
2375
2378
|
validateSafeRegexes(contract);
|
|
2376
2379
|
return contract;
|
|
@@ -2593,8 +2596,83 @@ function extractPath(obj, path) {
|
|
|
2593
2596
|
return current;
|
|
2594
2597
|
}
|
|
2595
2598
|
|
|
2599
|
+
// src/bindings.ts
|
|
2600
|
+
var MAX_BINDINGS = 256;
|
|
2601
|
+
function captureBindings(toolCall, contract, state, outputExtract) {
|
|
2602
|
+
const bindings = new Map(state.bindings);
|
|
2603
|
+
const captured = [];
|
|
2604
|
+
const diagnostics = [];
|
|
2605
|
+
if (!contract.binds || contract.binds.length === 0) {
|
|
2606
|
+
return { bindings, captured, diagnostics };
|
|
2607
|
+
}
|
|
2608
|
+
if (state.killed) {
|
|
2609
|
+
return { bindings, captured, diagnostics };
|
|
2610
|
+
}
|
|
2611
|
+
let parsedArgs;
|
|
2612
|
+
try {
|
|
2613
|
+
parsedArgs = JSON.parse(toolCall.arguments);
|
|
2614
|
+
} catch {
|
|
2615
|
+
parsedArgs = {};
|
|
2616
|
+
}
|
|
2617
|
+
for (const bind of contract.binds) {
|
|
2618
|
+
const source = bind.source ?? "arguments";
|
|
2619
|
+
const sourceObj = source === "output" ? outputExtract ?? {} : parsedArgs ?? {};
|
|
2620
|
+
const value = extractPath(sourceObj, bind.path);
|
|
2621
|
+
const overwritten = bindings.has(bind.name);
|
|
2622
|
+
if (overwritten) {
|
|
2623
|
+
const prev = bindings.get(bind.name);
|
|
2624
|
+
diagnostics.push({
|
|
2625
|
+
type: "binding_overwritten",
|
|
2626
|
+
slot: bind.name,
|
|
2627
|
+
detail: `Slot '${bind.name}' overwritten: ${JSON.stringify(prev.value)} \u2192 ${JSON.stringify(value)}`
|
|
2628
|
+
});
|
|
2629
|
+
}
|
|
2630
|
+
if (bindings.size >= MAX_BINDINGS && !bindings.has(bind.name)) {
|
|
2631
|
+
diagnostics.push({
|
|
2632
|
+
type: "binding_cap_exceeded",
|
|
2633
|
+
slot: bind.name,
|
|
2634
|
+
detail: `Binding cap (${MAX_BINDINGS}) exceeded, slot '${bind.name}' not written`
|
|
2635
|
+
});
|
|
2636
|
+
continue;
|
|
2637
|
+
}
|
|
2638
|
+
bindings.set(bind.name, {
|
|
2639
|
+
value,
|
|
2640
|
+
source_tool: toolCall.name,
|
|
2641
|
+
source_step: state.totalStepCount,
|
|
2642
|
+
bound_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
2643
|
+
});
|
|
2644
|
+
captured.push({
|
|
2645
|
+
slot: bind.name,
|
|
2646
|
+
source,
|
|
2647
|
+
path: bind.path,
|
|
2648
|
+
value,
|
|
2649
|
+
overwritten
|
|
2650
|
+
});
|
|
2651
|
+
}
|
|
2652
|
+
return { bindings, captured, diagnostics };
|
|
2653
|
+
}
|
|
2654
|
+
function evaluateRef(refName, actualValue, tolerance, bindings) {
|
|
2655
|
+
const bound = bindings.get(refName);
|
|
2656
|
+
if (!bound) {
|
|
2657
|
+
return { passed: false, reason: "binding_not_found" };
|
|
2658
|
+
}
|
|
2659
|
+
const expected = bound.value;
|
|
2660
|
+
if (tolerance !== void 0 && typeof expected === "number" && typeof actualValue === "number") {
|
|
2661
|
+
const denom = Math.max(Math.abs(expected), 1);
|
|
2662
|
+
const relDiff = Math.abs(actualValue - expected) / denom;
|
|
2663
|
+
if (relDiff <= tolerance) {
|
|
2664
|
+
return { passed: true, reason: "ref_match", expected, actual: actualValue };
|
|
2665
|
+
}
|
|
2666
|
+
return { passed: false, reason: "ref_mismatch", expected, actual: actualValue };
|
|
2667
|
+
}
|
|
2668
|
+
if (JSON.stringify(actualValue) === JSON.stringify(expected)) {
|
|
2669
|
+
return { passed: true, reason: "ref_match", expected, actual: actualValue };
|
|
2670
|
+
}
|
|
2671
|
+
return { passed: false, reason: "ref_mismatch", expected, actual: actualValue };
|
|
2672
|
+
}
|
|
2673
|
+
|
|
2596
2674
|
// src/argumentValues.ts
|
|
2597
|
-
function evaluateArgumentValueInvariants(parsedArguments, invariants) {
|
|
2675
|
+
function evaluateArgumentValueInvariants(parsedArguments, invariants, bindings) {
|
|
2598
2676
|
const failures = [];
|
|
2599
2677
|
for (const inv of invariants) {
|
|
2600
2678
|
const value = extractPath(parsedArguments, inv.path);
|
|
@@ -2684,6 +2762,18 @@ function evaluateArgumentValueInvariants(parsedArguments, invariants) {
|
|
|
2684
2762
|
});
|
|
2685
2763
|
}
|
|
2686
2764
|
}
|
|
2765
|
+
if (inv.ref !== void 0 && bindings) {
|
|
2766
|
+
const refResult = evaluateRef(inv.ref, value, inv.tolerance, bindings);
|
|
2767
|
+
if (!refResult.passed) {
|
|
2768
|
+
failures.push({
|
|
2769
|
+
path: inv.path,
|
|
2770
|
+
operator: refResult.reason,
|
|
2771
|
+
expected: refResult.expected,
|
|
2772
|
+
actual: refResult.actual ?? value,
|
|
2773
|
+
detail: refResult.reason === "binding_not_found" ? `Binding '${inv.ref}' not found \u2014 producing tool has not been called` : `Ref mismatch on '${inv.ref}': expected ${JSON.stringify(refResult.expected)}, got ${JSON.stringify(refResult.actual)}`
|
|
2774
|
+
});
|
|
2775
|
+
}
|
|
2776
|
+
}
|
|
2687
2777
|
}
|
|
2688
2778
|
return {
|
|
2689
2779
|
passed: failures.length === 0,
|
|
@@ -3113,10 +3203,10 @@ function toString6(value) {
|
|
|
3113
3203
|
}
|
|
3114
3204
|
|
|
3115
3205
|
// src/replay.ts
|
|
3116
|
-
var
|
|
3206
|
+
var import_node_crypto6 = __toESM(require("crypto"), 1);
|
|
3117
3207
|
var import_node_fs3 = require("fs");
|
|
3118
3208
|
var import_node_path3 = require("path");
|
|
3119
|
-
var
|
|
3209
|
+
var import_contracts_core7 = require("@replayci/contracts-core");
|
|
3120
3210
|
|
|
3121
3211
|
// src/redaction.ts
|
|
3122
3212
|
var import_node_crypto2 = __toESM(require("crypto"), 1);
|
|
@@ -3664,7 +3754,12 @@ function createInitialState(sessionId, options) {
|
|
|
3664
3754
|
totalBlockCount: 0,
|
|
3665
3755
|
totalUnguardedCalls: 0,
|
|
3666
3756
|
killed: false,
|
|
3667
|
-
contractHash: null
|
|
3757
|
+
contractHash: null,
|
|
3758
|
+
bindings: /* @__PURE__ */ new Map(),
|
|
3759
|
+
aggregates: /* @__PURE__ */ new Map(),
|
|
3760
|
+
envelopes: /* @__PURE__ */ new Map(),
|
|
3761
|
+
labels: new Set(options?.labels ?? []),
|
|
3762
|
+
checkpointCount: 0
|
|
3668
3763
|
};
|
|
3669
3764
|
}
|
|
3670
3765
|
function finalizeExecutedStep(state, step, contracts, compiledSession) {
|
|
@@ -3964,8 +4059,529 @@ function validateCrossStep(toolCalls, sessionState, contracts, ctx) {
|
|
|
3964
4059
|
};
|
|
3965
4060
|
}
|
|
3966
4061
|
|
|
3967
|
-
// src/
|
|
4062
|
+
// src/aggregates.ts
|
|
4063
|
+
var MAX_DISTINCT_VALUES = 1e4;
|
|
4064
|
+
function initializeAggregates(definitions) {
|
|
4065
|
+
const map = /* @__PURE__ */ new Map();
|
|
4066
|
+
for (const def of definitions) {
|
|
4067
|
+
const state = {
|
|
4068
|
+
name: def.name,
|
|
4069
|
+
metric: def.metric,
|
|
4070
|
+
currentValue: def.metric === "min" ? Infinity : 0
|
|
4071
|
+
};
|
|
4072
|
+
if (def.metric === "count_distinct") {
|
|
4073
|
+
state.distinctValues = /* @__PURE__ */ new Set();
|
|
4074
|
+
}
|
|
4075
|
+
map.set(def.name, state);
|
|
4076
|
+
}
|
|
4077
|
+
return map;
|
|
4078
|
+
}
|
|
4079
|
+
function matchesTool(toolName, filter) {
|
|
4080
|
+
if (filter === "*") return true;
|
|
4081
|
+
if (typeof filter === "string") return filter === toolName;
|
|
4082
|
+
return filter.includes(toolName);
|
|
4083
|
+
}
|
|
4084
|
+
function speculativeCheck(toolName, parsedArgs, aggregates, definitions) {
|
|
4085
|
+
const failures = [];
|
|
4086
|
+
for (const def of definitions) {
|
|
4087
|
+
if (!matchesTool(toolName, def.tool)) continue;
|
|
4088
|
+
const current = aggregates.get(def.name);
|
|
4089
|
+
if (!current) continue;
|
|
4090
|
+
const currentValue = current.currentValue;
|
|
4091
|
+
if (def.metric === "count") {
|
|
4092
|
+
const speculative2 = currentValue + 1;
|
|
4093
|
+
if (def.lte !== void 0 && speculative2 > def.lte) {
|
|
4094
|
+
failures.push({
|
|
4095
|
+
aggregate: def.name,
|
|
4096
|
+
reason: "aggregate_limit_exceeded",
|
|
4097
|
+
detail: `${def.reason} (count ${speculative2} > lte ${def.lte})`,
|
|
4098
|
+
current: currentValue,
|
|
4099
|
+
speculative: speculative2,
|
|
4100
|
+
bound: { lte: def.lte }
|
|
4101
|
+
});
|
|
4102
|
+
}
|
|
4103
|
+
if (def.gte !== void 0 && speculative2 < def.gte) {
|
|
4104
|
+
}
|
|
4105
|
+
continue;
|
|
4106
|
+
}
|
|
4107
|
+
if (!def.path) {
|
|
4108
|
+
failures.push({
|
|
4109
|
+
aggregate: def.name,
|
|
4110
|
+
reason: "aggregate_path_missing",
|
|
4111
|
+
detail: `Aggregate '${def.name}' requires path but none configured`,
|
|
4112
|
+
current: currentValue,
|
|
4113
|
+
speculative: currentValue
|
|
4114
|
+
});
|
|
4115
|
+
continue;
|
|
4116
|
+
}
|
|
4117
|
+
const extracted = extractPath(parsedArgs, def.path);
|
|
4118
|
+
if (extracted === void 0) {
|
|
4119
|
+
failures.push({
|
|
4120
|
+
aggregate: def.name,
|
|
4121
|
+
reason: "aggregate_path_missing",
|
|
4122
|
+
detail: `Tool '${toolName}' missing path '${def.path}' for aggregate '${def.name}'`,
|
|
4123
|
+
current: currentValue,
|
|
4124
|
+
speculative: currentValue
|
|
4125
|
+
});
|
|
4126
|
+
continue;
|
|
4127
|
+
}
|
|
4128
|
+
let speculative;
|
|
4129
|
+
switch (def.metric) {
|
|
4130
|
+
case "sum": {
|
|
4131
|
+
if (typeof extracted !== "number") {
|
|
4132
|
+
failures.push({
|
|
4133
|
+
aggregate: def.name,
|
|
4134
|
+
reason: "aggregate_type_error",
|
|
4135
|
+
detail: `Aggregate '${def.name}' (sum) requires numeric value, got ${typeof extracted}`,
|
|
4136
|
+
current: currentValue,
|
|
4137
|
+
speculative: currentValue
|
|
4138
|
+
});
|
|
4139
|
+
continue;
|
|
4140
|
+
}
|
|
4141
|
+
speculative = currentValue + extracted;
|
|
4142
|
+
break;
|
|
4143
|
+
}
|
|
4144
|
+
case "max": {
|
|
4145
|
+
if (typeof extracted !== "number") {
|
|
4146
|
+
failures.push({
|
|
4147
|
+
aggregate: def.name,
|
|
4148
|
+
reason: "aggregate_type_error",
|
|
4149
|
+
detail: `Aggregate '${def.name}' (max) requires numeric value, got ${typeof extracted}`,
|
|
4150
|
+
current: currentValue,
|
|
4151
|
+
speculative: currentValue
|
|
4152
|
+
});
|
|
4153
|
+
continue;
|
|
4154
|
+
}
|
|
4155
|
+
speculative = Math.max(currentValue, extracted);
|
|
4156
|
+
break;
|
|
4157
|
+
}
|
|
4158
|
+
case "min": {
|
|
4159
|
+
if (typeof extracted !== "number") {
|
|
4160
|
+
failures.push({
|
|
4161
|
+
aggregate: def.name,
|
|
4162
|
+
reason: "aggregate_type_error",
|
|
4163
|
+
detail: `Aggregate '${def.name}' (min) requires numeric value, got ${typeof extracted}`,
|
|
4164
|
+
current: currentValue,
|
|
4165
|
+
speculative: currentValue
|
|
4166
|
+
});
|
|
4167
|
+
continue;
|
|
4168
|
+
}
|
|
4169
|
+
speculative = Math.min(currentValue, extracted);
|
|
4170
|
+
break;
|
|
4171
|
+
}
|
|
4172
|
+
case "count_distinct": {
|
|
4173
|
+
const key = JSON.stringify(extracted);
|
|
4174
|
+
const existing = current.distinctValues ?? /* @__PURE__ */ new Set();
|
|
4175
|
+
if (existing.has(key)) {
|
|
4176
|
+
speculative = currentValue;
|
|
4177
|
+
} else {
|
|
4178
|
+
speculative = currentValue + 1;
|
|
4179
|
+
}
|
|
4180
|
+
break;
|
|
4181
|
+
}
|
|
4182
|
+
default:
|
|
4183
|
+
continue;
|
|
4184
|
+
}
|
|
4185
|
+
if (def.lte !== void 0 && speculative > def.lte) {
|
|
4186
|
+
failures.push({
|
|
4187
|
+
aggregate: def.name,
|
|
4188
|
+
reason: "aggregate_limit_exceeded",
|
|
4189
|
+
detail: `${def.reason} (${def.metric} ${speculative} > lte ${def.lte})`,
|
|
4190
|
+
current: currentValue,
|
|
4191
|
+
speculative,
|
|
4192
|
+
bound: { lte: def.lte }
|
|
4193
|
+
});
|
|
4194
|
+
}
|
|
4195
|
+
if (def.gte !== void 0 && speculative < def.gte) {
|
|
4196
|
+
failures.push({
|
|
4197
|
+
aggregate: def.name,
|
|
4198
|
+
reason: "aggregate_limit_exceeded",
|
|
4199
|
+
detail: `${def.reason} (${def.metric} ${speculative} < gte ${def.gte})`,
|
|
4200
|
+
current: currentValue,
|
|
4201
|
+
speculative,
|
|
4202
|
+
bound: { gte: def.gte }
|
|
4203
|
+
});
|
|
4204
|
+
}
|
|
4205
|
+
}
|
|
4206
|
+
return { passed: failures.length === 0, failures };
|
|
4207
|
+
}
|
|
4208
|
+
function commitAggregate(toolName, parsedArgs, aggregates, definitions) {
|
|
4209
|
+
const updated = new Map(aggregates);
|
|
4210
|
+
for (const def of definitions) {
|
|
4211
|
+
if (!matchesTool(toolName, def.tool)) continue;
|
|
4212
|
+
const current = updated.get(def.name);
|
|
4213
|
+
if (!current) continue;
|
|
4214
|
+
if (def.metric === "count") {
|
|
4215
|
+
updated.set(def.name, { ...current, currentValue: current.currentValue + 1 });
|
|
4216
|
+
continue;
|
|
4217
|
+
}
|
|
4218
|
+
if (!def.path) continue;
|
|
4219
|
+
const extracted = extractPath(parsedArgs, def.path);
|
|
4220
|
+
if (extracted === void 0) continue;
|
|
4221
|
+
switch (def.metric) {
|
|
4222
|
+
case "sum": {
|
|
4223
|
+
if (typeof extracted !== "number") continue;
|
|
4224
|
+
updated.set(def.name, { ...current, currentValue: current.currentValue + extracted });
|
|
4225
|
+
break;
|
|
4226
|
+
}
|
|
4227
|
+
case "max": {
|
|
4228
|
+
if (typeof extracted !== "number") continue;
|
|
4229
|
+
updated.set(def.name, { ...current, currentValue: Math.max(current.currentValue, extracted) });
|
|
4230
|
+
break;
|
|
4231
|
+
}
|
|
4232
|
+
case "min": {
|
|
4233
|
+
if (typeof extracted !== "number") continue;
|
|
4234
|
+
updated.set(def.name, { ...current, currentValue: Math.min(current.currentValue, extracted) });
|
|
4235
|
+
break;
|
|
4236
|
+
}
|
|
4237
|
+
case "count_distinct": {
|
|
4238
|
+
const key = JSON.stringify(extracted);
|
|
4239
|
+
const existing = current.distinctValues ?? /* @__PURE__ */ new Set();
|
|
4240
|
+
if (existing.has(key)) continue;
|
|
4241
|
+
if (existing.size >= MAX_DISTINCT_VALUES) continue;
|
|
4242
|
+
const newSet = new Set(existing);
|
|
4243
|
+
newSet.add(key);
|
|
4244
|
+
updated.set(def.name, { ...current, currentValue: current.currentValue + 1, distinctValues: newSet });
|
|
4245
|
+
break;
|
|
4246
|
+
}
|
|
4247
|
+
}
|
|
4248
|
+
}
|
|
4249
|
+
return updated;
|
|
4250
|
+
}
|
|
4251
|
+
|
|
4252
|
+
// src/envelopes.ts
|
|
4253
|
+
function initializeEnvelopes(definitions) {
|
|
4254
|
+
const map = /* @__PURE__ */ new Map();
|
|
4255
|
+
for (const def of definitions) {
|
|
4256
|
+
map.set(def.name, {
|
|
4257
|
+
name: def.name,
|
|
4258
|
+
constraint: def.constraint,
|
|
4259
|
+
established: false
|
|
4260
|
+
});
|
|
4261
|
+
}
|
|
4262
|
+
return map;
|
|
4263
|
+
}
|
|
4264
|
+
function evaluateEnvelopes(toolName, parsedArgs, envelopes, definitions) {
|
|
4265
|
+
const failures = [];
|
|
4266
|
+
const captures = [];
|
|
4267
|
+
for (const def of definitions) {
|
|
4268
|
+
const current = envelopes.get(def.name);
|
|
4269
|
+
if (!current) continue;
|
|
4270
|
+
for (const stage of def.stages) {
|
|
4271
|
+
if (stage.tool !== toolName) continue;
|
|
4272
|
+
const extracted = extractPath(parsedArgs, stage.path);
|
|
4273
|
+
if (stage.role === "constrained") {
|
|
4274
|
+
if (!current.established) {
|
|
4275
|
+
failures.push({
|
|
4276
|
+
envelope: def.name,
|
|
4277
|
+
reason: "envelope_not_established",
|
|
4278
|
+
detail: `Envelope '${def.name}': constrained tool '${toolName}' called before reference value established`,
|
|
4279
|
+
constraint: def.constraint
|
|
4280
|
+
});
|
|
4281
|
+
continue;
|
|
4282
|
+
}
|
|
4283
|
+
if (typeof extracted !== "number") {
|
|
4284
|
+
failures.push({
|
|
4285
|
+
envelope: def.name,
|
|
4286
|
+
reason: "envelope_type_error",
|
|
4287
|
+
detail: `Envelope '${def.name}': value at '${stage.path}' is ${typeof extracted}, expected number`,
|
|
4288
|
+
constraint: def.constraint
|
|
4289
|
+
});
|
|
4290
|
+
continue;
|
|
4291
|
+
}
|
|
4292
|
+
const violation = checkConstraint(current, def, extracted);
|
|
4293
|
+
if (violation) {
|
|
4294
|
+
failures.push(violation);
|
|
4295
|
+
}
|
|
4296
|
+
captures.push({
|
|
4297
|
+
envelope: def.name,
|
|
4298
|
+
role: "constrained",
|
|
4299
|
+
path: stage.path,
|
|
4300
|
+
value: extracted
|
|
4301
|
+
});
|
|
4302
|
+
} else {
|
|
4303
|
+
if (typeof extracted !== "number") {
|
|
4304
|
+
failures.push({
|
|
4305
|
+
envelope: def.name,
|
|
4306
|
+
reason: "envelope_type_error",
|
|
4307
|
+
detail: `Envelope '${def.name}': reference value at '${stage.path}' is ${typeof extracted}, expected number`,
|
|
4308
|
+
constraint: def.constraint
|
|
4309
|
+
});
|
|
4310
|
+
continue;
|
|
4311
|
+
}
|
|
4312
|
+
captures.push({
|
|
4313
|
+
envelope: def.name,
|
|
4314
|
+
role: stage.role,
|
|
4315
|
+
path: stage.path,
|
|
4316
|
+
value: extracted
|
|
4317
|
+
});
|
|
4318
|
+
}
|
|
4319
|
+
}
|
|
4320
|
+
}
|
|
4321
|
+
return { passed: failures.length === 0, failures, captures };
|
|
4322
|
+
}
|
|
4323
|
+
function commitEnvelope(captures, envelopes) {
|
|
4324
|
+
if (captures.length === 0) return envelopes;
|
|
4325
|
+
const updated = new Map(envelopes);
|
|
4326
|
+
for (const cap of captures) {
|
|
4327
|
+
const current = updated.get(cap.envelope);
|
|
4328
|
+
if (!current) continue;
|
|
4329
|
+
const next = { ...current };
|
|
4330
|
+
switch (cap.role) {
|
|
4331
|
+
case "ceiling":
|
|
4332
|
+
next.ceiling = cap.value;
|
|
4333
|
+
next.established = true;
|
|
4334
|
+
break;
|
|
4335
|
+
case "floor":
|
|
4336
|
+
next.floor = cap.value;
|
|
4337
|
+
next.established = true;
|
|
4338
|
+
break;
|
|
4339
|
+
case "anchor":
|
|
4340
|
+
next.anchor = cap.value;
|
|
4341
|
+
next.established = true;
|
|
4342
|
+
break;
|
|
4343
|
+
case "initial":
|
|
4344
|
+
next.lastValue = cap.value;
|
|
4345
|
+
next.established = true;
|
|
4346
|
+
break;
|
|
4347
|
+
case "constrained":
|
|
4348
|
+
next.lastValue = cap.value;
|
|
4349
|
+
break;
|
|
4350
|
+
}
|
|
4351
|
+
updated.set(cap.envelope, next);
|
|
4352
|
+
}
|
|
4353
|
+
return updated;
|
|
4354
|
+
}
|
|
4355
|
+
function checkConstraint(state, def, value) {
|
|
4356
|
+
switch (def.constraint) {
|
|
4357
|
+
case "lte_ceiling": {
|
|
4358
|
+
if (state.ceiling === void 0) return null;
|
|
4359
|
+
if (value <= state.ceiling) return null;
|
|
4360
|
+
return {
|
|
4361
|
+
envelope: def.name,
|
|
4362
|
+
reason: "envelope_violation",
|
|
4363
|
+
detail: `${def.reason} (${value} > ceiling ${state.ceiling})`,
|
|
4364
|
+
constraint: def.constraint,
|
|
4365
|
+
referenceValue: state.ceiling,
|
|
4366
|
+
actualValue: value
|
|
4367
|
+
};
|
|
4368
|
+
}
|
|
4369
|
+
case "gte_floor": {
|
|
4370
|
+
if (state.floor === void 0) return null;
|
|
4371
|
+
if (value >= state.floor) return null;
|
|
4372
|
+
return {
|
|
4373
|
+
envelope: def.name,
|
|
4374
|
+
reason: "envelope_violation",
|
|
4375
|
+
detail: `${def.reason} (${value} < floor ${state.floor})`,
|
|
4376
|
+
constraint: def.constraint,
|
|
4377
|
+
referenceValue: state.floor,
|
|
4378
|
+
actualValue: value
|
|
4379
|
+
};
|
|
4380
|
+
}
|
|
4381
|
+
case "within_band": {
|
|
4382
|
+
if (state.anchor === void 0) return null;
|
|
4383
|
+
const band = def.band ?? 0;
|
|
4384
|
+
const denom = Math.max(Math.abs(state.anchor), 1);
|
|
4385
|
+
const relDiff = Math.abs(value - state.anchor) / denom;
|
|
4386
|
+
if (relDiff <= band) return null;
|
|
4387
|
+
return {
|
|
4388
|
+
envelope: def.name,
|
|
4389
|
+
reason: "envelope_violation",
|
|
4390
|
+
detail: `${def.reason} (${value} is ${(relDiff * 100).toFixed(1)}% from anchor ${state.anchor}, band ${(band * 100).toFixed(1)}%)`,
|
|
4391
|
+
constraint: def.constraint,
|
|
4392
|
+
referenceValue: state.anchor,
|
|
4393
|
+
actualValue: value
|
|
4394
|
+
};
|
|
4395
|
+
}
|
|
4396
|
+
case "monotonic_decrease": {
|
|
4397
|
+
const ref = state.lastValue;
|
|
4398
|
+
if (ref === void 0) return null;
|
|
4399
|
+
if (value <= ref) return null;
|
|
4400
|
+
return {
|
|
4401
|
+
envelope: def.name,
|
|
4402
|
+
reason: "envelope_violation",
|
|
4403
|
+
detail: `${def.reason} (${value} > previous ${ref})`,
|
|
4404
|
+
constraint: def.constraint,
|
|
4405
|
+
referenceValue: ref,
|
|
4406
|
+
actualValue: value
|
|
4407
|
+
};
|
|
4408
|
+
}
|
|
4409
|
+
case "monotonic_increase": {
|
|
4410
|
+
const ref = state.lastValue;
|
|
4411
|
+
if (ref === void 0) return null;
|
|
4412
|
+
if (value >= ref) return null;
|
|
4413
|
+
return {
|
|
4414
|
+
envelope: def.name,
|
|
4415
|
+
reason: "envelope_violation",
|
|
4416
|
+
detail: `${def.reason} (${value} < previous ${ref})`,
|
|
4417
|
+
constraint: def.constraint,
|
|
4418
|
+
referenceValue: ref,
|
|
4419
|
+
actualValue: value
|
|
4420
|
+
};
|
|
4421
|
+
}
|
|
4422
|
+
case "bounded": {
|
|
4423
|
+
if (state.floor !== void 0 && value < state.floor) {
|
|
4424
|
+
return {
|
|
4425
|
+
envelope: def.name,
|
|
4426
|
+
reason: "envelope_violation",
|
|
4427
|
+
detail: `${def.reason} (${value} < floor ${state.floor})`,
|
|
4428
|
+
constraint: def.constraint,
|
|
4429
|
+
referenceValue: state.floor,
|
|
4430
|
+
actualValue: value
|
|
4431
|
+
};
|
|
4432
|
+
}
|
|
4433
|
+
if (state.ceiling !== void 0 && value > state.ceiling) {
|
|
4434
|
+
return {
|
|
4435
|
+
envelope: def.name,
|
|
4436
|
+
reason: "envelope_violation",
|
|
4437
|
+
detail: `${def.reason} (${value} > ceiling ${state.ceiling})`,
|
|
4438
|
+
constraint: def.constraint,
|
|
4439
|
+
referenceValue: state.ceiling,
|
|
4440
|
+
actualValue: value
|
|
4441
|
+
};
|
|
4442
|
+
}
|
|
4443
|
+
return null;
|
|
4444
|
+
}
|
|
4445
|
+
default:
|
|
4446
|
+
return null;
|
|
4447
|
+
}
|
|
4448
|
+
}
|
|
4449
|
+
|
|
4450
|
+
// src/checkpoints.ts
|
|
4451
|
+
var import_node_crypto4 = __toESM(require("crypto"), 1);
|
|
3968
4452
|
var import_contracts_core4 = require("@replayci/contracts-core");
|
|
4453
|
+
var MAX_CHECKPOINT_BUDGET = 10;
|
|
4454
|
+
function evaluateCheckpoints(toolCalls, checkpoints, contracts) {
|
|
4455
|
+
if (checkpoints.length === 0) return null;
|
|
4456
|
+
const contractByTool = new Map(contracts.map((c) => [c.tool, c]));
|
|
4457
|
+
for (const tc of toolCalls) {
|
|
4458
|
+
let parsedArgs;
|
|
4459
|
+
try {
|
|
4460
|
+
parsedArgs = JSON.parse(tc.arguments);
|
|
4461
|
+
} catch {
|
|
4462
|
+
parsedArgs = {};
|
|
4463
|
+
}
|
|
4464
|
+
for (const cp of checkpoints) {
|
|
4465
|
+
if (cp.tool !== "*" && cp.tool !== tc.name) continue;
|
|
4466
|
+
if (cp.when_side_effect) {
|
|
4467
|
+
const contract = contractByTool.get(tc.name);
|
|
4468
|
+
if (!contract?.side_effect || contract.side_effect !== cp.when_side_effect) {
|
|
4469
|
+
if (cp.when === "always" || Array.isArray(cp.when) && cp.when.length === 0) {
|
|
4470
|
+
continue;
|
|
4471
|
+
}
|
|
4472
|
+
} else {
|
|
4473
|
+
return { checkpoint: cp, toolCall: tc, matchedConditions: [`when_side_effect:${cp.when_side_effect}`] };
|
|
4474
|
+
}
|
|
4475
|
+
}
|
|
4476
|
+
if (cp.when === "always") {
|
|
4477
|
+
return { checkpoint: cp, toolCall: tc, matchedConditions: ["always"] };
|
|
4478
|
+
}
|
|
4479
|
+
if (Array.isArray(cp.when) && cp.when.length > 0) {
|
|
4480
|
+
const matched = [];
|
|
4481
|
+
for (const cond of cp.when) {
|
|
4482
|
+
if (evaluateCondition(parsedArgs, cond)) {
|
|
4483
|
+
matched.push(cond.path);
|
|
4484
|
+
}
|
|
4485
|
+
}
|
|
4486
|
+
const triggers = cp.match === "all" ? matched.length === cp.when.length : matched.length > 0;
|
|
4487
|
+
if (triggers) {
|
|
4488
|
+
return { checkpoint: cp, toolCall: tc, matchedConditions: matched };
|
|
4489
|
+
}
|
|
4490
|
+
}
|
|
4491
|
+
}
|
|
4492
|
+
}
|
|
4493
|
+
return null;
|
|
4494
|
+
}
|
|
4495
|
+
function evaluateCondition(args, cond) {
|
|
4496
|
+
const { exists, value } = (0, import_contracts_core4.getPathValue)(args, cond.path);
|
|
4497
|
+
if (!exists) return false;
|
|
4498
|
+
if (cond.gte !== void 0) {
|
|
4499
|
+
if (typeof value !== "number" || value < cond.gte) return false;
|
|
4500
|
+
}
|
|
4501
|
+
if (cond.lte !== void 0) {
|
|
4502
|
+
if (typeof value !== "number" || value > cond.lte) return false;
|
|
4503
|
+
}
|
|
4504
|
+
if (cond.equals !== void 0) {
|
|
4505
|
+
if (value !== cond.equals) return false;
|
|
4506
|
+
}
|
|
4507
|
+
if (cond.one_of !== void 0) {
|
|
4508
|
+
if (!cond.one_of.includes(value)) return false;
|
|
4509
|
+
}
|
|
4510
|
+
return true;
|
|
4511
|
+
}
|
|
4512
|
+
function buildApprovalRequest(checkpoint, toolCall, sessionId, matchedConditions) {
|
|
4513
|
+
let parsedArgs;
|
|
4514
|
+
try {
|
|
4515
|
+
parsedArgs = JSON.parse(toolCall.arguments);
|
|
4516
|
+
} catch {
|
|
4517
|
+
parsedArgs = {};
|
|
4518
|
+
}
|
|
4519
|
+
const context = [];
|
|
4520
|
+
for (const ctx of checkpoint.context) {
|
|
4521
|
+
const { exists, value } = (0, import_contracts_core4.getPathValue)(parsedArgs, ctx.path);
|
|
4522
|
+
context.push({ label: ctx.label, value: exists ? value : void 0 });
|
|
4523
|
+
}
|
|
4524
|
+
const reason = matchedConditions.includes("always") ? `Checkpoint '${checkpoint.name}': always requires approval for ${toolCall.name}` : matchedConditions.some((m) => m.startsWith("when_side_effect:")) ? `Checkpoint '${checkpoint.name}': tool ${toolCall.name} has ${matchedConditions[0]}` : `Checkpoint '${checkpoint.name}': conditions met [${matchedConditions.join(", ")}]`;
|
|
4525
|
+
return {
|
|
4526
|
+
checkpoint_id: import_node_crypto4.default.randomUUID(),
|
|
4527
|
+
session_id: sessionId,
|
|
4528
|
+
tool_name: toolCall.name,
|
|
4529
|
+
arguments: parsedArgs,
|
|
4530
|
+
context,
|
|
4531
|
+
reason,
|
|
4532
|
+
timeout_seconds: checkpoint.timeout_seconds,
|
|
4533
|
+
requested_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
4534
|
+
};
|
|
4535
|
+
}
|
|
4536
|
+
async function waitForApproval(request, onCheckpoint, timeoutSeconds, onTimeout, isKilled) {
|
|
4537
|
+
return new Promise((resolve2) => {
|
|
4538
|
+
let settled = false;
|
|
4539
|
+
const timer = setTimeout(() => {
|
|
4540
|
+
if (settled) return;
|
|
4541
|
+
settled = true;
|
|
4542
|
+
resolve2({
|
|
4543
|
+
checkpoint_id: request.checkpoint_id,
|
|
4544
|
+
decision: isKilled() ? "deny" : onTimeout === "allow" ? "approve" : "deny",
|
|
4545
|
+
decided_by: isKilled() ? "system:killed" : "system:timeout",
|
|
4546
|
+
decided_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4547
|
+
reason: isKilled() ? "Session killed during checkpoint wait" : `Timeout after ${timeoutSeconds}s`
|
|
4548
|
+
});
|
|
4549
|
+
}, timeoutSeconds * 1e3);
|
|
4550
|
+
onCheckpoint(request).then(
|
|
4551
|
+
(response) => {
|
|
4552
|
+
if (settled) return;
|
|
4553
|
+
settled = true;
|
|
4554
|
+
clearTimeout(timer);
|
|
4555
|
+
if (isKilled()) {
|
|
4556
|
+
resolve2({
|
|
4557
|
+
checkpoint_id: request.checkpoint_id,
|
|
4558
|
+
decision: "deny",
|
|
4559
|
+
decided_by: "system:killed",
|
|
4560
|
+
decided_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4561
|
+
reason: "Session killed during checkpoint wait"
|
|
4562
|
+
});
|
|
4563
|
+
} else {
|
|
4564
|
+
resolve2(response);
|
|
4565
|
+
}
|
|
4566
|
+
},
|
|
4567
|
+
(err) => {
|
|
4568
|
+
if (settled) return;
|
|
4569
|
+
settled = true;
|
|
4570
|
+
clearTimeout(timer);
|
|
4571
|
+
resolve2({
|
|
4572
|
+
checkpoint_id: request.checkpoint_id,
|
|
4573
|
+
decision: "deny",
|
|
4574
|
+
decided_by: "system:error",
|
|
4575
|
+
decided_at: (/* @__PURE__ */ new Date()).toISOString(),
|
|
4576
|
+
reason: `Checkpoint callback error: ${err instanceof Error ? err.message : String(err)}`
|
|
4577
|
+
});
|
|
4578
|
+
}
|
|
4579
|
+
);
|
|
4580
|
+
});
|
|
4581
|
+
}
|
|
4582
|
+
|
|
4583
|
+
// src/messageValidation.ts
|
|
4584
|
+
var import_contracts_core5 = require("@replayci/contracts-core");
|
|
3969
4585
|
function validateToolResultMessages(messages, contracts, provider) {
|
|
3970
4586
|
const failures = [];
|
|
3971
4587
|
const contractByTool = new Map(contracts.map((c) => [c.tool, c]));
|
|
@@ -3981,7 +4597,7 @@ function validateToolResultMessages(messages, contracts, provider) {
|
|
|
3981
4597
|
} catch {
|
|
3982
4598
|
continue;
|
|
3983
4599
|
}
|
|
3984
|
-
const invariantResult = (0,
|
|
4600
|
+
const invariantResult = (0, import_contracts_core5.evaluateInvariants)(parsed, outputInvariants, process.env);
|
|
3985
4601
|
for (const failure of invariantResult) {
|
|
3986
4602
|
failures.push({
|
|
3987
4603
|
toolName: result.toolName,
|
|
@@ -4245,6 +4861,29 @@ function narrowTools(requestedTools, sessionState, compiledSession, unmatchedPol
|
|
|
4245
4861
|
continue;
|
|
4246
4862
|
}
|
|
4247
4863
|
}
|
|
4864
|
+
if (compiledSession.labelGates && compiledSession.labelGates.length > 0 && sessionState.labels.size > 0) {
|
|
4865
|
+
let labelBlocked = false;
|
|
4866
|
+
for (const gate of compiledSession.labelGates) {
|
|
4867
|
+
if (sessionState.labels.has(gate.when_label) && gate.deny_tools.includes(tool.name)) {
|
|
4868
|
+
removed.push({
|
|
4869
|
+
tool: tool.name,
|
|
4870
|
+
reason: "label_gate",
|
|
4871
|
+
detail: gate.reason
|
|
4872
|
+
});
|
|
4873
|
+
ctx?.trace.push({
|
|
4874
|
+
stage: "narrow",
|
|
4875
|
+
tool: tool.name,
|
|
4876
|
+
verdict: "remove",
|
|
4877
|
+
reason: "label_gate",
|
|
4878
|
+
checked: { label: gate.when_label, gate_reason: gate.reason },
|
|
4879
|
+
found: { active_labels: [...sessionState.labels] }
|
|
4880
|
+
});
|
|
4881
|
+
labelBlocked = true;
|
|
4882
|
+
break;
|
|
4883
|
+
}
|
|
4884
|
+
}
|
|
4885
|
+
if (labelBlocked) continue;
|
|
4886
|
+
}
|
|
4248
4887
|
ctx?.trace.push({
|
|
4249
4888
|
stage: "narrow",
|
|
4250
4889
|
tool: tool.name,
|
|
@@ -4255,7 +4894,8 @@ function narrowTools(requestedTools, sessionState, compiledSession, unmatchedPol
|
|
|
4255
4894
|
phase_ok: true,
|
|
4256
4895
|
preconditions_ok: true,
|
|
4257
4896
|
not_forbidden: true,
|
|
4258
|
-
policy_ok: true
|
|
4897
|
+
policy_ok: true,
|
|
4898
|
+
label_gates_ok: true
|
|
4259
4899
|
},
|
|
4260
4900
|
found: {}
|
|
4261
4901
|
});
|
|
@@ -4285,12 +4925,12 @@ function getToolName(tool) {
|
|
|
4285
4925
|
}
|
|
4286
4926
|
|
|
4287
4927
|
// src/executionConstraints.ts
|
|
4288
|
-
var
|
|
4928
|
+
var import_contracts_core6 = require("@replayci/contracts-core");
|
|
4289
4929
|
function enforceExecutionConstraints(toolName, args, constraints) {
|
|
4290
4930
|
if (constraints.length === 0) {
|
|
4291
4931
|
return { passed: true, failures: [] };
|
|
4292
4932
|
}
|
|
4293
|
-
const failures = (0,
|
|
4933
|
+
const failures = (0, import_contracts_core6.evaluateInvariants)(args, constraints, process.env);
|
|
4294
4934
|
const constraintFailures = failures.map((f) => ({
|
|
4295
4935
|
path: f.path,
|
|
4296
4936
|
operator: f.rule,
|
|
@@ -4342,7 +4982,7 @@ function buildWrappedToolsMap(tools, compiledSession) {
|
|
|
4342
4982
|
}
|
|
4343
4983
|
|
|
4344
4984
|
// src/runtimeClient.ts
|
|
4345
|
-
var
|
|
4985
|
+
var import_node_crypto5 = __toESM(require("crypto"), 1);
|
|
4346
4986
|
var CIRCUIT_BREAKER_FAILURE_LIMIT2 = 5;
|
|
4347
4987
|
var CIRCUIT_BREAKER_MS2 = 10 * 6e4;
|
|
4348
4988
|
var DEFAULT_TIMEOUT_MS2 = 3e4;
|
|
@@ -4733,7 +5373,7 @@ function normalizeUrl(url) {
|
|
|
4733
5373
|
return url.endsWith("/") ? url.slice(0, -1) : url;
|
|
4734
5374
|
}
|
|
4735
5375
|
function generateIdempotencyKey() {
|
|
4736
|
-
return `sdk_${
|
|
5376
|
+
return `sdk_${import_node_crypto5.default.randomUUID().replace(/-/g, "")}`;
|
|
4737
5377
|
}
|
|
4738
5378
|
|
|
4739
5379
|
// src/replay.ts
|
|
@@ -4799,7 +5439,7 @@ function replay(client, opts = {}) {
|
|
|
4799
5439
|
}
|
|
4800
5440
|
let compiledSession = null;
|
|
4801
5441
|
try {
|
|
4802
|
-
compiledSession = (0,
|
|
5442
|
+
compiledSession = (0, import_contracts_core7.compileSession)(contracts, sessionYaml, {
|
|
4803
5443
|
principal: opts.principal,
|
|
4804
5444
|
tools: opts.tools ? new Map(Object.entries(opts.tools)) : void 0
|
|
4805
5445
|
});
|
|
@@ -4821,6 +5461,20 @@ function replay(client, opts = {}) {
|
|
|
4821
5461
|
});
|
|
4822
5462
|
}
|
|
4823
5463
|
}
|
|
5464
|
+
if (compiledSession?.graphDiagnostics && compiledSession.graphDiagnostics.length > 0) {
|
|
5465
|
+
for (const diag of compiledSession.graphDiagnostics) {
|
|
5466
|
+
emitDiagnostic2(diagnostics, {
|
|
5467
|
+
type: "replay_graph_analysis",
|
|
5468
|
+
session_id: sessionId,
|
|
5469
|
+
check: diag.check,
|
|
5470
|
+
severity: diag.severity,
|
|
5471
|
+
detail: diag.detail,
|
|
5472
|
+
...diag.tool ? { tool: diag.tool } : {},
|
|
5473
|
+
...diag.phase ? { phase: diag.phase } : {},
|
|
5474
|
+
...diag.suggestion ? { suggestion: diag.suggestion } : {}
|
|
5475
|
+
});
|
|
5476
|
+
}
|
|
5477
|
+
}
|
|
4824
5478
|
const providerConstraints = compiledSession?.providerConstraints ?? opts.providerConstraints ?? null;
|
|
4825
5479
|
if (providerConstraints) {
|
|
4826
5480
|
const spec = providerConstraints[provider];
|
|
@@ -4889,13 +5543,13 @@ function replay(client, opts = {}) {
|
|
|
4889
5543
|
compiledSession: compiledSession ? {
|
|
4890
5544
|
schemaVersion: "1",
|
|
4891
5545
|
hash: compiledSession.compiledHash,
|
|
4892
|
-
body: (0,
|
|
5546
|
+
body: (0, import_contracts_core7.serializeCompiledSession)(compiledSession)
|
|
4893
5547
|
} : void 0,
|
|
4894
5548
|
principal: opts.principal ?? null
|
|
4895
5549
|
};
|
|
4896
5550
|
if (workflowOpts && workflowId) {
|
|
4897
5551
|
if (workflowOpts.type === "root" && compiledWorkflow) {
|
|
4898
|
-
const serialized = (0,
|
|
5552
|
+
const serialized = (0, import_contracts_core7.serializeCompiledWorkflow)(compiledWorkflow);
|
|
4899
5553
|
sessionInitPayload.workflow = {
|
|
4900
5554
|
workflowId,
|
|
4901
5555
|
role: workflowOpts.role,
|
|
@@ -4936,7 +5590,7 @@ function replay(client, opts = {}) {
|
|
|
4936
5590
|
}
|
|
4937
5591
|
const initialTier = protectionLevel === "govern" && apiKey ? "strong" : "compat";
|
|
4938
5592
|
const principalValue = opts.principal != null && typeof opts.principal === "object" && !Array.isArray(opts.principal) ? opts.principal : null;
|
|
4939
|
-
let sessionState = createInitialState(sessionId, { tier: initialTier, agent, principal: principalValue });
|
|
5593
|
+
let sessionState = createInitialState(sessionId, { tier: initialTier, agent, principal: principalValue, labels: opts.labels });
|
|
4940
5594
|
if (compiledSession?.phases) {
|
|
4941
5595
|
const initial = compiledSession.phases.find((p) => p.initial);
|
|
4942
5596
|
if (initial) {
|
|
@@ -4944,6 +5598,12 @@ function replay(client, opts = {}) {
|
|
|
4944
5598
|
}
|
|
4945
5599
|
}
|
|
4946
5600
|
sessionState = { ...sessionState, contractHash: compiledSession?.compiledHash ?? null };
|
|
5601
|
+
if (compiledSession?.aggregates && compiledSession.aggregates.length > 0) {
|
|
5602
|
+
sessionState = { ...sessionState, aggregates: initializeAggregates(compiledSession.aggregates) };
|
|
5603
|
+
}
|
|
5604
|
+
if (compiledSession?.envelopes && compiledSession.envelopes.length > 0) {
|
|
5605
|
+
sessionState = { ...sessionState, envelopes: initializeEnvelopes(compiledSession.envelopes) };
|
|
5606
|
+
}
|
|
4947
5607
|
let killed = false;
|
|
4948
5608
|
let killedAt = null;
|
|
4949
5609
|
let restored = false;
|
|
@@ -5490,7 +6150,7 @@ function replay(client, opts = {}) {
|
|
|
5490
6150
|
} catch {
|
|
5491
6151
|
parsedArgs = {};
|
|
5492
6152
|
}
|
|
5493
|
-
const avResult = evaluateArgumentValueInvariants(parsedArgs, contract.argument_value_invariants);
|
|
6153
|
+
const avResult = evaluateArgumentValueInvariants(parsedArgs, contract.argument_value_invariants, sessionState.bindings);
|
|
5494
6154
|
if (!avResult.passed) {
|
|
5495
6155
|
for (const f of avResult.failures) {
|
|
5496
6156
|
validation.failures.push({
|
|
@@ -5552,6 +6212,93 @@ function replay(client, opts = {}) {
|
|
|
5552
6212
|
}
|
|
5553
6213
|
}
|
|
5554
6214
|
timing.argument_values_ms += Date.now() - argValuesStart;
|
|
6215
|
+
if (compiledSession?.aggregates && compiledSession.aggregates.length > 0) {
|
|
6216
|
+
const workingAggregates = new Map(sessionState.aggregates);
|
|
6217
|
+
for (const tc of toolCalls) {
|
|
6218
|
+
let parsedArgs;
|
|
6219
|
+
try {
|
|
6220
|
+
parsedArgs = JSON.parse(tc.arguments);
|
|
6221
|
+
} catch {
|
|
6222
|
+
parsedArgs = {};
|
|
6223
|
+
}
|
|
6224
|
+
const aggResult = speculativeCheck(tc.name, parsedArgs, workingAggregates, compiledSession.aggregates);
|
|
6225
|
+
if (!aggResult.passed) {
|
|
6226
|
+
for (const f of aggResult.failures) {
|
|
6227
|
+
trace.push({
|
|
6228
|
+
stage: "aggregate",
|
|
6229
|
+
tool: tc.name,
|
|
6230
|
+
verdict: "block",
|
|
6231
|
+
reason: f.reason,
|
|
6232
|
+
checked: { aggregate: f.aggregate, bound_lte: f.bound?.lte ?? null, bound_gte: f.bound?.gte ?? null },
|
|
6233
|
+
found: { current: f.current, speculative: f.speculative }
|
|
6234
|
+
});
|
|
6235
|
+
validation.failures.push({
|
|
6236
|
+
path: `$.tool_calls.${tc.name}`,
|
|
6237
|
+
operator: f.reason,
|
|
6238
|
+
expected: "",
|
|
6239
|
+
found: String(f.speculative),
|
|
6240
|
+
message: f.detail,
|
|
6241
|
+
contract_file: ""
|
|
6242
|
+
});
|
|
6243
|
+
}
|
|
6244
|
+
} else {
|
|
6245
|
+
const committed = commitAggregate(tc.name, parsedArgs, workingAggregates, compiledSession.aggregates);
|
|
6246
|
+
for (const [k, v] of committed) {
|
|
6247
|
+
workingAggregates.set(k, v);
|
|
6248
|
+
}
|
|
6249
|
+
}
|
|
6250
|
+
}
|
|
6251
|
+
}
|
|
6252
|
+
if (compiledSession?.envelopes && compiledSession.envelopes.length > 0) {
|
|
6253
|
+
const workingEnvelopes = new Map(sessionState.envelopes);
|
|
6254
|
+
for (const tc of toolCalls) {
|
|
6255
|
+
let parsedArgs;
|
|
6256
|
+
try {
|
|
6257
|
+
parsedArgs = JSON.parse(tc.arguments);
|
|
6258
|
+
} catch {
|
|
6259
|
+
parsedArgs = {};
|
|
6260
|
+
}
|
|
6261
|
+
const envResult = evaluateEnvelopes(tc.name, parsedArgs, workingEnvelopes, compiledSession.envelopes);
|
|
6262
|
+
if (!envResult.passed) {
|
|
6263
|
+
for (const f of envResult.failures) {
|
|
6264
|
+
trace.push({
|
|
6265
|
+
stage: "envelope",
|
|
6266
|
+
tool: tc.name,
|
|
6267
|
+
verdict: "block",
|
|
6268
|
+
reason: f.reason,
|
|
6269
|
+
checked: { envelope: f.envelope, constraint: f.constraint, reference: f.referenceValue ?? null },
|
|
6270
|
+
found: { value: f.actualValue ?? null }
|
|
6271
|
+
});
|
|
6272
|
+
validation.failures.push({
|
|
6273
|
+
path: `$.tool_calls.${tc.name}`,
|
|
6274
|
+
operator: f.reason,
|
|
6275
|
+
expected: "",
|
|
6276
|
+
found: String(f.actualValue ?? ""),
|
|
6277
|
+
message: f.detail,
|
|
6278
|
+
contract_file: ""
|
|
6279
|
+
});
|
|
6280
|
+
}
|
|
6281
|
+
}
|
|
6282
|
+
if (envResult.captures.length > 0) {
|
|
6283
|
+
const committed = commitEnvelope(envResult.captures, workingEnvelopes);
|
|
6284
|
+
for (const [k, v] of committed) {
|
|
6285
|
+
workingEnvelopes.set(k, v);
|
|
6286
|
+
}
|
|
6287
|
+
for (const cap of envResult.captures) {
|
|
6288
|
+
if (cap.role !== "constrained") {
|
|
6289
|
+
trace.push({
|
|
6290
|
+
stage: "envelope",
|
|
6291
|
+
tool: tc.name,
|
|
6292
|
+
verdict: "allow",
|
|
6293
|
+
reason: "ceiling_established",
|
|
6294
|
+
checked: { envelope: cap.envelope, role: cap.role, path: cap.path },
|
|
6295
|
+
found: { value: cap.value }
|
|
6296
|
+
});
|
|
6297
|
+
}
|
|
6298
|
+
}
|
|
6299
|
+
}
|
|
6300
|
+
}
|
|
6301
|
+
}
|
|
5555
6302
|
currentTraceStage = "policy";
|
|
5556
6303
|
let policyVerdicts = null;
|
|
5557
6304
|
const policyStart = Date.now();
|
|
@@ -5604,6 +6351,143 @@ function replay(client, opts = {}) {
|
|
|
5604
6351
|
trace.push({ stage: "policy", tool: null, verdict: "skip", reason: "no_policy_configured", checked: {}, found: {} });
|
|
5605
6352
|
}
|
|
5606
6353
|
timing.policy_ms += Date.now() - policyStart;
|
|
6354
|
+
currentTraceStage = "checkpoint";
|
|
6355
|
+
if (compiledSession?.checkpoints && compiledSession.checkpoints.length > 0 && validation.failures.length === 0) {
|
|
6356
|
+
const cpResult = evaluateCheckpoints(toolCalls, compiledSession.checkpoints, contracts);
|
|
6357
|
+
if (cpResult) {
|
|
6358
|
+
const { checkpoint: triggeredCp, toolCall: triggeredTc, matchedConditions } = cpResult;
|
|
6359
|
+
if (mode === "shadow") {
|
|
6360
|
+
trace.push({
|
|
6361
|
+
stage: "checkpoint",
|
|
6362
|
+
tool: triggeredTc.name,
|
|
6363
|
+
verdict: "info",
|
|
6364
|
+
reason: "checkpoint_would_trigger",
|
|
6365
|
+
checked: { checkpoint: triggeredCp.name, conditions: matchedConditions },
|
|
6366
|
+
found: { shadow: true }
|
|
6367
|
+
});
|
|
6368
|
+
emitDiagnostic2(diagnostics, {
|
|
6369
|
+
type: "replay_checkpoint_shadow",
|
|
6370
|
+
session_id: sessionId,
|
|
6371
|
+
checkpoint: triggeredCp.name,
|
|
6372
|
+
tool_name: triggeredTc.name
|
|
6373
|
+
});
|
|
6374
|
+
} else {
|
|
6375
|
+
if (sessionState.checkpointCount >= MAX_CHECKPOINT_BUDGET) {
|
|
6376
|
+
trace.push({
|
|
6377
|
+
stage: "checkpoint",
|
|
6378
|
+
tool: triggeredTc.name,
|
|
6379
|
+
verdict: "block",
|
|
6380
|
+
reason: "checkpoint_budget_exceeded",
|
|
6381
|
+
checked: { checkpoint: triggeredCp.name, budget: MAX_CHECKPOINT_BUDGET },
|
|
6382
|
+
found: { count: sessionState.checkpointCount }
|
|
6383
|
+
});
|
|
6384
|
+
validation.failures.push({
|
|
6385
|
+
path: `$.tool_calls.${triggeredTc.name}`,
|
|
6386
|
+
operator: "checkpoint_budget_exceeded",
|
|
6387
|
+
expected: `<= ${MAX_CHECKPOINT_BUDGET} checkpoints`,
|
|
6388
|
+
found: String(sessionState.checkpointCount),
|
|
6389
|
+
message: `Checkpoint budget exceeded: ${sessionState.checkpointCount} of ${MAX_CHECKPOINT_BUDGET}`,
|
|
6390
|
+
contract_file: ""
|
|
6391
|
+
});
|
|
6392
|
+
} else if (!opts.onCheckpoint) {
|
|
6393
|
+
trace.push({
|
|
6394
|
+
stage: "checkpoint",
|
|
6395
|
+
tool: triggeredTc.name,
|
|
6396
|
+
verdict: "block",
|
|
6397
|
+
reason: "checkpoint_no_handler",
|
|
6398
|
+
checked: { checkpoint: triggeredCp.name },
|
|
6399
|
+
found: { has_handler: false }
|
|
6400
|
+
});
|
|
6401
|
+
validation.failures.push({
|
|
6402
|
+
path: `$.tool_calls.${triggeredTc.name}`,
|
|
6403
|
+
operator: "checkpoint_no_handler",
|
|
6404
|
+
expected: "onCheckpoint callback",
|
|
6405
|
+
found: "not provided",
|
|
6406
|
+
message: `Checkpoint '${triggeredCp.name}' triggered but no onCheckpoint handler provided`,
|
|
6407
|
+
contract_file: ""
|
|
6408
|
+
});
|
|
6409
|
+
} else {
|
|
6410
|
+
trace.push({
|
|
6411
|
+
stage: "checkpoint",
|
|
6412
|
+
tool: triggeredTc.name,
|
|
6413
|
+
verdict: "paused",
|
|
6414
|
+
reason: "checkpoint_triggered",
|
|
6415
|
+
checked: { checkpoint: triggeredCp.name, conditions: matchedConditions },
|
|
6416
|
+
found: {}
|
|
6417
|
+
});
|
|
6418
|
+
emitDiagnostic2(diagnostics, {
|
|
6419
|
+
type: "replay_checkpoint_triggered",
|
|
6420
|
+
session_id: sessionId,
|
|
6421
|
+
checkpoint: triggeredCp.name,
|
|
6422
|
+
tool_name: triggeredTc.name
|
|
6423
|
+
});
|
|
6424
|
+
const approvalRequest = buildApprovalRequest(
|
|
6425
|
+
triggeredCp,
|
|
6426
|
+
triggeredTc,
|
|
6427
|
+
sessionId,
|
|
6428
|
+
matchedConditions
|
|
6429
|
+
);
|
|
6430
|
+
const approvalResponse = await waitForApproval(
|
|
6431
|
+
approvalRequest,
|
|
6432
|
+
opts.onCheckpoint,
|
|
6433
|
+
triggeredCp.timeout_seconds,
|
|
6434
|
+
triggeredCp.on_timeout,
|
|
6435
|
+
() => killed
|
|
6436
|
+
);
|
|
6437
|
+
sessionState = { ...sessionState, checkpointCount: sessionState.checkpointCount + 1 };
|
|
6438
|
+
if (approvalResponse.decision === "approve") {
|
|
6439
|
+
trace.push({
|
|
6440
|
+
stage: "checkpoint",
|
|
6441
|
+
tool: triggeredTc.name,
|
|
6442
|
+
verdict: "allow",
|
|
6443
|
+
reason: "checkpoint_approved",
|
|
6444
|
+
checked: { checkpoint: triggeredCp.name, decided_by: approvalResponse.decided_by },
|
|
6445
|
+
found: { decision: "approve", latency_ms: Date.now() - new Date(approvalRequest.requested_at).getTime() }
|
|
6446
|
+
});
|
|
6447
|
+
emitDiagnostic2(diagnostics, {
|
|
6448
|
+
type: "replay_checkpoint_resolved",
|
|
6449
|
+
session_id: sessionId,
|
|
6450
|
+
checkpoint: triggeredCp.name,
|
|
6451
|
+
decision: "approve"
|
|
6452
|
+
});
|
|
6453
|
+
} else {
|
|
6454
|
+
const isTimeout = approvalResponse.decided_by.startsWith("system:timeout");
|
|
6455
|
+
const blockReason = isTimeout ? "checkpoint_timeout" : "checkpoint_denied";
|
|
6456
|
+
trace.push({
|
|
6457
|
+
stage: "checkpoint",
|
|
6458
|
+
tool: triggeredTc.name,
|
|
6459
|
+
verdict: "block",
|
|
6460
|
+
reason: blockReason,
|
|
6461
|
+
checked: { checkpoint: triggeredCp.name, decided_by: approvalResponse.decided_by },
|
|
6462
|
+
found: {
|
|
6463
|
+
decision: "deny",
|
|
6464
|
+
...approvalResponse.reason ? { reason: approvalResponse.reason } : {},
|
|
6465
|
+
latency_ms: Date.now() - new Date(approvalRequest.requested_at).getTime()
|
|
6466
|
+
}
|
|
6467
|
+
});
|
|
6468
|
+
emitDiagnostic2(diagnostics, {
|
|
6469
|
+
type: "replay_checkpoint_resolved",
|
|
6470
|
+
session_id: sessionId,
|
|
6471
|
+
checkpoint: triggeredCp.name,
|
|
6472
|
+
decision: isTimeout ? "timeout" : "deny"
|
|
6473
|
+
});
|
|
6474
|
+
validation.failures.push({
|
|
6475
|
+
path: `$.tool_calls.${triggeredTc.name}`,
|
|
6476
|
+
operator: blockReason,
|
|
6477
|
+
expected: "approve",
|
|
6478
|
+
found: approvalResponse.decision,
|
|
6479
|
+
message: `Checkpoint '${triggeredCp.name}' ${isTimeout ? "timed out" : "denied"}${approvalResponse.reason ? `: ${approvalResponse.reason}` : ""}`,
|
|
6480
|
+
contract_file: ""
|
|
6481
|
+
});
|
|
6482
|
+
}
|
|
6483
|
+
}
|
|
6484
|
+
}
|
|
6485
|
+
} else {
|
|
6486
|
+
trace.push({ stage: "checkpoint", tool: null, verdict: "skip", reason: "no_checkpoint_triggered", checked: { checkpoint_count: compiledSession.checkpoints.length }, found: {} });
|
|
6487
|
+
}
|
|
6488
|
+
} else if (compiledSession?.checkpoints && compiledSession.checkpoints.length > 0 && validation.failures.length > 0) {
|
|
6489
|
+
trace.push({ stage: "checkpoint", tool: null, verdict: "skip", reason: "skipped_due_to_validation_failures", checked: {}, found: { failure_count: validation.failures.length } });
|
|
6490
|
+
}
|
|
5607
6491
|
currentTraceStage = "gate";
|
|
5608
6492
|
if (mode === "shadow") {
|
|
5609
6493
|
const shadowGateStart = Date.now();
|
|
@@ -5660,6 +6544,34 @@ function replay(client, opts = {}) {
|
|
|
5660
6544
|
} else {
|
|
5661
6545
|
completedStep.phase = sessionState.currentPhase;
|
|
5662
6546
|
}
|
|
6547
|
+
for (const tc of toolCalls) {
|
|
6548
|
+
const contract = contracts.find((c) => c.tool === tc.name);
|
|
6549
|
+
if (contract?.binds && contract.binds.length > 0) {
|
|
6550
|
+
const bindResult = captureBindings(tc, contract, sessionState);
|
|
6551
|
+
sessionState = { ...sessionState, bindings: bindResult.bindings };
|
|
6552
|
+
}
|
|
6553
|
+
if (compiledSession?.aggregates && compiledSession.aggregates.length > 0) {
|
|
6554
|
+
let parsedArgs;
|
|
6555
|
+
try {
|
|
6556
|
+
parsedArgs = JSON.parse(tc.arguments);
|
|
6557
|
+
} catch {
|
|
6558
|
+
parsedArgs = {};
|
|
6559
|
+
}
|
|
6560
|
+
sessionState = { ...sessionState, aggregates: commitAggregate(tc.name, parsedArgs, sessionState.aggregates, compiledSession.aggregates) };
|
|
6561
|
+
}
|
|
6562
|
+
if (compiledSession?.envelopes && compiledSession.envelopes.length > 0) {
|
|
6563
|
+
let parsedArgs;
|
|
6564
|
+
try {
|
|
6565
|
+
parsedArgs = JSON.parse(tc.arguments);
|
|
6566
|
+
} catch {
|
|
6567
|
+
parsedArgs = {};
|
|
6568
|
+
}
|
|
6569
|
+
const envResult = evaluateEnvelopes(tc.name, parsedArgs, sessionState.envelopes, compiledSession.envelopes);
|
|
6570
|
+
if (envResult.captures.length > 0) {
|
|
6571
|
+
sessionState = { ...sessionState, envelopes: commitEnvelope(envResult.captures, sessionState.envelopes) };
|
|
6572
|
+
}
|
|
6573
|
+
}
|
|
6574
|
+
}
|
|
5663
6575
|
const prevVersion = sessionState.stateVersion;
|
|
5664
6576
|
sessionState = finalizeExecutedStep(sessionState, completedStep, contracts, compiledSession);
|
|
5665
6577
|
syncStateToStore(prevVersion, sessionState);
|
|
@@ -5714,6 +6626,44 @@ function replay(client, opts = {}) {
|
|
|
5714
6626
|
} else {
|
|
5715
6627
|
completedStep.phase = sessionState.currentPhase;
|
|
5716
6628
|
}
|
|
6629
|
+
for (const tc of toolCalls) {
|
|
6630
|
+
const contract = contracts.find((c) => c.tool === tc.name);
|
|
6631
|
+
if (contract?.binds && contract.binds.length > 0) {
|
|
6632
|
+
const bindResult = captureBindings(tc, contract, sessionState);
|
|
6633
|
+
sessionState = { ...sessionState, bindings: bindResult.bindings };
|
|
6634
|
+
for (const cap of bindResult.captured) {
|
|
6635
|
+
trace.push({
|
|
6636
|
+
stage: "bind",
|
|
6637
|
+
tool: tc.name,
|
|
6638
|
+
verdict: "captured",
|
|
6639
|
+
reason: cap.overwritten ? "binding_overwritten" : "binding_captured",
|
|
6640
|
+
checked: { slot: cap.slot, source: cap.source, path: cap.path },
|
|
6641
|
+
found: { value: cap.value }
|
|
6642
|
+
});
|
|
6643
|
+
}
|
|
6644
|
+
}
|
|
6645
|
+
if (compiledSession?.aggregates && compiledSession.aggregates.length > 0) {
|
|
6646
|
+
let parsedArgs;
|
|
6647
|
+
try {
|
|
6648
|
+
parsedArgs = JSON.parse(tc.arguments);
|
|
6649
|
+
} catch {
|
|
6650
|
+
parsedArgs = {};
|
|
6651
|
+
}
|
|
6652
|
+
sessionState = { ...sessionState, aggregates: commitAggregate(tc.name, parsedArgs, sessionState.aggregates, compiledSession.aggregates) };
|
|
6653
|
+
}
|
|
6654
|
+
if (compiledSession?.envelopes && compiledSession.envelopes.length > 0) {
|
|
6655
|
+
let parsedArgs;
|
|
6656
|
+
try {
|
|
6657
|
+
parsedArgs = JSON.parse(tc.arguments);
|
|
6658
|
+
} catch {
|
|
6659
|
+
parsedArgs = {};
|
|
6660
|
+
}
|
|
6661
|
+
const envResult = evaluateEnvelopes(tc.name, parsedArgs, sessionState.envelopes, compiledSession.envelopes);
|
|
6662
|
+
if (envResult.captures.length > 0) {
|
|
6663
|
+
sessionState = { ...sessionState, envelopes: commitEnvelope(envResult.captures, sessionState.envelopes) };
|
|
6664
|
+
}
|
|
6665
|
+
}
|
|
6666
|
+
}
|
|
5717
6667
|
const prevVersionAllow = sessionState.stateVersion;
|
|
5718
6668
|
sessionState = finalizeExecutedStep(sessionState, completedStep, contracts, compiledSession);
|
|
5719
6669
|
sessionState = recordDecisionOutcome(sessionState, "allowed");
|
|
@@ -6005,6 +6955,22 @@ function replay(client, opts = {}) {
|
|
|
6005
6955
|
});
|
|
6006
6956
|
}
|
|
6007
6957
|
},
|
|
6958
|
+
addLabel(label) {
|
|
6959
|
+
if (killed || restored) return;
|
|
6960
|
+
if (sessionState.labels.has(label)) return;
|
|
6961
|
+
if (sessionState.labels.size >= 64) {
|
|
6962
|
+
emitDiagnostic2(diagnostics, {
|
|
6963
|
+
type: "replay_label_cap",
|
|
6964
|
+
session_id: sessionId,
|
|
6965
|
+
label,
|
|
6966
|
+
detail: "Label cap (64) reached, label not added"
|
|
6967
|
+
});
|
|
6968
|
+
return;
|
|
6969
|
+
}
|
|
6970
|
+
const newLabels = new Set(sessionState.labels);
|
|
6971
|
+
newLabels.add(label);
|
|
6972
|
+
sessionState = { ...sessionState, labels: newLabels };
|
|
6973
|
+
},
|
|
6008
6974
|
tools: wrapToolsWithDeferredReceipts(
|
|
6009
6975
|
buildWrappedToolsMap(opts.tools, compiledSession)
|
|
6010
6976
|
),
|
|
@@ -6193,7 +7159,7 @@ function discoverSessionYaml(opts) {
|
|
|
6193
7159
|
if (opts.sessionYamlPath) {
|
|
6194
7160
|
const resolved = (0, import_node_path3.resolve)(opts.sessionYamlPath);
|
|
6195
7161
|
const raw = (0, import_node_fs3.readFileSync)(resolved, "utf8");
|
|
6196
|
-
return (0,
|
|
7162
|
+
return (0, import_contracts_core7.parseSessionYaml)(raw);
|
|
6197
7163
|
}
|
|
6198
7164
|
if (opts.contractsDir) {
|
|
6199
7165
|
const dir = (0, import_node_path3.resolve)(opts.contractsDir);
|
|
@@ -6201,7 +7167,7 @@ function discoverSessionYaml(opts) {
|
|
|
6201
7167
|
const candidate = (0, import_node_path3.join)(dir, name);
|
|
6202
7168
|
if ((0, import_node_fs3.existsSync)(candidate)) {
|
|
6203
7169
|
const raw = (0, import_node_fs3.readFileSync)(candidate, "utf8");
|
|
6204
|
-
return (0,
|
|
7170
|
+
return (0, import_contracts_core7.parseSessionYaml)(raw);
|
|
6205
7171
|
}
|
|
6206
7172
|
}
|
|
6207
7173
|
}
|
|
@@ -6225,11 +7191,11 @@ function discoverWorkflowYaml(opts, workflowOpts) {
|
|
|
6225
7191
|
}
|
|
6226
7192
|
}
|
|
6227
7193
|
if (!raw) return null;
|
|
6228
|
-
const parsed = (0,
|
|
6229
|
-
return (0,
|
|
7194
|
+
const parsed = (0, import_contracts_core7.parseWorkflowYaml)(raw);
|
|
7195
|
+
return (0, import_contracts_core7.compileWorkflow)(parsed);
|
|
6230
7196
|
}
|
|
6231
7197
|
function generateWorkflowId() {
|
|
6232
|
-
return `rw_${
|
|
7198
|
+
return `rw_${import_node_crypto6.default.randomUUID().replace(/-/g, "").slice(0, 24)}`;
|
|
6233
7199
|
}
|
|
6234
7200
|
function validateConfig(contracts, opts) {
|
|
6235
7201
|
const hasPolicyBlock = contracts.some((c) => c.policy != null);
|
|
@@ -6321,7 +7287,7 @@ function validateResponse2(response, toolCalls, contracts, requestToolNames, unm
|
|
|
6321
7287
|
const outputInvariants = contract.assertions.output_invariants;
|
|
6322
7288
|
if (outputInvariants.length > 0) {
|
|
6323
7289
|
const normalizedResponse = buildNormalizedResponse(response, toolCalls);
|
|
6324
|
-
const result = (0,
|
|
7290
|
+
const result = (0, import_contracts_core7.evaluateInvariants)(normalizedResponse, outputInvariants, process.env);
|
|
6325
7291
|
for (const failure of result) {
|
|
6326
7292
|
failures.push({
|
|
6327
7293
|
path: failure.path,
|
|
@@ -6334,7 +7300,7 @@ function validateResponse2(response, toolCalls, contracts, requestToolNames, unm
|
|
|
6334
7300
|
}
|
|
6335
7301
|
}
|
|
6336
7302
|
if (contract.expected_tool_calls && contract.expected_tool_calls.length > 0) {
|
|
6337
|
-
const result = (0,
|
|
7303
|
+
const result = (0, import_contracts_core7.evaluateExpectedToolCalls)(
|
|
6338
7304
|
toolCalls,
|
|
6339
7305
|
contract.expected_tool_calls,
|
|
6340
7306
|
contract.pass_threshold ?? 1,
|
|
@@ -6487,7 +7453,7 @@ function evaluateInputInvariants(request, contracts) {
|
|
|
6487
7453
|
for (const contract of contracts) {
|
|
6488
7454
|
if (!requestToolSet.has(contract.tool)) continue;
|
|
6489
7455
|
if (contract.assertions.input_invariants.length === 0) continue;
|
|
6490
|
-
const result = (0,
|
|
7456
|
+
const result = (0, import_contracts_core7.evaluateInvariants)(request, contract.assertions.input_invariants, process.env);
|
|
6491
7457
|
for (const failure of result) {
|
|
6492
7458
|
failures.push({
|
|
6493
7459
|
path: failure.path,
|
|
@@ -6811,6 +7777,8 @@ function createInactiveSession(client, sessionId, reason) {
|
|
|
6811
7777
|
},
|
|
6812
7778
|
widen() {
|
|
6813
7779
|
},
|
|
7780
|
+
addLabel() {
|
|
7781
|
+
},
|
|
6814
7782
|
tools: {},
|
|
6815
7783
|
getWorkflowState: () => Promise.resolve(null),
|
|
6816
7784
|
handoff: () => Promise.resolve(null)
|
|
@@ -6853,6 +7821,8 @@ function createBlockingInactiveSession(client, sessionId, detail, configError) {
|
|
|
6853
7821
|
},
|
|
6854
7822
|
widen() {
|
|
6855
7823
|
},
|
|
7824
|
+
addLabel() {
|
|
7825
|
+
},
|
|
6856
7826
|
tools: {},
|
|
6857
7827
|
getWorkflowState: () => Promise.resolve(null),
|
|
6858
7828
|
handoff: () => Promise.resolve(null)
|
|
@@ -6928,7 +7898,7 @@ function resolveApiKey2(opts) {
|
|
|
6928
7898
|
return typeof envKey === "string" && envKey.length > 0 ? envKey : void 0;
|
|
6929
7899
|
}
|
|
6930
7900
|
function generateSessionId2() {
|
|
6931
|
-
return `rs_${
|
|
7901
|
+
return `rs_${import_node_crypto6.default.randomUUID().replace(/-/g, "").slice(0, 24)}`;
|
|
6932
7902
|
}
|
|
6933
7903
|
function stripHashPrefix(hash) {
|
|
6934
7904
|
return hash.startsWith("sha256:") ? hash.slice(7) : hash;
|
|
@@ -6954,6 +7924,9 @@ function defaultReplayDiagnosticsHandler(event) {
|
|
|
6954
7924
|
case "replay_compile_warning":
|
|
6955
7925
|
console.warn(`[replayci] compile warning: ${event.details}`);
|
|
6956
7926
|
break;
|
|
7927
|
+
case "replay_graph_analysis":
|
|
7928
|
+
console.warn(`[replayci] graph analysis: ${event.check} \u2014 ${event.detail}${event.suggestion ? ` (${event.suggestion})` : ""}`);
|
|
7929
|
+
break;
|
|
6957
7930
|
case "replay_bypass_detected":
|
|
6958
7931
|
console.warn(`[replayci] bypass detected on session ${event.session_id}`);
|
|
6959
7932
|
break;
|