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.
- package/lib/gateway/engine/dispatcher.js +48 -16
- package/package.json +1 -1
|
@@ -107,9 +107,9 @@ class Dispatcher {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
// Timeout — default to
|
|
111
|
-
log.warn('Dispatcher: classify timeout');
|
|
112
|
-
return { action: '
|
|
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
|
-
*
|
|
120
|
+
* Handles: bare JSON lines, markdown code blocks, partial lines with JSON.
|
|
121
121
|
*/
|
|
122
122
|
_parseResponse(output, originalText) {
|
|
123
|
-
const
|
|
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
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
const
|
|
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
|
-
}
|
|
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
|
-
|
|
146
|
-
|
|
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
|
/**
|