@miller-tech/uap 1.118.1 → 1.119.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.
@@ -1 +1 @@
1
- {"version":3,"file":"handsfree.d.ts","sourceRoot":"","sources":["../../src/cli/handsfree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AA4DH;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,OAAO,GACtB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CA2CvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA8Jf"}
1
+ {"version":3,"file":"handsfree.d.ts","sourceRoot":"","sources":["../../src/cli/handsfree.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAiEH;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,GAAG,EAAE,MAAM,EACX,cAAc,EAAE,OAAO,GACtB;IAAE,KAAK,EAAE,OAAO,CAAC;IAAC,OAAO,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAgFvD;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,GAAG,EAAE,MAAM,GAAG,SAAS,EACvB,OAAO,GAAE,gBAAqB,GAC7B,OAAO,CAAC,IAAI,CAAC,CA8Jf"}
@@ -22,6 +22,8 @@ import { findUapConfigPath, modifyUapConfig } from '../utils/config-loader.js';
22
22
  import { loadLedger, initLedger, markItem, remainingItems, isComplete, formatRemaining, progress, syncLedgerFromTodos, } from '../delivery/completion-ledger.js';
23
23
  import { loadPersistenceConfig, resolveActiveModel } from '../delivery/handsfree-config.js';
24
24
  import { resolvePersistenceProfile } from '../delivery/persistence-profile.js';
25
+ /** Fix C: hard cap on pre-ledger nudges so a planning stall can never wedge. */
26
+ const PRE_LEDGER_MAX = Math.max(0, Number(process.env.UAP_HANDSFREE_PRELEDGER_MAX ?? 1));
25
27
  function statePath(cwd) {
26
28
  return join(cwd, '.uap', 'handsfree-state.json');
27
29
  }
@@ -44,7 +46,7 @@ function saveState(cwd, st) {
44
46
  }
45
47
  }
46
48
  function resetState(cwd) {
47
- saveState(cwd, { blocks: 0, lastRemaining: Number.MAX_SAFE_INTEGER });
49
+ saveState(cwd, { blocks: 0, lastRemaining: Number.MAX_SAFE_INTEGER, preLedgerBlocks: 0 });
48
50
  }
49
51
  async function readStdin() {
50
52
  if (process.stdin.isTTY)
@@ -70,8 +72,42 @@ export function decideStopCheck(cwd, stopHookActive) {
70
72
  if (stopHookActive)
71
73
  return { block: false, message: 'stop_hook_active — never re-block' };
72
74
  const ledger = loadLedger(cwd);
73
- if (!ledger)
74
- return { block: false, message: 'no active build ledger' };
75
+ if (!ledger) {
76
+ // Fix C: pre-ledger safety net. A LOCAL model (qwen) can defer at the
77
+ // PLANNING stage — "I need more exploration cycles to complete the plan" —
78
+ // and end the turn before ever writing a TodoWrite plan, so no ledger exists
79
+ // and the build silently stops. Nudge it (bounded to PRE_LEDGER_MAX,
80
+ // anti-wedge) to lay out the plan and start. Scoped to the LOCAL family only:
81
+ // it is the one that emits these deferrals and drives hands-free builds here.
82
+ // Frontier models keep their post-ledger blocker + intrinsic persistence, and
83
+ // Fable is trusted — so a casual (non-build) frontier/Fable session is never
84
+ // pre-ledger blocked. Disable via config / UAP_HANDSFREE_PRELEDGER=0.
85
+ const preProfile = resolvePersistenceProfile(resolveActiveModel(cwd), cfg);
86
+ const nudgeOn = cfg.preLedgerNudge !== false && PRE_LEDGER_MAX > 0;
87
+ const driven = preProfile.stopHookBlocks && preProfile.injectAutonomy && preProfile.family === 'local';
88
+ if (!nudgeOn || !driven) {
89
+ return { block: false, message: 'no active build ledger' };
90
+ }
91
+ const st0 = loadState(cwd);
92
+ const pre = st0.preLedgerBlocks ?? 0;
93
+ if (pre >= PRE_LEDGER_MAX) {
94
+ resetState(cwd);
95
+ return {
96
+ block: false,
97
+ giveUp: true,
98
+ message: `hands-free pre-ledger nudge exhausted after ${pre} attempt(s) — no ` +
99
+ 'build plan was started; allowing stop. Consider escalating the model.',
100
+ };
101
+ }
102
+ saveState(cwd, { ...st0, preLedgerBlocks: pre + 1 });
103
+ return {
104
+ block: true,
105
+ message: 'NOT DONE — hands-free is on but no build plan exists yet. Do NOT stop to ' +
106
+ 'ask for more cycles or permission. Lay out the full build as a TodoWrite ' +
107
+ 'plan now (it auto-creates the completion ledger) and start building the ' +
108
+ 'first item this turn.',
109
+ };
110
+ }
75
111
  if (isComplete(ledger)) {
76
112
  resetState(cwd);
77
113
  return { block: false, message: 'build complete — all ledger items done' };
@@ -1 +1 @@
1
- {"version":3,"file":"handsfree.js","sourceRoot":"","sources":["../../src/cli/handsfree.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"handsfree.js","sourceRoot":"","sources":["../../src/cli/handsfree.ts"],"names":[],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"handsfree-config.d.ts","sourceRoot":"","sources":["../../src/delivery/handsfree-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAsB,GAAG,iBAAiB,CAepF;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAWtE"}
1
+ {"version":3,"file":"handsfree-config.d.ts","sourceRoot":"","sources":["../../src/delivery/handsfree-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAElE,mFAAmF;AACnF,wBAAgB,qBAAqB,CAAC,GAAG,GAAE,MAAsB,GAAG,iBAAiB,CAiBpF;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAsB,GAAG,MAAM,CAWtE"}
@@ -22,6 +22,8 @@ export function loadPersistenceConfig(cwd = process.cwd()) {
22
22
  enabled: hf.enabled !== false && process.env.UAP_HANDSFREE !== '0',
23
23
  ...(envIntensity ? { intensity: envIntensity } : hf.intensity ? { intensity: hf.intensity } : {}),
24
24
  ...(hf.overrides ? { overrides: hf.overrides } : {}),
25
+ // Fix C: pre-ledger nudge, default ON; disable via config or UAP_HANDSFREE_PRELEDGER=0.
26
+ preLedgerNudge: hf.preLedgerNudge !== false && process.env.UAP_HANDSFREE_PRELEDGER !== '0',
25
27
  };
26
28
  }
27
29
  /** The model doing the work — env override, else the executor role, else unknown. */
@@ -1 +1 @@
1
- {"version":3,"file":"handsfree-config.js","sourceRoot":"","sources":["../../src/delivery/handsfree-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAG7D,mFAAmF;AACnF,MAAM,UAAU,qBAAqB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC/D,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,GAAG;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACjE,IAAI,GAAG,GAA4B,EAAE,CAAC;IACtC,IAAI,CAAC;QACH,GAAG,GAAI,gBAAgB,CAAC,GAAG,CAA6B,IAAI,EAAE,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,GAAG,EAAE,CAAC;IACX,CAAC;IACD,MAAM,EAAE,GAAI,GAAG,CAAC,SAA2C,IAAI,EAAE,CAAC;IAClE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAqE,CAAC;IACvG,OAAO;QACL,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,GAAG;QAClE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjG,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrD,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,kBAAkB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC5D,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACtE,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACpE,IAAI,CAAC;QACH,MAAM,GAAG,GAAI,gBAAgB,CAAC,GAAG,CAA6B,IAAI,EAAE,CAAC;QACrE,MAAM,EAAE,GAAG,GAAG,CAAC,UAA2D,CAAC;QAC3E,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ;YAAE,OAAO,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
1
+ {"version":3,"file":"handsfree-config.js","sourceRoot":"","sources":["../../src/delivery/handsfree-config.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAG7D,mFAAmF;AACnF,MAAM,UAAU,qBAAqB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC/D,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,GAAG;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACjE,IAAI,GAAG,GAA4B,EAAE,CAAC;IACtC,IAAI,CAAC;QACH,GAAG,GAAI,gBAAgB,CAAC,GAAG,CAA6B,IAAI,EAAE,CAAC;IACjE,CAAC;IAAC,MAAM,CAAC;QACP,GAAG,GAAG,EAAE,CAAC;IACX,CAAC;IACD,MAAM,EAAE,GAAI,GAAG,CAAC,SAA2C,IAAI,EAAE,CAAC;IAClE,MAAM,YAAY,GAAG,OAAO,CAAC,GAAG,CAAC,uBAAqE,CAAC;IACvG,OAAO;QACL,OAAO,EAAE,EAAE,CAAC,OAAO,KAAK,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,aAAa,KAAK,GAAG;QAClE,GAAG,CAAC,YAAY,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,YAAY,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACjG,GAAG,CAAC,EAAE,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,CAAC,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;QACpD,wFAAwF;QACxF,cAAc,EAAE,EAAE,CAAC,cAAc,KAAK,KAAK,IAAI,OAAO,CAAC,GAAG,CAAC,uBAAuB,KAAK,GAAG;KAC3F,CAAC;AACJ,CAAC;AAED,qFAAqF;AACrF,MAAM,UAAU,kBAAkB,CAAC,MAAc,OAAO,CAAC,GAAG,EAAE;IAC5D,IAAI,OAAO,CAAC,GAAG,CAAC,gBAAgB;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC;IACtE,IAAI,OAAO,CAAC,GAAG,CAAC,eAAe;QAAE,OAAO,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACpE,IAAI,CAAC;QACH,MAAM,GAAG,GAAI,gBAAgB,CAAC,GAAG,CAA6B,IAAI,EAAE,CAAC;QACrE,MAAM,EAAE,GAAG,GAAG,CAAC,UAA2D,CAAC;QAC3E,IAAI,EAAE,EAAE,KAAK,EAAE,QAAQ;YAAE,OAAO,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC;IACpD,CAAC;IAAC,MAAM,CAAC;QACP,kBAAkB;IACpB,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC"}
@@ -37,6 +37,15 @@ export interface PersistenceConfig {
37
37
  intensity?: PersistenceIntensity;
38
38
  /** per-model overrides keyed by model id substring. */
39
39
  overrides?: Record<string, Partial<PersistenceProfile>>;
40
+ /**
41
+ * Fix C: pre-ledger safety net. When hands-free is ON but NO completion
42
+ * ledger exists yet, a driven model (local/frontier) can defer at the
43
+ * planning stage — "I need more exploration cycles to complete the plan" —
44
+ * and end the turn before ever writing a plan, so nothing blocks the stop.
45
+ * When enabled, the Stop hook nudges once (bounded, anti-wedge) to lay out
46
+ * the plan and start building. Default ON; only ever nudges driven models.
47
+ */
48
+ preLedgerNudge?: boolean;
40
49
  }
41
50
  /**
42
51
  * Resolve the effective persistence profile for a model. `off` when hands-free
@@ -1 +1 @@
1
- {"version":3,"file":"persistence-profile.d.ts","sourceRoot":"","sources":["../../src/delivery/persistence-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,OAAO,GAAG,UAAU,GAAG,YAAY,CAAC;AAC/E,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAErE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,oBAAoB,CAAC;IAChC,iFAAiF;IACjF,cAAc,EAAE,OAAO,CAAC;IACxB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,eAAe,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,cAAc,EAAE,OAAO,CAAC;CACzB;AAmBD,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAMhE;AAeD,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC;CACzD;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,GAAG,GAAE,iBAAsB,GAC1B,kBAAkB,CAqBpB"}
1
+ {"version":3,"file":"persistence-profile.d.ts","sourceRoot":"","sources":["../../src/delivery/persistence-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,MAAM,MAAM,oBAAoB,GAAG,KAAK,GAAG,OAAO,GAAG,UAAU,GAAG,YAAY,CAAC;AAC/E,MAAM,MAAM,WAAW,GAAG,OAAO,GAAG,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;AAErE,MAAM,WAAW,kBAAkB;IACjC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;IACpB,SAAS,EAAE,oBAAoB,CAAC;IAChC,iFAAiF;IACjF,cAAc,EAAE,OAAO,CAAC;IACxB,8EAA8E;IAC9E,SAAS,EAAE,MAAM,CAAC;IAClB,wEAAwE;IACxE,eAAe,EAAE,MAAM,CAAC;IACxB,gFAAgF;IAChF,cAAc,EAAE,OAAO,CAAC;CACzB;AAmBD,wEAAwE;AACxE,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,WAAW,CAMhE;AAeD,MAAM,WAAW,iBAAiB;IAChC,2DAA2D;IAC3D,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,sDAAsD;IACtD,SAAS,CAAC,EAAE,oBAAoB,CAAC;IACjC,uDAAuD;IACvD,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,kBAAkB,CAAC,CAAC,CAAC;IACxD;;;;;;;OAOG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;;;GAIG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,EAAE,MAAM,GAAG,SAAS,EAC3B,GAAG,GAAE,iBAAsB,GAC1B,kBAAkB,CAqBpB"}
@@ -1 +1 @@
1
- {"version":3,"file":"persistence-profile.js","sourceRoot":"","sources":["../../src/delivery/persistence-profile.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAmBH,MAAM,QAAQ,GAA+F;IAC3G,qEAAqE;IACrE,KAAK,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,cAAc,EAAE,KAAK,EAAE;IAC5G,kEAAkE;IAClE,QAAQ,EAAE,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE;IACjH,gFAAgF;IAChF,UAAU,EAAE,EAAE,SAAS,EAAE,YAAY,EAAE,cAAc,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,EAAE,eAAe,EAAE,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE;CACtH,CAAC;AAEF,MAAM,WAAW,GAAiD;IAChE,SAAS,EAAE,KAAK;IAChB,cAAc,EAAE,KAAK;IACrB,SAAS,EAAE,CAAC;IACZ,eAAe,EAAE,CAAC;IAClB,cAAc,EAAE,KAAK;CACtB,CAAC;AAEF,wEAAwE;AACxE,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,MAAM,CAAC,GAAG,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC,WAAW,EAAE,CAAC;IACxC,IAAI,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC;QAAE,OAAO,OAAO,CAAC;IACxC,IAAI,wEAAwE,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC;IACrG,IAAI,gDAAgD,CAAC,IAAI,CAAC,CAAC,CAAC;QAAE,OAAO,UAAU,CAAC;IAChF,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,SAAS,eAAe,CAAC,MAAmB;IAC1C,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,OAAO;YACV,OAAO,OAAO,CAAC;QACjB,KAAK,OAAO;YACV,OAAO,YAAY,CAAC;QACtB,KAAK,UAAU;YACb,OAAO,UAAU,CAAC;QACpB;YACE,OAAO,UAAU,CAAC;IACtB,CAAC;AACH,CAAC;AAWD;;;;GAIG;AACH,MAAM,UAAU,yBAAyB,CACvC,OAA2B,EAC3B,MAAyB,EAAE;IAE3B,MAAM,KAAK,GAAG,OAAO,IAAI,SAAS,CAAC;IACnC,MAAM,MAAM,GAAG,mBAAmB,CAAC,KAAK,CAAC,CAAC;IAE1C,IAAI,GAAG,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAC1B,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;IAC3C,CAAC;IAED,MAAM,SAAS,GAAyB,GAAG,CAAC,SAAS,IAAI,eAAe,CAAC,MAAM,CAAC,CAAC;IACjF,MAAM,IAAI,GAAG,SAAS,KAAK,KAAK,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAC;IACrE,IAAI,OAAO,GAAuB,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,IAAI,EAAE,SAAS,EAAE,CAAC;IAExE,yEAAyE;IACzE,IAAI,GAAG,CAAC,SAAS,EAAE,CAAC;QAClB,KAAK,MAAM,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,SAAS,CAAC,EAAE,CAAC;YACtD,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,EAAE,CAAC;gBACpD,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,GAAG,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,CAAC;YACjD,CAAC;QACH,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC"}
1
+ {"version":3,"file":"persistence-profile.js","sourceRoot":"","sources":["../../src/delivery/persistence-profile.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miller-tech/uap",
3
- "version": "1.118.1",
3
+ "version": "1.119.0",
4
4
  "description": "Autonomous AI agent memory system with CLAUDE.md protocol enforcement",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -231,6 +231,48 @@ _RATE_LIMITED_API_RE = re.compile(r"api\.github\.com", re.IGNORECASE)
231
231
  PROXY_STUCK_TEXT_THRESHOLD = int(os.environ.get("PROXY_STUCK_TEXT_THRESHOLD", "2"))
232
232
  PROXY_STUCK_API_THRESHOLD = int(os.environ.get("PROXY_STUCK_API_THRESHOLD", "3"))
233
233
 
234
+ # ---------------------------------------------------------------------------
235
+ # DEFERRAL-BREAK guardrail (Fix A): a model can end a turn with plain prose that
236
+ # DEFERS the work instead of doing it -- "I need more exploration cycles to
237
+ # complete the plan", "let me continue exploring", "I'll need a few more passes"
238
+ # -- with NO tool call. This is a turn-ending *capitulation*, not a loop
239
+ # admission, so STUCK-BREAK's phrase list never matches it; and because it is a
240
+ # no-tool turn, recon-convergence never advanced its streak either (that counter
241
+ # only moves on turns that emit tool calls). The stall therefore slips through
242
+ # every guardrail and silently halts a hands-free build (observed live: a run of
243
+ # back-to-back session-ends with "Code changed: false"). When a no-tool turn
244
+ # matches the deferral phrasing PROXY_DEFERRAL_THRESHOLD times in a row, the
245
+ # proxy forces the NEXT turn to take a concrete action: tool_choice=required plus
246
+ # a firm "continue autonomously, you will not be re-prompted" directive. This is
247
+ # the INVERSE of STUCK-BREAK (which releases to a prose exit) -- a deferral means
248
+ # work REMAINS, so we drive an action rather than allow a stop. Default on;
249
+ # PROXY_DEFERRAL_BREAK=off to disable.
250
+ PROXY_DEFERRAL_BREAK = os.environ.get("PROXY_DEFERRAL_BREAK", "on").lower() not in {
251
+ "0", "false", "off", "no",
252
+ }
253
+ # Turn-ending deferral/capitulation phrases (lowercased match). Deliberately
254
+ # narrow -- it must catch "I need more exploration cycles to complete the plan"
255
+ # and close kin, WITHOUT firing on ordinary forward-looking narration that is
256
+ # actually followed by a tool call (the no-tool-turn gate handles that too).
257
+ _DEFERRAL_PHRASE_RE = re.compile(
258
+ r"need (?:more|additional|further|extra) (?:exploration |investigation |research )?"
259
+ r"(?:cycles|passes|turns|iterations|rounds)"
260
+ r"|more (?:exploration |investigation )?(?:cycles|passes|iterations) "
261
+ r"(?:to|are needed|is needed|required|before)"
262
+ r"|to (?:complete|finish|continue) (?:the|my) (?:plan|exploration|investigation)"
263
+ r"|(?:let me|allow me to|i'?ll|i will|i need to|i'?d need to|"
264
+ r"we(?:'?ll| will| need to)) (?:keep|continue) "
265
+ r"(?:exploring|investigating|researching|working through|going)"
266
+ r"|(?:once|after) i(?:'ve| have)? (?:explored|investigated|gathered|reviewed) "
267
+ r"(?:more|additional|further|enough)"
268
+ r"|need to (?:gather|do|run|perform) (?:more|additional|further) "
269
+ r"(?:exploration|research|investigation|analysis)",
270
+ re.IGNORECASE,
271
+ )
272
+ # Fire on the FIRST deferral by default: a single "I need more cycles" no-tool
273
+ # turn IS the halt, unlike STUCK-BREAK which waits for a sustained loop.
274
+ PROXY_DEFERRAL_THRESHOLD = int(os.environ.get("PROXY_DEFERRAL_THRESHOLD", "1"))
275
+
234
276
  PROXY_TOOL_STATE_MACHINE = os.environ.get(
235
277
  "PROXY_TOOL_STATE_MACHINE", "on"
236
278
  ).lower() not in {
@@ -340,6 +382,83 @@ _WRITE_TOOL_CLASS = frozenset({
340
382
  "write_file", "edit_file", "save_file",
341
383
  })
342
384
 
385
+ # Open-ended exploration tools the agent uses to make a DIFFERENT move once a
386
+ # read/grep/glob loop is broken. The code cannot enumerate every exploration
387
+ # tool, but these are the universal escape hatches. Cycle-break narrowing must
388
+ # NEVER strip these: a cycling *Bash* means "vary the command" (handled by the
389
+ # injected cycle hint), NOT "lose the ability to run bash at all". Excluding
390
+ # Bash here stranded autonomous ("auto mode") agents with no way to explore the
391
+ # filesystem — the whole toolset minus its exploration escape hatch.
392
+ _EXPLORATION_ESCAPE_TOOLS = frozenset({
393
+ "bash", "shell", "sh", "run_bash", "runbash", "run_command",
394
+ "run_terminal_cmd", "execute_command", "executecommand", "terminal",
395
+ "webfetch", "web_fetch", "fetch", "websearch", "web_search",
396
+ "agent", "task", "dispatch_agent",
397
+ })
398
+
399
+
400
+ def _narrow_tools_for_cycle_break(tools, cycling_tool_names, session_banned_tools):
401
+ """Drop cycling + session-banned tools from the toolset, expanding to the
402
+ whole read-only class when any excluded tool is read-only.
403
+
404
+ The open-ended exploration escape hatch (Bash/WebFetch/Agent) is kept out of
405
+ the CYCLING-derived exclusion: a cycling Bash means "vary the command"
406
+ (handled by the injected cycle hint), not "lose the ability to run bash".
407
+ An explicit session ban is a stronger, deliberate signal (a tool that keeps
408
+ malforming) and IS still honored, even for those tools.
409
+
410
+ Returns ``(narrowed_tools, expanded_read_only_class)``. Tool-name matching is
411
+ case-insensitive so "Bash"/"bash" and "Read"/"read" behave identically.
412
+ """
413
+ read_only_lower = {c.lower() for c in _READ_ONLY_TOOL_CLASS}
414
+ cycling_lower = {n.lower() for n in cycling_tool_names}
415
+ banned_lower = {n.lower() for n in session_banned_tools}
416
+ # Expand to the full read-only class if any excluded tool is read-only
417
+ # (preserves the original trigger over cycling ∪ banned).
418
+ expanded = any(n in read_only_lower for n in (cycling_lower | banned_lower))
419
+ cycling_exclude = set(cycling_lower)
420
+ if expanded:
421
+ cycling_exclude |= read_only_lower
422
+ # Never let the cycling path narrow away the exploration escape hatch — that
423
+ # is exactly the filesystem-exploration capability the cycle-break is trying
424
+ # to redirect the agent toward.
425
+ cycling_exclude -= _EXPLORATION_ESCAPE_TOOLS
426
+ exclude_set = cycling_exclude | banned_lower
427
+
428
+ def _name(t):
429
+ return ((t.get("function", {}) or {}).get("name", "") or "").lower()
430
+
431
+ narrowed = [t for t in tools if _name(t) not in exclude_set]
432
+
433
+ # Floor invariant: a loop-breaker must never strand the agent. If narrowing
434
+ # would remove the last way to make a DIFFERENT move — i.e. no exploration
435
+ # escape hatch AND no write tool survives, but the original set had one —
436
+ # keep the original toolset. This closes the door where an explicit ban of
437
+ # the sole exploration tool re-creates the very "can't explore" bug.
438
+ write_lower = {w.lower() for w in _WRITE_TOOL_CLASS}
439
+
440
+ def _has_action_path(tool_list):
441
+ names = {_name(t) for t in tool_list}
442
+ return bool(names & _EXPLORATION_ESCAPE_TOOLS) or bool(names & write_lower)
443
+
444
+ if _has_action_path(tools) and not _has_action_path(narrowed):
445
+ return list(tools), expanded
446
+ return narrowed, expanded
447
+
448
+
449
+ def _should_auto_ban(name, cycle_count, ban_at):
450
+ """Whether a cycling tool should be added to session_banned_tools this round.
451
+
452
+ The auto-ban accumulator is itself cycling-derived (ban after N detections),
453
+ so exploration escape-hatch tools (Bash/WebFetch/Agent) are NEVER auto-banned
454
+ — banning Bash would re-strip the agent's only way to explore the filesystem
455
+ on the Nth cycle, re-creating the bug the narrowing exemption fixes for
456
+ earlier cycles. Repeated Bash is redirected via the injected cycle hint.
457
+ """
458
+ if name.lower() in _EXPLORATION_ESCAPE_TOOLS:
459
+ return False
460
+ return cycle_count >= ban_at
461
+
343
462
  PROXY_GUARDRAIL_RETRY = os.environ.get("PROXY_GUARDRAIL_RETRY", "on").lower() not in {
344
463
  "0",
345
464
  "false",
@@ -1175,6 +1294,8 @@ class SessionMonitor:
1175
1294
  self_stuck_streak: int = 0 # consecutive assistant texts self-reporting a loop
1176
1295
  rate_limited_api_streak: int = 0 # consecutive tool calls hitting a rate-limited API host
1177
1296
  stuck_break_fires: int = 0 # monotonic count of forced stuck-breaks
1297
+ deferral_streak: int = 0 # consecutive no-tool turns deferring the work (Fix A)
1298
+ deferral_break_fires: int = 0 # monotonic count of forced deferral-breaks (Fix A)
1178
1299
  tool_starvation_streak: int = 0 # Consecutive forced turns with no tool_calls produced
1179
1300
  malformed_tool_streak: int = 0 # consecutive malformed pseudo tool payloads
1180
1301
  invalid_tool_call_streak: int = 0 # consecutive invalid tool arg payloads
@@ -1444,6 +1565,50 @@ class SessionMonitor:
1444
1565
  return True, f"rate-limited-API retries x{self.rate_limited_api_streak}"
1445
1566
  return False, ""
1446
1567
 
1568
+ def note_deferral_signal(self, text: str, had_tool_call: bool) -> None:
1569
+ """Track a turn-ending deferral/capitulation stall (Fix A): a plain-text
1570
+ assistant turn with NO tool call that asks for more cycles / more
1571
+ exploration / permission to keep going instead of taking the next build
1572
+ action. Only a no-tool turn whose text matches the deferral phrasing
1573
+ increments the streak; ANY tool call or non-matching text resets it, so a
1574
+ model that actually acts (or writes a normal summary) is never flagged."""
1575
+ if not PROXY_DEFERRAL_BREAK:
1576
+ self.deferral_streak = 0
1577
+ return
1578
+ if (not had_tool_call) and text and _DEFERRAL_PHRASE_RE.search(text):
1579
+ self.deferral_streak += 1
1580
+ else:
1581
+ self.deferral_streak = 0
1582
+
1583
+ def should_force_deferral_break(self) -> tuple[bool, str]:
1584
+ """True + reason when a deferral-break should be forced this turn."""
1585
+ if not PROXY_DEFERRAL_BREAK:
1586
+ return False, ""
1587
+ if self.deferral_streak >= PROXY_DEFERRAL_THRESHOLD:
1588
+ return True, f"deferral/plan-capitulation x{self.deferral_streak}"
1589
+ return False, ""
1590
+
1591
+ def recon_convergence_pending(self) -> bool:
1592
+ """True when recon-convergence will own this turn. DEFERRAL-BREAK yields
1593
+ to it (as it does to STUCK-BREAK) so the two guards never emit
1594
+ contradictory tool_choice/directives in the same request. Fix B makes
1595
+ their triggers climb in lockstep on prose-only stalls, so without this
1596
+ yield a deep prose stall could trip both and undo recon's terminal tier
1597
+ (which may strip tools and force a plain-text summary that ends the task)."""
1598
+ return (
1599
+ PROXY_RECON_CONVERGENCE_THRESHOLD > 0
1600
+ and self.consecutive_no_write_turns >= PROXY_RECON_CONVERGENCE_THRESHOLD
1601
+ )
1602
+
1603
+ def note_no_tool_turn(self) -> None:
1604
+ """Fix B: a plain-text assistant turn with no tool call is still a
1605
+ non-write turn, so advance the recon-convergence streak. The old code
1606
+ only moved `consecutive_no_write_turns` on turns that emitted tool calls,
1607
+ so a model that stalls in prose (no tools at all) froze the counter and
1608
+ never escalated to the recon-convergence directive. Resets, like the
1609
+ tool-turn path, whenever a write tool is later emitted."""
1610
+ self.consecutive_no_write_turns += 1
1611
+
1447
1612
  def has_duplicate_read_target(self, threshold: int = 2) -> tuple[bool, str]:
1448
1613
  """Check if any read-only tool has re-read the same target >= threshold times.
1449
1614
 
@@ -4028,7 +4193,13 @@ def _resolve_state_machine_tool_choice(
4028
4193
  and PROXY_COORDINATION_BAN_THRESHOLD > 0
4029
4194
  )
4030
4195
  ban_at = PROXY_COORDINATION_BAN_THRESHOLD if is_coord else 3
4031
- if monitor.tool_cycle_counts[name] >= ban_at and name not in monitor.session_banned_tools:
4196
+ # Exploration escape-hatch tools are never auto-banned see
4197
+ # _should_auto_ban (banning Bash re-creates the "can't explore"
4198
+ # bug since the ban is itself cycling-derived).
4199
+ if (
4200
+ _should_auto_ban(name, monitor.tool_cycle_counts[name], ban_at)
4201
+ and name not in monitor.session_banned_tools
4202
+ ):
4032
4203
  monitor.session_banned_tools.add(name)
4033
4204
  logger.warning(
4034
4205
  "TOOL BAN: '%s' banned for session after %d cycle detections "
@@ -4178,6 +4349,60 @@ def _maybe_inject_stuck_break(openai_body: dict, monitor: "SessionMonitor") -> N
4178
4349
  logger.warning("STUCK-BREAK: forced terminal turn (%s, fires=%d)", reason, monitor.stuck_break_fires)
4179
4350
 
4180
4351
 
4352
+ def _maybe_inject_deferral_break(openai_body: dict, monitor: "SessionMonitor") -> None:
4353
+ """Convert a turn-ending deferral into forward motion (Fix A).
4354
+
4355
+ The model ended a turn with prose that DEFERS the work -- "I need more
4356
+ exploration cycles to complete the plan", "let me continue exploring" -- with
4357
+ no tool call, instead of taking the next build step. That stall is invisible
4358
+ to STUCK-BREAK (not a loop-admission phrase) and to recon-convergence (a
4359
+ no-tool turn never advanced its streak), so it silently ends a hands-free
4360
+ build. This forces the NEXT turn to ACT: it forces tool_choice and tells the
4361
+ model it will not be re-prompted. The INVERSE of STUCK-BREAK, which releases
4362
+ to a prose exit -- a deferral means work REMAINS.
4363
+ """
4364
+ should, reason = monitor.should_force_deferral_break()
4365
+ if not should:
4366
+ return
4367
+ # STUCK-BREAK (a self-aware loop wanting a prose exit) is the more urgent
4368
+ # signal; if it will fire this turn, yield to it rather than force a tool.
4369
+ stuck, _ = monitor.should_force_stuck_break()
4370
+ if stuck:
4371
+ return
4372
+ # Also yield to recon-convergence: once the no-write streak is deep enough
4373
+ # for recon to own the turn (it may strip tools / force a terminal summary),
4374
+ # a "keep building" deferral directive would directly contradict it.
4375
+ if monitor.recon_convergence_pending():
4376
+ return
4377
+ # Only force an action if there are tools to call and the choice is not
4378
+ # already pinned to a specific tool by an earlier guard.
4379
+ has_tools = bool(openai_body.get("tools"))
4380
+ if has_tools and openai_body.get("tool_choice") in (None, "auto"):
4381
+ openai_body["tool_choice"] = "required"
4382
+ monitor.deferral_break_fires += 1
4383
+ directive = (
4384
+ "\n\nCONTINUE AUTONOMOUSLY (" + reason + "). You do NOT need more "
4385
+ "exploration cycles, another pass, or permission, and you will NOT be "
4386
+ "re-prompted to keep going. Do the next concrete step of the plan NOW: "
4387
+ "pick the next un-built file/component and create it with the write tool "
4388
+ "(or run the next required command). Never end a turn with only a plan, a "
4389
+ "status note, or a request for more cycles -- take the action this turn."
4390
+ )
4391
+ msgs = openai_body.get("messages")
4392
+ if not isinstance(msgs, list):
4393
+ msgs = []
4394
+ if msgs and msgs[0].get("role") == "system":
4395
+ msgs[0]["content"] = (msgs[0].get("content") or "") + directive
4396
+ else:
4397
+ msgs.insert(0, {"role": "system", "content": directive.strip()})
4398
+ openai_body["messages"] = msgs # reattach in case messages was empty/absent
4399
+ logger.warning(
4400
+ "DEFERRAL-BREAK: forced action turn (%s, fires=%d)",
4401
+ reason,
4402
+ monitor.deferral_break_fires,
4403
+ )
4404
+
4405
+
4181
4406
  def _maybe_inject_recon_convergence(
4182
4407
  openai_body: dict,
4183
4408
  monitor: "SessionMonitor",
@@ -4853,16 +5078,15 @@ def build_openai_request(
4853
5078
  (monitor.cycling_tool_names or monitor.session_banned_tools)
4854
5079
  and "tools" in openai_body
4855
5080
  ):
4856
- exclude_set = set(monitor.cycling_tool_names) | monitor.session_banned_tools
4857
- # Expand to full read-only class if any cycling tool is read-only
4858
- if any(n.lower() in {c.lower() for c in _READ_ONLY_TOOL_CLASS} for n in exclude_set):
4859
- exclude_set |= _READ_ONLY_TOOL_CLASS
4860
5081
  original_count = len(openai_body["tools"])
4861
- narrowed = [
4862
- t
4863
- for t in openai_body["tools"]
4864
- if t.get("function", {}).get("name") not in exclude_set
4865
- ]
5082
+ # Drop cycling/banned tools (and the read-only class if any
5083
+ # cycling tool is read-only) but always keep the exploration
5084
+ # escape hatch — see _narrow_tools_for_cycle_break.
5085
+ narrowed, expanded_read_only = _narrow_tools_for_cycle_break(
5086
+ openai_body["tools"],
5087
+ monitor.cycling_tool_names,
5088
+ monitor.session_banned_tools,
5089
+ )
4866
5090
  if narrowed:
4867
5091
  openai_body["tools"] = narrowed
4868
5092
  # Only log on first activation or phase transitions to reduce noise
@@ -4872,7 +5096,7 @@ def build_openai_request(
4872
5096
  original_count,
4873
5097
  len(narrowed),
4874
5098
  monitor.cycling_tool_names,
4875
- any(n.lower() in {c.lower() for c in _READ_ONLY_TOOL_CLASS} for n in monitor.cycling_tool_names),
5099
+ expanded_read_only,
4876
5100
  )
4877
5101
  else:
4878
5102
  logger.warning(
@@ -4959,6 +5183,10 @@ def build_openai_request(
4959
5183
 
4960
5184
  _maybe_inject_stuck_break(openai_body, monitor)
4961
5185
 
5186
+ # DEFERRAL-BREAK (Fix A): after stuck-break, drive a no-tool deferral turn
5187
+ # into a concrete action. Runs last so it can yield to stuck-break.
5188
+ _maybe_inject_deferral_break(openai_body, monitor)
5189
+
4962
5190
  _apply_thinking_grammar(openai_body)
4963
5191
 
4964
5192
  _apply_json_response_grammar(openai_body, anthropic_body)
@@ -5090,12 +5318,21 @@ def _record_last_assistant_tool_calls(
5090
5318
  messages = anthropic_body.get("messages", [])
5091
5319
  tool_fingerprints = []
5092
5320
  tool_targets: dict[str, str] = {}
5321
+ assistant_had_text = False # Fix B: did the last assistant turn emit prose?
5093
5322
  for msg in reversed(messages):
5094
5323
  if msg.get("role") != "assistant":
5095
5324
  continue
5096
5325
  content = msg.get("content")
5326
+ if isinstance(content, str) and content.strip():
5327
+ assistant_had_text = True
5097
5328
  if isinstance(content, list):
5098
5329
  for block in content:
5330
+ if (
5331
+ isinstance(block, dict)
5332
+ and block.get("type") == "text"
5333
+ and str(block.get("text", "")).strip()
5334
+ ):
5335
+ assistant_had_text = True
5099
5336
  if isinstance(block, dict) and block.get("type") == "tool_use":
5100
5337
  tool_fingerprints.append(_tool_call_fingerprint(block))
5101
5338
  # Extract target key for read-only dedup (Option 3)
@@ -5119,6 +5356,12 @@ def _record_last_assistant_tool_calls(
5119
5356
  fingerprint=fingerprint,
5120
5357
  )
5121
5358
  return fingerprint
5359
+ # Fix B: no tool call in the last assistant turn. A plain-text turn is still
5360
+ # a non-write turn, so advance the recon-convergence streak (previously it
5361
+ # only moved on tool turns, freezing the counter through prose-only stalls).
5362
+ # Guard on real prose so an empty/absent assistant turn never inflates it.
5363
+ if assistant_had_text:
5364
+ monitor.note_no_tool_turn()
5122
5365
  return ""
5123
5366
 
5124
5367
 
@@ -8729,6 +8972,9 @@ async def stream_anthropic_response(
8729
8972
  try:
8730
8973
  monitor.note_assistant_text(accumulated_text)
8731
8974
  monitor.note_tool_arg_hosts(list(tc_args))
8975
+ # DEFERRAL-BREAK signal (Fix A): a no-tool prose turn that defers the
8976
+ # work. `tc_names` empty means this turn emitted no tool call.
8977
+ monitor.note_deferral_signal(accumulated_text, bool(tc_names))
8732
8978
  except Exception:
8733
8979
  pass
8734
8980
 
@@ -3465,8 +3465,10 @@ class TestCycleBreakOptions(unittest.TestCase):
3465
3465
  setattr(proxy, "PROXY_TOOL_STATE_STAGNATION_THRESHOLD", old_stagnation)
3466
3466
  setattr(proxy, "PROXY_TOOL_STATE_CYCLE_WINDOW", old_cycle_window)
3467
3467
 
3468
- def test_cycle_break_narrows_tools(self):
3469
- """Option 2: cycling tools are excluded from the tools array during review."""
3468
+ def test_cycle_break_keeps_bash_exploration(self):
3469
+ """Cycling on Bash must NOT strip the Bash tool it is the agent's
3470
+ exploration escape hatch. (Regression: the cycle-break used to exclude
3471
+ Bash by name, blocking all filesystem exploration.)"""
3470
3472
  old_state = getattr(proxy, "PROXY_TOOL_STATE_MACHINE")
3471
3473
  old_min_msgs = getattr(proxy, "PROXY_TOOL_STATE_MIN_MESSAGES")
3472
3474
  old_forced = getattr(proxy, "PROXY_TOOL_STATE_FORCED_BUDGET")
@@ -3516,9 +3518,10 @@ class TestCycleBreakOptions(unittest.TestCase):
3516
3518
 
3517
3519
  openai = proxy.build_openai_request(body, monitor)
3518
3520
  self.assertEqual(monitor.tool_turn_phase, "review")
3519
- # Bash should be excluded, Read and Write should remain
3521
+ # Bash is the exploration escape hatch it must remain available
3522
+ # even while cycling. Read and Write remain too.
3520
3523
  tool_names = [t["function"]["name"] for t in openai.get("tools", [])]
3521
- self.assertNotIn("Bash", tool_names)
3524
+ self.assertIn("Bash", tool_names)
3522
3525
  self.assertIn("Read", tool_names)
3523
3526
  self.assertIn("Write", tool_names)
3524
3527
  finally:
@@ -4548,7 +4551,8 @@ class TestReadOnlyCycleClassExclusion(unittest.TestCase):
4548
4551
  setattr(proxy, k, v)
4549
4552
 
4550
4553
  def test_non_read_tool_cycling_no_class_expansion(self):
4551
- """When 'bash' is cycling, only 'bash' is excluded, not read-only tools."""
4554
+ """When 'bash' is cycling, the read-only class is NOT expanded, and bash
4555
+ itself is preserved as the exploration escape hatch."""
4552
4556
  old_vals = {
4553
4557
  "PROXY_TOOL_STATE_MACHINE": getattr(proxy, "PROXY_TOOL_STATE_MACHINE"),
4554
4558
  "PROXY_TOOL_STATE_MIN_MESSAGES": getattr(proxy, "PROXY_TOOL_STATE_MIN_MESSAGES"),
@@ -4581,8 +4585,9 @@ class TestReadOnlyCycleClassExclusion(unittest.TestCase):
4581
4585
  remaining_names = [
4582
4586
  t.get("function", {}).get("name") for t in openai_body.get("tools", [])
4583
4587
  ]
4584
- self.assertNotIn("bash", remaining_names)
4585
- # Read-only tools should still be available
4588
+ # bash is an exploration escape hatch — cycling does not strip it.
4589
+ self.assertIn("bash", remaining_names)
4590
+ # Read-only tools should still be available (no class expansion)
4586
4591
  self.assertIn("read", remaining_names)
4587
4592
  self.assertIn("glob", remaining_names)
4588
4593
  self.assertIn("grep", remaining_names)
@@ -4685,16 +4690,20 @@ class TestPersistentCycleExclusion(unittest.TestCase):
4685
4690
  body = self._make_body_with_tools(all_tools)
4686
4691
  monitor = proxy.SessionMonitor(context_window=262144)
4687
4692
 
4688
- # Simulate bash cycling that triggers review
4689
- monitor.cycling_tool_names = ["bash"]
4693
+ # Simulate a non-exploration tool cycling that triggers review. (Bash
4694
+ # is exempt from cycling exclusion, so use 'edit' to exercise the
4695
+ # persistence path.)
4696
+ monitor.cycling_tool_names = ["edit"]
4690
4697
  monitor.tool_turn_phase = "act"
4691
4698
  monitor.tool_state_forced_budget_remaining = 5
4692
4699
 
4693
4700
  openai = proxy.build_openai_request(body, monitor)
4694
4701
 
4695
- # In act phase with cycling_tool_names set, bash should be excluded
4702
+ # In act phase with cycling_tool_names set, the cycling tool stays
4703
+ # excluded — but the exploration escape hatch (bash) is preserved.
4696
4704
  remaining = [t["function"]["name"] for t in openai.get("tools", [])]
4697
- self.assertNotIn("bash", remaining)
4705
+ self.assertNotIn("edit", remaining)
4706
+ self.assertIn("bash", remaining)
4698
4707
  self.assertIn("read", remaining)
4699
4708
  self.assertIn("write", remaining)
4700
4709
  finally: