bingocode 1.1.184 → 1.1.186

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.
@@ -3,21 +3,23 @@
3
3
  *
4
4
  * Fires after each turn completes (triggered by lastQueryCompletionTime change).
5
5
  * Evaluates progress toward active goal using env-aware evaluator v2.
6
+ *
7
+ * Migrated from old state.ts flat fields to the new GoalStore API.
8
+ * All state access goes through GoalStore — no more STATE.goalCondition fallbacks.
6
9
  */
7
10
 
8
11
  import { useEffect, useRef } from 'react'
9
12
  import type { MutableRefObject } from 'react'
10
13
  import type { MessageType } from '../components/messages.js'
11
14
  import {
12
- getGoalCondition,
13
- getGoalIterationCount,
14
- getGoalMaxIterations,
15
- incrementGoalIterationCount,
16
- setGoalCondition,
17
- getGoalEvalHistory,
18
- updateGoalEvalHistory,
19
- } from '../bootstrap/state.js'
20
- import { getGoalState, recalculateProgress } from '../utils/goalStore.js'
15
+ getGoalState,
16
+ recalculateProgress,
17
+ incrementIteration,
18
+ recordEvalGap,
19
+ getActiveGoalId,
20
+ getReadySubGoals,
21
+ getBlockedSubGoals,
22
+ } from '../utils/goalStore.js'
21
23
  import { enqueue } from '../utils/messageQueueManager.js'
22
24
  import { evaluateGoal } from '../utils/goalEvaluator.js'
23
25
 
