orchestrix-yuri 4.7.3 → 4.7.4

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.
@@ -107,9 +107,9 @@ class Dispatcher {
107
107
  }
108
108
  }
109
109
 
110
- // Timeout — default to conversation
111
- log.warn('Dispatcher: classify timeout');
112
- return { action: 'conversation', description: text, reasoning: 'classifier timeout' };
110
+ // Timeout — default to change (bias toward action)
111
+ log.warn('Dispatcher: classify timeout, defaulting to change');
112
+ return { action: 'change', description: text, reasoning: 'classifier timeout' };
113
113
  } finally {
114
114
  this._busy = false;
115
115
  }
@@ -117,33 +117,65 @@ class Dispatcher {
117
117
 
118
118
  /**
119
119
  * Parse the dispatcher's tmux pane output to extract the JSON response.
120
- * Searches from bottom up for the first valid JSON line with "action".
120
+ * Handles: bare JSON lines, markdown code blocks, partial lines with JSON.
121
121
  */
122
122
  _parseResponse(output, originalText) {
123
- const lines = output.split('\n');
123
+ const valid = ['bugfix', 'change', 'plan', 'develop', 'test', 'deploy', 'status', 'iterate', 'conversation'];
124
124
 
125
+ // Strategy 1: Find JSON on its own line (bottom-up)
126
+ const lines = output.split('\n');
125
127
  for (let i = lines.length - 1; i >= 0; i--) {
126
128
  const line = lines[i].trim();
127
- // Look for JSON that contains "action"
128
- if (line.startsWith('{') && line.includes('"action"')) {
129
- try {
130
- const parsed = JSON.parse(line);
131
- if (parsed.action) {
132
- const valid = ['bugfix', 'change', 'plan', 'develop', 'test', 'deploy', 'status', 'iterate', 'conversation'];
133
- if (valid.includes(parsed.action)) {
129
+ if (line.includes('"action"')) {
130
+ // Extract JSON substring (may be embedded in other text)
131
+ const jsonMatch = line.match(/\{[^{}]*"action"\s*:\s*"[^"]+?"[^{}]*\}/);
132
+ if (jsonMatch) {
133
+ try {
134
+ const parsed = JSON.parse(jsonMatch[0]);
135
+ if (parsed.action && valid.includes(parsed.action)) {
134
136
  return {
135
137
  action: parsed.action,
136
138
  description: parsed.description || originalText,
137
139
  reasoning: parsed.reasoning || '',
138
140
  };
139
141
  }
140
- }
141
- } catch { /* not valid JSON, continue searching */ }
142
+ } catch { /* continue */ }
143
+ }
144
+ }
145
+ }
146
+
147
+ // Strategy 2: Search entire output for JSON pattern (handles multiline/code blocks)
148
+ const fullMatch = output.match(/\{\s*"action"\s*:\s*"(\w+)"[^}]*\}/);
149
+ if (fullMatch) {
150
+ try {
151
+ const parsed = JSON.parse(fullMatch[0]);
152
+ if (parsed.action && valid.includes(parsed.action)) {
153
+ return {
154
+ action: parsed.action,
155
+ description: parsed.description || originalText,
156
+ reasoning: parsed.reasoning || '',
157
+ };
158
+ }
159
+ } catch { /* continue */ }
160
+
161
+ // Even if full JSON parse fails, extract action from regex
162
+ const action = fullMatch[1];
163
+ if (valid.includes(action)) {
164
+ return { action, description: originalText, reasoning: 'partial parse' };
165
+ }
166
+ }
167
+
168
+ // Strategy 3: Look for bare action words in the last few lines
169
+ const tail = lines.slice(-5).join(' ').toLowerCase();
170
+ for (const action of valid) {
171
+ if (action !== 'conversation' && tail.includes(`"${action}"`)) {
172
+ return { action, description: originalText, reasoning: 'keyword match' };
142
173
  }
143
174
  }
144
175
 
145
- log.warn('Dispatcher: failed to parse response, defaulting to conversation');
146
- return { action: 'conversation', description: originalText, reasoning: 'parse failed' };
176
+ // Parse failed — default to "change" (bias toward action, not conversation)
177
+ log.warn('Dispatcher: failed to parse response, defaulting to change');
178
+ return { action: 'change', description: originalText, reasoning: 'parse failed' };
147
179
  }
148
180
 
149
181
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "orchestrix-yuri",
3
- "version": "4.7.3",
3
+ "version": "4.7.4",
4
4
  "description": "Yuri — Meta-Orchestrator for Orchestrix. Drive your entire project lifecycle with natural language.",
5
5
  "main": "lib/installer.js",
6
6
  "bin": {