oxe-cc 1.8.0 → 1.8.3
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/CHANGELOG.md +63 -0
- package/README.md +1 -1
- package/bin/lib/oxe-manifest.cjs +20 -13
- package/bin/lib/oxe-operational.cjs +96 -10
- package/bin/lib/oxe-project-health.cjs +77 -18
- package/bin/lib/oxe-rationality.cjs +9 -7
- package/bin/oxe-cc.js +202 -39
- package/lib/runtime/compiler/graph-compiler.js +1 -1
- package/lib/runtime/executor/action-tool-map.js +4 -0
- package/lib/runtime/executor/built-in-tools.js +27 -0
- package/lib/runtime/executor/llm-task-executor.d.ts +4 -1
- package/lib/runtime/executor/llm-task-executor.js +41 -5
- package/lib/runtime/executor/node-prompt-builder.d.ts +4 -1
- package/lib/runtime/executor/node-prompt-builder.js +13 -2
- package/lib/runtime/models/failure.d.ts +1 -1
- package/lib/runtime/scheduler/scheduler.d.ts +5 -1
- package/lib/runtime/scheduler/scheduler.js +82 -14
- package/lib/runtime/verification/verification-compiler.js +7 -5
- package/lib/sdk/index.cjs +48 -44
- package/oxe/templates/PLAN.template.md +23 -9
- package/oxe/templates/SPEC.template.md +55 -22
- package/oxe/workflows/plan.md +18 -6
- package/oxe/workflows/spec.md +31 -9
- package/package.json +103 -100
- package/packages/runtime/package.json +18 -18
- package/packages/runtime/src/compiler/graph-compiler.ts +1 -1
- package/packages/runtime/src/evidence/evidence-store.ts +2 -2
- package/packages/runtime/src/executor/action-tool-map.ts +4 -0
- package/packages/runtime/src/executor/built-in-tools.ts +29 -0
- package/packages/runtime/src/executor/llm-task-executor.ts +46 -4
- package/packages/runtime/src/executor/node-prompt-builder.ts +18 -1
- package/packages/runtime/src/models/failure.ts +2 -0
- package/packages/runtime/src/scheduler/scheduler.ts +93 -15
- package/packages/runtime/src/verification/verification-compiler.ts +7 -5
- package/vscode-extension/package.json +185 -185
- package/vscode-extension/oxe-agents-0.9.1.vsix +0 -0
- package/vscode-extension/oxe-agents-0.9.2.vsix +0 -0
- package/vscode-extension/oxe-agents-1.0.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.4.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.5.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.5.1.vsix +0 -0
- package/vscode-extension/oxe-agents-1.6.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.7.0.vsix +0 -0
- package/vscode-extension/oxe-agents-1.8.0.vsix +0 -0
package/bin/oxe-cc.js
CHANGED
|
@@ -209,8 +209,8 @@ function buildInstallSummary(opts, fullLayout) {
|
|
|
209
209
|
if (opts.copilotCli || opts.allAgents || anyGranularAgent(opts)) agentHint.push('CLIs / multi-agente');
|
|
210
210
|
if (agentHint.length) {
|
|
211
211
|
nextSteps.push({
|
|
212
|
-
desc: `
|
|
213
|
-
cmd: '/oxe
|
|
212
|
+
desc: `Entrar no fluxo OXE no agente (${agentHint.join(', ')}) e deixar o router indicar o primeiro passo:`,
|
|
213
|
+
cmd: '/oxe',
|
|
214
214
|
});
|
|
215
215
|
} else if (opts.oxeOnly) {
|
|
216
216
|
nextSteps.push({
|
|
@@ -220,7 +220,7 @@ function buildInstallSummary(opts, fullLayout) {
|
|
|
220
220
|
} else {
|
|
221
221
|
nextSteps.push({
|
|
222
222
|
desc: 'Primeiro passo do fluxo no seu editor:',
|
|
223
|
-
cmd: '/oxe
|
|
223
|
+
cmd: '/oxe',
|
|
224
224
|
});
|
|
225
225
|
}
|
|
226
226
|
|
|
@@ -252,6 +252,7 @@ function buildUninstallFooter(u) {
|
|
|
252
252
|
const bullets = [];
|
|
253
253
|
const p = u.dryRun ? '[simulação] ' : '';
|
|
254
254
|
const rm = u.dryRun ? 'Seriam removidos' : 'Removidos';
|
|
255
|
+
const localIdeArtifacts = shouldAlsoRemoveLocalIdeArtifacts(u);
|
|
255
256
|
const granularAgents = [
|
|
256
257
|
u.agentOpenCode ? 'OpenCode' : null,
|
|
257
258
|
u.agentGemini ? 'Gemini' : null,
|
|
@@ -277,9 +278,9 @@ function buildUninstallFooter(u) {
|
|
|
277
278
|
} else if (granularAgents.length) {
|
|
278
279
|
bullets.push(`${p}${rm} apenas as integrações selecionadas: ${granularAgents.join(', ')}.`);
|
|
279
280
|
}
|
|
280
|
-
if (
|
|
281
|
+
if (localIdeArtifacts) {
|
|
281
282
|
bullets.push(
|
|
282
|
-
`${p}${rm} integrações OXE no repositório (.cursor, .github, .claude, .copilot, .opencode, … conforme flags).`
|
|
283
|
+
`${p}${rm} integrações OXE no repositório (.cursor, .github, .claude, .copilot, .opencode, … conforme flags ou artefatos locais detectados).`
|
|
283
284
|
);
|
|
284
285
|
}
|
|
285
286
|
if (u.globalCli) bullets.push(`${p}${rm} também o pacote npm global oxe-cc do PATH.`);
|
|
@@ -1533,7 +1534,9 @@ function printOxeHealthDiagnostics(target, c, diagOpts = {}) {
|
|
|
1533
1534
|
typeof r.planSelfEvaluation.confidence === 'number' ? `${r.planSelfEvaluation.confidence}%` : '—';
|
|
1534
1535
|
console.log(` ${c ? dim : ''}Plano (autoavaliação):${c ? reset : ''} melhor=${best} | confiança=${conf}`);
|
|
1535
1536
|
}
|
|
1536
|
-
if (
|
|
1537
|
+
if (r.executionRationality && r.executionRationality.applicable === false) {
|
|
1538
|
+
console.log(` ${c ? dim : ''}Prontidão racional:${c ? reset : ''} não aplicável ainda (PLAN.md ausente)`);
|
|
1539
|
+
} else if (typeof r.executionRationalityReady === 'boolean') {
|
|
1537
1540
|
console.log(
|
|
1538
1541
|
` ${c ? dim : ''}Prontidão racional:${c ? reset : ''} implementation=${r.implementationPackReady ? 'ok' : 'pendente'} | anchors=${r.referenceAnchorsReady ? 'ok' : 'pendente'} | fixtures=${r.fixturePackReady ? 'ok' : 'pendente'}`
|
|
1539
1542
|
);
|
|
@@ -1670,7 +1673,7 @@ function runStatusFull(target) {
|
|
|
1670
1673
|
|
|
1671
1674
|
printSection('OXE ▸ status --full');
|
|
1672
1675
|
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${target}${c ? reset : ''}`);
|
|
1673
|
-
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '
|
|
1676
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '— (sem sessão ativa)'}${c ? reset : ''}`);
|
|
1674
1677
|
console.log(` ${c ? green : ''}Workspace mode:${c ? reset : ''} ${report.workspaceMode || 'oxe_project'}`);
|
|
1675
1678
|
console.log(` ${c ? green : ''}Fase:${c ? reset : ''} ${report.phase || '—'}`);
|
|
1676
1679
|
|
|
@@ -2102,10 +2105,18 @@ function runDoctor(target, options = {}) {
|
|
|
2102
2105
|
.sort();
|
|
2103
2106
|
|
|
2104
2107
|
if (!wfTgt) {
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
)
|
|
2108
|
-
|
|
2108
|
+
// If the project has .oxe/runs/ it's a runtime-only project — workflows are optional
|
|
2109
|
+
const hasRuntimeRuns = fs.existsSync(path.join(target, '.oxe', 'runs'));
|
|
2110
|
+
if (hasRuntimeRuns) {
|
|
2111
|
+
console.log(`${green}OK${reset} Runtime-only project (.oxe/runs/ presente) — workflows não obrigatórios.`);
|
|
2112
|
+
console.log(`${c ? dim : ''} Para instalar workflows completos: ${cyan}npx oxe-cc@latest${reset}`);
|
|
2113
|
+
} else {
|
|
2114
|
+
console.log(
|
|
2115
|
+
`${yellow}AVISO${reset} Não há oxe/workflows/ nem .oxe/workflows/ neste projeto — rode ${cyan}npx oxe-cc@latest${reset} para instalar.`
|
|
2116
|
+
);
|
|
2117
|
+
process.exit(1);
|
|
2118
|
+
}
|
|
2119
|
+
return;
|
|
2109
2120
|
}
|
|
2110
2121
|
|
|
2111
2122
|
const actual = fs
|
|
@@ -2190,6 +2201,8 @@ function runDoctor(target, options = {}) {
|
|
|
2190
2201
|
}
|
|
2191
2202
|
}
|
|
2192
2203
|
|
|
2204
|
+
const report = oxeHealth.buildHealthReport(target);
|
|
2205
|
+
|
|
2193
2206
|
// IDE health gates
|
|
2194
2207
|
console.log('');
|
|
2195
2208
|
const ideChecks = [];
|
|
@@ -2204,25 +2217,44 @@ function runDoctor(target, options = {}) {
|
|
|
2204
2217
|
|
|
2205
2218
|
const claudeLocalDir = path.join(target, 'commands', 'oxe');
|
|
2206
2219
|
const claudeGlobalDir = path.join(require('os').homedir(), '.claude', 'commands');
|
|
2207
|
-
const
|
|
2208
|
-
|
|
2220
|
+
const claudeLocalReady = fs.existsSync(claudeLocalDir);
|
|
2221
|
+
const claudeGlobalReady = fs.existsSync(claudeGlobalDir);
|
|
2222
|
+
ideChecks.push({
|
|
2223
|
+
label: 'Claude Code',
|
|
2224
|
+
ready: claudeLocalReady || claudeGlobalReady,
|
|
2225
|
+
hint: claudeLocalReady
|
|
2226
|
+
? 'commands/oxe/'
|
|
2227
|
+
: claudeGlobalReady
|
|
2228
|
+
? '~/.claude/commands/ (global do utilizador)'
|
|
2229
|
+
: 'commands/oxe/ ou ~/.claude/commands/',
|
|
2230
|
+
status: claudeLocalReady ? 'local' : claudeGlobalReady ? 'global_only' : 'missing',
|
|
2231
|
+
});
|
|
2209
2232
|
|
|
2210
2233
|
const codexReport = oxeHealth.codexIntegrationReport(target);
|
|
2211
|
-
ideChecks.push({
|
|
2234
|
+
ideChecks.push({
|
|
2235
|
+
label: 'Codex',
|
|
2236
|
+
ready: codexReport.commandsReady,
|
|
2237
|
+
hint: codexReport.promptSource === 'workspace'
|
|
2238
|
+
? '.codex/prompts/oxe.md'
|
|
2239
|
+
: codexReport.promptSource === 'global'
|
|
2240
|
+
? '~/.codex/prompts/oxe.md (global do utilizador)'
|
|
2241
|
+
: '.codex/prompts/oxe.md ou ~/.codex/prompts/oxe.md',
|
|
2242
|
+
status: codexReport.promptSource,
|
|
2243
|
+
});
|
|
2212
2244
|
|
|
2213
2245
|
for (const ide of ideChecks) {
|
|
2214
2246
|
if (ide.ready) {
|
|
2215
|
-
|
|
2247
|
+
const suffix = ide.status === 'global_only' || ide.status === 'global'
|
|
2248
|
+
? ' — apenas global'
|
|
2249
|
+
: '';
|
|
2250
|
+
console.log(`${c ? green : ''}OK${c ? reset : ''} ${ide.label} pronto (${ide.hint})${suffix}`);
|
|
2216
2251
|
} else {
|
|
2217
2252
|
console.log(`${c ? dim : ''}Obs.:${c ? reset : ''} ${ide.label} não detectado — esperado ${ide.hint}`);
|
|
2218
2253
|
}
|
|
2219
2254
|
}
|
|
2220
2255
|
|
|
2221
2256
|
// Runtime compilation check
|
|
2222
|
-
|
|
2223
|
-
const runtimeDistPath = path.join(target, 'packages', 'runtime', 'dist-tests');
|
|
2224
|
-
const runtimeCompiled = fs.existsSync(runtimeCompiledPath) || fs.existsSync(runtimeDistPath);
|
|
2225
|
-
if (runtimeCompiled) {
|
|
2257
|
+
if (report.runtimeMode && report.runtimeMode.enterprise_available) {
|
|
2226
2258
|
console.log(`${c ? green : ''}OK${c ? reset : ''} Runtime compilado detectado — modo enterprise disponível`);
|
|
2227
2259
|
} else {
|
|
2228
2260
|
console.log(`${c ? dim : ''}Obs.:${c ? reset : ''} Runtime não compilado — operando em modo legado (sem perda de UX)`);
|
|
@@ -2230,7 +2262,7 @@ function runDoctor(target, options = {}) {
|
|
|
2230
2262
|
|
|
2231
2263
|
// Readiness gate summary
|
|
2232
2264
|
const stateFilePath = path.join(target, '.oxe', 'STATE.md');
|
|
2233
|
-
let readinessCmd = '/oxe
|
|
2265
|
+
let readinessCmd = '/oxe';
|
|
2234
2266
|
let readinessDesc = 'Nenhum STATE.md encontrado';
|
|
2235
2267
|
if (fs.existsSync(stateFilePath)) {
|
|
2236
2268
|
try {
|
|
@@ -2238,7 +2270,7 @@ function runDoctor(target, options = {}) {
|
|
|
2238
2270
|
const phaseMatch = stateContent.match(/fase[:\s]+([a-z_]+)/i) || stateContent.match(/phase[:\s]+([a-z_]+)/i) || stateContent.match(/status[:\s]+([a-z_]+)/i);
|
|
2239
2271
|
const phase = phaseMatch ? phaseMatch[1].toLowerCase() : 'init';
|
|
2240
2272
|
const phaseMap = {
|
|
2241
|
-
init: { cmd: '/oxe
|
|
2273
|
+
init: { cmd: '/oxe', desc: 'Pronto para /oxe' },
|
|
2242
2274
|
scan_complete: { cmd: '/oxe-spec', desc: 'Pronto para /oxe-spec' },
|
|
2243
2275
|
spec_complete: { cmd: '/oxe-plan', desc: 'Pronto para /oxe-plan' },
|
|
2244
2276
|
plan_complete: { cmd: '/oxe-execute', desc: 'Pronto para /oxe-execute' },
|
|
@@ -2260,7 +2292,6 @@ function runDoctor(target, options = {}) {
|
|
|
2260
2292
|
}
|
|
2261
2293
|
|
|
2262
2294
|
printOxeHealthDiagnostics(target, c);
|
|
2263
|
-
const report = oxeHealth.buildHealthReport(target);
|
|
2264
2295
|
const statusColor = report.healthStatus === 'healthy' ? green : report.healthStatus === 'warning' ? yellow : red;
|
|
2265
2296
|
console.log(`\n ${statusColor}Diagnóstico ${report.healthStatus}${reset}`);
|
|
2266
2297
|
if (report.healthStatus === 'broken') {
|
|
@@ -2275,7 +2306,7 @@ function runDoctor(target, options = {}) {
|
|
|
2275
2306
|
`Saúde lógica: ${report.healthStatus}`,
|
|
2276
2307
|
],
|
|
2277
2308
|
nextSteps: [
|
|
2278
|
-
{ desc: '
|
|
2309
|
+
{ desc: 'Entrar no fluxo OXE e deixar o router apontar o primeiro passo:', cmd: '/oxe' },
|
|
2279
2310
|
{ desc: 'Ver ajuda e ordem dos passos OXE:', cmd: '/oxe-help' },
|
|
2280
2311
|
{ desc: 'Reinstalar ou atualizar arquivos do OXE:', cmd: 'npx oxe-cc@latest --force' },
|
|
2281
2312
|
],
|
|
@@ -2496,7 +2527,7 @@ ${cyan}oxe-cc${reset} — instala workflows OXE (núcleo .oxe/ + integrações:
|
|
|
2496
2527
|
npx oxe-cc init-oxe [opções] [pasta-do-projeto]
|
|
2497
2528
|
npx oxe-cc context <build|inspect> [opções] [pasta-do-projeto]
|
|
2498
2529
|
npx oxe-cc dashboard [opções] [pasta-do-projeto]
|
|
2499
|
-
npx oxe-cc runtime <status|start|pause|resume|replay|compile|verify|project|ci|promote|recover|gates|agents> [opções] [pasta-do-projeto]
|
|
2530
|
+
npx oxe-cc runtime <status|start|pause|resume|replay|compile|verify|project|ci|promote|recover|gates|agents|execute> [opções] [pasta-do-projeto]
|
|
2500
2531
|
npx oxe-cc azure <status|doctor|auth|sync|find|servicebus|eventgrid|sql|operations> [opções] [pasta-do-projeto]
|
|
2501
2532
|
npx oxe-cc capabilities <list|install|remove|update> [opções] [id]
|
|
2502
2533
|
npx oxe-cc uninstall [opções] [pasta-do-projeto]
|
|
@@ -2695,7 +2726,19 @@ function runInstall(opts) {
|
|
|
2695
2726
|
assertNotWslWindowsNode();
|
|
2696
2727
|
const home = os.homedir();
|
|
2697
2728
|
const prevManifest = oxeManifest.loadFileManifest(home);
|
|
2698
|
-
|
|
2729
|
+
const backupScopeRoots = [target];
|
|
2730
|
+
const installAgentPaths = oxeAgentInstall.buildAgentInstallPaths(!opts.ideLocal, target);
|
|
2731
|
+
if (opts.cursor) backupScopeRoots.push(installCursorBase(opts));
|
|
2732
|
+
if (opts.copilot) backupScopeRoots.push(path.join(target, '.github'));
|
|
2733
|
+
if (opts.copilotCli || opts.allAgents) {
|
|
2734
|
+
backupScopeRoots.push(installClaudeBase(opts), installCopilotCliHome(opts));
|
|
2735
|
+
}
|
|
2736
|
+
if (opts.agentOpenCode || opts.allAgents) backupScopeRoots.push(...installAgentPaths.opencodeCommandDirs);
|
|
2737
|
+
if (opts.agentGemini || opts.allAgents) backupScopeRoots.push(installAgentPaths.geminiCommandsBase);
|
|
2738
|
+
if (opts.agentCodex || opts.allAgents) backupScopeRoots.push(installAgentPaths.codexPromptsDir, installAgentPaths.codexAgentsSkillsRoot);
|
|
2739
|
+
if (opts.agentWindsurf || opts.allAgents) backupScopeRoots.push(installAgentPaths.windsurfWorkflowsDir);
|
|
2740
|
+
if (opts.agentAntigravity || opts.allAgents) backupScopeRoots.push(installAgentPaths.antigravitySkillsRoot);
|
|
2741
|
+
oxeManifest.backupModifiedFromManifest(home, prevManifest, opts, { yellow, cyan, dim, reset }, { scopeRoots: backupScopeRoots });
|
|
2699
2742
|
|
|
2700
2743
|
printSection('OXE ▸ Instalação no projeto');
|
|
2701
2744
|
const c = useAnsiColors();
|
|
@@ -2735,7 +2778,7 @@ function runInstall(opts) {
|
|
|
2735
2778
|
}
|
|
2736
2779
|
|
|
2737
2780
|
const copyOpts = { dryRun: opts.dryRun, force: opts.force };
|
|
2738
|
-
const agentPaths =
|
|
2781
|
+
const agentPaths = installAgentPaths;
|
|
2739
2782
|
|
|
2740
2783
|
if (fullLayout) {
|
|
2741
2784
|
copyDir(path.join(PKG_ROOT, 'oxe'), path.join(target, 'oxe'), copyOpts, false);
|
|
@@ -3145,6 +3188,31 @@ function uninstallLocalIdeFromProject(u, removedPaths) {
|
|
|
3145
3188
|
}
|
|
3146
3189
|
}
|
|
3147
3190
|
|
|
3191
|
+
/**
|
|
3192
|
+
* @param {UninstallOpts} u
|
|
3193
|
+
*/
|
|
3194
|
+
function shouldAlsoRemoveLocalIdeArtifacts(u) {
|
|
3195
|
+
if (u.ideLocal) return true;
|
|
3196
|
+
if (u.noProject) return false;
|
|
3197
|
+
if (!(u.allAgents || anyGranularUninstallAgent(u) || u.cursor || u.copilot || u.copilotCli)) return false;
|
|
3198
|
+
const proj = path.resolve(u.dir);
|
|
3199
|
+
const localAgentPaths = oxeAgentInstall.buildAgentInstallPaths(false, proj);
|
|
3200
|
+
if (u.cursor && fs.existsSync(path.join(proj, '.cursor', 'commands'))) return true;
|
|
3201
|
+
if (u.copilot && (fs.existsSync(path.join(proj, '.github', 'prompts')) || fs.existsSync(path.join(proj, '.github', 'copilot-instructions.md')))) {
|
|
3202
|
+
return true;
|
|
3203
|
+
}
|
|
3204
|
+
if (u.copilotCli && (fs.existsSync(path.join(proj, '.claude', 'commands')) || fs.existsSync(path.join(proj, '.copilot', 'commands')))) {
|
|
3205
|
+
return true;
|
|
3206
|
+
}
|
|
3207
|
+
const selectedAll = u.allAgents || !anyGranularUninstallAgent(u);
|
|
3208
|
+
if ((selectedAll || u.agentOpenCode) && localAgentPaths.opencodeCommandDirs.some((dir) => fs.existsSync(dir))) return true;
|
|
3209
|
+
if ((selectedAll || u.agentGemini) && fs.existsSync(localAgentPaths.geminiCommandsBase)) return true;
|
|
3210
|
+
if ((selectedAll || u.agentCodex) && (fs.existsSync(localAgentPaths.codexPromptsDir) || fs.existsSync(localAgentPaths.codexAgentsSkillsRoot))) return true;
|
|
3211
|
+
if ((selectedAll || u.agentWindsurf) && fs.existsSync(localAgentPaths.windsurfWorkflowsDir)) return true;
|
|
3212
|
+
if ((selectedAll || u.agentAntigravity) && fs.existsSync(localAgentPaths.antigravitySkillsRoot)) return true;
|
|
3213
|
+
return false;
|
|
3214
|
+
}
|
|
3215
|
+
|
|
3148
3216
|
/**
|
|
3149
3217
|
* @param {string[]} argv
|
|
3150
3218
|
* @returns {UninstallOpts}
|
|
@@ -3376,6 +3444,7 @@ function runUninstall(u) {
|
|
|
3376
3444
|
if (u.dryRun) console.log(` ${c ? yellow : ''}(dry-run)${c ? reset : ''}`);
|
|
3377
3445
|
|
|
3378
3446
|
const removedPaths = [];
|
|
3447
|
+
const removeLocalIdeArtifacts = shouldAlsoRemoveLocalIdeArtifacts(u);
|
|
3379
3448
|
|
|
3380
3449
|
if (u.cursor) {
|
|
3381
3450
|
const base = cursorUserDir(ideOpts);
|
|
@@ -3531,7 +3600,7 @@ function runUninstall(u) {
|
|
|
3531
3600
|
}
|
|
3532
3601
|
}
|
|
3533
3602
|
|
|
3534
|
-
if (
|
|
3603
|
+
if (removeLocalIdeArtifacts) {
|
|
3535
3604
|
uninstallLocalIdeFromProject(u, removedPaths);
|
|
3536
3605
|
}
|
|
3537
3606
|
|
|
@@ -3976,6 +4045,13 @@ function parseRuntimeArgs(argv) {
|
|
|
3976
4045
|
fromEventId: '',
|
|
3977
4046
|
writeReport: false,
|
|
3978
4047
|
gateId: '',
|
|
4048
|
+
// execute flags
|
|
4049
|
+
provider: '',
|
|
4050
|
+
apiKey: '',
|
|
4051
|
+
model: '',
|
|
4052
|
+
baseUrl: '',
|
|
4053
|
+
maxTurns: null,
|
|
4054
|
+
recompile: false,
|
|
3979
4055
|
decision: '',
|
|
3980
4056
|
actor: '',
|
|
3981
4057
|
targetKind: '',
|
|
@@ -4013,6 +4089,12 @@ function parseRuntimeArgs(argv) {
|
|
|
4013
4089
|
else if (a === '--status' && argv[i + 1]) out.gateStatus = String(argv[++i]);
|
|
4014
4090
|
else if (a === '--scope' && argv[i + 1]) out.gateScope = String(argv[++i]);
|
|
4015
4091
|
else if (a === '--json') out.jsonOutput = true;
|
|
4092
|
+
else if (a === '--provider' && argv[i + 1]) out.provider = String(argv[++i]);
|
|
4093
|
+
else if (a === '--api-key' && argv[i + 1]) out.apiKey = String(argv[++i]);
|
|
4094
|
+
else if (a === '--model' && argv[i + 1]) out.model = String(argv[++i]);
|
|
4095
|
+
else if (a === '--base-url' && argv[i + 1]) out.baseUrl = String(argv[++i]);
|
|
4096
|
+
else if (a === '--max-turns' && argv[i + 1]) out.maxTurns = Number(argv[++i]);
|
|
4097
|
+
else if (a === '--recompile') out.recompile = true;
|
|
4016
4098
|
else if (!a.startsWith('-')) positionals.push(a);
|
|
4017
4099
|
else {
|
|
4018
4100
|
out.parseError = true;
|
|
@@ -4155,7 +4237,7 @@ function runContext(opts) {
|
|
|
4155
4237
|
return;
|
|
4156
4238
|
}
|
|
4157
4239
|
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
4158
|
-
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '
|
|
4240
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '— (sem sessão ativa)'}${c ? reset : ''}`);
|
|
4159
4241
|
console.log(` ${c ? green : ''}Workflow:${c ? reset : ''} ${selectedWorkflow}`);
|
|
4160
4242
|
console.log(` ${c ? green : ''}Tier:${c ? reset : ''} ${pack.context_tier}`);
|
|
4161
4243
|
console.log(` ${c ? green : ''}Quality:${c ? reset : ''} ${pack.context_quality.score} (${pack.context_quality.status})`);
|
|
@@ -4222,7 +4304,7 @@ function runContext(opts) {
|
|
|
4222
4304
|
return;
|
|
4223
4305
|
}
|
|
4224
4306
|
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
4225
|
-
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '
|
|
4307
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '— (sem sessão ativa)'}${c ? reset : ''}`);
|
|
4226
4308
|
console.log(` ${c ? green : ''}Packs:${c ? reset : ''} ${packs.length}`);
|
|
4227
4309
|
for (const pack of packs) {
|
|
4228
4310
|
const qualityFlag = (pack.context_quality.score < 30 || pack.context_quality.status === 'critical') ? ` ${yellow}[CRÍTICO]${reset}` : '';
|
|
@@ -4287,7 +4369,7 @@ async function runRuntime(opts) {
|
|
|
4287
4369
|
const activeSession = opts.activeSession || oxeHealth.parseActiveSession(stateText) || null;
|
|
4288
4370
|
const p = oxeOperational.operationalPaths(opts.dir, activeSession);
|
|
4289
4371
|
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
4290
|
-
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '
|
|
4372
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '— (sem sessão ativa)'}${c ? reset : ''}`);
|
|
4291
4373
|
|
|
4292
4374
|
if (opts.action === 'status') {
|
|
4293
4375
|
const current = oxeOperational.readRunState(opts.dir, activeSession);
|
|
@@ -4350,13 +4432,28 @@ async function runRuntime(opts) {
|
|
|
4350
4432
|
console.log(` ${c ? green : ''}Runtime:${c ? reset : ''} ${runtimeMode.runtime_mode || 'legacy'} · fallback=${runtimeMode.fallback_mode || 'none'}`);
|
|
4351
4433
|
}
|
|
4352
4434
|
if (report.policyCoverage) {
|
|
4353
|
-
|
|
4435
|
+
const pctCov = report.policyCoverage.coveragePercent;
|
|
4436
|
+
const policyLabel = pctCov === 0
|
|
4437
|
+
? `${c ? dim : ''}não configurada (opcional)${c ? reset : ''}`
|
|
4438
|
+
: `${pctCov}% · uncovered=${report.policyCoverage.uncoveredMutations}`;
|
|
4439
|
+
console.log(` ${c ? green : ''}Policy coverage:${c ? reset : ''} ${policyLabel}`);
|
|
4354
4440
|
}
|
|
4355
4441
|
if (report.promotionReadiness) {
|
|
4356
|
-
|
|
4442
|
+
const promoStatus = report.promotionReadiness.status;
|
|
4443
|
+
const blockers = Array.isArray(report.promotionReadiness.blockers) ? report.promotionReadiness.blockers : [];
|
|
4444
|
+
// Only show blockers that are not solely due to unconfigured policy
|
|
4445
|
+
const meaningfulBlockers = blockers.filter(b => b !== 'policy_uncovered_mutations' || report.policyCoverage.coveragePercent > 0);
|
|
4446
|
+
const promoLabel = promoStatus === 'blocked' && meaningfulBlockers.length === 0
|
|
4447
|
+
? `${c ? dim : ''}n/a (execução não concluída)${c ? reset : ''}`
|
|
4448
|
+
: `${promoStatus}${meaningfulBlockers.length ? ` · ${meaningfulBlockers.join(', ')}` : ''}`;
|
|
4449
|
+
console.log(` ${c ? green : ''}Promotion readiness:${c ? reset : ''} ${promoLabel}`);
|
|
4357
4450
|
}
|
|
4358
4451
|
if (report.recoveryState) {
|
|
4359
|
-
|
|
4452
|
+
const recIssues = Array.isArray(report.recoveryState.issues) ? report.recoveryState.issues.length : 0;
|
|
4453
|
+
const recLabel = recIssues === 0
|
|
4454
|
+
? `${report.recoveryState.status} · ok`
|
|
4455
|
+
: `${report.recoveryState.status} · issues=${recIssues} — rode ${c ? cyan : ''}runtime status --verbose${c ? reset : ''} para detalhes`;
|
|
4456
|
+
console.log(` ${c ? green : ''}Recovery:${c ? reset : ''} ${recLabel} · recoveries=${report.recoveryState.recoverCount ?? 0}`);
|
|
4360
4457
|
}
|
|
4361
4458
|
if (multiAgent) {
|
|
4362
4459
|
console.log(` ${c ? green : ''}Multi-agent:${c ? reset : ''} ${multiAgent.enabled ? (multiAgent.mode || 'active') : 'disabled'} · agentes=${Array.isArray(multiAgent.agents) ? multiAgent.agents.length : 0} · ownership=${Array.isArray(multiAgent.ownership) ? multiAgent.ownership.length : 0}`);
|
|
@@ -4526,6 +4623,17 @@ async function runRuntime(opts) {
|
|
|
4526
4623
|
|
|
4527
4624
|
if (opts.action === 'compile') {
|
|
4528
4625
|
try {
|
|
4626
|
+
// --recompile: delete existing run state files so a fresh run_id is generated
|
|
4627
|
+
if (opts.recompile) {
|
|
4628
|
+
const runsDir = path.join(opts.dir, '.oxe', 'runs');
|
|
4629
|
+
if (require('fs').existsSync(runsDir)) {
|
|
4630
|
+
require('fs').readdirSync(runsDir).forEach(f => {
|
|
4631
|
+
if (f.endsWith('.json') && !f.includes('ci-results')) {
|
|
4632
|
+
require('fs').unlinkSync(path.join(runsDir, f));
|
|
4633
|
+
}
|
|
4634
|
+
});
|
|
4635
|
+
}
|
|
4636
|
+
}
|
|
4529
4637
|
const compiled = oxeOperational.compileExecutionGraphFromArtifacts(opts.dir, activeSession);
|
|
4530
4638
|
const suite = oxeOperational.compileVerificationSuiteFromArtifacts(opts.dir, activeSession, {
|
|
4531
4639
|
runState: compiled.run,
|
|
@@ -4535,7 +4643,33 @@ async function runRuntime(opts) {
|
|
|
4535
4643
|
console.log(` ${c ? green : ''}Graph:${c ? reset : ''} ${compiled.graph.metadata.node_count} nó(s) · ${compiled.graph.metadata.wave_count} onda(s)`);
|
|
4536
4644
|
console.log(` ${c ? green : ''}Checks:${c ? reset : ''} ${Array.isArray(suite.suite.checks) ? suite.suite.checks.length : 0}`);
|
|
4537
4645
|
if (compiled.validationErrors.length) {
|
|
4538
|
-
|
|
4646
|
+
// Bucket 1: static-analysis hints (HINT(...))
|
|
4647
|
+
const hints = compiled.validationErrors.filter(e => e.startsWith('HINT('));
|
|
4648
|
+
// Bucket 2: mutation-scope overlaps (info-only in single-agent)
|
|
4649
|
+
const mutationWarns = compiled.validationErrors.filter(e => e.includes('mutate the same paths in parallel'));
|
|
4650
|
+
// Bucket 3: structural errors (cycles, missing deps)
|
|
4651
|
+
const blocking = compiled.validationErrors.filter(e =>
|
|
4652
|
+
!e.startsWith('HINT(') && !e.includes('mutate the same paths in parallel')
|
|
4653
|
+
);
|
|
4654
|
+
if (blocking.length) {
|
|
4655
|
+
console.log(` ${yellow}Validation (erros):${reset} ${blocking.join(' | ')}`);
|
|
4656
|
+
}
|
|
4657
|
+
if (mutationWarns.length) {
|
|
4658
|
+
console.log(` ${c ? dim : ''}Info (single-agent — tarefas na mesma onda são sequenciais, sobreposição de arquivos não bloqueia):${c ? reset : ''}`);
|
|
4659
|
+
mutationWarns.forEach(w => console.log(` ${c ? dim : ''} · ${w}${c ? reset : ''}`));
|
|
4660
|
+
}
|
|
4661
|
+
if (hints.length) {
|
|
4662
|
+
console.log(` ${yellow}Dicas de spec/plan (detectadas em tempo de compilação):${reset}`);
|
|
4663
|
+
hints.forEach(h => {
|
|
4664
|
+
// Format: HINT(type): message
|
|
4665
|
+
const match = h.match(/^HINT\(([^)]+)\):\s*(.*)/s);
|
|
4666
|
+
if (match) {
|
|
4667
|
+
console.log(` ${yellow}⚑${reset} [${match[1]}] ${match[2]}`);
|
|
4668
|
+
} else {
|
|
4669
|
+
console.log(` ${yellow}⚑${reset} ${h}`);
|
|
4670
|
+
}
|
|
4671
|
+
});
|
|
4672
|
+
}
|
|
4539
4673
|
}
|
|
4540
4674
|
console.log(` ${c ? green : ''}Arquivo:${c ? reset : ''} ${path.join(p.runsDir, `${compiled.run.run_id}.json`)}`);
|
|
4541
4675
|
return;
|
|
@@ -4585,7 +4719,7 @@ async function runRuntime(opts) {
|
|
|
4585
4719
|
console.log(` ${c ? green : ''}✓${c ? reset : ''} Projeções geradas a partir do estado canônico.`);
|
|
4586
4720
|
console.log(` ${c ? green : ''}Run:${c ? reset : ''} ${projected.run.run_id}`);
|
|
4587
4721
|
console.log(` ${c ? green : ''}STATE:${c ? reset : ''} ${projected.paths.state}`);
|
|
4588
|
-
console.log(` ${c ? green : ''}PLAN:${c ? reset : ''} ${projected.paths.plan}`);
|
|
4722
|
+
console.log(` ${c ? green : ''}PLAN-STATUS:${c ? reset : ''} ${projected.paths.plan.replace(/PLAN\.md$/, 'PLAN-STATUS.md')}`);
|
|
4589
4723
|
console.log(` ${c ? green : ''}VERIFY:${c ? reset : ''} ${projected.paths.verify}`);
|
|
4590
4724
|
console.log(` ${c ? green : ''}RUN-SUMMARY:${c ? reset : ''} ${projected.paths.runSummary}`);
|
|
4591
4725
|
console.log(` ${c ? green : ''}PR-SUMMARY:${c ? reset : ''} ${projected.paths.prSummary}`);
|
|
@@ -4598,9 +4732,37 @@ async function runRuntime(opts) {
|
|
|
4598
4732
|
|
|
4599
4733
|
if (opts.action === 'execute') {
|
|
4600
4734
|
try {
|
|
4735
|
+
// Resolve provider: flag > env vars
|
|
4736
|
+
const apiKey = opts.apiKey || process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY || '';
|
|
4737
|
+
const model = opts.model || process.env.OXE_MODEL || 'claude-sonnet-4-6';
|
|
4738
|
+
const baseUrl = opts.baseUrl || process.env.OXE_BASE_URL || 'https://api.anthropic.com/v1';
|
|
4739
|
+
if (!apiKey) {
|
|
4740
|
+
console.error(`${red}Erro: executor LLM não configurado.${reset}`);
|
|
4741
|
+
console.error(` Use: --api-key <chave> ou exporte ANTHROPIC_API_KEY`);
|
|
4742
|
+
console.error(` Exemplo: oxe-cc runtime execute --api-key $ANTHROPIC_API_KEY --model claude-sonnet-4-6`);
|
|
4743
|
+
process.exit(1);
|
|
4744
|
+
}
|
|
4745
|
+
const providerConfig = { baseUrl, apiKey, model, maxTurns: opts.maxTurns || 10 };
|
|
4601
4746
|
const result = await oxeOperational.runRuntimeExecute(opts.dir, activeSession, {
|
|
4602
4747
|
runId: opts.runId || undefined,
|
|
4603
4748
|
heartbeatTimeoutMs: opts.heartbeatTimeoutMs || undefined,
|
|
4749
|
+
providerConfig,
|
|
4750
|
+
onProgress: (event) => {
|
|
4751
|
+
if (event.type === 'turn_start') {
|
|
4752
|
+
const turn = event.detail?.turn ?? 0;
|
|
4753
|
+
if (turn === 0) process.stdout.write(` → ${event.nodeId} `);
|
|
4754
|
+
} else if (event.type === 'tool_call') {
|
|
4755
|
+
process.stdout.write('.');
|
|
4756
|
+
} else if (event.type === 'VerificationStarted') {
|
|
4757
|
+
process.stdout.write(' [verify]');
|
|
4758
|
+
} else if (event.type === 'WorkItemCompleted') {
|
|
4759
|
+
process.stdout.write(` ✓\n`);
|
|
4760
|
+
} else if (event.type === 'WorkItemBlocked') {
|
|
4761
|
+
process.stdout.write(` ✗\n`);
|
|
4762
|
+
} else if (event.type === 'RetryScheduled') {
|
|
4763
|
+
process.stdout.write(` ↺ retry ${event.payload?.next_attempt}\n → ${event.work_item_id} `);
|
|
4764
|
+
}
|
|
4765
|
+
},
|
|
4604
4766
|
});
|
|
4605
4767
|
if (opts.jsonOutput) {
|
|
4606
4768
|
console.log(JSON.stringify(result, null, 2));
|
|
@@ -4794,7 +4956,7 @@ function runAzure(opts) {
|
|
|
4794
4956
|
};
|
|
4795
4957
|
|
|
4796
4958
|
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
4797
|
-
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '
|
|
4959
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '— (sem sessão ativa)'}${c ? reset : ''}`);
|
|
4798
4960
|
|
|
4799
4961
|
try {
|
|
4800
4962
|
if (opts.scope === 'status') {
|
|
@@ -5118,7 +5280,7 @@ function runPlanVisual(opts) {
|
|
|
5118
5280
|
const planPath = sp.plan || oxeHealth.oxePaths(opts.dir).plan;
|
|
5119
5281
|
|
|
5120
5282
|
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
5121
|
-
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '
|
|
5283
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '— (sem sessão ativa)'}${c ? reset : ''}`);
|
|
5122
5284
|
|
|
5123
5285
|
if (!fs.existsSync(planPath)) {
|
|
5124
5286
|
console.log(`\n ${yellow}PLAN.md não encontrado em ${planPath}${reset}`);
|
|
@@ -5172,7 +5334,7 @@ function runVerifyMatrix(opts) {
|
|
|
5172
5334
|
const verifyPath = sp.verify || oxeHealth.oxePaths(opts.dir).verify;
|
|
5173
5335
|
|
|
5174
5336
|
console.log(` ${c ? green : ''}Projeto:${c ? reset : ''} ${c ? cyan : ''}${opts.dir}${c ? reset : ''}`);
|
|
5175
|
-
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '
|
|
5337
|
+
console.log(` ${c ? green : ''}Sessão:${c ? reset : ''} ${c ? cyan : ''}${activeSession || '— (sem sessão ativa)'}${c ? reset : ''}`);
|
|
5176
5338
|
|
|
5177
5339
|
const specMd = fs.existsSync(specPath) ? fs.readFileSync(specPath, 'utf8') : '';
|
|
5178
5340
|
const planMd = fs.existsSync(planPath) ? fs.readFileSync(planPath, 'utf8') : '';
|
|
@@ -5556,7 +5718,8 @@ async function main() {
|
|
|
5556
5718
|
nextSteps: [
|
|
5557
5719
|
{ desc: 'Validar o projeto:', cmd: 'npx oxe-cc doctor' },
|
|
5558
5720
|
{ desc: 'Instalar integrações IDE/CLI (se ainda não fez):', cmd: 'npx oxe-cc@latest' },
|
|
5559
|
-
{ desc: '
|
|
5721
|
+
{ desc: 'Fluxo runtime — criar spec (no agente):', cmd: '/oxe-spec → /oxe-plan → oxe-cc runtime compile → oxe-cc runtime execute' },
|
|
5722
|
+
{ desc: 'Começar pelo scan do codebase (agente):', cmd: '/oxe-scan' },
|
|
5560
5723
|
],
|
|
5561
5724
|
dryRun: opts.dryRun,
|
|
5562
5725
|
});
|
|
@@ -26,7 +26,7 @@ function compile(plan, spec, options = {}) {
|
|
|
26
26
|
mutation_scope: task.files,
|
|
27
27
|
actions: buildActions(task),
|
|
28
28
|
verify: {
|
|
29
|
-
must_pass: task.verifyCommand ? ['tests'] : [],
|
|
29
|
+
must_pass: task.verifyCommand ? (task.aceite.length > 0 ? task.aceite : ['tests']) : [],
|
|
30
30
|
acceptance_refs: task.aceite,
|
|
31
31
|
command: task.verifyCommand,
|
|
32
32
|
},
|
|
@@ -37,5 +37,9 @@ function selectToolsForActions(actions) {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
+
// finish_task is always available so the LLM can signal authoritative completion
|
|
41
|
+
if (!seen.has('finish_task') && built_in_tools_1.BUILT_IN_TOOLS.finish_task) {
|
|
42
|
+
result.push(built_in_tools_1.BUILT_IN_TOOLS.finish_task.schema);
|
|
43
|
+
}
|
|
40
44
|
return result;
|
|
41
45
|
}
|
|
@@ -255,6 +255,32 @@ function runShell(command, cwd, timeoutMs) {
|
|
|
255
255
|
proc.on('error', (err) => { clearTimeout(timer); resolve(`[error] ${err}`); });
|
|
256
256
|
});
|
|
257
257
|
}
|
|
258
|
+
// ─── finish_task ──────────────────────────────────────────────────────────────
|
|
259
|
+
const finishTask = {
|
|
260
|
+
idempotent: true,
|
|
261
|
+
schema: {
|
|
262
|
+
type: 'function',
|
|
263
|
+
function: {
|
|
264
|
+
name: 'finish_task',
|
|
265
|
+
description: 'Signal that the task is complete. Call this when ALL required actions have been performed.',
|
|
266
|
+
parameters: {
|
|
267
|
+
type: 'object',
|
|
268
|
+
properties: {
|
|
269
|
+
summary: { type: 'string', description: 'Summary of what was accomplished' },
|
|
270
|
+
evidence_paths: { type: 'array', items: { type: 'string' }, description: 'Paths to files created or modified' },
|
|
271
|
+
},
|
|
272
|
+
required: ['summary'],
|
|
273
|
+
},
|
|
274
|
+
},
|
|
275
|
+
},
|
|
276
|
+
async execute(args, _cwd) {
|
|
277
|
+
return JSON.stringify({
|
|
278
|
+
__finish_task__: true,
|
|
279
|
+
summary: String(args.summary || ''),
|
|
280
|
+
evidence_paths: Array.isArray(args.evidence_paths) ? args.evidence_paths : [],
|
|
281
|
+
});
|
|
282
|
+
},
|
|
283
|
+
};
|
|
258
284
|
// ─── Registry ─────────────────────────────────────────────────────────────────
|
|
259
285
|
exports.BUILT_IN_TOOLS = {
|
|
260
286
|
read_file: readFile,
|
|
@@ -263,5 +289,6 @@ exports.BUILT_IN_TOOLS = {
|
|
|
263
289
|
glob,
|
|
264
290
|
grep,
|
|
265
291
|
run_command: runCommand,
|
|
292
|
+
finish_task: finishTask,
|
|
266
293
|
};
|
|
267
294
|
exports.ALL_BUILT_IN_SCHEMAS = Object.values(exports.BUILT_IN_TOOLS).map((t) => t.schema);
|
|
@@ -18,12 +18,15 @@ export interface LlmExecutorEvent {
|
|
|
18
18
|
attempt: number;
|
|
19
19
|
detail?: Record<string, unknown>;
|
|
20
20
|
}
|
|
21
|
+
export interface LlmExecuteOptions {
|
|
22
|
+
previousError?: string | null;
|
|
23
|
+
}
|
|
21
24
|
export declare class LlmTaskExecutor implements TaskExecutor {
|
|
22
25
|
private readonly provider;
|
|
23
26
|
private readonly registry?;
|
|
24
27
|
private readonly onProgress?;
|
|
25
28
|
constructor(provider: LlmProviderConfig, registry?: PluginRegistry | undefined, onProgress?: ((event: LlmExecutorEvent) => void) | undefined);
|
|
26
|
-
execute(node: GraphNode, lease: WorkspaceLease, runId: string, attempt: number): Promise<TaskResult>;
|
|
29
|
+
execute(node: GraphNode, lease: WorkspaceLease, runId: string, attempt: number, options?: LlmExecuteOptions): Promise<TaskResult>;
|
|
27
30
|
private invokeToolCall;
|
|
28
31
|
private emit;
|
|
29
32
|
}
|
|
@@ -6,15 +6,15 @@ const built_in_tools_1 = require("./built-in-tools");
|
|
|
6
6
|
const action_tool_map_1 = require("./action-tool-map");
|
|
7
7
|
const node_prompt_builder_1 = require("./node-prompt-builder");
|
|
8
8
|
const DEFAULT_SYSTEM_PROMPT = 'You are a precise software engineering agent. Use the tools provided to complete the task. ' +
|
|
9
|
-
'When
|
|
9
|
+
'When all actions are done, call finish_task with a summary of what was accomplished.';
|
|
10
10
|
class LlmTaskExecutor {
|
|
11
11
|
constructor(provider, registry, onProgress) {
|
|
12
12
|
this.provider = provider;
|
|
13
13
|
this.registry = registry;
|
|
14
14
|
this.onProgress = onProgress;
|
|
15
15
|
}
|
|
16
|
-
async execute(node, lease, runId, attempt) {
|
|
17
|
-
const prompt = (0, node_prompt_builder_1.buildNodePrompt)(node, lease, runId, attempt);
|
|
16
|
+
async execute(node, lease, runId, attempt, options = {}) {
|
|
17
|
+
const prompt = (0, node_prompt_builder_1.buildNodePrompt)(node, lease, runId, attempt, { previousError: options.previousError ?? null });
|
|
18
18
|
const tools = (0, action_tool_map_1.selectToolsForActions)(node.actions);
|
|
19
19
|
const cwd = lease.root_path;
|
|
20
20
|
const maxTurns = this.provider.maxTurns ?? 10;
|
|
@@ -25,7 +25,10 @@ class LlmTaskExecutor {
|
|
|
25
25
|
];
|
|
26
26
|
let finalOutput = '';
|
|
27
27
|
const evidencePaths = [];
|
|
28
|
-
|
|
28
|
+
let completedByFinishTask = false;
|
|
29
|
+
let finishTaskSummary = '';
|
|
30
|
+
let turn = 0;
|
|
31
|
+
for (; turn < maxTurns; turn++) {
|
|
29
32
|
this.emit({ type: 'turn_start', nodeId: node.id, attempt, detail: { turn } });
|
|
30
33
|
let response;
|
|
31
34
|
try {
|
|
@@ -60,12 +63,45 @@ class LlmTaskExecutor {
|
|
|
60
63
|
serialResults.push(await this.invokeToolCall(tc, cwd, node, evidencePaths));
|
|
61
64
|
}
|
|
62
65
|
messages.push(...concurrentResults, ...serialResults);
|
|
66
|
+
// Detect finish_task call — authoritative completion signal
|
|
67
|
+
const allResults = [...concurrentResults, ...serialResults];
|
|
68
|
+
const finishResult = allResults.find((r) => r.name === 'finish_task');
|
|
69
|
+
if (finishResult) {
|
|
70
|
+
try {
|
|
71
|
+
const parsed = JSON.parse(finishResult.content);
|
|
72
|
+
if (parsed.__finish_task__) {
|
|
73
|
+
completedByFinishTask = true;
|
|
74
|
+
finishTaskSummary = parsed.summary || '';
|
|
75
|
+
if (Array.isArray(parsed.evidence_paths)) {
|
|
76
|
+
evidencePaths.push(...parsed.evidence_paths.filter((p) => typeof p === 'string'));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
catch { /* ignore parse errors */ }
|
|
81
|
+
if (completedByFinishTask)
|
|
82
|
+
break;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
const completedBy = completedByFinishTask
|
|
86
|
+
? 'finish_task'
|
|
87
|
+
: turn < maxTurns
|
|
88
|
+
? 'no_tool_call'
|
|
89
|
+
: 'turn_limit_exhausted';
|
|
90
|
+
if (completedBy === 'turn_limit_exhausted') {
|
|
91
|
+
return {
|
|
92
|
+
success: false,
|
|
93
|
+
failure_class: 'llm',
|
|
94
|
+
evidence: evidencePaths,
|
|
95
|
+
output: finalOutput || `Task exhausted ${maxTurns} turns without calling finish_task`,
|
|
96
|
+
completed_by: completedBy,
|
|
97
|
+
};
|
|
63
98
|
}
|
|
64
99
|
return {
|
|
65
100
|
success: true,
|
|
66
101
|
failure_class: null,
|
|
67
102
|
evidence: evidencePaths,
|
|
68
|
-
output: finalOutput,
|
|
103
|
+
output: completedByFinishTask ? (finishTaskSummary || finalOutput) : finalOutput,
|
|
104
|
+
completed_by: completedBy,
|
|
69
105
|
};
|
|
70
106
|
}
|
|
71
107
|
async invokeToolCall(tc, cwd, node, evidencePaths) {
|
|
@@ -1,3 +1,6 @@
|
|
|
1
1
|
import type { GraphNode } from '../compiler/graph-compiler';
|
|
2
2
|
import type { WorkspaceLease } from '../models/workspace';
|
|
3
|
-
export
|
|
3
|
+
export interface NodePromptOptions {
|
|
4
|
+
previousError?: string | null;
|
|
5
|
+
}
|
|
6
|
+
export declare function buildNodePrompt(node: GraphNode, lease: WorkspaceLease, runId: string, attempt: number, options?: NodePromptOptions): string;
|