@oooooooooo/ch0de 1.9.63 → 1.9.65
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/chode.js +55 -7
- package/debtaur-orchestration.js +1 -1
- package/package.json +1 -1
package/chode.js
CHANGED
|
@@ -1036,6 +1036,21 @@ function say(msg, color) {
|
|
|
1036
1036
|
// so `ai "..." > file` and pipes compose cleanly (Unix-correct).
|
|
1037
1037
|
process.stderr.write(terminalUI.colorEnabled(process.stderr) ? (palette[color] || palette.white) + msg + '\x1b[0m' : msg);
|
|
1038
1038
|
}
|
|
1039
|
+
|
|
1040
|
+
// Structured JSONL logger: one line per event, appended to
|
|
1041
|
+
// .chode/logs/build.log. Safe to call from any thread/turn; never throws.
|
|
1042
|
+
function chodeLog(level, category, msg, extra) {
|
|
1043
|
+
try {
|
|
1044
|
+
var logDir = path.join(CONFIG_DIR, 'logs');
|
|
1045
|
+
fs.mkdirSync(logDir, { recursive: true });
|
|
1046
|
+
var entry = { t: new Date().toISOString(), l: level, c: category, m: String(msg || '') };
|
|
1047
|
+
if (extra) entry.x = extra;
|
|
1048
|
+
fs.appendFileSync(path.join(logDir, 'build.log'), JSON.stringify(entry) + '\n', 'utf8');
|
|
1049
|
+
} catch (e) { /* never fail the build because of logging */ }
|
|
1050
|
+
}
|
|
1051
|
+
function logBuild(msg, extra) { chodeLog('info', 'build', msg, extra); }
|
|
1052
|
+
function logError(msg, extra) { chodeLog('error', 'build', msg, extra); }
|
|
1053
|
+
function logStall(msg, extra) { chodeLog('warn', 'stall', msg, extra); }
|
|
1039
1054
|
function ok(msg) { say(' ✓ ' + msg + '\n', 'green'); }
|
|
1040
1055
|
function warn(msg) { say(' ! ' + msg + '\n', 'yellow'); }
|
|
1041
1056
|
function fail(msg) { say(' ✗ ' + msg + '\n', 'red'); }
|
|
@@ -3520,6 +3535,7 @@ async function defaultAgentArtifactBuild(brief, failedChecks, ctx) {
|
|
|
3520
3535
|
async function runAgentLoop(prompt, sessionId, force, callOptions, aiCall, onTool, onAsk, taskContext) {
|
|
3521
3536
|
taskContext = Object.assign({},taskContext||{});
|
|
3522
3537
|
taskContext.onTool=onTool||taskContext.onTool;taskContext.onAsk=onAsk||taskContext.onAsk;
|
|
3538
|
+
logBuild('runAgentLoop start', { sessionId, force, freeOnly: !!callOptions.freeOnly, maxCalls: callOptions.maxCalls, mode: callOptions.mode });
|
|
3523
3539
|
var budgetModule=require('./agent-budget');
|
|
3524
3540
|
var runBudget=callOptions&&callOptions._runBudget||budgetModule.create(callOptions&&callOptions.maxModelCalls);
|
|
3525
3541
|
callOptions=Object.assign({maxRetries:2,timeoutMs:90000,noRateWait:true,nativeTools:true},callOptions,{_runBudget:runBudget});
|
|
@@ -3572,6 +3588,7 @@ async function runAgentLoop(prompt, sessionId, force, callOptions, aiCall, onToo
|
|
|
3572
3588
|
if((v2&&classification.mutating)||callOptions.repairChecks||callOptions.scopeTargets)callOptions.isolateContext=true;
|
|
3573
3589
|
var completionStatus = (v2 && classification.mutating) ? 'PENDING' : 'NOT_REQUIRED';
|
|
3574
3590
|
var planSkipped = true;
|
|
3591
|
+
var planAttempted = !!callOptions._planAttempted;
|
|
3575
3592
|
var verifySkipped = true;
|
|
3576
3593
|
var verifyVerdict = null;
|
|
3577
3594
|
var verifyStatic = false;
|
|
@@ -3606,11 +3623,11 @@ async function runAgentLoop(prompt, sessionId, force, callOptions, aiCall, onToo
|
|
|
3606
3623
|
}
|
|
3607
3624
|
}
|
|
3608
3625
|
async function tryLocalCertification(reason){if(!v2||!callOptions.acceptanceTest)return false;var mutated=!!((actions.writes||[]).length||(actions.edits||[]).length||(actions.execs||[]).length||(actions.toolsInstalled||[]).length);if(!mutated)return false;var gateOptions=Object.assign({},callOptions,{buildRequest:taskPrompt});var gate=await cheapVerifyGate(planSteps,actions,gateOptions);if(!gate||!gate.pass)return false;if(callOptions._debtaur&&!(await callOptions._debtaur.confirm()).allowed)return false;var checkedFiles=taskMutationFiles((actions.writes||[]).concat(actions.edits||[]));last=last||{};last.provider=last.provider||'local';last.result='The saved work passed the local verification checks.'+(checkedFiles.length?'\nFiles: '+checkedFiles.slice(0,12).join(', ')+'.':'')+'\n\nVerification: all deterministic checks passed locally. No independent AI review was completed.';if(gateOptions.browserVerification)last.browserVerification=gateOptions.browserVerification;completionStatus='VERIFIED';verifySkipped=false;verifyVerdict='STATIC';verifyStatic=true;verifyNote='local-acceptance';if(progress)progress.clear();return true;}
|
|
3609
|
-
async function capacityPause(){if(callOptions.freeOnly){var capacityRetryAt=nextProviderRetryAt(true,force);if(capacityRetryAt){if(await tryLocalCertification('free providers were cooling down'))return Object.assign({res:last,actions:actions,steps:steps},v2meta());stepFailure='provider capacity';completionStatus='PARTIAL';saveProgress();return Object.assign({res:{result:'PARTIAL: free providers are cooling down. Work is preserved; resume when capacity returns.',provider:'capacity',partial:true,retryAt:capacityRetryAt},actions:actions,steps:steps,partial:true,retryAt:capacityRetryAt},v2meta());}}return null;}
|
|
3626
|
+
async function capacityPause(){if(callOptions.freeOnly){var capacityRetryAt=nextProviderRetryAt(true,force);if(capacityRetryAt){logStall('Providers cooling down; capacity retry at ' + new Date(capacityRetryAt).toISOString(), { retryAt: capacityRetryAt, waitMs: Math.max(0, capacityRetryAt - Date.now()) });if(await tryLocalCertification('free providers were cooling down'))return Object.assign({res:last,actions:actions,steps:steps},v2meta());stepFailure='provider capacity';completionStatus='PARTIAL';saveProgress();return Object.assign({res:{result:'PARTIAL: free providers are cooling down. Work is preserved; resume when capacity returns.',provider:'capacity',partial:true,retryAt:capacityRetryAt},actions:actions,steps:steps,partial:true,retryAt:capacityRetryAt},v2meta());}}return null;}
|
|
3610
3627
|
var initialCapacityPause=await capacityPause();if(initialCapacityPause)return initialCapacityPause;
|
|
3611
3628
|
if (classification.mutating) narrate('start', v2 && classification.multi ? 'Planning how to complete this request.' : 'Looking at the workspace before making changes.');
|
|
3612
3629
|
var directFreeEdit=callOptions.freeOnly&&effTargets.length===1&&!require('./build-quality').website(taskPrompt)&&!classification.verbs.some(verb=>['clone','migrate'].includes(verb));
|
|
3613
|
-
if (v2 && classification.multi && classification.mutating && !directFreeEdit && (!savedProgress || !planSteps.length)) {
|
|
3630
|
+
if (v2 && classification.multi && classification.mutating && !directFreeEdit && (!savedProgress || !planSteps.length) && !planAttempted) {
|
|
3614
3631
|
try {
|
|
3615
3632
|
narrate('plan', 'Working out the steps and the files each one needs.');
|
|
3616
3633
|
var planRes = await runPlanStage(taskPrompt, aiCall, sessionId, callOptions, force);
|
|
@@ -3629,6 +3646,10 @@ async function runAgentLoop(prompt, sessionId, force, callOptions, aiCall, onToo
|
|
|
3629
3646
|
collected.targets.forEach(function (f) { if ((!callOptions.acceptanceTest || normRel(f) !== normRel(callOptions.acceptanceTest)) && effTargets.indexOf(f) === -1) effTargets.push(f); });
|
|
3630
3647
|
collected.overwrites.forEach(function (f) { if ((!callOptions.acceptanceTest || normRel(f) !== normRel(callOptions.acceptanceTest)) && effOverwrites.indexOf(f) === -1) effOverwrites.push(f); });
|
|
3631
3648
|
}
|
|
3649
|
+
planAttempted = true; callOptions._planAttempted = true;
|
|
3650
|
+
if (!planRes || !planRes.steps || !planRes.steps.length) {
|
|
3651
|
+
logBuild('Planning stage produced no steps; proceeding without plan', { sessionId, failed: !!planRes && planRes.failed });
|
|
3652
|
+
}
|
|
3632
3653
|
} catch (e) {}
|
|
3633
3654
|
}
|
|
3634
3655
|
saveProgress();
|
|
@@ -3685,7 +3706,9 @@ async function runAgentLoop(prompt, sessionId, force, callOptions, aiCall, onToo
|
|
|
3685
3706
|
}
|
|
3686
3707
|
// Final-answer gate: no mutating task reports DONE without VERIFY PASS.
|
|
3687
3708
|
async function v2Finalize() {
|
|
3688
|
-
|
|
3709
|
+
var allTasks = taskGroup.status();
|
|
3710
|
+
var unjoinedRunning = allTasks.filter(function(t){return !t.joined && t.status==='running';});
|
|
3711
|
+
if(unjoinedRunning.length){completionStatus='PARTIAL';if(last)last.result='PARTIAL: unjoined subagents: '+unjoinedRunning.map(function(t){return t.id;}).join(', ')+'. Results are saved; join them before claiming completion.';saveProgress();return false;}
|
|
3689
3712
|
if (!v2) return false;
|
|
3690
3713
|
var observedMutation = !!((actions.writes || []).length || (actions.edits || []).length ||
|
|
3691
3714
|
(actions.execs || []).length || (actions.toolsInstalled || []).length || (actions.tasks || 0));
|
|
@@ -3761,6 +3784,7 @@ async function runAgentLoop(prompt, sessionId, force, callOptions, aiCall, onToo
|
|
|
3761
3784
|
if(callOptions._toolBudget)runCap=Math.min(runCap,totalTools+Math.max(0,callOptions._toolBudget.limit-callOptions._toolBudget.calls));
|
|
3762
3785
|
var turnCapacityPause=await capacityPause();if(turnCapacityPause)return turnCapacityPause;
|
|
3763
3786
|
steps++;
|
|
3787
|
+
logBuild('turn ' + steps + ' of while loop', { sessionId, totalTools: totalTools, runCap: runCap, budgetCalls: runBudget.calls, budgetLimit: runBudget.limit });
|
|
3764
3788
|
if(reliabilityContextStore){try{projectContext=projectBaseContext+(await reliabilityContextStore.context(taskPrompt)).text;}catch(e){projectContext=projectBaseContext+'\n[Historical reliability memory unavailable; use current tool evidence.]\n';}callOptions.projectContext=projectContext;}
|
|
3765
3789
|
var res = await aiCall(transcript+projectContext+(callOptions.freeOnly&&classification.mutating?'\n[IMPLEMENTATION TURN] Use at most one brief progress sentence before tools. Implement one complete useful change, keep tool JSON complete, and do not write placeholders or interim final answers while requirements remain.':''), sessionId, force, callOptions);
|
|
3766
3790
|
// Unknown/empty model finish = step failure, never success.
|
|
@@ -3773,6 +3797,7 @@ async function runAgentLoop(prompt, sessionId, force, callOptions, aiCall, onToo
|
|
|
3773
3797
|
}
|
|
3774
3798
|
if (!drafterId && res.provider) drafterId = String(res.provider);
|
|
3775
3799
|
last = res;
|
|
3800
|
+
logBuild('turn ' + steps + ' result', { sessionId, provider: res.provider, completionStatus: res.completionStatus, partial: !!res.partial, resultLen: String(res.result||'').length, toolCalls: (res.actions&&res.actions.writes?res.actions.writes.length:0)+(res.actions&&res.actions.edits?res.actions.edits.length:0)+(res.actions&&res.actions.execs?res.actions.execs.length:0) });
|
|
3776
3801
|
if (hasMalformedHarmonyToolCall(res.result)) {
|
|
3777
3802
|
stepFailure = 'malformed tool protocol';
|
|
3778
3803
|
completionStatus = 'FAILED';
|
|
@@ -3809,9 +3834,9 @@ async function runAgentLoop(prompt, sessionId, force, callOptions, aiCall, onToo
|
|
|
3809
3834
|
// A FAILED finalize with spare budget continues the same loop with the
|
|
3810
3835
|
// failed checks attached, instead of ending the run. Single shot:
|
|
3811
3836
|
// nested repairs already ran, and caps bound total spend.
|
|
3812
|
-
if (!verifyContinued && completionStatus === 'FAILED' && steps < maxSteps && totalTools < runCap && !runBudget.exhausted) {
|
|
3837
|
+
if (!verifyContinued && (completionStatus === 'FAILED' || completionStatus === 'PARTIAL') && steps < maxSteps && totalTools < runCap && !runBudget.exhausted) {
|
|
3813
3838
|
verifyContinued = true;
|
|
3814
|
-
transcript += '\n[VERIFICATION FAILED — the task is not complete.
|
|
3839
|
+
transcript += '\n[VERIFICATION FAILED — the task is not complete. Continue with tools: fix exactly these failures, then verify. Do not emit a final answer or claim completion until every check passes. Budget remains for this continuation. [END VERIFICATION FAILED]';
|
|
3815
3840
|
continue;
|
|
3816
3841
|
}
|
|
3817
3842
|
return Object.assign({ res: last, actions: actions, steps: steps }, v2meta());
|
|
@@ -3901,6 +3926,7 @@ async function runAgentLoop(prompt, sessionId, force, callOptions, aiCall, onToo
|
|
|
3901
3926
|
var mutatedThisTurn = ((actions.writes || []).length + (actions.edits || []).length + (actions.execs || []).length) > mutBefore;
|
|
3902
3927
|
stagnantTurns = mutatedThisTurn ? 0 : stagnantTurns + 1;
|
|
3903
3928
|
if (repeatTurns >= 4 || stagnantTurns >= 6) {
|
|
3929
|
+
logStall('Stagnation breaker tripped', { repeatTurns: repeatTurns, stagnantTurns: stagnantTurns, reason: repeatTurns >= 4 ? '4 consecutive identical tool turns' : '6 consecutive turns with zero file/test changes', provider: drafterId });
|
|
3904
3930
|
completionStatus = 'PARTIAL'; saveProgress();
|
|
3905
3931
|
return Object.assign({ res: { result: 'PARTIAL: no progress for ' + (repeatTurns >= 4 ? '4 consecutive identical tool turns' : '6 consecutive turns with zero file/test changes') + '. Saved work is ready for continuation; vary the approach on resume.', provider: drafterId, partial: true }, actions: actions, steps: steps, partial: true }, v2meta());
|
|
3906
3932
|
}
|
|
@@ -5937,7 +5963,10 @@ async function cmdAI(rawArgs, invocationOptions) {
|
|
|
5937
5963
|
if(htmlArtifact){callOptions._runBudget=require('./agent-budget').create(args.maxCalls!==undefined?args.maxCalls:(Number(process.env.CHODE_MAX_MODEL_CALLS)||undefined));aiCall=require('./agent-budget').wrap(aiCall,callOptions._runBudget);}
|
|
5938
5964
|
if(htmlArtifact&&councilEnabled)prompt+=await prepareCouncil(callOptions._runBudget);
|
|
5939
5965
|
var agentActions = { reads: 0, writes: [], edits: [], execs: [], fetched: [], searched: 0, toolsInstalled: [], asks: 0 };
|
|
5940
|
-
if(automaticRe&&automaticRe.ok)agentActions.rea=[Object.assign({target:automaticRe.target},automaticRe.evidence)];
|
|
5966
|
+
if(automaticRe&&automaticRe.ok)agentActions.rea=[Object.assign({target:automaticRe.target},automaticRe.evidence)];
|
|
5967
|
+
// Cumulative mutation totals across completion rounds, used to detect
|
|
5968
|
+
// cross-round stagnation (a round that makes zero new file changes).
|
|
5969
|
+
var cumWrites = 0, cumEdits = 0, cumExecs = 0;
|
|
5941
5970
|
(web.sourcePages || []).forEach(function (page) { evidenceLedger.record(agentActions, { tool: 'webfetch', args: { url: page.url } }, { ok: true, url: page.url, text: page.text }); });
|
|
5942
5971
|
try {
|
|
5943
5972
|
if (args.noTools) {
|
|
@@ -5953,6 +5982,7 @@ async function cmdAI(rawArgs, invocationOptions) {
|
|
|
5953
5982
|
var capacityWaitCap = Number(process.env.CHODE_MAX_CAPACITY_WAIT_MS || 900000);
|
|
5954
5983
|
callOptions._runBudget = sharedBudget;
|
|
5955
5984
|
if(!htmlArtifact)agentPrompt+=await prepareCouncil(sharedBudget);
|
|
5985
|
+
logBuild('completion rounds starting', { sessionId, completionRounds, budgetLimit: sharedBudget.limit, budgetCalls: sharedBudget.calls, freeOnly: !!args.free });
|
|
5956
5986
|
for (var round = 0; round <= completionRounds; round++) {
|
|
5957
5987
|
if (round > 0) {
|
|
5958
5988
|
var previous = (looped && looped.res) || res || {};
|
|
@@ -5986,10 +6016,27 @@ async function cmdAI(rawArgs, invocationOptions) {
|
|
|
5986
6016
|
if(args.free&&looped&&looped.progressSaved&&(looped.partial||looped.budgetExhausted)&&res.completionStatus!=='VERIFIED'&&res.completionStatus!=='NOT_REQUIRED'){res=res||{result:'PARTIAL: this work window ended. Build progress is saved for continuation.',provider:'budget'};res.partial=true;res.retryAt=Date.now()+1000;}
|
|
5987
6017
|
if (res && looped){res.completionStatus = looped.completionStatus;if(callOptions.acceptanceTest){res.acceptanceTest=callOptions.acceptanceTest;res.acceptanceDigest=callOptions.acceptanceDigest;}}
|
|
5988
6018
|
agentActions = (looped && looped.actions) || agentActions;
|
|
6019
|
+
if (looped && looped.actions) {
|
|
6020
|
+
cumWrites += (looped.actions.writes || []).length;
|
|
6021
|
+
cumEdits += (looped.actions.edits || []).length;
|
|
6022
|
+
cumExecs += (looped.actions.execs || []).length;
|
|
6023
|
+
}
|
|
6024
|
+
// Cross-round stagnation: if this round added zero new mutations and
|
|
6025
|
+
// ended in PARTIAL, further rounds will repeat the same idle turns.
|
|
6026
|
+
// Stop early instead of burning the remaining completion rounds.
|
|
6027
|
+
var roundActions = looped && looped.actions;
|
|
6028
|
+
if (looped && looped.partial &&
|
|
6029
|
+
(!roundActions || roundActions.writes.length === 0) &&
|
|
6030
|
+
(!roundActions || roundActions.edits.length === 0) &&
|
|
6031
|
+
(!roundActions || roundActions.execs.length === 0)) {
|
|
6032
|
+
logBuild('Cross-round stagnation: no new mutations in this round, stopping completion rounds', { sessionId, round: round });
|
|
6033
|
+
break;
|
|
6034
|
+
}
|
|
5989
6035
|
var complete = res && (res.completionStatus === 'VERIFIED' || res.completionStatus === 'NOT_REQUIRED') && !res.partial && !res.degraded;
|
|
5990
6036
|
if (complete) break;
|
|
5991
6037
|
if (!looped || !looped.classify || !looped.classify.mutating) break;
|
|
5992
6038
|
if (sharedBudget.exhausted) {
|
|
6039
|
+
logBuild('Model-call budget exhausted', { calls: sharedBudget.calls, limit: sharedBudget.limit, sessionId });
|
|
5993
6040
|
info('The model-call budget for this run is used up. The saved work and a retry entry are kept so it can continue.', 'yellow');
|
|
5994
6041
|
break;
|
|
5995
6042
|
}
|
|
@@ -6063,6 +6110,7 @@ async function cmdAI(rawArgs, invocationOptions) {
|
|
|
6063
6110
|
if (args.verbose) info(' Tools: ' + bits.join(' · ') + '\n', 'dim');
|
|
6064
6111
|
}
|
|
6065
6112
|
if (!res && !fromRepl && !htmlArtifact) {
|
|
6113
|
+
logError('Providers exhausted; queuing task for retry', { sessionId, prompt: String(originalPrompt).slice(0,200) });
|
|
6066
6114
|
// Never-stops: providers down is not a dead end. Queue the task and
|
|
6067
6115
|
// answer from local context so the harness keeps moving.
|
|
6068
6116
|
var task = queuePendingTask(originalPrompt, 'providers_exhausted', sessionId);
|
|
@@ -7315,7 +7363,7 @@ function cmdServe(argv) {
|
|
|
7315
7363
|
}
|
|
7316
7364
|
|
|
7317
7365
|
function loadServeConfig() {
|
|
7318
|
-
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); } catch(e) { return { freeOnly:
|
|
7366
|
+
try { return JSON.parse(fs.readFileSync(CONFIG_PATH, 'utf8')); } catch(e) { return { freeOnly: false, defaultProvider: 'auto', providers: {}, connections: {} }; }
|
|
7319
7367
|
}
|
|
7320
7368
|
|
|
7321
7369
|
function saveServeConfig(cfg) {
|
package/debtaur-orchestration.js
CHANGED
|
@@ -314,7 +314,7 @@ function create(root, options = {}) {
|
|
|
314
314
|
const owns = !!lease && leases.get(lease.id) === lease && lease.active,
|
|
315
315
|
after = lease ? snapshot(lease.files) : { ok: false };
|
|
316
316
|
const stable =
|
|
317
|
-
|
|
317
|
+
after.ok && (!beforeChecks || beforeChecks.ok || beforeChecks.files.length === 0);
|
|
318
318
|
const decision = await decide(
|
|
319
319
|
"handoff",
|
|
320
320
|
[owns, stable, verdict?.ok === true],
|
package/package.json
CHANGED