@replayci/replay 0.1.12 → 0.1.14

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 CHANGED
@@ -2578,6 +2578,22 @@ function evaluatePrecondition(precondition, sessionState, currentArguments) {
2578
2578
  detail: `Prior tool output assertion failed: ${assertion.path} \u2014 expected ${JSON.stringify(assertion.equals)}, got ${JSON.stringify(value)}`
2579
2579
  };
2580
2580
  }
2581
+ if (assertion.gte !== void 0) {
2582
+ if (typeof value !== "number" || value < assertion.gte) {
2583
+ return {
2584
+ satisfied: false,
2585
+ detail: `Prior tool output assertion failed: ${assertion.path} \u2014 expected >= ${assertion.gte}, got ${JSON.stringify(value)}`
2586
+ };
2587
+ }
2588
+ }
2589
+ if (assertion.lte !== void 0) {
2590
+ if (typeof value !== "number" || value > assertion.lte) {
2591
+ return {
2592
+ satisfied: false,
2593
+ detail: `Prior tool output assertion failed: ${assertion.path} \u2014 expected <= ${assertion.lte}, got ${JSON.stringify(value)}`
2594
+ };
2595
+ }
2596
+ }
2581
2597
  }
2582
2598
  }
2583
2599
  }
@@ -3762,7 +3778,7 @@ function createInitialState(sessionId, options) {
3762
3778
  checkpointCount: 0
3763
3779
  };
3764
3780
  }
