opencode-swarm 7.74.3 → 7.75.0
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 +174 -23
- package/dist/commands/full-auto.d.ts +15 -3
- package/dist/commands/registry.d.ts +3 -3
- package/dist/config/loader.d.ts +6 -11
- package/dist/config/schema.d.ts +22 -0
- package/dist/full-auto/state.d.ts +12 -0
- package/dist/hooks/auto-review.d.ts +82 -0
- package/dist/hooks/review-receipt-collector.d.ts +58 -0
- package/dist/hooks/review-receipt.d.ts +7 -0
- package/dist/index.js +1817 -1194
- package/dist/session/snapshot-reader.d.ts +1 -1
- package/dist/turbo/lean/reviewer.d.ts +3 -1
- package/package.json +1 -1
package/dist/cli/index.js
CHANGED
|
@@ -52,7 +52,7 @@ var package_default;
|
|
|
52
52
|
var init_package = __esm(() => {
|
|
53
53
|
package_default = {
|
|
54
54
|
name: "opencode-swarm",
|
|
55
|
-
version: "7.
|
|
55
|
+
version: "7.75.0",
|
|
56
56
|
description: "Architect-centric agentic swarm plugin for OpenCode - hub-and-spoke orchestration with SME consultation, code generation, and QA review",
|
|
57
57
|
main: "dist/index.js",
|
|
58
58
|
types: "dist/index.d.ts",
|
|
@@ -18015,6 +18015,7 @@ __export(exports_schema, {
|
|
|
18015
18015
|
AutomationModeSchema: () => AutomationModeSchema,
|
|
18016
18016
|
AutomationConfigSchema: () => AutomationConfigSchema,
|
|
18017
18017
|
AutomationCapabilitiesSchema: () => AutomationCapabilitiesSchema,
|
|
18018
|
+
AutoReviewConfigSchema: () => AutoReviewConfigSchema,
|
|
18018
18019
|
AuthorityConfigSchema: () => AuthorityConfigSchema,
|
|
18019
18020
|
ArchitecturalSupervisionConfigSchema: () => ArchitecturalSupervisionConfigSchema,
|
|
18020
18021
|
AgentThinkingConfigSchema: () => AgentThinkingConfigSchema,
|
|
@@ -18100,7 +18101,7 @@ function resolveExternalSkillsConfig(input) {
|
|
|
18100
18101
|
};
|
|
18101
18102
|
return merged;
|
|
18102
18103
|
}
|
|
18103
|
-
var _internals5, SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentReasoningConfigSchema, AgentThinkingConfigSchema, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillPropagationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, PrMonitorConfigSchema, ParallelizationConfigSchema, WorktreeIsolationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, ExternalSkillCandidateSourceTypeSchema, ExternalSkillCandidateEvaluationVerdictSchema, DiscoverySourceSchema, ExternalSkillCandidateSchema, ExternalSkillsConfigSchema, DEFAULT_EXTERNAL_SKILLS_CONFIG, PluginConfigSchema;
|
|
18104
|
+
var _internals5, SEPARATORS, CANONICAL_ROLES_LONGEST_FIRST, CANONICAL_ROLES_SET, AgentReasoningConfigSchema, AgentThinkingConfigSchema, AgentOverrideConfigSchema, SwarmConfigSchema, HooksConfigSchema, ScoringWeightsSchema, DecisionDecaySchema, TokenRatiosSchema, ScoringConfigSchema, ContextBudgetConfigSchema, EvidenceConfigSchema, GateFeatureSchema, PlaceholderScanConfigSchema, QualityBudgetConfigSchema, GateConfigSchema, PipelineConfigSchema, PhaseCompleteConfigSchema, SummaryConfigSchema, ReviewPassesConfigSchema, AutoReviewConfigSchema, AdversarialDetectionConfigSchema, AdversarialTestingConfigSchemaBase, AdversarialTestingConfigSchema, IntegrationAnalysisConfigSchema, DocsConfigSchema, DesignDocsConfigSchema, UIReviewConfigSchema, CompactionAdvisoryConfigSchema, LintConfigSchema, SecretscanConfigSchema, GuardrailsProfileSchema, DEFAULT_AGENT_PROFILES, DEFAULT_ARCHITECT_PROFILE, GuardrailsConfigSchema, WatchdogConfigSchema, SelfReviewConfigSchema, ToolFilterConfigSchema, PlanCursorConfigSchema, ContextMapConfigSchema, CheckpointConfigSchema, AutomationModeSchema, AutomationCapabilitiesSchema, AutomationConfigSchemaBase, AutomationConfigSchema, KnowledgeConfigSchema, MemoryConfigSchema, CuratorConfigSchema, ArchitecturalSupervisionConfigSchema, KnowledgeApplicationConfigSchema, SkillPropagationConfigSchema, SkillImproverConfigSchema, SpecWriterConfigSchema, SlopDetectorConfigSchema, IncrementalVerifyConfigSchema, CompactionConfigSchema, PrmConfigSchema, AgentAuthorityRuleSchema, AuthorityConfigSchema, GeneralCouncilMemberConfigSchema, GeneralCouncilConfigSchema, CouncilConfigSchema, PrMonitorConfigSchema, ParallelizationConfigSchema, WorktreeIsolationConfigSchema, LeanTurboConfigSchema, StandardTurboConfigSchema, LeanTurboStrategyConfigSchema, TurboConfigSchema, ExternalSkillCandidateSourceTypeSchema, ExternalSkillCandidateEvaluationVerdictSchema, DiscoverySourceSchema, ExternalSkillCandidateSchema, ExternalSkillsConfigSchema, DEFAULT_EXTERNAL_SKILLS_CONFIG, PluginConfigSchema;
|
|
18104
18105
|
var init_schema = __esm(() => {
|
|
18105
18106
|
init_zod();
|
|
18106
18107
|
init_constants();
|
|
@@ -18288,6 +18289,12 @@ var init_schema = __esm(() => {
|
|
|
18288
18289
|
"**/token/**"
|
|
18289
18290
|
])
|
|
18290
18291
|
});
|
|
18292
|
+
AutoReviewConfigSchema = exports_external.object({
|
|
18293
|
+
enabled: exports_external.boolean().default(false),
|
|
18294
|
+
trigger: exports_external.enum(["task_completion", "phase_boundary", "both"]).default("phase_boundary"),
|
|
18295
|
+
timeout_ms: exports_external.number().int().min(1e4).max(1800000).default(300000),
|
|
18296
|
+
max_diff_kb: exports_external.number().int().min(16).max(2048).default(256)
|
|
18297
|
+
});
|
|
18291
18298
|
AdversarialDetectionConfigSchema = exports_external.object({
|
|
18292
18299
|
enabled: exports_external.boolean().default(true),
|
|
18293
18300
|
policy: exports_external.enum(["warn", "gate", "ignore"]).default("warn"),
|
|
@@ -18971,6 +18978,7 @@ var init_schema = __esm(() => {
|
|
|
18971
18978
|
guardrails: GuardrailsConfigSchema.optional(),
|
|
18972
18979
|
watchdog: WatchdogConfigSchema.optional(),
|
|
18973
18980
|
self_review: SelfReviewConfigSchema.optional(),
|
|
18981
|
+
auto_review: AutoReviewConfigSchema.optional(),
|
|
18974
18982
|
tool_filter: ToolFilterConfigSchema.optional(),
|
|
18975
18983
|
authority: AuthorityConfigSchema.optional(),
|
|
18976
18984
|
plan_cursor: PlanCursorConfigSchema.optional(),
|
|
@@ -19021,6 +19029,7 @@ var init_schema = __esm(() => {
|
|
|
19021
19029
|
version_check: exports_external.boolean().default(true).optional(),
|
|
19022
19030
|
full_auto: exports_external.object({
|
|
19023
19031
|
enabled: exports_external.boolean().default(false),
|
|
19032
|
+
locked: exports_external.boolean().default(false),
|
|
19024
19033
|
critic_model: exports_external.string().optional(),
|
|
19025
19034
|
max_interactions_per_phase: exports_external.number().int().min(5).max(200).default(50),
|
|
19026
19035
|
deadlock_threshold: exports_external.number().int().min(2).max(10).default(3),
|
|
@@ -19034,6 +19043,7 @@ var init_schema = __esm(() => {
|
|
|
19034
19043
|
protected_paths: exports_external.array(exports_external.string()).default([
|
|
19035
19044
|
".git",
|
|
19036
19045
|
".github/workflows",
|
|
19046
|
+
".opencode",
|
|
19037
19047
|
".swarm",
|
|
19038
19048
|
"package.json",
|
|
19039
19049
|
"package-lock.json",
|
|
@@ -19061,6 +19071,7 @@ var init_schema = __esm(() => {
|
|
|
19061
19071
|
protected_paths: [
|
|
19062
19072
|
".git",
|
|
19063
19073
|
".github/workflows",
|
|
19074
|
+
".opencode",
|
|
19064
19075
|
".swarm",
|
|
19065
19076
|
"package.json",
|
|
19066
19077
|
"package-lock.json",
|
|
@@ -19112,6 +19123,7 @@ var init_schema = __esm(() => {
|
|
|
19112
19123
|
}))
|
|
19113
19124
|
}).optional().default(() => ({
|
|
19114
19125
|
enabled: false,
|
|
19126
|
+
locked: false,
|
|
19115
19127
|
max_interactions_per_phase: 50,
|
|
19116
19128
|
deadlock_threshold: 3,
|
|
19117
19129
|
escalation_mode: "pause",
|
|
@@ -19124,6 +19136,7 @@ var init_schema = __esm(() => {
|
|
|
19124
19136
|
protected_paths: [
|
|
19125
19137
|
".git",
|
|
19126
19138
|
".github/workflows",
|
|
19139
|
+
".opencode",
|
|
19127
19140
|
".swarm",
|
|
19128
19141
|
"package.json",
|
|
19129
19142
|
"package-lock.json",
|
|
@@ -19319,6 +19332,15 @@ function sanitizeExternalSkillsConfig(raw) {
|
|
|
19319
19332
|
delete cleaned.external_skills;
|
|
19320
19333
|
return cleaned;
|
|
19321
19334
|
}
|
|
19335
|
+
function rawFullAutoLocked(raw) {
|
|
19336
|
+
if (!raw || typeof raw !== "object")
|
|
19337
|
+
return false;
|
|
19338
|
+
const fullAuto = raw.full_auto;
|
|
19339
|
+
if (!fullAuto || typeof fullAuto !== "object" || Array.isArray(fullAuto)) {
|
|
19340
|
+
return false;
|
|
19341
|
+
}
|
|
19342
|
+
return fullAuto.locked === true;
|
|
19343
|
+
}
|
|
19322
19344
|
function loadPluginConfig(directory) {
|
|
19323
19345
|
const userConfigPath = path7.join(getUserConfigDir(), "opencode", CONFIG_FILENAME);
|
|
19324
19346
|
const projectConfigPath = path7.join(directory, ".opencode", CONFIG_FILENAME);
|
|
@@ -19332,6 +19354,13 @@ function loadPluginConfig(directory) {
|
|
|
19332
19354
|
if (rawProjectConfig) {
|
|
19333
19355
|
mergedRaw = deepMerge(mergedRaw, rawProjectConfig);
|
|
19334
19356
|
}
|
|
19357
|
+
if (rawFullAutoLocked(rawUserConfig) || rawFullAutoLocked(rawProjectConfig)) {
|
|
19358
|
+
const fullAutoRaw = mergedRaw.full_auto && typeof mergedRaw.full_auto === "object" && !Array.isArray(mergedRaw.full_auto) ? mergedRaw.full_auto : {};
|
|
19359
|
+
mergedRaw = {
|
|
19360
|
+
...mergedRaw,
|
|
19361
|
+
full_auto: { ...fullAutoRaw, locked: true }
|
|
19362
|
+
};
|
|
19363
|
+
}
|
|
19335
19364
|
mergedRaw = migratePresetsConfig(mergedRaw);
|
|
19336
19365
|
mergedRaw = sanitizeExternalSkillsConfig(mergedRaw);
|
|
19337
19366
|
const result = PluginConfigSchema.safeParse(mergedRaw);
|
|
@@ -19364,8 +19393,9 @@ function loadPluginConfigWithMeta(directory) {
|
|
|
19364
19393
|
const userResult = loadRawConfigFromPath(userConfigPath);
|
|
19365
19394
|
const projectResult = loadRawConfigFromPath(projectConfigPath);
|
|
19366
19395
|
const loadedFromFile = userResult.fileExisted || projectResult.fileExisted;
|
|
19396
|
+
const configHadErrors = userResult.hadError || projectResult.hadError;
|
|
19367
19397
|
const config2 = loadPluginConfig(directory);
|
|
19368
|
-
return { config: config2, loadedFromFile };
|
|
19398
|
+
return { config: config2, loadedFromFile, configHadErrors };
|
|
19369
19399
|
}
|
|
19370
19400
|
var CONFIG_FILENAME = "opencode-swarm.json", MAX_CONFIG_FILE_BYTES = 102400;
|
|
19371
19401
|
var init_loader = __esm(() => {
|
|
@@ -51242,6 +51272,25 @@ function emptyCounters() {
|
|
|
51242
51272
|
consecutiveNoProgressTurns: 0
|
|
51243
51273
|
};
|
|
51244
51274
|
}
|
|
51275
|
+
function sanitizeRunState(raw) {
|
|
51276
|
+
if (!raw || typeof raw !== "object" || Array.isArray(raw))
|
|
51277
|
+
return null;
|
|
51278
|
+
const r = raw;
|
|
51279
|
+
if (typeof r.sessionID !== "string" || !r.sessionID)
|
|
51280
|
+
return null;
|
|
51281
|
+
const mode = VALID_RUN_MODES.has(r.mode) ? r.mode : "supervised";
|
|
51282
|
+
const status = VALID_RUN_STATUSES.has(r.status) ? r.status : "idle";
|
|
51283
|
+
return { ...raw, mode, status };
|
|
51284
|
+
}
|
|
51285
|
+
function sanitizeSessions(raw) {
|
|
51286
|
+
const result = {};
|
|
51287
|
+
for (const [id, session] of Object.entries(raw)) {
|
|
51288
|
+
const sanitized = sanitizeRunState(session);
|
|
51289
|
+
if (sanitized)
|
|
51290
|
+
result[id] = sanitized;
|
|
51291
|
+
}
|
|
51292
|
+
return result;
|
|
51293
|
+
}
|
|
51245
51294
|
function emptyState(sessionID, mode = "supervised") {
|
|
51246
51295
|
const now = nowISO();
|
|
51247
51296
|
return {
|
|
@@ -51306,26 +51355,45 @@ function clearStateUnreadable() {
|
|
|
51306
51355
|
stateUnreadable = false;
|
|
51307
51356
|
stateUnreadableReason = "";
|
|
51308
51357
|
}
|
|
51358
|
+
function isFullAutoStateUnreadable() {
|
|
51359
|
+
return { unreadable: stateUnreadable, reason: stateUnreadableReason };
|
|
51360
|
+
}
|
|
51309
51361
|
function readPersisted(directory) {
|
|
51310
51362
|
try {
|
|
51311
51363
|
const filePath = validateSwarmPath(directory, STATE_FILE);
|
|
51312
|
-
|
|
51364
|
+
let stats;
|
|
51365
|
+
try {
|
|
51366
|
+
stats = fs15.statSync(filePath);
|
|
51367
|
+
} catch {
|
|
51313
51368
|
clearStateUnreadable();
|
|
51369
|
+
readCache.delete(filePath);
|
|
51314
51370
|
return emptyPersisted();
|
|
51315
51371
|
}
|
|
51372
|
+
const cached3 = readCache.get(filePath);
|
|
51373
|
+
if (cached3 && cached3.mtimeMs === stats.mtimeMs && cached3.size === stats.size) {
|
|
51374
|
+
clearStateUnreadable();
|
|
51375
|
+
return structuredClone(cached3.state);
|
|
51376
|
+
}
|
|
51316
51377
|
const raw = fs15.readFileSync(filePath, "utf-8");
|
|
51317
51378
|
const parsed = JSON.parse(raw);
|
|
51318
51379
|
if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.version !== 2 || !parsed.sessions || typeof parsed.sessions !== "object" || Array.isArray(parsed.sessions)) {
|
|
51319
51380
|
markStateUnreadable(`malformed shape (version=${parsed?.version}, sessions type=${Array.isArray(parsed?.sessions) ? "array" : typeof parsed?.sessions})`);
|
|
51381
|
+
readCache.delete(filePath);
|
|
51320
51382
|
return emptyPersisted();
|
|
51321
51383
|
}
|
|
51322
51384
|
clearStateUnreadable();
|
|
51323
|
-
|
|
51385
|
+
const state = {
|
|
51324
51386
|
version: 2,
|
|
51325
51387
|
updatedAt: parsed.updatedAt ?? nowISO(),
|
|
51326
51388
|
oversightSequence: typeof parsed.oversightSequence === "number" ? parsed.oversightSequence : 0,
|
|
51327
|
-
sessions: parsed.sessions
|
|
51389
|
+
sessions: sanitizeSessions(parsed.sessions)
|
|
51328
51390
|
};
|
|
51391
|
+
readCache.set(filePath, {
|
|
51392
|
+
mtimeMs: stats.mtimeMs,
|
|
51393
|
+
size: stats.size,
|
|
51394
|
+
state: structuredClone(state)
|
|
51395
|
+
});
|
|
51396
|
+
return state;
|
|
51329
51397
|
} catch (error93) {
|
|
51330
51398
|
const reason = error93 instanceof Error ? error93.message : String(error93);
|
|
51331
51399
|
error(`[full-auto/state] Failed to read ${STATE_FILE}: ${reason} \u2014 attempting .bak recovery`);
|
|
@@ -51341,7 +51409,7 @@ function readPersisted(directory) {
|
|
|
51341
51409
|
version: 2,
|
|
51342
51410
|
updatedAt: parsed.updatedAt ?? nowISO(),
|
|
51343
51411
|
oversightSequence: typeof parsed.oversightSequence === "number" ? parsed.oversightSequence : 0,
|
|
51344
|
-
sessions: parsed.sessions
|
|
51412
|
+
sessions: sanitizeSessions(parsed.sessions)
|
|
51345
51413
|
};
|
|
51346
51414
|
}
|
|
51347
51415
|
}
|
|
@@ -51349,6 +51417,7 @@ function readPersisted(directory) {
|
|
|
51349
51417
|
error(`[full-auto/state] .bak recovery also failed: ${bakError instanceof Error ? bakError.message : String(bakError)}`);
|
|
51350
51418
|
}
|
|
51351
51419
|
markStateUnreadable(`canonical=${reason}; .bak=missing-or-corrupt`);
|
|
51420
|
+
readCache.clear();
|
|
51352
51421
|
return emptyPersisted();
|
|
51353
51422
|
}
|
|
51354
51423
|
}
|
|
@@ -51386,6 +51455,7 @@ function writePersisted(directory, persisted) {
|
|
|
51386
51455
|
}
|
|
51387
51456
|
} catch {}
|
|
51388
51457
|
fs15.renameSync(tmpPath, filePath);
|
|
51458
|
+
readCache.delete(filePath);
|
|
51389
51459
|
const readback = fs15.readFileSync(filePath, "utf-8");
|
|
51390
51460
|
const parsed = JSON.parse(readback);
|
|
51391
51461
|
if (parsed?.version !== 2) {
|
|
@@ -51397,6 +51467,10 @@ function writePersisted(directory, persisted) {
|
|
|
51397
51467
|
throw new Error(`Full-Auto state persistence failed: ${msg}`);
|
|
51398
51468
|
}
|
|
51399
51469
|
}
|
|
51470
|
+
function loadFullAutoRunState(directory, sessionID) {
|
|
51471
|
+
const persisted = readPersisted(directory);
|
|
51472
|
+
return persisted.sessions[sessionID];
|
|
51473
|
+
}
|
|
51400
51474
|
function startFullAutoRun(directory, sessionID, config3, options = {}) {
|
|
51401
51475
|
return withStateLock(directory, () => {
|
|
51402
51476
|
const persisted = readPersisted(directory);
|
|
@@ -51428,14 +51502,15 @@ function startFullAutoRun(directory, sessionID, config3, options = {}) {
|
|
|
51428
51502
|
return state;
|
|
51429
51503
|
});
|
|
51430
51504
|
}
|
|
51431
|
-
function
|
|
51505
|
+
function disarmFullAutoRun(directory, sessionID, reason) {
|
|
51432
51506
|
return withStateLock(directory, () => {
|
|
51433
51507
|
const persisted = readPersisted(directory);
|
|
51434
51508
|
const state = persisted.sessions[sessionID];
|
|
51435
51509
|
if (!state)
|
|
51436
51510
|
return;
|
|
51437
|
-
state.status = "
|
|
51511
|
+
state.status = "idle";
|
|
51438
51512
|
state.pauseReason = reason;
|
|
51513
|
+
state.terminateReason = undefined;
|
|
51439
51514
|
state.updatedAt = nowISO();
|
|
51440
51515
|
persisted.sessions[sessionID] = state;
|
|
51441
51516
|
writePersisted(directory, persisted);
|
|
@@ -51456,15 +51531,26 @@ function terminateFullAutoRun(directory, sessionID, reason) {
|
|
|
51456
51531
|
return state;
|
|
51457
51532
|
});
|
|
51458
51533
|
}
|
|
51459
|
-
var import_proper_lockfile8, lockfile8, STATE_FILE = "full-auto-state.json", stateUnreadable = false, stateUnreadableReason = "";
|
|
51534
|
+
var import_proper_lockfile8, lockfile8, STATE_FILE = "full-auto-state.json", VALID_RUN_MODES, VALID_RUN_STATUSES, stateUnreadable = false, stateUnreadableReason = "", readCache;
|
|
51460
51535
|
var init_state2 = __esm(() => {
|
|
51461
51536
|
init_utils2();
|
|
51462
51537
|
init_logger();
|
|
51463
51538
|
import_proper_lockfile8 = __toESM(require_proper_lockfile(), 1);
|
|
51464
51539
|
lockfile8 = import_proper_lockfile8.default;
|
|
51540
|
+
VALID_RUN_MODES = new Set(["assisted", "supervised", "strict"]);
|
|
51541
|
+
VALID_RUN_STATUSES = new Set([
|
|
51542
|
+
"idle",
|
|
51543
|
+
"running",
|
|
51544
|
+
"paused",
|
|
51545
|
+
"terminated"
|
|
51546
|
+
]);
|
|
51547
|
+
readCache = new Map;
|
|
51465
51548
|
});
|
|
51466
51549
|
|
|
51467
51550
|
// src/commands/full-auto.ts
|
|
51551
|
+
function isValidMode(value) {
|
|
51552
|
+
return VALID_MODES.includes(value);
|
|
51553
|
+
}
|
|
51468
51554
|
async function handleFullAutoCommand(directory, args, sessionID) {
|
|
51469
51555
|
if (!sessionID || sessionID.trim() === "") {
|
|
51470
51556
|
return "Error: No active session context. Full-Auto Mode requires an active session. Use /swarm-full-auto from within an OpenCode session, or start a session first.";
|
|
@@ -51474,39 +51560,63 @@ async function handleFullAutoCommand(directory, args, sessionID) {
|
|
|
51474
51560
|
return "Error: No active session. Full-Auto Mode requires an active session to operate.";
|
|
51475
51561
|
}
|
|
51476
51562
|
const arg = args[0]?.toLowerCase();
|
|
51563
|
+
if (arg === "status") {
|
|
51564
|
+
return buildStatusReport(directory, sessionID, session.fullAutoMode);
|
|
51565
|
+
}
|
|
51477
51566
|
let newFullAutoMode;
|
|
51567
|
+
let modeOverride;
|
|
51478
51568
|
if (arg === "on") {
|
|
51479
51569
|
newFullAutoMode = true;
|
|
51570
|
+
const modeArg = args[1]?.toLowerCase();
|
|
51571
|
+
if (modeArg) {
|
|
51572
|
+
if (!isValidMode(modeArg)) {
|
|
51573
|
+
return `Error: invalid Full-Auto mode '${args[1]}'. Valid modes: ${VALID_MODES.join(", ")}.`;
|
|
51574
|
+
}
|
|
51575
|
+
modeOverride = modeArg;
|
|
51576
|
+
}
|
|
51480
51577
|
} else if (arg === "off") {
|
|
51481
51578
|
newFullAutoMode = false;
|
|
51579
|
+
} else if (arg && isValidMode(arg)) {
|
|
51580
|
+
newFullAutoMode = true;
|
|
51581
|
+
modeOverride = arg;
|
|
51482
51582
|
} else {
|
|
51483
51583
|
newFullAutoMode = !session.fullAutoMode;
|
|
51484
51584
|
}
|
|
51485
|
-
if (newFullAutoMode && !swarmState.fullAutoEnabledInConfig) {
|
|
51486
|
-
return "Error: Full-Auto Mode cannot be enabled because full_auto.enabled is not set to true in the swarm plugin config. The autonomous oversight hook is inactive without config-level enablement. Set full_auto.enabled = true in your opencode-swarm config and restart.";
|
|
51487
|
-
}
|
|
51488
51585
|
let v2Status = "unavailable";
|
|
51489
51586
|
let modeLabel = "supervised";
|
|
51490
51587
|
let denialMaxConsecutive = 3;
|
|
51491
51588
|
let denialMaxTotal = 20;
|
|
51492
51589
|
let failClosed = true;
|
|
51493
51590
|
let durableError;
|
|
51591
|
+
let criticModelAdvisory = "";
|
|
51494
51592
|
try {
|
|
51495
|
-
const { config: config3 } = loadPluginConfigWithMeta(directory);
|
|
51593
|
+
const { config: config3, configHadErrors } = loadPluginConfigWithMeta(directory);
|
|
51496
51594
|
const fullAutoConfig = config3.full_auto;
|
|
51497
|
-
|
|
51595
|
+
if (newFullAutoMode && configHadErrors) {
|
|
51596
|
+
return "Error: Full-Auto Mode cannot be enabled \u2014 a swarm plugin config file exists but could not be loaded, so full_auto.locked cannot be verified. Fix the config file (see warnings above) and retry.";
|
|
51597
|
+
}
|
|
51598
|
+
if (newFullAutoMode && fullAutoConfig?.locked === true) {
|
|
51599
|
+
return "Error: Full-Auto Mode is locked for this project (full_auto.locked is true in the swarm plugin config). Runtime activation is disabled by configuration; remove the lock to use /swarm full-auto on.";
|
|
51600
|
+
}
|
|
51601
|
+
const effectiveMode = modeOverride ?? fullAutoConfig?.mode ?? "supervised";
|
|
51602
|
+
modeLabel = effectiveMode;
|
|
51498
51603
|
denialMaxConsecutive = fullAutoConfig?.denials?.max_consecutive ?? 3;
|
|
51499
51604
|
denialMaxTotal = fullAutoConfig?.denials?.max_total ?? 20;
|
|
51500
51605
|
failClosed = fullAutoConfig?.fail_closed !== false;
|
|
51501
51606
|
if (newFullAutoMode) {
|
|
51502
|
-
startFullAutoRun(directory, sessionID, fullAutoConfig);
|
|
51607
|
+
startFullAutoRun(directory, sessionID, fullAutoConfig ? { ...fullAutoConfig, mode: effectiveMode } : undefined);
|
|
51503
51608
|
v2Status = "running";
|
|
51609
|
+
const criticModel = fullAutoConfig?.critic_model ?? config3.agents?.critic?.model;
|
|
51610
|
+
const architectModel = config3.agents?.architect?.model;
|
|
51611
|
+
if (criticModel && architectModel && criticModel === architectModel) {
|
|
51612
|
+
criticModelAdvisory = " WARNING: critic model matches architect model \u2014 set full_auto.critic_model (or agents.critic.model) to a different model for independent oversight.";
|
|
51613
|
+
}
|
|
51504
51614
|
} else {
|
|
51505
|
-
const
|
|
51506
|
-
if (!
|
|
51615
|
+
const disarmed = disarmFullAutoRun(directory, sessionID, "/swarm full-auto off");
|
|
51616
|
+
if (!disarmed) {
|
|
51507
51617
|
terminateFullAutoRun(directory, sessionID, "never started");
|
|
51508
51618
|
}
|
|
51509
|
-
v2Status = "
|
|
51619
|
+
v2Status = "idle";
|
|
51510
51620
|
}
|
|
51511
51621
|
} catch (error93) {
|
|
51512
51622
|
durableError = error93 instanceof Error ? error93.message : String(error93);
|
|
@@ -51535,13 +51645,54 @@ async function handleFullAutoCommand(directory, args, sessionID) {
|
|
|
51535
51645
|
"Full-Auto Mode enabled",
|
|
51536
51646
|
`(v2 mode=${modeLabel}, fail_closed=${failClosed},`,
|
|
51537
51647
|
`denials max ${denialMaxConsecutive} consecutive / ${denialMaxTotal} total)`
|
|
51538
|
-
].join(" ");
|
|
51648
|
+
].join(" ") + criticModelAdvisory;
|
|
51649
|
+
}
|
|
51650
|
+
function buildStatusReport(directory, sessionID, sessionFlag) {
|
|
51651
|
+
const lines = [];
|
|
51652
|
+
lines.push(`Full-Auto session flag: ${sessionFlag ? "on" : "off"}`);
|
|
51653
|
+
try {
|
|
51654
|
+
const { config: config3, configHadErrors } = loadPluginConfigWithMeta(directory);
|
|
51655
|
+
if (configHadErrors) {
|
|
51656
|
+
lines.push("Config: UNREADABLE (a config file exists but could not be loaded; `full_auto.locked` cannot be verified, so runtime activation refuses by fail-closed default). Fix the config file to restore normal status.");
|
|
51657
|
+
}
|
|
51658
|
+
if (config3.full_auto?.locked === true) {
|
|
51659
|
+
lines.push("Config: locked (runtime activation disabled via full_auto.locked)");
|
|
51660
|
+
}
|
|
51661
|
+
} catch {}
|
|
51662
|
+
try {
|
|
51663
|
+
const runState = loadFullAutoRunState(directory, sessionID);
|
|
51664
|
+
const stateHealth = isFullAutoStateUnreadable();
|
|
51665
|
+
if (stateHealth.unreadable) {
|
|
51666
|
+
lines.push(`Durable run-state: UNREADABLE (${stateHealth.reason}). Non-read-only tools are blocked fail-closed until .swarm/full-auto-state.json (or .bak) is restored or deleted.`);
|
|
51667
|
+
} else if (!runState) {
|
|
51668
|
+
lines.push("Durable run-state: none (no Full-Auto run for this session)");
|
|
51669
|
+
} else {
|
|
51670
|
+
lines.push(`Durable run-state: ${runState.status} (mode=${runState.mode})`);
|
|
51671
|
+
if (runState.pauseReason) {
|
|
51672
|
+
lines.push(`Pause reason: ${runState.pauseReason}`);
|
|
51673
|
+
}
|
|
51674
|
+
if (runState.terminateReason) {
|
|
51675
|
+
lines.push(`Terminate reason: ${runState.terminateReason}`);
|
|
51676
|
+
}
|
|
51677
|
+
lines.push(`Counters: ${runState.counters.toolCalls} tool calls, ${runState.counters.architectTurns} architect turns, ${runState.counters.oversightChecks} oversight checks`);
|
|
51678
|
+
lines.push(`Denials: ${runState.denialCounters.consecutive} consecutive / ${runState.denialCounters.total} total`);
|
|
51679
|
+
if (runState.lastOversightVerdict) {
|
|
51680
|
+
lines.push(`Last oversight verdict: ${runState.lastOversightVerdict}${runState.lastOversightAt ? ` at ${runState.lastOversightAt}` : ""}`);
|
|
51681
|
+
}
|
|
51682
|
+
}
|
|
51683
|
+
} catch (error93) {
|
|
51684
|
+
lines.push(`Durable run-state: unreadable (${error93 instanceof Error ? error93.message : String(error93)})`);
|
|
51685
|
+
}
|
|
51686
|
+
return lines.join(`
|
|
51687
|
+
`);
|
|
51539
51688
|
}
|
|
51689
|
+
var VALID_MODES;
|
|
51540
51690
|
var init_full_auto = __esm(() => {
|
|
51541
51691
|
init_config();
|
|
51542
51692
|
init_state2();
|
|
51543
51693
|
init_state();
|
|
51544
51694
|
init_logger();
|
|
51695
|
+
VALID_MODES = ["assisted", "supervised", "strict"];
|
|
51545
51696
|
});
|
|
51546
51697
|
|
|
51547
51698
|
// src/services/handoff-service.ts
|
|
@@ -66676,9 +66827,9 @@ Subcommands:
|
|
|
66676
66827
|
},
|
|
66677
66828
|
"full-auto": {
|
|
66678
66829
|
handler: (ctx) => handleFullAutoCommand(ctx.directory, ctx.args, ctx.sessionID),
|
|
66679
|
-
description: "Toggle Full-Auto Mode for the active session [on|off]",
|
|
66680
|
-
args: "on, off",
|
|
66681
|
-
details: '
|
|
66830
|
+
description: "Toggle Full-Auto Mode for the active session [on [mode]|off|status]",
|
|
66831
|
+
args: "on [assisted|supervised|strict], off, status",
|
|
66832
|
+
details: 'First-class toggle for Full-Auto Mode \u2014 autonomous execution with the critic reviewing escalations on your behalf. No config-level enablement is required: "on" activates immediately (unless full_auto.locked is true in config), "off" disarms the run and returns the session to normal interactive operation, "status" reports the durable run state. ' + 'An optional mode after "on" overrides full_auto.mode for this run: assisted (critic consulted only on policy escalations), supervised (default \u2014 risky/high-impact actions reviewed by the critic), strict (ALL plan mutations reviewed by the critic). ' + "While active, the critic answers architect questions and reviews phase boundaries, delegations, and risky actions on your behalf; only ESCALATE_TO_HUMAN verdicts halt the run for your input. The run state is durable (.swarm/full-auto-state.json) and survives restarts; toggle with no argument flips the current state.",
|
|
66682
66833
|
category: "utility"
|
|
66683
66834
|
},
|
|
66684
66835
|
"auto-proceed": {
|
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Handles the /swarm full-auto command.
|
|
3
|
-
*
|
|
3
|
+
* First-class session toggle for Full-Auto Mode: on / off / status / bare toggle.
|
|
4
|
+
*
|
|
5
|
+
* Full-Auto no longer requires `full_auto.enabled: true` in the plugin config —
|
|
6
|
+
* the v2 hooks are always armed and gated at runtime by the durable per-session
|
|
7
|
+
* run state, so activation is a pure runtime decision (like switching permission
|
|
8
|
+
* modes in other agent CLIs). Administrators can set `full_auto.locked: true`
|
|
9
|
+
* to refuse runtime activation entirely.
|
|
10
|
+
*
|
|
11
|
+
* `on` accepts an optional mode argument (`assisted` | `supervised` | `strict`)
|
|
12
|
+
* that overrides `full_auto.mode` for this run. In every mode the critic
|
|
13
|
+
* reviews escalations on the user's behalf; `strict` routes ALL plan mutations
|
|
14
|
+
* through the critic, `supervised` (default) routes risky/high-impact actions,
|
|
15
|
+
* `assisted` only consults the critic when the deterministic policy escalates.
|
|
4
16
|
*
|
|
5
17
|
* In Full-Auto v2 this also creates a durable run-state record under
|
|
6
18
|
* .swarm/full-auto-state.json so the permission/oversight infrastructure can
|
|
7
19
|
* fail-closed across hooks and across process restarts.
|
|
8
20
|
*
|
|
9
|
-
* H2 fix: durable write happens BEFORE flipping the legacy
|
|
21
|
+
* H2 fix (preserved): durable write happens BEFORE flipping the legacy
|
|
10
22
|
* `session.fullAutoMode` flag. If the durable write fails, the command
|
|
11
23
|
* surfaces the error in its return string and does NOT enable the legacy
|
|
12
24
|
* reactive intercept — preventing a silent fail-open where reactive checks
|
|
@@ -14,7 +26,7 @@
|
|
|
14
26
|
* durable run.
|
|
15
27
|
*
|
|
16
28
|
* @param directory - Project directory (used to persist Full-Auto run state)
|
|
17
|
-
* @param args -
|
|
29
|
+
* @param args - "on [mode]" | "off" | "status" | undefined (toggle behavior)
|
|
18
30
|
* @param sessionID - Session ID for accessing active session state
|
|
19
31
|
* @returns Feedback message about Full-Auto Mode state
|
|
20
32
|
*/
|
|
@@ -501,9 +501,9 @@ export declare const COMMAND_REGISTRY: {
|
|
|
501
501
|
};
|
|
502
502
|
readonly 'full-auto': {
|
|
503
503
|
readonly handler: (ctx: CommandContext) => Promise<string>;
|
|
504
|
-
readonly description: "Toggle Full-Auto Mode for the active session [on|off]";
|
|
505
|
-
readonly args: "on, off";
|
|
506
|
-
readonly details:
|
|
504
|
+
readonly description: "Toggle Full-Auto Mode for the active session [on [mode]|off|status]";
|
|
505
|
+
readonly args: "on [assisted|supervised|strict], off, status";
|
|
506
|
+
readonly details: string;
|
|
507
507
|
readonly category: "utility";
|
|
508
508
|
};
|
|
509
509
|
readonly 'auto-proceed': {
|
package/dist/config/loader.d.ts
CHANGED
|
@@ -1,17 +1,6 @@
|
|
|
1
1
|
import { type PluginConfig } from './schema';
|
|
2
2
|
export declare const MAX_CONFIG_FILE_BYTES = 102400;
|
|
3
3
|
export { deepMerge, MAX_MERGE_DEPTH } from '../utils/merge';
|
|
4
|
-
/**
|
|
5
|
-
* Load plugin configuration from user and project config files.
|
|
6
|
-
*
|
|
7
|
-
* Config locations:
|
|
8
|
-
* 1. User config: ~/.config/opencode/opencode-swarm.json
|
|
9
|
-
* 2. Project config: <directory>/.opencode/opencode-swarm.json
|
|
10
|
-
*
|
|
11
|
-
* Project config takes precedence. Nested objects are deep-merged.
|
|
12
|
-
* IMPORTANT: Raw configs are merged BEFORE Zod parsing so that
|
|
13
|
-
* Zod defaults don't override explicit user values.
|
|
14
|
-
*/
|
|
15
4
|
export declare function loadPluginConfig(directory: string): PluginConfig;
|
|
16
5
|
/**
|
|
17
6
|
* Internal variant of loadPluginConfig that also returns loader metadata.
|
|
@@ -21,6 +10,11 @@ export declare function loadPluginConfig(directory: string): PluginConfig;
|
|
|
21
10
|
export declare function loadPluginConfigWithMeta(directory: string): {
|
|
22
11
|
config: PluginConfig;
|
|
23
12
|
loadedFromFile: boolean;
|
|
13
|
+
/** True when a config file existed but could not be loaded (corrupt JSON,
|
|
14
|
+
* oversized, permission error). Consumers with fail-closed semantics —
|
|
15
|
+
* e.g. the Full-Auto `locked` activation guard — must treat this as
|
|
16
|
+
* "config unknown", not "config defaults". */
|
|
17
|
+
configHadErrors: boolean;
|
|
24
18
|
};
|
|
25
19
|
/**
|
|
26
20
|
* Async variant of `loadPluginConfigWithMeta`. Used by the plugin entry
|
|
@@ -29,6 +23,7 @@ export declare function loadPluginConfigWithMeta(directory: string): {
|
|
|
29
23
|
export declare function loadPluginConfigWithMetaAsync(directory: string): Promise<{
|
|
30
24
|
config: PluginConfig;
|
|
31
25
|
loadedFromFile: boolean;
|
|
26
|
+
configHadErrors: boolean;
|
|
32
27
|
}>;
|
|
33
28
|
/**
|
|
34
29
|
* Load custom prompt for an agent from the prompts directory.
|
package/dist/config/schema.d.ts
CHANGED
|
@@ -333,6 +333,17 @@ export declare const ReviewPassesConfigSchema: z.ZodObject<{
|
|
|
333
333
|
security_globs: z.ZodDefault<z.ZodArray<z.ZodString>>;
|
|
334
334
|
}, z.core.$strip>;
|
|
335
335
|
export type ReviewPassesConfig = z.infer<typeof ReviewPassesConfigSchema>;
|
|
336
|
+
export declare const AutoReviewConfigSchema: z.ZodObject<{
|
|
337
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
338
|
+
trigger: z.ZodDefault<z.ZodEnum<{
|
|
339
|
+
task_completion: "task_completion";
|
|
340
|
+
phase_boundary: "phase_boundary";
|
|
341
|
+
both: "both";
|
|
342
|
+
}>>;
|
|
343
|
+
timeout_ms: z.ZodDefault<z.ZodNumber>;
|
|
344
|
+
max_diff_kb: z.ZodDefault<z.ZodNumber>;
|
|
345
|
+
}, z.core.$strip>;
|
|
346
|
+
export type AutoReviewConfig = z.infer<typeof AutoReviewConfigSchema>;
|
|
336
347
|
export declare const AdversarialDetectionConfigSchema: z.ZodObject<{
|
|
337
348
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
338
349
|
policy: z.ZodDefault<z.ZodEnum<{
|
|
@@ -1392,6 +1403,16 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
1392
1403
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1393
1404
|
skip_in_turbo: z.ZodDefault<z.ZodBoolean>;
|
|
1394
1405
|
}, z.core.$strip>>;
|
|
1406
|
+
auto_review: z.ZodOptional<z.ZodObject<{
|
|
1407
|
+
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1408
|
+
trigger: z.ZodDefault<z.ZodEnum<{
|
|
1409
|
+
task_completion: "task_completion";
|
|
1410
|
+
phase_boundary: "phase_boundary";
|
|
1411
|
+
both: "both";
|
|
1412
|
+
}>>;
|
|
1413
|
+
timeout_ms: z.ZodDefault<z.ZodNumber>;
|
|
1414
|
+
max_diff_kb: z.ZodDefault<z.ZodNumber>;
|
|
1415
|
+
}, z.core.$strip>>;
|
|
1395
1416
|
tool_filter: z.ZodOptional<z.ZodObject<{
|
|
1396
1417
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1397
1418
|
overrides: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodArray<z.ZodString>>>;
|
|
@@ -1858,6 +1879,7 @@ export declare const PluginConfigSchema: z.ZodObject<{
|
|
|
1858
1879
|
version_check: z.ZodOptional<z.ZodDefault<z.ZodBoolean>>;
|
|
1859
1880
|
full_auto: z.ZodDefault<z.ZodOptional<z.ZodObject<{
|
|
1860
1881
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
1882
|
+
locked: z.ZodDefault<z.ZodBoolean>;
|
|
1861
1883
|
critic_model: z.ZodOptional<z.ZodString>;
|
|
1862
1884
|
max_interactions_per_phase: z.ZodDefault<z.ZodNumber>;
|
|
1863
1885
|
deadlock_threshold: z.ZodDefault<z.ZodNumber>;
|
|
@@ -88,6 +88,18 @@ export declare function startFullAutoRun(directory: string, sessionID: string, c
|
|
|
88
88
|
taskID?: string;
|
|
89
89
|
}): FullAutoRunState;
|
|
90
90
|
export declare function pauseFullAutoRun(directory: string, sessionID: string, reason: string): FullAutoRunState | undefined;
|
|
91
|
+
/**
|
|
92
|
+
* Disarm a Full-Auto run in response to an explicit user `off`.
|
|
93
|
+
*
|
|
94
|
+
* Unlike `pauseFullAutoRun` / `terminateFullAutoRun` (system-initiated halts
|
|
95
|
+
* that fail-closed-block non-read-only tools until the user re-enables),
|
|
96
|
+
* disarming returns the session to normal interactive operation: the record
|
|
97
|
+
* transitions to `'idle'`, which every enforcement path treats as
|
|
98
|
+
* "no active Full-Auto run". Counters and denial history are preserved for
|
|
99
|
+
* audit. (Adversarial review F3: `off` must not be a one-way door into a
|
|
100
|
+
* write-blocked session.)
|
|
101
|
+
*/
|
|
102
|
+
export declare function disarmFullAutoRun(directory: string, sessionID: string, reason: string): FullAutoRunState | undefined;
|
|
91
103
|
export declare function terminateFullAutoRun(directory: string, sessionID: string, reason: string): FullAutoRunState | undefined;
|
|
92
104
|
export declare function isFullAutoRunActive(directory: string, sessionID: string): boolean;
|
|
93
105
|
export type FullAutoCounterKey = keyof FullAutoCounters;
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-review hook (auto-review machinery, piece B) — opt-in.
|
|
3
|
+
*
|
|
4
|
+
* When `auto_review.enabled` is true, completing a task
|
|
5
|
+
* (`update_task_status` → status 'completed') and/or a phase
|
|
6
|
+
* (`phase_complete`) automatically dispatches the registered reviewer agent
|
|
7
|
+
* over a fresh ephemeral session to review the current execution diff —
|
|
8
|
+
* the same "second model reviews the work in a clean context" pattern used
|
|
9
|
+
* by Claude Code's auto-review and Codex's review model. The reviewer agent
|
|
10
|
+
* carries its own configured model (`agents.reviewer.model`), so the review
|
|
11
|
+
* model is independently configurable from the coder/architect models.
|
|
12
|
+
*
|
|
13
|
+
* The pass is ADVISORY and fully fail-open:
|
|
14
|
+
* - fire-and-forget from `tool.execute.after` (never blocks the tool)
|
|
15
|
+
* - verdicts are persisted as durable review receipts
|
|
16
|
+
* (`.swarm/review-receipts/`, scope-fingerprinted over the diff) and an
|
|
17
|
+
* `auto_review` event is appended to `.swarm/events.jsonl`
|
|
18
|
+
* - a REJECTED or unparseable verdict injects a `[AUTO-REVIEW]` advisory
|
|
19
|
+
* into the architect's next prompt; APPROVED stays silent
|
|
20
|
+
*
|
|
21
|
+
* Bounds (AGENTS.md invariants 3 and 8): the diff subprocess uses execFile
|
|
22
|
+
* with cwd/timeout/maxBuffer and ignored stdin; dispatches are guarded by a
|
|
23
|
+
* per-session in-flight set plus a 60s cooldown in a bounded FIFO map.
|
|
24
|
+
*/
|
|
25
|
+
import { type AutoReviewConfig } from '../config/schema.js';
|
|
26
|
+
/** Test-only: clear module-level dispatch tracking. */
|
|
27
|
+
export declare function resetAutoReviewTracking(): void;
|
|
28
|
+
export type ExecutionDiffResult = {
|
|
29
|
+
status: 'ok';
|
|
30
|
+
diff: string;
|
|
31
|
+
} | {
|
|
32
|
+
status: 'clean';
|
|
33
|
+
} | {
|
|
34
|
+
status: 'error';
|
|
35
|
+
reason: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* Collect the execution diff for review: `git diff HEAD` (tracked changes)
|
|
39
|
+
* plus a porcelain summary of untracked files. Distinguishes a clean working
|
|
40
|
+
* tree from collection failures (git missing, timeout, diff exceeding the
|
|
41
|
+
* 2× maxBuffer cap) so events report honestly. Output is truncated to
|
|
42
|
+
* `maxBytes`.
|
|
43
|
+
*/
|
|
44
|
+
declare function computeExecutionDiff(directory: string, maxBytes: number): Promise<ExecutionDiffResult>;
|
|
45
|
+
declare function dispatchReviewer(directory: string, prompt: string, agentName: string, timeoutMs: number): Promise<string>;
|
|
46
|
+
export interface AutoReviewRunInput {
|
|
47
|
+
directory: string;
|
|
48
|
+
sessionID: string;
|
|
49
|
+
trigger: 'task_completion' | 'phase_boundary';
|
|
50
|
+
taskId?: string;
|
|
51
|
+
phase?: number;
|
|
52
|
+
config: Required<Pick<AutoReviewConfig, 'timeout_ms' | 'max_diff_kb'>>;
|
|
53
|
+
injectAdvisory: (sessionId: string, message: string) => void;
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Execute one auto-review pass: collect diff → dispatch reviewer over an
|
|
57
|
+
* ephemeral session → persist receipt + event → advisory on REJECTED or
|
|
58
|
+
* unparseable output. Fully fail-open; never throws.
|
|
59
|
+
*/
|
|
60
|
+
export declare function runAutoReview(input: AutoReviewRunInput): Promise<void>;
|
|
61
|
+
export interface AutoReviewHookOptions {
|
|
62
|
+
config: AutoReviewConfig;
|
|
63
|
+
directory: string;
|
|
64
|
+
injectAdvisory: (sessionId: string, message: string) => void;
|
|
65
|
+
}
|
|
66
|
+
export declare function createAutoReviewHook(options: AutoReviewHookOptions): {
|
|
67
|
+
toolAfter: (input: {
|
|
68
|
+
tool: string;
|
|
69
|
+
sessionID: string;
|
|
70
|
+
callID?: string;
|
|
71
|
+
}, output: {
|
|
72
|
+
args?: unknown;
|
|
73
|
+
output?: unknown;
|
|
74
|
+
}) => Promise<void>;
|
|
75
|
+
};
|
|
76
|
+
export declare const _internals: {
|
|
77
|
+
computeExecutionDiff: typeof computeExecutionDiff;
|
|
78
|
+
dispatchReviewer: typeof dispatchReviewer;
|
|
79
|
+
runAutoReview: typeof runAutoReview;
|
|
80
|
+
now: () => number;
|
|
81
|
+
};
|
|
82
|
+
export {};
|