@@ -47,7 +49,8 @@ export function useGoalEvaluator({
47
49
  const evaluating = useRef(false)
48
50
 
49
51
  useEffect(() => {
50
- const condition = getGoalCondition()
52
+ const goalState = getGoalState()
53
+ const condition = goalState.userGoal?.text ?? null
51
54
  if (!condition) return
52
55
  if (isQueryActive) return
53
56
  if (lastQueryCompletionTime === 0) return
@@ -59,56 +62,71 @@ export function useGoalEvaluator({
59
62
 
60
63
  void (async () => {
61
64
  try {
62
- const iterCount = getGoalIterationCount()
63
- const maxIter = getGoalMaxIterations()
65
+ const iterCount = goalState.metrics.iterationCount
66
+ const maxIter = goalState.metrics.maxIterations
64
67
 
65
- // Max iterations check
68
+ // Max iterations check — circuit breaker
66
69
  if (iterCount >= maxIter) {
67
- setGoalCondition(null)
70
+ // Clear goal from store (delegates to clear() which resets engine too)
71
+ const { clear } = require('../utils/goalStore.js')
72
+ clear()
68
73
  enqueue({
69
- value: `⚠️ /goal stopped after ${maxIter} iterations. Goal not achieved: "${condition}"`,
74
+ value: `Goal not achieved after ${maxIter} iterations. Goal was: "${condition}"`,
70
75
  mode: 'task-notification',
71
76
  priority: 'now',
72
77
  })
73
78
  return
74
79
  }
75
80
 
76
- // Get current goal state + sub-goal from store
81
+ // Get messages + current sub-goal for evaluation context
77
82
  const messages = messagesRef.current
78
- const goalState = getGoalState()
79
83
  const currentSubGoal = goalState.immediateGoal
80
84
  ? goalState.dag.nodes.get(goalState.immediateGoal.subGoalId)
81
85
  : undefined
82
86
 
83
- // Run environment-aware evaluation
87
+ // Run environment-aware evaluation (rule engine + semantic + final)
84
88
  const result = await evaluateGoal(condition, messages, currentSubGoal)
85
89
 
86
- // Race: user may have cleared goal during await
87
- if (getGoalCondition() !== condition) return
90
+ // Race: user may have cleared goal during async evaluation
91
+ if (!getActiveGoalId()) return
88
92
 
89
- incrementGoalIterationCount()
90
- updateGoalEvalHistory(result.gap)
93
+ incrementIteration()
94
+ recordEvalGap(result.gap)
91
95
  recalculateProgress()
92
96
 
97
+ // Check active sub-goals and blocked count for drift diagnosis
98
+ const ready = getReadySubGoals()
99
+ const blocked = getBlockedSubGoals()
100
+ if (blocked.length > 0 && ready.length === 0) {
101
+ // All sub-goals blocked — need to replan or escalate
102
+ void ready
103
+ void blocked
104
+ }
105
+
93
106
  // Circuit breaker: 3 consecutive same gap
94
- const evalHistory = getGoalEvalHistory()
107
+ const evalHistory = goalState.metrics.evalHistory
95
108
  const isRepeatedGap = evalHistory.consecutiveSameGapCount >= 3 && result.gap !== null
96
109
 
97
110
  if (result.satisfied) {
98
- setGoalCondition(null)
111
+ // Goal achieved — clear from store and file system
112
+ const { clearAndDelete } = require('../utils/goalStore.js')
113
+ clearAndDelete()
99
114
  enqueue({
100
- value: `✅ Goal achieved (iteration ${iterCount + 1}): ${result.reason}`,
115
+ value: `Goal achieved (iteration ${iterCount + 1}): ${result.reason}`,
101
116
  mode: 'task-notification',
102
117
  priority: 'now',
103
118
  })
104
119
  } else if (isRepeatedGap) {
105
- setGoalCondition(null)
120
+ // Circuit breaker — same gap repeated 3 times, stop looping
121
+ const { clear } = require('../utils/goalStore.js')
122
+ clear()
106
123
  enqueue({
107
- value: `⚠️ Goal evaluator stopped — same gap "${result.gap}" 3x. Adjust approach or output EVAL blocks.`,
124
+ value: `Goal evaluator stopped — same gap "${result.gap}" ${evalHistory.consecutiveSameGapCount}x. Adjust approach or output EVAL blocks.`,
108
125
  mode: 'task-notification',
109
126
  priority: 'now',
110
127
  })
111
128
  } else {
129
+ // Not yet satisfied — continue
112
130
  const continueMsg = result.gap
113
131
  ? `Goal not yet met (${iterCount + 1}/${maxIter}). Gap: ${result.gap}. Continue toward: "${condition}"`
114
132
  : `Goal not yet met (${iterCount + 1}/${maxIter}, reason: ${result.reason}). Continue toward: "${condition}"`
@@ -118,9 +136,12 @@ export function useGoalEvaluator({
118
136
  priority: 'now',
119
137
  })
120
138
  }
139
+ } catch (err) {
140
+ // Evaluation error is non-fatal — log and continue
141
+ console.error('[GoalEvaluator] Evaluation error:', err)
121
142
  } finally {
122
143
  evaluating.current = false
123
144
  }
124
145
  })()
125
- }, [lastQueryCompletionTime, isQueryActive])
146
+ }, [lastQueryCompletionTime, isQueryActive]) // messagesRef is stable ref — not included as dep
126
147
  }
@@ -63,7 +63,7 @@ import {
63
63
  isValidImagePaste,
64
64
  } from 'src/types/textInputTypes.js'
65
65
  import { randomUUID, type UUID } from 'crypto'
66
- import { getSettings_DEPRECATED } from './settings/settings.js'
66
+ import { getInitialSettings, getSettings_DEPRECATED } from './settings/settings.js'
67
67
  import { getSnippetForTwoFileDiff } from 'src/tools/FileEditTool/utils.js'
68
68
  import type {
69
69
  ContentBlockParam,
@@ -915,6 +915,9 @@ export async function getAttachments(
915
915
  maybe('critical_system_reminder', () =>
916
916
  Promise.resolve(getCriticalSystemReminderAttachment(toolUseContext)),
917
917
  ),
918
+ maybe('exec_mode_reminder', () =>
919
+ Promise.resolve(getExecModeReminderAttachment()),
920
+ ),
918
921
  ...(feature('COMPACTION_REMINDERS')
919
922
  ? [
920
923
  maybe('compaction_reminder', () =>
@@ -1590,6 +1593,19 @@ function getCriticalSystemReminderAttachment(
1590
1593
  return [{ type: 'critical_system_reminder', content: reminder }]
1591
1594
  }
1592
1595
 
1596
+ function getExecModeReminderAttachment(): Attachment[] {
1597
+ if (!getInitialSettings().execMode) {
1598
+ return []
1599
+ }
1600
+ return [
1601
+ {
1602
+ type: 'system_reminder' as const,
1603
+ content:
1604
+ '/exec active — Dispatch first. Protect context. Report decisions, blockers, results.',
1605
+ },
1606
+ ]
1607
+ }
1608
+
1593
1609
  function getOutputStyleAttachment(): Attachment[] {
1594
1610
  const settings = getSettings_DEPRECATED()
1595
1611
  const outputStyle = settings?.outputStyle || 'default'
@@ -725,6 +725,12 @@ export const SettingsSchema = lazySchema(() =>
725
725
  .describe(
726
726
  'When true, fast mode does not persist across sessions. Each session starts with fast mode off.',
727
727
  ),
728
+ execMode: z
729
+ .boolean()
730
+ .optional()
731
+ .describe(
732
+ 'Priority dispatch, context protection, compressed reporting.',
733
+ ),
728
734
  promptSuggestionEnabled: z
729
735
  .boolean()
730
736
  .optional()