opencode-swarm 6.86.2 → 6.86.5
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/dist/cli/index.js +107 -5
- package/dist/index.js +143 -15
- package/dist/state.d.ts +18 -0
- package/dist/telemetry.d.ts +1 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -14230,11 +14230,17 @@ async function readLedgerEvents(directory) {
|
|
|
14230
14230
|
const lines = content.trim().split(`
|
|
14231
14231
|
`).filter((line) => line.trim() !== "");
|
|
14232
14232
|
const events = [];
|
|
14233
|
+
let skippedCount = 0;
|
|
14233
14234
|
for (const line of lines) {
|
|
14234
14235
|
try {
|
|
14235
14236
|
const event = JSON.parse(line);
|
|
14236
14237
|
events.push(event);
|
|
14237
|
-
} catch {
|
|
14238
|
+
} catch {
|
|
14239
|
+
skippedCount++;
|
|
14240
|
+
}
|
|
14241
|
+
}
|
|
14242
|
+
if (skippedCount > 0) {
|
|
14243
|
+
console.warn(`[ledger] Skipped ${skippedCount} malformed line(s) in plan-ledger.jsonl`);
|
|
14238
14244
|
}
|
|
14239
14245
|
events.sort((a, b) => a.seq - b.seq);
|
|
14240
14246
|
return events;
|
|
@@ -14331,10 +14337,13 @@ async function takeSnapshotEvent(directory, plan, options) {
|
|
|
14331
14337
|
}, { planHashAfter: options?.planHashAfter });
|
|
14332
14338
|
}
|
|
14333
14339
|
async function replayFromLedger(directory, _options) {
|
|
14334
|
-
const events = await
|
|
14340
|
+
const { events, truncated, badSuffix } = await readLedgerEventsWithIntegrity(directory);
|
|
14335
14341
|
if (events.length === 0) {
|
|
14336
14342
|
return null;
|
|
14337
14343
|
}
|
|
14344
|
+
if (truncated && badSuffix !== null) {
|
|
14345
|
+
await quarantineLedgerSuffix(directory, badSuffix);
|
|
14346
|
+
}
|
|
14338
14347
|
const targetPlanId = events[0].plan_id;
|
|
14339
14348
|
const relevantEvents = events.filter((e) => e.plan_id === targetPlanId);
|
|
14340
14349
|
{
|
|
@@ -14455,6 +14464,46 @@ function applyEventToPlan(plan, event) {
|
|
|
14455
14464
|
throw new Error(`applyEventToPlan: unhandled event type "${event.event_type}" at seq ${event.seq}`);
|
|
14456
14465
|
}
|
|
14457
14466
|
}
|
|
14467
|
+
async function readLedgerEventsWithIntegrity(directory) {
|
|
14468
|
+
const ledgerPath = getLedgerPath(directory);
|
|
14469
|
+
if (!fs.existsSync(ledgerPath)) {
|
|
14470
|
+
return { events: [], truncated: false, badSuffix: null };
|
|
14471
|
+
}
|
|
14472
|
+
try {
|
|
14473
|
+
const content = fs.readFileSync(ledgerPath, "utf8");
|
|
14474
|
+
const lines = content.split(`
|
|
14475
|
+
`);
|
|
14476
|
+
const events = [];
|
|
14477
|
+
let truncated = false;
|
|
14478
|
+
let badSuffix = null;
|
|
14479
|
+
for (let i = 0;i < lines.length; i++) {
|
|
14480
|
+
const line = lines[i];
|
|
14481
|
+
if (line.trim() === "") {
|
|
14482
|
+
continue;
|
|
14483
|
+
}
|
|
14484
|
+
try {
|
|
14485
|
+
const event = JSON.parse(line);
|
|
14486
|
+
events.push(event);
|
|
14487
|
+
} catch {
|
|
14488
|
+
truncated = true;
|
|
14489
|
+
badSuffix = lines.slice(i).join(`
|
|
14490
|
+
`);
|
|
14491
|
+
break;
|
|
14492
|
+
}
|
|
14493
|
+
}
|
|
14494
|
+
events.sort((a, b) => a.seq - b.seq);
|
|
14495
|
+
return { events, truncated, badSuffix };
|
|
14496
|
+
} catch {
|
|
14497
|
+
return { events: [], truncated: false, badSuffix: null };
|
|
14498
|
+
}
|
|
14499
|
+
}
|
|
14500
|
+
async function quarantineLedgerSuffix(directory, badSuffix) {
|
|
14501
|
+
try {
|
|
14502
|
+
const quarantinePath = path2.join(directory, ".swarm", "plan-ledger.quarantine");
|
|
14503
|
+
fs.writeFileSync(quarantinePath, badSuffix, "utf8");
|
|
14504
|
+
console.warn(`[ledger] Corrupted suffix quarantined to ${path2.relative(directory, quarantinePath)}`);
|
|
14505
|
+
} catch {}
|
|
14506
|
+
}
|
|
14458
14507
|
async function loadLastApprovedPlan(directory, expectedPlanId) {
|
|
14459
14508
|
const events = await readLedgerEvents(directory);
|
|
14460
14509
|
if (events.length === 0) {
|
|
@@ -15059,7 +15108,15 @@ ${markdown}`;
|
|
|
15059
15108
|
} catch {}
|
|
15060
15109
|
}
|
|
15061
15110
|
} catch (mdError) {
|
|
15062
|
-
|
|
15111
|
+
const message = mdError instanceof Error ? mdError.message : String(mdError);
|
|
15112
|
+
warn(`[savePlan] plan.md write failed (non-fatal, plan.json is authoritative): ${message}`);
|
|
15113
|
+
try {
|
|
15114
|
+
emit("plan_md_write_failed", {
|
|
15115
|
+
directory,
|
|
15116
|
+
error: message,
|
|
15117
|
+
timestamp: new Date().toISOString()
|
|
15118
|
+
});
|
|
15119
|
+
} catch {}
|
|
15063
15120
|
}
|
|
15064
15121
|
try {
|
|
15065
15122
|
const markerPath = path3.join(swarmDir, ".plan-write-marker");
|
|
@@ -15108,7 +15165,9 @@ function derivePlanMarkdown(plan) {
|
|
|
15108
15165
|
pending: "PENDING",
|
|
15109
15166
|
in_progress: "IN PROGRESS",
|
|
15110
15167
|
complete: "COMPLETE",
|
|
15111
|
-
|
|
15168
|
+
completed: "COMPLETE",
|
|
15169
|
+
blocked: "BLOCKED",
|
|
15170
|
+
closed: "CLOSED"
|
|
15112
15171
|
};
|
|
15113
15172
|
const now = new Date().toISOString();
|
|
15114
15173
|
const currentPhase = plan.current_phase ?? 1;
|
|
@@ -44369,7 +44428,9 @@ ${error93 instanceof Error ? error93.message : String(error93)}`;
|
|
|
44369
44428
|
}
|
|
44370
44429
|
|
|
44371
44430
|
// src/commands/rollback.ts
|
|
44431
|
+
init_plan_schema();
|
|
44372
44432
|
init_utils2();
|
|
44433
|
+
init_ledger();
|
|
44373
44434
|
import * as fs21 from "fs";
|
|
44374
44435
|
import * as path32 from "path";
|
|
44375
44436
|
async function handleRollbackCommand(directory, args) {
|
|
@@ -44426,9 +44487,16 @@ async function handleRollbackCommand(directory, args) {
|
|
|
44426
44487
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
44427
44488
|
}
|
|
44428
44489
|
const swarmDir = validateSwarmPath(directory, "");
|
|
44490
|
+
const EXCLUDE_FILES = new Set([
|
|
44491
|
+
"plan-ledger.jsonl",
|
|
44492
|
+
"plan-ledger.quarantine"
|
|
44493
|
+
]);
|
|
44429
44494
|
const successes = [];
|
|
44430
44495
|
const failures = [];
|
|
44431
44496
|
for (const file3 of checkpointFiles) {
|
|
44497
|
+
if (EXCLUDE_FILES.has(file3) || file3.startsWith("plan-ledger.archived-")) {
|
|
44498
|
+
continue;
|
|
44499
|
+
}
|
|
44432
44500
|
const src = path32.join(checkpointDir, file3);
|
|
44433
44501
|
const dest = path32.join(swarmDir, file3);
|
|
44434
44502
|
try {
|
|
@@ -44439,7 +44507,41 @@ async function handleRollbackCommand(directory, args) {
|
|
|
44439
44507
|
}
|
|
44440
44508
|
}
|
|
44441
44509
|
if (failures.length > 0) {
|
|
44442
|
-
return
|
|
44510
|
+
return [
|
|
44511
|
+
`Rollback partially completed. Successfully restored ${successes.length} files.`,
|
|
44512
|
+
`Failed on ${failures.length} files:`,
|
|
44513
|
+
...failures.map((f) => ` - ${f.file}: ${f.error}`),
|
|
44514
|
+
"",
|
|
44515
|
+
"Some files could not be restored. The .swarm/ directory may be in an inconsistent state.",
|
|
44516
|
+
"Check permissions and disk space, then retry the rollback."
|
|
44517
|
+
].join(`
|
|
44518
|
+
`);
|
|
44519
|
+
}
|
|
44520
|
+
const existingLedgerPath = path32.join(swarmDir, "plan-ledger.jsonl");
|
|
44521
|
+
if (fs21.existsSync(existingLedgerPath)) {
|
|
44522
|
+
fs21.unlinkSync(existingLedgerPath);
|
|
44523
|
+
}
|
|
44524
|
+
try {
|
|
44525
|
+
const planJsonPath = path32.join(swarmDir, "plan.json");
|
|
44526
|
+
if (fs21.existsSync(planJsonPath)) {
|
|
44527
|
+
const planRaw = fs21.readFileSync(planJsonPath, "utf-8");
|
|
44528
|
+
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
44529
|
+
const planId = `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
44530
|
+
const planHash = computePlanHash(plan);
|
|
44531
|
+
await initLedger(directory, planId, planHash, plan);
|
|
44532
|
+
await appendLedgerEvent(directory, {
|
|
44533
|
+
event_type: "plan_rebuilt",
|
|
44534
|
+
source: "rollback",
|
|
44535
|
+
plan_id: planId
|
|
44536
|
+
});
|
|
44537
|
+
}
|
|
44538
|
+
} catch (initError) {
|
|
44539
|
+
return [
|
|
44540
|
+
`Rollback restored files but failed to initialize ledger: ${initError instanceof Error ? initError.message : String(initError)}`,
|
|
44541
|
+
"The .swarm/plan.json has been restored but the ledger may be out of sync.",
|
|
44542
|
+
"Run /swarm reset-session to reinitialize the ledger."
|
|
44543
|
+
].join(`
|
|
44544
|
+
`);
|
|
44443
44545
|
}
|
|
44444
44546
|
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
44445
44547
|
const rollbackEvent = {
|
package/dist/index.js
CHANGED
|
@@ -16158,11 +16158,17 @@ async function readLedgerEvents(directory) {
|
|
|
16158
16158
|
const lines = content.trim().split(`
|
|
16159
16159
|
`).filter((line) => line.trim() !== "");
|
|
16160
16160
|
const events = [];
|
|
16161
|
+
let skippedCount = 0;
|
|
16161
16162
|
for (const line of lines) {
|
|
16162
16163
|
try {
|
|
16163
16164
|
const event = JSON.parse(line);
|
|
16164
16165
|
events.push(event);
|
|
16165
|
-
} catch {
|
|
16166
|
+
} catch {
|
|
16167
|
+
skippedCount++;
|
|
16168
|
+
}
|
|
16169
|
+
}
|
|
16170
|
+
if (skippedCount > 0) {
|
|
16171
|
+
console.warn(`[ledger] Skipped ${skippedCount} malformed line(s) in plan-ledger.jsonl`);
|
|
16166
16172
|
}
|
|
16167
16173
|
events.sort((a, b) => a.seq - b.seq);
|
|
16168
16174
|
return events;
|
|
@@ -16259,10 +16265,13 @@ async function takeSnapshotEvent(directory, plan, options) {
|
|
|
16259
16265
|
}, { planHashAfter: options?.planHashAfter });
|
|
16260
16266
|
}
|
|
16261
16267
|
async function replayFromLedger(directory, _options) {
|
|
16262
|
-
const events = await
|
|
16268
|
+
const { events, truncated, badSuffix } = await readLedgerEventsWithIntegrity(directory);
|
|
16263
16269
|
if (events.length === 0) {
|
|
16264
16270
|
return null;
|
|
16265
16271
|
}
|
|
16272
|
+
if (truncated && badSuffix !== null) {
|
|
16273
|
+
await quarantineLedgerSuffix(directory, badSuffix);
|
|
16274
|
+
}
|
|
16266
16275
|
const targetPlanId = events[0].plan_id;
|
|
16267
16276
|
const relevantEvents = events.filter((e) => e.plan_id === targetPlanId);
|
|
16268
16277
|
{
|
|
@@ -16383,6 +16392,46 @@ function applyEventToPlan(plan, event) {
|
|
|
16383
16392
|
throw new Error(`applyEventToPlan: unhandled event type "${event.event_type}" at seq ${event.seq}`);
|
|
16384
16393
|
}
|
|
16385
16394
|
}
|
|
16395
|
+
async function readLedgerEventsWithIntegrity(directory) {
|
|
16396
|
+
const ledgerPath = getLedgerPath(directory);
|
|
16397
|
+
if (!fs4.existsSync(ledgerPath)) {
|
|
16398
|
+
return { events: [], truncated: false, badSuffix: null };
|
|
16399
|
+
}
|
|
16400
|
+
try {
|
|
16401
|
+
const content = fs4.readFileSync(ledgerPath, "utf8");
|
|
16402
|
+
const lines = content.split(`
|
|
16403
|
+
`);
|
|
16404
|
+
const events = [];
|
|
16405
|
+
let truncated = false;
|
|
16406
|
+
let badSuffix = null;
|
|
16407
|
+
for (let i2 = 0;i2 < lines.length; i2++) {
|
|
16408
|
+
const line = lines[i2];
|
|
16409
|
+
if (line.trim() === "") {
|
|
16410
|
+
continue;
|
|
16411
|
+
}
|
|
16412
|
+
try {
|
|
16413
|
+
const event = JSON.parse(line);
|
|
16414
|
+
events.push(event);
|
|
16415
|
+
} catch {
|
|
16416
|
+
truncated = true;
|
|
16417
|
+
badSuffix = lines.slice(i2).join(`
|
|
16418
|
+
`);
|
|
16419
|
+
break;
|
|
16420
|
+
}
|
|
16421
|
+
}
|
|
16422
|
+
events.sort((a, b) => a.seq - b.seq);
|
|
16423
|
+
return { events, truncated, badSuffix };
|
|
16424
|
+
} catch {
|
|
16425
|
+
return { events: [], truncated: false, badSuffix: null };
|
|
16426
|
+
}
|
|
16427
|
+
}
|
|
16428
|
+
async function quarantineLedgerSuffix(directory, badSuffix) {
|
|
16429
|
+
try {
|
|
16430
|
+
const quarantinePath = path4.join(directory, ".swarm", "plan-ledger.quarantine");
|
|
16431
|
+
fs4.writeFileSync(quarantinePath, badSuffix, "utf8");
|
|
16432
|
+
console.warn(`[ledger] Corrupted suffix quarantined to ${path4.relative(directory, quarantinePath)}`);
|
|
16433
|
+
} catch {}
|
|
16434
|
+
}
|
|
16386
16435
|
async function loadLastApprovedPlan(directory, expectedPlanId) {
|
|
16387
16436
|
const events = await readLedgerEvents(directory);
|
|
16388
16437
|
if (events.length === 0) {
|
|
@@ -16987,7 +17036,15 @@ ${markdown}`;
|
|
|
16987
17036
|
} catch {}
|
|
16988
17037
|
}
|
|
16989
17038
|
} catch (mdError) {
|
|
16990
|
-
|
|
17039
|
+
const message = mdError instanceof Error ? mdError.message : String(mdError);
|
|
17040
|
+
warn(`[savePlan] plan.md write failed (non-fatal, plan.json is authoritative): ${message}`);
|
|
17041
|
+
try {
|
|
17042
|
+
emit("plan_md_write_failed", {
|
|
17043
|
+
directory,
|
|
17044
|
+
error: message,
|
|
17045
|
+
timestamp: new Date().toISOString()
|
|
17046
|
+
});
|
|
17047
|
+
} catch {}
|
|
16991
17048
|
}
|
|
16992
17049
|
try {
|
|
16993
17050
|
const markerPath = path5.join(swarmDir, ".plan-write-marker");
|
|
@@ -17088,7 +17145,9 @@ function derivePlanMarkdown(plan) {
|
|
|
17088
17145
|
pending: "PENDING",
|
|
17089
17146
|
in_progress: "IN PROGRESS",
|
|
17090
17147
|
complete: "COMPLETE",
|
|
17091
|
-
|
|
17148
|
+
completed: "COMPLETE",
|
|
17149
|
+
blocked: "BLOCKED",
|
|
17150
|
+
closed: "CLOSED"
|
|
17092
17151
|
};
|
|
17093
17152
|
const now = new Date().toISOString();
|
|
17094
17153
|
const currentPhase = plan.current_phase ?? 1;
|
|
@@ -24990,7 +25049,7 @@ function createDelegationGateHook(config2, directory) {
|
|
|
24990
25049
|
});
|
|
24991
25050
|
if (councilActive && result.overallVerdict === "APPROVE" && result.allCriteriaMet === true && (result.requiredFixesCount ?? 0) === 0) {
|
|
24992
25051
|
try {
|
|
24993
|
-
|
|
25052
|
+
await advanceTaskStateAndPersist(session, taskId, "complete", directory);
|
|
24994
25053
|
} catch (err2) {
|
|
24995
25054
|
console.warn(`[delegation-gate] toolAfter convene_council: could not advance ${taskId} \u2192 complete: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
24996
25055
|
}
|
|
@@ -25377,7 +25436,7 @@ ${trimComment}${after}`;
|
|
|
25377
25436
|
pendingCoderScopeByTaskId.delete(currentTaskId);
|
|
25378
25437
|
}
|
|
25379
25438
|
try {
|
|
25380
|
-
|
|
25439
|
+
await advanceTaskStateAndPersist(session, currentTaskId, "coder_delegated", directory);
|
|
25381
25440
|
} catch (err2) {
|
|
25382
25441
|
console.warn(`[delegation-gate] state machine warn: ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
25383
25442
|
}
|
|
@@ -25561,6 +25620,7 @@ __export(exports_state, {
|
|
|
25561
25620
|
buildRehydrationCache: () => buildRehydrationCache,
|
|
25562
25621
|
beginInvocation: () => beginInvocation,
|
|
25563
25622
|
applyRehydrationCache: () => applyRehydrationCache,
|
|
25623
|
+
advanceTaskStateAndPersist: () => advanceTaskStateAndPersist,
|
|
25564
25624
|
advanceTaskState: () => advanceTaskState,
|
|
25565
25625
|
_resetCouncilDisagreementWarnings: () => _resetCouncilDisagreementWarnings,
|
|
25566
25626
|
AgentRunContext: () => AgentRunContext
|
|
@@ -25933,6 +25993,18 @@ function advanceTaskState(session, taskId, newState) {
|
|
|
25933
25993
|
session.taskWorkflowStates.set(taskId, newState);
|
|
25934
25994
|
telemetry.taskStateChanged(session.agentName, taskId, newState, current);
|
|
25935
25995
|
}
|
|
25996
|
+
async function advanceTaskStateAndPersist(session, taskId, newState, directory) {
|
|
25997
|
+
advanceTaskState(session, taskId, newState);
|
|
25998
|
+
if (newState !== "coder_delegated" && newState !== "complete") {
|
|
25999
|
+
return;
|
|
26000
|
+
}
|
|
26001
|
+
const planStatus = newState === "complete" ? "completed" : "in_progress";
|
|
26002
|
+
try {
|
|
26003
|
+
await updateTaskStatus(directory, taskId, planStatus);
|
|
26004
|
+
} catch (err2) {
|
|
26005
|
+
console.warn(`[advanceTaskStateAndPersist] persist ${taskId} \u2192 ${planStatus} failed (in-memory state still advanced): ${err2 instanceof Error ? err2.message : String(err2)}`);
|
|
26006
|
+
}
|
|
26007
|
+
}
|
|
25936
26008
|
function getTaskState(session, taskId) {
|
|
25937
26009
|
if (!isValidTaskId2(taskId)) {
|
|
25938
26010
|
return "idle";
|
|
@@ -53226,9 +53298,16 @@ async function handleRollbackCommand(directory, args2) {
|
|
|
53226
53298
|
return `Error: Checkpoint for phase ${targetPhase} is empty. Cannot rollback.`;
|
|
53227
53299
|
}
|
|
53228
53300
|
const swarmDir = validateSwarmPath(directory, "");
|
|
53301
|
+
const EXCLUDE_FILES = new Set([
|
|
53302
|
+
"plan-ledger.jsonl",
|
|
53303
|
+
"plan-ledger.quarantine"
|
|
53304
|
+
]);
|
|
53229
53305
|
const successes = [];
|
|
53230
53306
|
const failures = [];
|
|
53231
53307
|
for (const file3 of checkpointFiles) {
|
|
53308
|
+
if (EXCLUDE_FILES.has(file3) || file3.startsWith("plan-ledger.archived-")) {
|
|
53309
|
+
continue;
|
|
53310
|
+
}
|
|
53232
53311
|
const src = path40.join(checkpointDir, file3);
|
|
53233
53312
|
const dest = path40.join(swarmDir, file3);
|
|
53234
53313
|
try {
|
|
@@ -53239,7 +53318,41 @@ async function handleRollbackCommand(directory, args2) {
|
|
|
53239
53318
|
}
|
|
53240
53319
|
}
|
|
53241
53320
|
if (failures.length > 0) {
|
|
53242
|
-
return
|
|
53321
|
+
return [
|
|
53322
|
+
`Rollback partially completed. Successfully restored ${successes.length} files.`,
|
|
53323
|
+
`Failed on ${failures.length} files:`,
|
|
53324
|
+
...failures.map((f) => ` - ${f.file}: ${f.error}`),
|
|
53325
|
+
"",
|
|
53326
|
+
"Some files could not be restored. The .swarm/ directory may be in an inconsistent state.",
|
|
53327
|
+
"Check permissions and disk space, then retry the rollback."
|
|
53328
|
+
].join(`
|
|
53329
|
+
`);
|
|
53330
|
+
}
|
|
53331
|
+
const existingLedgerPath = path40.join(swarmDir, "plan-ledger.jsonl");
|
|
53332
|
+
if (fs28.existsSync(existingLedgerPath)) {
|
|
53333
|
+
fs28.unlinkSync(existingLedgerPath);
|
|
53334
|
+
}
|
|
53335
|
+
try {
|
|
53336
|
+
const planJsonPath = path40.join(swarmDir, "plan.json");
|
|
53337
|
+
if (fs28.existsSync(planJsonPath)) {
|
|
53338
|
+
const planRaw = fs28.readFileSync(planJsonPath, "utf-8");
|
|
53339
|
+
const plan = PlanSchema.parse(JSON.parse(planRaw));
|
|
53340
|
+
const planId = `${plan.swarm}-${plan.title}`.replace(/[^a-zA-Z0-9-_]/g, "_");
|
|
53341
|
+
const planHash = computePlanHash(plan);
|
|
53342
|
+
await initLedger(directory, planId, planHash, plan);
|
|
53343
|
+
await appendLedgerEvent(directory, {
|
|
53344
|
+
event_type: "plan_rebuilt",
|
|
53345
|
+
source: "rollback",
|
|
53346
|
+
plan_id: planId
|
|
53347
|
+
});
|
|
53348
|
+
}
|
|
53349
|
+
} catch (initError) {
|
|
53350
|
+
return [
|
|
53351
|
+
`Rollback restored files but failed to initialize ledger: ${initError instanceof Error ? initError.message : String(initError)}`,
|
|
53352
|
+
"The .swarm/plan.json has been restored but the ledger may be out of sync.",
|
|
53353
|
+
"Run /swarm reset-session to reinitialize the ledger."
|
|
53354
|
+
].join(`
|
|
53355
|
+
`);
|
|
53243
53356
|
}
|
|
53244
53357
|
const eventsPath = validateSwarmPath(directory, "events.jsonl");
|
|
53245
53358
|
const rollbackEvent = {
|
|
@@ -53257,7 +53370,9 @@ async function handleRollbackCommand(directory, args2) {
|
|
|
53257
53370
|
return `Rolled back to phase ${targetPhase}: ${checkpoint2.label || "no label"}`;
|
|
53258
53371
|
}
|
|
53259
53372
|
var init_rollback = __esm(() => {
|
|
53373
|
+
init_plan_schema();
|
|
53260
53374
|
init_utils2();
|
|
53375
|
+
init_ledger();
|
|
53261
53376
|
});
|
|
53262
53377
|
|
|
53263
53378
|
// src/commands/simulate.ts
|
|
@@ -63224,7 +63339,7 @@ init_schema();
|
|
|
63224
63339
|
init_state();
|
|
63225
63340
|
init_utils();
|
|
63226
63341
|
init_utils2();
|
|
63227
|
-
import { renameSync as renameSync11, unlinkSync as
|
|
63342
|
+
import { renameSync as renameSync11, unlinkSync as unlinkSync8 } from "fs";
|
|
63228
63343
|
import * as nodePath2 from "path";
|
|
63229
63344
|
function createAgentActivityHooks(config3, directory) {
|
|
63230
63345
|
if (config3.hooks?.agent_activity === false) {
|
|
@@ -63301,7 +63416,7 @@ async function doFlush(directory) {
|
|
|
63301
63416
|
renameSync11(tempPath, path46);
|
|
63302
63417
|
} catch (writeError) {
|
|
63303
63418
|
try {
|
|
63304
|
-
|
|
63419
|
+
unlinkSync8(tempPath);
|
|
63305
63420
|
} catch {}
|
|
63306
63421
|
throw writeError;
|
|
63307
63422
|
}
|
|
@@ -85502,6 +85617,17 @@ init_state();
|
|
|
85502
85617
|
function slugify2(str) {
|
|
85503
85618
|
return str.replace(/[^a-zA-Z0-9_-]/g, "_").replace(/_+/g, "_");
|
|
85504
85619
|
}
|
|
85620
|
+
function extractJsonArray(text) {
|
|
85621
|
+
const trimmed = text.trim();
|
|
85622
|
+
const fenceMatch = trimmed.match(/```(?:json)?\s*\n?([\s\S]*?)\n?```/);
|
|
85623
|
+
if (fenceMatch)
|
|
85624
|
+
return fenceMatch[1].trim();
|
|
85625
|
+
const start2 = trimmed.search(/\[\s*[{["0-9\]tfn]/);
|
|
85626
|
+
const end = trimmed.lastIndexOf("]");
|
|
85627
|
+
if (start2 !== -1 && end > start2)
|
|
85628
|
+
return trimmed.slice(start2, end + 1);
|
|
85629
|
+
return trimmed;
|
|
85630
|
+
}
|
|
85505
85631
|
async function generateMutants(files, ctx) {
|
|
85506
85632
|
if (!ctx) {
|
|
85507
85633
|
console.warn("[generateMutants] No ToolContext \u2014 cannot call LLM; returning empty patch set");
|
|
@@ -85546,9 +85672,9 @@ Return a JSON array where each element has:
|
|
|
85546
85672
|
- id: unique string like "mut-001"
|
|
85547
85673
|
- mutationType: one of: ${mutationTypes}
|
|
85548
85674
|
- patch: unified diff format (--- a/file\\n+++ a/file\\n@@ ... @@\\n-old\\n+new)
|
|
85549
|
-
- Generate 5
|
|
85675
|
+
- Generate 3-5 mutations per function
|
|
85550
85676
|
|
|
85551
|
-
Return ONLY valid JSON array, no
|
|
85677
|
+
Return ONLY a valid JSON array. No markdown, no code fences, no explanation. Start your response with [ and end with ].`;
|
|
85552
85678
|
const promptResult = await client.session.prompt({
|
|
85553
85679
|
path: { id: ephemeralSessionId },
|
|
85554
85680
|
body: {
|
|
@@ -85566,9 +85692,11 @@ Return ONLY valid JSON array, no markdown, no explanation.`;
|
|
|
85566
85692
|
`);
|
|
85567
85693
|
let parsed;
|
|
85568
85694
|
try {
|
|
85569
|
-
parsed = JSON.parse(rawText);
|
|
85695
|
+
parsed = JSON.parse(extractJsonArray(rawText));
|
|
85570
85696
|
} catch (error93) {
|
|
85571
|
-
|
|
85697
|
+
const msg = error93 instanceof Error ? error93.message : String(error93);
|
|
85698
|
+
const hint = msg.includes("EOF") || msg.includes("Unexpected end") ? " (response appears truncated \u2014 LLM may have hit an output token limit)" : "";
|
|
85699
|
+
console.warn(`[generateMutants] Failed to parse LLM response as MutationPatch[]: ${msg}${hint}; returning empty patch set`);
|
|
85572
85700
|
return [];
|
|
85573
85701
|
}
|
|
85574
85702
|
if (!Array.isArray(parsed) || parsed.length === 0) {
|
|
@@ -85781,7 +85909,7 @@ import * as path95 from "path";
|
|
|
85781
85909
|
|
|
85782
85910
|
// src/mutation/engine.ts
|
|
85783
85911
|
import { spawnSync as spawnSync3 } from "child_process";
|
|
85784
|
-
import { unlinkSync as
|
|
85912
|
+
import { unlinkSync as unlinkSync13, writeFileSync as writeFileSync18 } from "fs";
|
|
85785
85913
|
import * as path94 from "path";
|
|
85786
85914
|
|
|
85787
85915
|
// src/mutation/equivalence.ts
|
|
@@ -86015,7 +86143,7 @@ async function executeMutation(patch, testCommand, _testFiles, workingDir) {
|
|
|
86015
86143
|
revertError = new Error(`Failed to revert mutation ${patch.id}: ${revertErr}. Working tree may be dirty.`);
|
|
86016
86144
|
}
|
|
86017
86145
|
try {
|
|
86018
|
-
|
|
86146
|
+
unlinkSync13(patchFile);
|
|
86019
86147
|
} catch (_unlinkErr) {}
|
|
86020
86148
|
}
|
|
86021
86149
|
}
|
package/dist/state.d.ts
CHANGED
|
@@ -360,6 +360,24 @@ export declare function recordPhaseAgentDispatch(sessionId: string, agentName: s
|
|
|
360
360
|
* @param newState - The requested new state
|
|
361
361
|
*/
|
|
362
362
|
export declare function advanceTaskState(session: AgentSessionState, taskId: string, newState: TaskWorkflowState): void;
|
|
363
|
+
/**
|
|
364
|
+
* Advance the per-task workflow state machine AND persist the corresponding
|
|
365
|
+
* plan.json status at meaningful workflow boundaries.
|
|
366
|
+
*
|
|
367
|
+
* The two-layer model splits in-memory workflow state (Layer 1, fast, used by
|
|
368
|
+
* gates) from the durable plan (Layer 2, projected to plan.md). Without this
|
|
369
|
+
* bridge, council APPROVE → 'complete' updates Layer 1 only and plan.md goes
|
|
370
|
+
* stale. This helper closes the gap by mapping:
|
|
371
|
+
* - 'coder_delegated' → plan.json status 'in_progress'
|
|
372
|
+
* - 'complete' → plan.json status 'completed'
|
|
373
|
+
* Other transitions are in-memory only (the task is already in_progress on disk
|
|
374
|
+
* once coder_delegated has fired).
|
|
375
|
+
*
|
|
376
|
+
* Persistence errors are logged and swallowed so a transient disk failure does
|
|
377
|
+
* not break the in-memory state machine — matches the existing defensive
|
|
378
|
+
* pattern around advanceTaskState call sites.
|
|
379
|
+
*/
|
|
380
|
+
export declare function advanceTaskStateAndPersist(session: AgentSessionState, taskId: string, newState: TaskWorkflowState, directory: string): Promise<void>;
|
|
363
381
|
/**
|
|
364
382
|
* Get the current workflow state for a task.
|
|
365
383
|
* Returns 'idle' if no entry exists.
|
package/dist/telemetry.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type TelemetryEvent = 'session_started' | 'session_ended' | 'agent_activated' | 'delegation_begin' | 'delegation_end' | 'task_state_changed' | 'gate_passed' | 'gate_failed' | 'phase_changed' | 'budget_updated' | 'model_fallback' | 'hard_limit_hit' | 'revision_limit_hit' | 'loop_detected' | 'scope_violation' | 'qa_skip_violation' | 'heartbeat' | 'turbo_mode_changed' | 'auto_oversight_escalation' | 'environment_detected' | 'evidence_lock_acquired' | 'evidence_lock_contended' | 'evidence_lock_stale_recovered' | 'plan_ledger_cas_retry' | 'prm_pattern_detected' | 'prm_course_correction_injected' | 'prm_escalation_triggered' | 'prm_hard_stop';
|
|
1
|
+
export type TelemetryEvent = 'session_started' | 'session_ended' | 'agent_activated' | 'delegation_begin' | 'delegation_end' | 'task_state_changed' | 'gate_passed' | 'gate_failed' | 'phase_changed' | 'budget_updated' | 'model_fallback' | 'hard_limit_hit' | 'revision_limit_hit' | 'loop_detected' | 'scope_violation' | 'qa_skip_violation' | 'heartbeat' | 'turbo_mode_changed' | 'auto_oversight_escalation' | 'environment_detected' | 'evidence_lock_acquired' | 'evidence_lock_contended' | 'evidence_lock_stale_recovered' | 'plan_ledger_cas_retry' | 'plan_md_write_failed' | 'prm_pattern_detected' | 'prm_course_correction_injected' | 'prm_escalation_triggered' | 'prm_hard_stop';
|
|
2
2
|
export type TelemetryListener = (event: TelemetryEvent, data: Record<string, unknown>) => void;
|
|
3
3
|
/** @internal - For testing only */
|
|
4
4
|
export declare function resetTelemetryForTesting(): void;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-swarm",
|
|
3
|
-
"version": "6.86.
|
|
3
|
+
"version": "6.86.5",
|
|
4
4
|
"description": "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|