3765
- function finalizeExecutedStep(state, step, contracts, compiledSession) {
3781
+ function finalizeExecutedStep(state, step, contracts, compiledSession, options) {
3766
3782
  const newSteps = [...state.steps, step];
3767
3783
  const newToolCallCounts = updateToolCallCounts(state.toolCallCounts, step);
3768
3784
  const resolvedContracts = compiledSession ? Array.from(compiledSession.perToolContracts.values()) : contracts;
@@ -3772,7 +3788,7 @@ function finalizeExecutedStep(state, step, contracts, compiledSession) {
3772
3788
  step
3773
3789
  );
3774
3790
  const costDelta = computeStepCost(step);
3775
- const newPhase = compiledSession ? recomputePhaseFromCommitted(step.toolCalls, state, compiledSession) : state.currentPhase;
3791
+ const newPhase = options?.deferPhase ? state.currentPhase : compiledSession ? recomputePhaseFromCommitted(step.toolCalls, state, compiledSession) : state.currentPhase;
3776
3792
  return {
3777
3793
  ...state,
3778
3794
  steps: newSteps,
@@ -4081,26 +4097,27 @@ function matchesTool(toolName, filter) {
4081
4097
  if (typeof filter === "string") return filter === toolName;
4082
4098
  return filter.includes(toolName);
4083
4099
  }
4084
- function speculativeCheck(toolName, parsedArgs, aggregates, definitions) {
4100
+ function speculativeCheck(toolName, parsedArgs, aggregates, definitions, bindings) {
4085
4101
  const failures = [];
4086
4102
  for (const def of definitions) {
4087
4103
  if (!matchesTool(toolName, def.tool)) continue;
4088
4104
  const current = aggregates.get(def.name);
4089
4105
  if (!current) continue;
4106
+ const resolved = resolveBounds(def, bindings);
4090
4107
  const currentValue = current.currentValue;
4091
4108
  if (def.metric === "count") {
4092
4109
  const speculative2 = currentValue + 1;
4093
- if (def.lte !== void 0 && speculative2 > def.lte) {
4110
+ if (resolved.lte !== void 0 && speculative2 > resolved.lte) {
4094
4111
  failures.push({
4095
4112
  aggregate: def.name,
4096
4113
  reason: "aggregate_limit_exceeded",
4097
- detail: `${def.reason} (count ${speculative2} > lte ${def.lte})`,
4114
+ detail: `${def.reason} (count ${speculative2} > lte ${resolved.lte})`,
4098
4115
  current: currentValue,
4099
4116
  speculative: speculative2,
4100
- bound: { lte: def.lte }
4117
+ bound: { lte: resolved.lte }
4101
4118
  });
4102
4119
  }
4103
- if (def.gte !== void 0 && speculative2 < def.gte) {
4120
+ if (resolved.gte !== void 0 && speculative2 < resolved.gte) {
4104
4121
  }
4105
4122
  continue;
4106
4123
  }
@@ -4182,21 +4199,21 @@ function speculativeCheck(toolName, parsedArgs, aggregates, definitions) {
4182
4199
  default:
4183
4200
  continue;
4184
4201
  }
4185
- if (def.lte !== void 0 && speculative > def.lte) {
4202
+ if (resolved.lte !== void 0 && speculative > resolved.lte) {
4186
4203
  failures.push({
4187
4204
  aggregate: def.name,
4188
4205
  reason: "aggregate_limit_exceeded",
4189
- detail: `${def.reason} (${def.metric} ${speculative} > lte ${def.lte})`,
4206
+ detail: `${def.reason} (${def.metric} ${speculative} > lte ${resolved.lte})`,
4190
4207
  current: currentValue,
4191
4208
  speculative,
4192
- bound: { lte: def.lte }
4209
+ bound: { lte: resolved.lte }
4193
4210
  });
4194
4211
  }
4195
- if (def.gte !== void 0 && speculative < def.gte) {
4212
+ if (resolved.gte !== void 0 && speculative < resolved.gte) {
4196
4213
  failures.push({
4197
4214
  aggregate: def.name,
4198
4215
  reason: "aggregate_limit_exceeded",
4199
- detail: `${def.reason} (${def.metric} ${speculative} < gte ${def.gte})`,
4216
+ detail: `${def.reason} (${def.metric} ${speculative} < gte ${resolved.gte})`,
4200
4217
  current: currentValue,
4201
4218
  speculative,
4202
4219
  bound: { gte: def.gte }
@@ -4248,6 +4265,34 @@ function commitAggregate(toolName, parsedArgs, aggregates, definitions) {
4248
4265
  }
4249
4266
  return updated;
4250
4267
  }
4268
+ function resolveBounds(def, bindings) {
4269
+ if (!def.when || def.when.length === 0 || !bindings) {
4270
+ return { gte: def.gte, lte: def.lte };
4271
+ }
4272
+ for (const cond of def.when) {
4273
+ const bound = bindings.get(cond.binding);
4274
+ if (!bound) continue;
4275
+ const val = bound.value;
4276
+ if (!matchesCondition(val, cond)) continue;
4277
+ return {
4278
+ gte: cond.then_gte ?? def.gte,
4279
+ lte: cond.then_lte ?? def.lte
4280
+ };
4281
+ }
4282
+ return { gte: def.gte, lte: def.lte };
4283
+ }
4284
+ function matchesCondition(value, cond) {
4285
+ if (cond.equals !== void 0) {
4286
+ if (JSON.stringify(value) !== JSON.stringify(cond.equals)) return false;
4287
+ }
4288
+ if (cond.gte !== void 0) {
4289
+ if (typeof value !== "number" || value < cond.gte) return false;
4290
+ }
4291
+ if (cond.lte !== void 0) {
4292
+ if (typeof value !== "number" || value > cond.lte) return false;
4293
+ }
4294
+ return true;
4295
+ }
4251
4296
 
4252
4297
  // src/envelopes.ts
4253
4298
  function initializeEnvelopes(definitions) {
@@ -5614,6 +5659,7 @@ function replay(client, opts = {}) {
5614
5659
  let shadowEvaluationCount = 0;
5615
5660
  let manualFilter = null;
5616
5661
  const deferredReceipts = /* @__PURE__ */ new Map();
5662
+ let deferredPhase = null;
5617
5663
  const contractLimits = resolveSessionLimits(contracts);
5618
5664
  const compiledLimits = compiledSession?.sessionLimits;
5619
5665
  const mergedLimits = { ...contractLimits ?? {}, ...compiledLimits ?? {} };
@@ -5750,6 +5796,7 @@ function replay(client, opts = {}) {
5750
5796
  total_ms: 0,
5751
5797
  enforcement_ms: 0
5752
5798
  };
5799
+ deferredPhase = null;
5753
5800
  const trace = createTrace(sessionState.totalStepCount);
5754
5801
  const traceCtx = { trace };
5755
5802
  let currentTraceStage = "narrow";
@@ -6221,7 +6268,7 @@ function replay(client, opts = {}) {
6221
6268
  } catch {
6222
6269
  parsedArgs = {};
6223
6270
  }
6224
- const aggResult = speculativeCheck(tc.name, parsedArgs, workingAggregates, compiledSession.aggregates);
6271
+ const aggResult = speculativeCheck(tc.name, parsedArgs, workingAggregates, compiledSession.aggregates, sessionState.bindings);
6225
6272
  if (!aggResult.passed) {
6226
6273
  for (const f of aggResult.failures) {
6227
6274
  trace.push({
@@ -6664,11 +6711,31 @@ function replay(client, opts = {}) {
6664
6711
  }
6665
6712
  }
6666
6713
  }
6714
+ const hasPhaseTransition = phaseResult?.legal && phaseResult.newPhase !== sessionState.currentPhase;
6715
+ const shouldDeferPhase = isActiveGovern && !attemptDegraded && hasPhaseTransition;
6667
6716
  const prevVersionAllow = sessionState.stateVersion;
6668
- sessionState = finalizeExecutedStep(sessionState, completedStep, contracts, compiledSession);
6717
+ sessionState = finalizeExecutedStep(
6718
+ sessionState,
6719
+ completedStep,
6720
+ contracts,
6721
+ compiledSession,
6722
+ shouldDeferPhase ? { deferPhase: true } : void 0
6723
+ );
6669
6724
  sessionState = recordDecisionOutcome(sessionState, "allowed");
6670
6725
  syncStateToStore(prevVersionAllow, sessionState);
6671
6726
  timing.finalize_ms += Date.now() - enforceFinalizeStart;
6727
+ if (shouldDeferPhase && compiledSession) {
6728
+ const advancingTools = /* @__PURE__ */ new Set();
6729
+ for (const tc of toolCalls) {
6730
+ const contract = compiledSession.perToolContracts.get(tc.name);
6731
+ if (contract?.transitions?.advances_to === phaseResult.newPhase) {
6732
+ advancingTools.add(tc.name);
6733
+ }
6734
+ }
6735
+ if (advancingTools.size > 0 && phaseResult.newPhase != null) {
6736
+ deferredPhase = { newPhase: phaseResult.newPhase, toolNames: advancingTools };
6737
+ }
6738
+ }
6672
6739
  if (isActiveGovern && !attemptDegraded && attemptPendingCalls && attemptPendingCalls.size > 0) {
6673
6740
  for (const [toolCallId, pending] of attemptPendingCalls) {
6674
6741
  deferredReceipts.set(toolCallId, {
@@ -6686,8 +6753,8 @@ function replay(client, opts = {}) {
6686
6753
  checked: { gate_mode: gateMode },
6687
6754
  found: { blocked_count: 0, action: "allow" }
6688
6755
  });
6689
- const allowNewPhase = phaseResult && phaseResult.legal && phaseResult.newPhase !== sessionState.currentPhase ? phaseResult.newPhase : sessionState.currentPhase;
6690
- trace.push({ stage: "finalize", tool: null, verdict: "info", reason: "cycle_complete", checked: {}, found: { state_version: sessionState.stateVersion, phase_before: completedStep.phase, phase_after: allowNewPhase, tools_committed: toolCalls.map((tc) => tc.name), tools_blocked: [], killed: false, step_index: sessionState.totalStepCount } });
6756
+ const allowNewPhase = phaseResult && phaseResult.legal && phaseResult.newPhase !== (completedStep.phase ?? sessionState.currentPhase) ? phaseResult.newPhase : sessionState.currentPhase;
6757
+ trace.push({ stage: "finalize", tool: null, verdict: "info", reason: "cycle_complete", checked: {}, found: { state_version: sessionState.stateVersion, phase_before: completedStep.phase, phase_after: allowNewPhase, ...shouldDeferPhase ? { phase_deferred: true } : {}, tools_committed: toolCalls.map((tc) => tc.name), tools_blocked: [], killed: false, step_index: sessionState.totalStepCount } });
6691
6758
  trace.complete = true;
6692
6759
  lastTrace = trace;
6693
6760
  emitDiagnostic2(diagnostics, { type: "replay_trace", session_id: sessionId, trace });
@@ -7034,6 +7101,10 @@ function replay(client, opts = {}) {
7034
7101
  throw new ReplayKillError(sessionId, killedAt);
7035
7102
  }
7036
7103
  const result = await executor(args);
7104
+ if (deferredPhase && deferredPhase.toolNames.has(toolName)) {
7105
+ sessionState = { ...sessionState, currentPhase: deferredPhase.newPhase };
7106
+ deferredPhase = null;
7107
+ }
7037
7108
  if (runtimeClient && leaseFence && !runtimeDegraded) {
7038
7109
  for (const [callId, deferred] of deferredReceipts) {
7039
7110
  if (deferred.toolName === toolName) {
package/dist/index.js CHANGED
@@ -2558,6 +2558,22 @@ function evaluatePrecondition(precondition, sessionState, currentArguments) {
2558
2558
  detail: `Prior tool output assertion failed: ${assertion.path} \u2014 expected ${JSON.stringify(assertion.equals)}, got ${JSON.stringify(value)}`
2559
2559
  };
2560
2560
  }
2561
+ if (assertion.gte !== void 0) {
2562
+ if (typeof value !== "number" || value < assertion.gte) {
2563
+ return {
2564
+ satisfied: false,
2565
+ detail: `Prior tool output assertion failed: ${assertion.path} \u2014 expected >= ${assertion.gte}, got ${JSON.stringify(value)}`
2566
+ };
2567
+ }
2568
+ }
2569
+ if (assertion.lte !== void 0) {
2570
+ if (typeof value !== "number" || value > assertion.lte) {
2571
+ return {
2572
+ satisfied: false,
2573
+ detail: `Prior tool output assertion failed: ${assertion.path} \u2014 expected <= ${assertion.lte}, got ${JSON.stringify(value)}`
2574
+ };
2575
+ }
2576
+ }
2561
2577
  }
2562
2578
  }
2563
2579
  }
@@ -3751,7 +3767,7 @@ function createInitialState(sessionId, options) {
3751
3767
  checkpointCount: 0
3752
3768
  };
3753
3769
  }
3754
- function finalizeExecutedStep(state, step, contracts, compiledSession) {
3770
+ function finalizeExecutedStep(state, step, contracts, compiledSession, options) {
3755
3771
  const newSteps = [...state.steps, step];
3756
3772
  const newToolCallCounts = updateToolCallCounts(state.toolCallCounts, step);
3757
3773
  const resolvedContracts = compiledSession ? Array.from(compiledSession.perToolContracts.values()) : contracts;
@@ -3761,7 +3777,7 @@ function finalizeExecutedStep(state, step, contracts, compiledSession) {
3761
3777
  step
3762
3778
  );
3763
3779
  const costDelta = computeStepCost(step);
3764
- const newPhase = compiledSession ? recomputePhaseFromCommitted(step.toolCalls, state, compiledSession) : state.currentPhase;
3780
+ const newPhase = options?.deferPhase ? state.currentPhase : compiledSession ? recomputePhaseFromCommitted(step.toolCalls, state, compiledSession) : state.currentPhase;
3765
3781
  return {
3766
3782
  ...state,
3767
3783
  steps: newSteps,
@@ -4070,26 +4086,27 @@ function matchesTool(toolName, filter) {
4070
4086
  if (typeof filter === "string") return filter === toolName;
4071
4087
  return filter.includes(toolName);
4072
4088
  }
4073
- function speculativeCheck(toolName, parsedArgs, aggregates, definitions) {
4089
+ function speculativeCheck(toolName, parsedArgs, aggregates, definitions, bindings) {
4074
4090
  const failures = [];
4075
4091
  for (const def of definitions) {
4076
4092
  if (!matchesTool(toolName, def.tool)) continue;
4077
4093
  const current = aggregates.get(def.name);
4078
4094
  if (!current) continue;
4095
+ const resolved = resolveBounds(def, bindings);
4079
4096
  const currentValue = current.currentValue;
4080
4097
  if (def.metric === "count") {
4081
4098
  const speculative2 = currentValue + 1;
4082
- if (def.lte !== void 0 && speculative2 > def.lte) {
4099
+ if (resolved.lte !== void 0 && speculative2 > resolved.lte) {
4083
4100
  failures.push({
4084
4101
  aggregate: def.name,
4085
4102
  reason: "aggregate_limit_exceeded",
4086
- detail: `${def.reason} (count ${speculative2} > lte ${def.lte})`,
4103
+ detail: `${def.reason} (count ${speculative2} > lte ${resolved.lte})`,
4087
4104
  current: currentValue,
4088
4105
  speculative: speculative2,
4089
- bound: { lte: def.lte }
4106
+ bound: { lte: resolved.lte }
4090
4107
  });
4091
4108
  }
4092
- if (def.gte !== void 0 && speculative2 < def.gte) {
4109
+ if (resolved.gte !== void 0 && speculative2 < resolved.gte) {
4093
4110
  }
4094
4111
  continue;
4095
4112
  }
@@ -4171,21 +4188,21 @@ function speculativeCheck(toolName, parsedArgs, aggregates, definitions) {
4171
4188
  default:
4172
4189
  continue;
4173
4190
  }
4174
- if (def.lte !== void 0 && speculative > def.lte) {
4191
+ if (resolved.lte !== void 0 && speculative > resolved.lte) {
4175
4192
  failures.push({
4176
4193
  aggregate: def.name,
4177
4194
  reason: "aggregate_limit_exceeded",
4178
- detail: `${def.reason} (${def.metric} ${speculative} > lte ${def.lte})`,
4195
+ detail: `${def.reason} (${def.metric} ${speculative} > lte ${resolved.lte})`,
4179
4196
  current: currentValue,
4180
4197
  speculative,
4181
- bound: { lte: def.lte }
4198
+ bound: { lte: resolved.lte }
4182
4199
  });
4183
4200
  }
4184
- if (def.gte !== void 0 && speculative < def.gte) {
4201
+ if (resolved.gte !== void 0 && speculative < resolved.gte) {
4185
4202
  failures.push({
4186
4203
  aggregate: def.name,
4187
4204
  reason: "aggregate_limit_exceeded",
4188
- detail: `${def.reason} (${def.metric} ${speculative} < gte ${def.gte})`,
4205
+ detail: `${def.reason} (${def.metric} ${speculative} < gte ${resolved.gte})`,
4189
4206
  current: currentValue,
4190
4207
  speculative,
4191
4208
  bound: { gte: def.gte }
@@ -4237,6 +4254,34 @@ function commitAggregate(toolName, parsedArgs, aggregates, definitions) {
4237
4254
  }
4238
4255
  return updated;
4239
4256
  }
4257
+ function resolveBounds(def, bindings) {
4258
+ if (!def.when || def.when.length === 0 || !bindings) {
4259
+ return { gte: def.gte, lte: def.lte };
4260
+ }
4261
+ for (const cond of def.when) {
4262
+ const bound = bindings.get(cond.binding);
4263
+ if (!bound) continue;
4264
+ const val = bound.value;
4265
+ if (!matchesCondition(val, cond)) continue;
4266
+ return {
4267
+ gte: cond.then_gte ?? def.gte,
4268
+ lte: cond.then_lte ?? def.lte
4269
+ };
4270
+ }
4271
+ return { gte: def.gte, lte: def.lte };
4272
+ }
4273
+ function matchesCondition(value, cond) {
4274
+ if (cond.equals !== void 0) {
4275
+ if (JSON.stringify(value) !== JSON.stringify(cond.equals)) return false;
4276
+ }
4277
+ if (cond.gte !== void 0) {
4278
+ if (typeof value !== "number" || value < cond.gte) return false;
4279
+ }
4280
+ if (cond.lte !== void 0) {
4281
+ if (typeof value !== "number" || value > cond.lte) return false;
4282
+ }
4283
+ return true;
4284
+ }
4240
4285
 
4241
4286
  // src/envelopes.ts
4242
4287
  function initializeEnvelopes(definitions) {
@@ -5605,6 +5650,7 @@ function replay(client, opts = {}) {
5605
5650
  let shadowEvaluationCount = 0;
5606
5651
  let manualFilter = null;
5607
5652
  const deferredReceipts = /* @__PURE__ */ new Map();
5653
+ let deferredPhase = null;
5608
5654
  const contractLimits = resolveSessionLimits(contracts);
5609
5655
  const compiledLimits = compiledSession?.sessionLimits;
5610
5656
  const mergedLimits = { ...contractLimits ?? {}, ...compiledLimits ?? {} };
@@ -5741,6 +5787,7 @@ function replay(client, opts = {}) {
5741
5787
  total_ms: 0,
5742
5788
  enforcement_ms: 0
5743
5789
  };
5790
+ deferredPhase = null;
5744
5791
  const trace = createTrace(sessionState.totalStepCount);
5745
5792
  const traceCtx = { trace };
5746
5793
  let currentTraceStage = "narrow";
@@ -6212,7 +6259,7 @@ function replay(client, opts = {}) {
6212
6259
  } catch {
6213
6260
  parsedArgs = {};
6214
6261
  }
6215
- const aggResult = speculativeCheck(tc.name, parsedArgs, workingAggregates, compiledSession.aggregates);
6262
+ const aggResult = speculativeCheck(tc.name, parsedArgs, workingAggregates, compiledSession.aggregates, sessionState.bindings);
6216
6263
  if (!aggResult.passed) {
6217
6264
  for (const f of aggResult.failures) {
6218
6265
  trace.push({
@@ -6655,11 +6702,31 @@ function replay(client, opts = {}) {
6655
6702
  }
6656
6703
  }
6657
6704
  }
6705
+ const hasPhaseTransition = phaseResult?.legal && phaseResult.newPhase !== sessionState.currentPhase;
6706
+ const shouldDeferPhase = isActiveGovern && !attemptDegraded && hasPhaseTransition;
6658
6707
  const prevVersionAllow = sessionState.stateVersion;
6659
- sessionState = finalizeExecutedStep(sessionState, completedStep, contracts, compiledSession);
6708
+ sessionState = finalizeExecutedStep(
6709
+ sessionState,
6710
+ completedStep,
6711
+ contracts,
6712
+ compiledSession,
6713
+ shouldDeferPhase ? { deferPhase: true } : void 0
6714
+ );
6660
6715
  sessionState = recordDecisionOutcome(sessionState, "allowed");
6661
6716
  syncStateToStore(prevVersionAllow, sessionState);
6662
6717
  timing.finalize_ms += Date.now() - enforceFinalizeStart;
6718
+ if (shouldDeferPhase && compiledSession) {
6719
+ const advancingTools = /* @__PURE__ */ new Set();
6720
+ for (const tc of toolCalls) {
6721
+ const contract = compiledSession.perToolContracts.get(tc.name);
6722
+ if (contract?.transitions?.advances_to === phaseResult.newPhase) {
6723
+ advancingTools.add(tc.name);
6724
+ }
6725
+ }
6726
+ if (advancingTools.size > 0 && phaseResult.newPhase != null) {
6727
+ deferredPhase = { newPhase: phaseResult.newPhase, toolNames: advancingTools };
6728
+ }
6729
+ }
6663
6730
  if (isActiveGovern && !attemptDegraded && attemptPendingCalls && attemptPendingCalls.size > 0) {
6664
6731
  for (const [toolCallId, pending] of attemptPendingCalls) {
6665
6732
  deferredReceipts.set(toolCallId, {
@@ -6677,8 +6744,8 @@ function replay(client, opts = {}) {
6677
6744
  checked: { gate_mode: gateMode },
6678
6745
  found: { blocked_count: 0, action: "allow" }
6679
6746
  });
6680
- const allowNewPhase = phaseResult && phaseResult.legal && phaseResult.newPhase !== sessionState.currentPhase ? phaseResult.newPhase : sessionState.currentPhase;
6681
- trace.push({ stage: "finalize", tool: null, verdict: "info", reason: "cycle_complete", checked: {}, found: { state_version: sessionState.stateVersion, phase_before: completedStep.phase, phase_after: allowNewPhase, tools_committed: toolCalls.map((tc) => tc.name), tools_blocked: [], killed: false, step_index: sessionState.totalStepCount } });
6747
+ const allowNewPhase = phaseResult && phaseResult.legal && phaseResult.newPhase !== (completedStep.phase ?? sessionState.currentPhase) ? phaseResult.newPhase : sessionState.currentPhase;
6748
+ trace.push({ stage: "finalize", tool: null, verdict: "info", reason: "cycle_complete", checked: {}, found: { state_version: sessionState.stateVersion, phase_before: completedStep.phase, phase_after: allowNewPhase, ...shouldDeferPhase ? { phase_deferred: true } : {}, tools_committed: toolCalls.map((tc) => tc.name), tools_blocked: [], killed: false, step_index: sessionState.totalStepCount } });
6682
6749
  trace.complete = true;
6683
6750
  lastTrace = trace;
6684
6751
  emitDiagnostic2(diagnostics, { type: "replay_trace", session_id: sessionId, trace });
@@ -7025,6 +7092,10 @@ function replay(client, opts = {}) {
7025
7092
  throw new ReplayKillError(sessionId, killedAt);
7026
7093
  }
7027
7094
  const result = await executor(args);
7095
+ if (deferredPhase && deferredPhase.toolNames.has(toolName)) {
7096
+ sessionState = { ...sessionState, currentPhase: deferredPhase.newPhase };
7097
+ deferredPhase = null;
7098
+ }
7028
7099
  if (runtimeClient && leaseFence && !runtimeDegraded) {
7029
7100
  for (const [callId, deferred] of deferredReceipts) {
7030
7101
  if (deferred.toolName === toolName) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@replayci/replay",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "ReplayCI SDK for deterministic tool-call validation and observation.",
5
5
  "license": "ISC",
6
6
  "author": "ReplayCI",