dual-brain 0.2.28 → 0.2.29

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dual-brain",
3
- "version": "0.2.28",
3
+ "version": "0.2.29",
4
4
  "description": "AI orchestration across Claude + OpenAI subscriptions — smart routing, budget awareness, and dual-brain collaboration",
5
5
  "type": "module",
6
6
  "bin": {
@@ -3,7 +3,8 @@ import { join, resolve } from 'node:path';
3
3
 
4
4
  export const MODEL_FORMAT = {
5
5
  claude: 'xml', sonnet: 'xml', haiku: 'xml', opus: 'xml',
6
- gpt: 'markdown', o3: 'markdown', 'o4-mini': 'markdown',
6
+ gpt: 'markdown', 'o4-mini': 'markdown',
7
+ o3: 'prose',
7
8
  };
8
9
 
9
10
  function detectFormat(targetModel, role) {
@@ -14,6 +15,7 @@ function detectFormat(targetModel, role) {
14
15
  }
15
16
 
16
17
  export function selectRelevant(pack, role) {
18
+ if (!pack) return { intent: '', constraints: [], acceptanceCriteria: [] };
17
19
  const { intent, constraints, priorAttempts, repoState, fileSummaries,
18
20
  acceptanceCriteria, files } = pack;
19
21
  if (role === 'thinker') {
@@ -134,7 +136,7 @@ export function attachOutputSchema(role) {
134
136
  return 'Return JSON: { pass: boolean, findings: [{ severity, file, line, issue, fix }] }';
135
137
  }
136
138
 
137
- export function shapeForRole(pack, role, targetModel, tokenBudget) {
139
+ export function shapeForRole(pack, role, targetModel = 'sonnet', tokenBudget = 8000) {
138
140
  const sections = selectRelevant(pack, role);
139
141
 
140
142
  if (role === 'worker' && sections.inScope?.length) {
@@ -267,7 +267,7 @@ export function formatRecommendations(recs) {
267
267
  const line = (content) => `│ ${pad(content)} │`;
268
268
 
269
269
  const lines = [
270
- '╭─ Recommendations ' + '─'.repeat(WIDTH - 19) + '╮',
270
+ '╭─ Recommendations ' + '─'.repeat(WIDTH - 20) + '╮',
271
271
  line(''),
272
272
  ];
273
273
 
@@ -78,6 +78,7 @@ export function selectStrategy(failure, originalDecision, attemptNumber) {
78
78
  case 'specification':
79
79
  return { strategy: 'give-up', reason: 'ambiguous specification; user clarification needed' };
80
80
  default: // unknown
81
+ if (tier >= 3) return { strategy: 'split', newDecision: originalDecision, reason: 'unknown failure at max tier; decomposing' };
81
82
  return { strategy: 'escalate', newDecision: originalDecision, reason: 'unknown failure; escalating as precaution' };
82
83
  }
83
84
  }
package/src/signal.mjs CHANGED
@@ -71,7 +71,7 @@ export function scoreOutcome(outcome, context = {}) {
71
71
  // Signal 3: token efficiency (weight 0.25)
72
72
  let effVal = null;
73
73
  const filesChanged = outcome.filesChanged ?? 0;
74
- const fileCount = typeof filesChanged === 'number' ? filesChanged : filesChanged.length;
74
+ const fileCount = Array.isArray(filesChanged) ? filesChanged.length : (typeof filesChanged === 'number' ? filesChanged : 0);
75
75
  if (!(fileCount === 0 && tier === 'think')) {
76
76
  const tokensUsed =
77
77
  outcome.tokensUsed?.output ??