|
1181 | 1181 | // This caused the selector to always return null, so a brand-new model bubble-wrap |
1182 | 1182 | // was appended on every single onAiMessage call instead of updating the pending one — |
1183 | 1183 | // flooding the chat with stray bubbles and pushing the user bubble out of view. |
1184 | | - const lastWrap = msgs.lastElementChild; |
1185 | | - const last = lastWrap && lastWrap.classList.contains('bubble-wrap') |
1186 | | - const last = (lastWrap && lastWrap.classList.contains('bubble-wrap')) |
1187 | | - ? (lastWrap.querySelector('.bubble.model')) |
1188 | | - if (last && last.querySelector('.model-icon.loading')) { |
| 1184 | + const allModelBubbles = [...msgs.querySelectorAll('.bubble.model')]; |
| 1185 | + const last = allModelBubbles.length ? allModelBubbles[allModelBubbles.length - 1] : null; |
| 1186 | + const parentWrap = last && last.closest('.bubble-wrap'); |
| 1187 | + if (parentWrap && last.querySelector('.model-icon.loading')) { |
1189 | 1188 | const textSpan = last.querySelector('.model-text') || last; |
1190 | 1189 | textSpan.textContent = text; |
1191 | 1190 | if (!isPending) { |
|
1575 | 1574 | */ |
1576 | 1575 | function _truncateCommands(commands) { |
1577 | 1576 | const limit = _executionPolicy.maxCommandsPerMessage; |
1578 | | - if (limit <= 0) { |
| 1577 | + if (limit <= 0 || commands.length <= limit) { |
1579 | 1578 | return { commandsToExecute: commands, wasTruncated: false, totalCount: commands.length, executedCount: commands.length }; |
1580 | 1579 | } |
1581 | | - |
1582 | | - const countedCommands = commands.filter(c => c.type !== 'COMPLETED'); |
1583 | | - const wasTruncated = countedCommands.length > limit; |
1584 | | - if (!wasTruncated) { |
1585 | | - return { commandsToExecute: commands, wasTruncated: false, totalCount: countedCommands.length, executedCount: countedCommands.length }; |
1586 | | - } |
1587 | | - |
1588 | | - let executedCount = 0; |
1589 | | - const commandsToExecute = []; |
1590 | | - for (const command of commands) { |
1591 | | - if (command.type === 'COMPLETED') continue; |
1592 | | - if (executedCount >= limit) continue; |
1593 | | - commandsToExecute.push(command); |
1594 | | - executedCount++; |
1595 | | - } |
1596 | | - |
1597 | 1580 | return { |
1598 | | - commandsToExecute, |
| 1581 | + commandsToExecute: commands.slice(0, limit), |
1599 | 1582 | wasTruncated: true, |
1600 | | - totalCount: countedCommands.length, |
1601 | | - executedCount, |
1602 | | - hadCompleted: commands.some(c => c.type === 'COMPLETED'), |
| 1583 | + totalCount: commands.length, |
| 1584 | + executedCount: limit, |
1603 | 1585 | }; |
1604 | 1586 | } |
1605 | 1587 |
|
|
3242 | 3224 | const parsedCommands = parseCommandsFromText(text); |
3243 | 3225 | if (!parsedCommands.length) return; |
3244 | 3226 |
|
3245 | | - // Apply ExecutionPolicyConfig cap (maxCommandsPerMessage). completed() is a terminal |
3246 | | - // marker, not an action command, so it does not consume one of the two action slots. |
| 3227 | + // Apply ExecutionPolicyConfig cap (maxCommandsPerMessage) - mirrors CommandExecutionLimiter. |
3247 | 3228 | const trunc = _truncateCommands(parsedCommands); |
3248 | 3229 | const commands = trunc.commandsToExecute; |
3249 | 3230 |
|
3250 | 3231 | if (trunc.wasTruncated) { |
3251 | 3232 | _pendingTruncationWarning = 'Only two commands may be executed at a time. The first two commands were executed, and the rest were ignored.'; |
3252 | 3233 | } |
3253 | 3234 |
|
3254 | | - // Determine if we have a screenshot or completed command. A truncated response that |
3255 | | - // also contained completed() must not auto-request screen/Termux feedback; the only |
3256 | | - // feedback for that turn is the truncation warning. |
| 3235 | + // Determine if we have a screenshot or completed command |
3257 | 3236 | const hasScreenshot = commands.some(c => c.type === 'SCREENSHOT'); |
3258 | 3237 | const hasCompleted = commands.some(c => c.type === 'COMPLETED'); |
3259 | | - const suppressAutoFeedback = !!(trunc.wasTruncated && trunc.hadCompleted); |
3260 | 3238 |
|
3261 | 3239 | // Show detected commands in status area |
3262 | 3240 | const cmdStr = commands.map(_cmdToString).join(', '); |
|
3265 | 3243 | // If no screenshot and no completed: auto-append a screenshot at the end |
3266 | 3244 | // so the AI always gets screen feedback (matches Kotlin behavior). |
3267 | 3245 | const cmdList = [...commands]; |
3268 | | - if (!hasScreenshot && !hasCompleted && !suppressAutoFeedback) { |
| 3246 | + if (!hasScreenshot && !hasCompleted) { |
3269 | 3247 | cmdList.push({ type: 'SCREENSHOT' }); |
3270 | 3248 | } |
3271 | 3249 |
|
|
0 commit comments