jonah-fleet 1.33.0 → 1.35.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/CHANGELOG.md +22 -0
- package/dist/commands/daemon.d.ts +1 -0
- package/dist/commands/daemon.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +626 -55
- package/dist/lib/daemon-keys.d.ts +2 -0
- package/dist/lib/daemon-keys.d.ts.map +1 -1
- package/dist/lib/daemon.d.ts +21 -1
- package/dist/lib/daemon.d.ts.map +1 -1
- package/dist/lib/telemetry.d.ts +47 -0
- package/dist/lib/telemetry.d.ts.map +1 -1
- package/dist/lib/terminal-card.d.ts.map +1 -1
- package/package.json +1 -1
- package/templates/prompts/ORCHESTRATION.md +5 -1
- package/templates/prompts/optimizer.md +23 -13
package/dist/index.js
CHANGED
|
@@ -1461,6 +1461,23 @@ import pc from "picocolors";
|
|
|
1461
1461
|
var execFileAsync2 = promisify2(execFile2);
|
|
1462
1462
|
var GLOBAL_WEEKLY_TOKEN_BUDGET = 875e4;
|
|
1463
1463
|
var GLOBAL_5H_TOKEN_BUDGET = 2e6;
|
|
1464
|
+
var TOKEN_ANOMALY_THRESHOLDS = {
|
|
1465
|
+
CONTEXT_ASYMMETRY_RATIO: 35,
|
|
1466
|
+
CONTEXT_ASYMMETRY_MIN_INPUT_TOKENS: 5e4,
|
|
1467
|
+
BUDGET_HOG_PERCENT: 75,
|
|
1468
|
+
MULTI_ROUTINE_CANNIBALIZATION_PERCENT: 80,
|
|
1469
|
+
MULTI_ROUTINE_MIN_ROUTINES: 3,
|
|
1470
|
+
ITERATION_EXHAUSTION_PERCENT: 20,
|
|
1471
|
+
PLAN_QUOTA_WEEKLY_WARNING_PERCENT: 30,
|
|
1472
|
+
PLAN_QUOTA_WEEKLY_CRITICAL_PERCENT: 20,
|
|
1473
|
+
PLAN_QUOTA_5H_CRITICAL_PERCENT: 20,
|
|
1474
|
+
RE_REVIEW_THRASH_THRESHOLD: 2
|
|
1475
|
+
};
|
|
1476
|
+
function formatIoRatio(inputTokens, outputTokens, precision = 0) {
|
|
1477
|
+
const safeOut = Math.max(1, outputTokens);
|
|
1478
|
+
const ratio = inputTokens / safeOut;
|
|
1479
|
+
return `${ratio.toFixed(precision)}:1`;
|
|
1480
|
+
}
|
|
1464
1481
|
function formatQuotaStatusBadge(status) {
|
|
1465
1482
|
if (status === "HEALTHY") return pc.green("[HEALTHY]");
|
|
1466
1483
|
if (status === "WARNING") return pc.yellow("[WARNING]");
|
|
@@ -1776,36 +1793,133 @@ async function fetchActualPlanQuota(options = {}) {
|
|
|
1776
1793
|
};
|
|
1777
1794
|
}
|
|
1778
1795
|
}
|
|
1796
|
+
function checkPlanQuotaDepletion(quota, options = {}) {
|
|
1797
|
+
const threshold = options.thresholdPct ?? 20;
|
|
1798
|
+
if (!quota || !quota.available) {
|
|
1799
|
+
return { depleted: false, thresholdPct: threshold };
|
|
1800
|
+
}
|
|
1801
|
+
const model = options.model?.toLowerCase() || "";
|
|
1802
|
+
const isClaude = model.includes("claude") || model.includes("gpt") || model.includes("openai") || model.includes("anthropic");
|
|
1803
|
+
let groupName = isClaude ? "Claude and GPT models" : "Gemini Models";
|
|
1804
|
+
let weeklyPct = isClaude ? quota.claudeWeeklyRemainingPct : quota.geminiWeeklyRemainingPct;
|
|
1805
|
+
let weeklyReset = isClaude ? quota.claudeWeeklyResetTime : quota.geminiWeeklyResetTime;
|
|
1806
|
+
let window5hPct = isClaude ? quota.claude5hRemainingPct : quota.gemini5hRemainingPct;
|
|
1807
|
+
let window5hReset = isClaude ? quota.claude5hResetTime : quota.gemini5hResetTime;
|
|
1808
|
+
if (quota.groups) {
|
|
1809
|
+
const targetGroup = quota.groups[groupName];
|
|
1810
|
+
if (targetGroup) {
|
|
1811
|
+
if (weeklyPct === void 0) {
|
|
1812
|
+
weeklyPct = targetGroup.weeklyRemainingPct;
|
|
1813
|
+
weeklyReset = targetGroup.weeklyResetTime;
|
|
1814
|
+
}
|
|
1815
|
+
if (window5hPct === void 0) {
|
|
1816
|
+
window5hPct = targetGroup.window5hRemainingPct;
|
|
1817
|
+
window5hReset = targetGroup.window5hResetTime;
|
|
1818
|
+
}
|
|
1819
|
+
if (weeklyPct === void 0 || window5hPct === void 0) {
|
|
1820
|
+
for (const b of targetGroup.buckets || []) {
|
|
1821
|
+
if ((b.window === "5h" || b.id.includes("5h")) && window5hPct === void 0) {
|
|
1822
|
+
window5hPct = b.remainingPercentage;
|
|
1823
|
+
window5hReset = b.resetTime;
|
|
1824
|
+
} else if ((b.window === "weekly" || b.id.includes("weekly")) && weeklyPct === void 0) {
|
|
1825
|
+
weeklyPct = b.remainingPercentage;
|
|
1826
|
+
weeklyReset = b.resetTime;
|
|
1827
|
+
}
|
|
1828
|
+
}
|
|
1829
|
+
}
|
|
1830
|
+
} else if (weeklyPct === void 0 && window5hPct === void 0) {
|
|
1831
|
+
const groupKeys = Object.keys(quota.groups);
|
|
1832
|
+
if (groupKeys.length > 0) {
|
|
1833
|
+
const fallbackGroup = quota.groups[groupKeys[0]];
|
|
1834
|
+
if (fallbackGroup) {
|
|
1835
|
+
groupName = fallbackGroup.name;
|
|
1836
|
+
weeklyPct = fallbackGroup.weeklyRemainingPct;
|
|
1837
|
+
weeklyReset = fallbackGroup.weeklyResetTime;
|
|
1838
|
+
window5hPct = fallbackGroup.window5hRemainingPct;
|
|
1839
|
+
window5hReset = fallbackGroup.window5hResetTime;
|
|
1840
|
+
}
|
|
1841
|
+
}
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
const is5hDepleted = window5hPct !== void 0 && window5hPct < threshold;
|
|
1845
|
+
const isWeeklyDepleted = weeklyPct !== void 0 && weeklyPct < threshold;
|
|
1846
|
+
if (is5hDepleted || isWeeklyDepleted) {
|
|
1847
|
+
if (is5hDepleted && isWeeklyDepleted) {
|
|
1848
|
+
const lowerWindow = window5hPct <= weeklyPct ? "5h" : "7days";
|
|
1849
|
+
const remainingPct = Math.min(window5hPct, weeklyPct);
|
|
1850
|
+
const resetTime = lowerWindow === "5h" ? window5hReset : weeklyReset;
|
|
1851
|
+
return {
|
|
1852
|
+
depleted: true,
|
|
1853
|
+
thresholdPct: threshold,
|
|
1854
|
+
window: lowerWindow,
|
|
1855
|
+
remainingPct,
|
|
1856
|
+
resetTime,
|
|
1857
|
+
groupName,
|
|
1858
|
+
message: `${groupName} quota is below ${threshold}% in both 5h (${window5hPct.toFixed(1)}%) and 7days (${weeklyPct.toFixed(1)}%) windows`
|
|
1859
|
+
};
|
|
1860
|
+
}
|
|
1861
|
+
if (is5hDepleted) {
|
|
1862
|
+
return {
|
|
1863
|
+
depleted: true,
|
|
1864
|
+
thresholdPct: threshold,
|
|
1865
|
+
window: "5h",
|
|
1866
|
+
remainingPct: window5hPct,
|
|
1867
|
+
resetTime: window5hReset,
|
|
1868
|
+
groupName,
|
|
1869
|
+
message: `${groupName} 5h quota is at ${window5hPct.toFixed(1)}% remaining (<${threshold}% floor)`
|
|
1870
|
+
};
|
|
1871
|
+
}
|
|
1872
|
+
return {
|
|
1873
|
+
depleted: true,
|
|
1874
|
+
thresholdPct: threshold,
|
|
1875
|
+
window: "7days",
|
|
1876
|
+
remainingPct: weeklyPct,
|
|
1877
|
+
resetTime: weeklyReset,
|
|
1878
|
+
groupName,
|
|
1879
|
+
message: `${groupName} 7days weekly quota is at ${weeklyPct.toFixed(1)}% remaining (<${threshold}% floor)`
|
|
1880
|
+
};
|
|
1881
|
+
}
|
|
1882
|
+
return { depleted: false, thresholdPct: threshold };
|
|
1883
|
+
}
|
|
1779
1884
|
function formatPlanQuotaSummary(quota, options = {}) {
|
|
1780
1885
|
if (!quota || !quota.available) {
|
|
1781
1886
|
return "Plan Quota: Unavailable";
|
|
1782
1887
|
}
|
|
1783
1888
|
const parts = [];
|
|
1784
1889
|
const fmt = (pct) => options.markdown ? `\`${pct.toFixed(1)}% remaining\`` : `${pct.toFixed(1)}% remaining`;
|
|
1890
|
+
const geminiUnused = (quota.gemini5hRemainingPct === void 0 || quota.gemini5hRemainingPct === 100) && (quota.geminiWeeklyRemainingPct === void 0 || quota.geminiWeeklyRemainingPct === 100);
|
|
1891
|
+
const claudeUnused = (quota.claude5hRemainingPct === void 0 || quota.claude5hRemainingPct === 100) && (quota.claudeWeeklyRemainingPct === void 0 || quota.claudeWeeklyRemainingPct === 100);
|
|
1892
|
+
const allProvidersUnused = geminiUnused && claudeUnused;
|
|
1893
|
+
const shouldDisplayGemini = !options.hideUnused || !geminiUnused || allProvidersUnused;
|
|
1894
|
+
const shouldDisplayClaude = !options.hideUnused || !claudeUnused;
|
|
1785
1895
|
if (quota.gemini5hRemainingPct !== void 0 || quota.geminiWeeklyRemainingPct !== void 0) {
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1896
|
+
if (shouldDisplayGemini) {
|
|
1897
|
+
const subParts = [];
|
|
1898
|
+
if (quota.gemini5hRemainingPct !== void 0) {
|
|
1899
|
+
subParts.push(`Gemini 5h: ${fmt(quota.gemini5hRemainingPct)}`);
|
|
1900
|
+
}
|
|
1901
|
+
if (quota.geminiWeeklyRemainingPct !== void 0) {
|
|
1902
|
+
const label = quota.gemini5hRemainingPct !== void 0 ? "Weekly" : "Gemini Weekly";
|
|
1903
|
+
subParts.push(`${label}: ${fmt(quota.geminiWeeklyRemainingPct)}`);
|
|
1904
|
+
}
|
|
1905
|
+
if (subParts.length > 0) {
|
|
1906
|
+
parts.push(subParts.join(" \xB7 "));
|
|
1907
|
+
}
|
|
1796
1908
|
}
|
|
1797
1909
|
}
|
|
1798
1910
|
if (quota.claude5hRemainingPct !== void 0 || quota.claudeWeeklyRemainingPct !== void 0) {
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1911
|
+
if (!options.hideUnused || !claudeUnused) {
|
|
1912
|
+
const subParts = [];
|
|
1913
|
+
if (quota.claude5hRemainingPct !== void 0) {
|
|
1914
|
+
subParts.push(`Claude/GPT 5h: ${fmt(quota.claude5hRemainingPct)}`);
|
|
1915
|
+
}
|
|
1916
|
+
if (quota.claudeWeeklyRemainingPct !== void 0) {
|
|
1917
|
+
const label = quota.claude5hRemainingPct !== void 0 ? "Weekly" : "Claude/GPT Weekly";
|
|
1918
|
+
subParts.push(`${label}: ${fmt(quota.claudeWeeklyRemainingPct)}`);
|
|
1919
|
+
}
|
|
1920
|
+
if (subParts.length > 0) {
|
|
1921
|
+
parts.push(subParts.join(" \xB7 "));
|
|
1922
|
+
}
|
|
1809
1923
|
}
|
|
1810
1924
|
}
|
|
1811
1925
|
return parts.join(options.markdown ? "<br>" : "\n");
|
|
@@ -1876,6 +1990,17 @@ function parseLogToTelemetry(content, options = {}) {
|
|
|
1876
1990
|
);
|
|
1877
1991
|
questionsAskedCount = questionLines.length > 0 ? questionLines.length : 2;
|
|
1878
1992
|
}
|
|
1993
|
+
let targetPr;
|
|
1994
|
+
const rawTarget = metadata["target"] || metadata["target / context"] || metadata["pr"];
|
|
1995
|
+
if (rawTarget) {
|
|
1996
|
+
const prMatch = rawTarget.match(/PR\s*#?(\d+)/i);
|
|
1997
|
+
if (prMatch) targetPr = parseInt(prMatch[1], 10);
|
|
1998
|
+
}
|
|
1999
|
+
let commitSha = metadata["commit"] || metadata["commit sha"] || metadata["head sha"] || metadata["sha"];
|
|
2000
|
+
if (!commitSha) {
|
|
2001
|
+
const shaMatch = content.match(/(?:commit|head\s+sha|head\s+commit)[:\s]+`?([0-9a-f]{7,40})`?/i);
|
|
2002
|
+
if (shaMatch) commitSha = shaMatch[1];
|
|
2003
|
+
}
|
|
1879
2004
|
return {
|
|
1880
2005
|
schemaVersion: "1.0.0",
|
|
1881
2006
|
routine,
|
|
@@ -1896,7 +2021,9 @@ function parseLogToTelemetry(content, options = {}) {
|
|
|
1896
2021
|
promptSha,
|
|
1897
2022
|
ambiguityGateTriggered: ambiguityGateTriggered || void 0,
|
|
1898
2023
|
questionsAskedCount,
|
|
1899
|
-
needsInfoApplied: needsInfoApplied || void 0
|
|
2024
|
+
needsInfoApplied: needsInfoApplied || void 0,
|
|
2025
|
+
targetPr,
|
|
2026
|
+
commitSha
|
|
1900
2027
|
};
|
|
1901
2028
|
}
|
|
1902
2029
|
function checkWeeklyBudgetLimit(usedTokens, ceilingTokens = GLOBAL_WEEKLY_TOKEN_BUDGET) {
|
|
@@ -2059,15 +2186,72 @@ function collectLocalTelemetryLogs(dir, repositoryName = "local") {
|
|
|
2059
2186
|
const traverse = (currentDir) => {
|
|
2060
2187
|
if (!fs6.existsSync(currentDir)) return;
|
|
2061
2188
|
const entries = fs6.readdirSync(currentDir, { withFileTypes: true });
|
|
2189
|
+
const filesByBase = /* @__PURE__ */ new Map();
|
|
2062
2190
|
for (const entry of entries) {
|
|
2063
|
-
const fullPath = path6.join(currentDir, entry.name);
|
|
2064
2191
|
if (entry.isDirectory()) {
|
|
2065
|
-
traverse(
|
|
2066
|
-
} else if (entry.isFile()
|
|
2192
|
+
traverse(path6.join(currentDir, entry.name));
|
|
2193
|
+
} else if (entry.isFile()) {
|
|
2194
|
+
if (entry.name.endsWith(".md")) {
|
|
2195
|
+
const base = entry.name.slice(0, -3);
|
|
2196
|
+
const current = filesByBase.get(base) || {};
|
|
2197
|
+
current.md = path6.join(currentDir, entry.name);
|
|
2198
|
+
filesByBase.set(base, current);
|
|
2199
|
+
} else if (entry.name.endsWith(".json")) {
|
|
2200
|
+
const base = entry.name.slice(0, -5);
|
|
2201
|
+
const current = filesByBase.get(base) || {};
|
|
2202
|
+
current.json = path6.join(currentDir, entry.name);
|
|
2203
|
+
filesByBase.set(base, current);
|
|
2204
|
+
}
|
|
2205
|
+
}
|
|
2206
|
+
}
|
|
2207
|
+
for (const [, paths] of filesByBase) {
|
|
2208
|
+
let parsed = false;
|
|
2209
|
+
if (paths.json) {
|
|
2067
2210
|
try {
|
|
2068
|
-
const content = fs6.readFileSync(
|
|
2211
|
+
const content = fs6.readFileSync(paths.json, "utf8");
|
|
2212
|
+
const data = JSON.parse(content);
|
|
2213
|
+
if (data.routine && data.timestamp) {
|
|
2214
|
+
const inTokens = data.usage?.inputTokens ?? data.usage?.input_tokens ?? data.inputTokens ?? 0;
|
|
2215
|
+
const outTokens = data.usage?.outputTokens ?? data.usage?.output_tokens ?? data.outputTokens ?? 0;
|
|
2216
|
+
const totTokens = data.usage?.totalTokens ?? data.usage?.total_tokens ?? data.totalTokens ?? inTokens + outTokens;
|
|
2217
|
+
const durationSec = data.durationMs ? Math.round(data.durationMs / 1e3) : data.durationSeconds ?? void 0;
|
|
2218
|
+
const result = data.result ?? (data.success === true ? "SUCCESS" : data.success === false ? "FAILURE" : "UNKNOWN");
|
|
2219
|
+
const targetPr = data.pr ?? data.targetPr ?? (typeof data.target === "string" ? data.target.match(/PR\s*#?(\d+)/i)?.[1] ? parseInt(data.target.match(/PR\s*#?(\d+)/i)[1], 10) : void 0 : void 0);
|
|
2220
|
+
const commitSha = data.commitSha ?? data.headRefOid ?? data.headSha ?? (typeof data.commit === "string" ? data.commit : data.commit?.oid);
|
|
2221
|
+
summaries.push({
|
|
2222
|
+
schemaVersion: "1.0.0",
|
|
2223
|
+
routine: data.routine,
|
|
2224
|
+
repository: repositoryName,
|
|
2225
|
+
timestamp: data.timestamp,
|
|
2226
|
+
result,
|
|
2227
|
+
errorReason: data.errorReason || data.error,
|
|
2228
|
+
failureCategory: data.failureCategory,
|
|
2229
|
+
inputTokens: inTokens,
|
|
2230
|
+
outputTokens: outTokens,
|
|
2231
|
+
totalTokens: totTokens,
|
|
2232
|
+
estimatedCost: data.estimatedCost ?? totTokens / 1e6 * 0.35,
|
|
2233
|
+
durationSeconds: durationSec,
|
|
2234
|
+
iterationsUsed: data.iterationsUsed,
|
|
2235
|
+
maxIterations: data.maxIterations,
|
|
2236
|
+
ambiguityGateTriggered: Boolean(data.ambiguityGateTriggered),
|
|
2237
|
+
questionsAskedCount: data.questionsAskedCount || 0,
|
|
2238
|
+
needsInfoApplied: Boolean(data.needsInfoApplied),
|
|
2239
|
+
targetPr,
|
|
2240
|
+
commitSha
|
|
2241
|
+
});
|
|
2242
|
+
parsed = true;
|
|
2243
|
+
}
|
|
2244
|
+
} catch {
|
|
2245
|
+
}
|
|
2246
|
+
}
|
|
2247
|
+
if (!parsed && paths.md) {
|
|
2248
|
+
try {
|
|
2249
|
+
const content = fs6.readFileSync(paths.md, "utf8");
|
|
2069
2250
|
const summary = parseLogToTelemetry(content, { repository: repositoryName });
|
|
2070
|
-
if (summary)
|
|
2251
|
+
if (summary) {
|
|
2252
|
+
summaries.push(summary);
|
|
2253
|
+
parsed = true;
|
|
2254
|
+
}
|
|
2071
2255
|
} catch {
|
|
2072
2256
|
}
|
|
2073
2257
|
}
|
|
@@ -2159,6 +2343,129 @@ function renderProgressBar(percentage, width = 25) {
|
|
|
2159
2343
|
if (clamped >= 70) return pc.yellow(filledBar) + pc.gray(emptyBar);
|
|
2160
2344
|
return pc.green(filledBar) + pc.gray(emptyBar);
|
|
2161
2345
|
}
|
|
2346
|
+
function detectTokenAnomalies(telemetry, options = {}) {
|
|
2347
|
+
const anomalies = [];
|
|
2348
|
+
for (const [routineName, r] of Object.entries(telemetry.byRoutine)) {
|
|
2349
|
+
const ratio = r.totalInputTokens / Math.max(1, r.totalOutputTokens);
|
|
2350
|
+
if (ratio > TOKEN_ANOMALY_THRESHOLDS.CONTEXT_ASYMMETRY_RATIO && r.totalInputTokens > TOKEN_ANOMALY_THRESHOLDS.CONTEXT_ASYMMETRY_MIN_INPUT_TOKENS) {
|
|
2351
|
+
anomalies.push({
|
|
2352
|
+
type: "context_asymmetry",
|
|
2353
|
+
severity: "WARNING",
|
|
2354
|
+
routine: routineName,
|
|
2355
|
+
message: `Context Asymmetry on '${routineName}': Input/Output ratio is ${formatIoRatio(r.totalInputTokens, r.totalOutputTokens, 1)} (${r.totalInputTokens.toLocaleString()} input vs ${r.totalOutputTokens.toLocaleString()} output tokens).`,
|
|
2356
|
+
remediation: `Apply progressive disclosure and skill pruning. Scope skills strictly per routine rather than injecting all skills globally.`
|
|
2357
|
+
});
|
|
2358
|
+
}
|
|
2359
|
+
}
|
|
2360
|
+
if (telemetry.totalTokens > 0) {
|
|
2361
|
+
for (const [routineName, r] of Object.entries(telemetry.byRoutine)) {
|
|
2362
|
+
const share = r.totalTokens / telemetry.totalTokens * 100;
|
|
2363
|
+
if (share > TOKEN_ANOMALY_THRESHOLDS.BUDGET_HOG_PERCENT && Object.keys(telemetry.byRoutine).length > 1) {
|
|
2364
|
+
anomalies.push({
|
|
2365
|
+
type: "budget_hog",
|
|
2366
|
+
severity: "CRITICAL",
|
|
2367
|
+
routine: routineName,
|
|
2368
|
+
message: `Budget Hog on '${routineName}': Consumes ${share.toFixed(1)}% of total fleet token allowance.`,
|
|
2369
|
+
remediation: `Throttle dispatch cadence, introduce stricter pre-qualification filters, or add early exit guards.`
|
|
2370
|
+
});
|
|
2371
|
+
}
|
|
2372
|
+
}
|
|
2373
|
+
}
|
|
2374
|
+
if (telemetry.totalTokens > 0 && Object.keys(telemetry.byRoutine).length >= TOKEN_ANOMALY_THRESHOLDS.MULTI_ROUTINE_MIN_ROUTINES) {
|
|
2375
|
+
const sortedRoutines = Object.values(telemetry.byRoutine).sort(
|
|
2376
|
+
(a, b) => b.totalTokens - a.totalTokens
|
|
2377
|
+
);
|
|
2378
|
+
const top2Spend = (sortedRoutines[0]?.totalTokens || 0) + (sortedRoutines[1]?.totalTokens || 0);
|
|
2379
|
+
const top2Share = top2Spend / telemetry.totalTokens * 100;
|
|
2380
|
+
if (top2Share > TOKEN_ANOMALY_THRESHOLDS.MULTI_ROUTINE_CANNIBALIZATION_PERCENT) {
|
|
2381
|
+
anomalies.push({
|
|
2382
|
+
type: "multi_routine_cannibalization",
|
|
2383
|
+
severity: "WARNING",
|
|
2384
|
+
message: `Multi-Routine Budget Cannibalization: Top 2 routines ('${sortedRoutines[0]?.routine}' and '${sortedRoutines[1]?.routine}') consume ${top2Share.toFixed(1)}% of fleet tokens.`,
|
|
2385
|
+
remediation: `Rebalance dispatch pacing and coordinate candidate skips across authoring and review routines.`
|
|
2386
|
+
});
|
|
2387
|
+
}
|
|
2388
|
+
}
|
|
2389
|
+
const prShaEvaluations = /* @__PURE__ */ new Map();
|
|
2390
|
+
for (const e of telemetry.events || []) {
|
|
2391
|
+
if (e.targetPr && e.commitSha) {
|
|
2392
|
+
const key = `${e.targetPr}:${e.commitSha}`;
|
|
2393
|
+
const existing = prShaEvaluations.get(key);
|
|
2394
|
+
if (existing) {
|
|
2395
|
+
existing.count++;
|
|
2396
|
+
} else {
|
|
2397
|
+
prShaEvaluations.set(key, { pr: e.targetPr, sha: e.commitSha, count: 1, routine: e.routine });
|
|
2398
|
+
}
|
|
2399
|
+
}
|
|
2400
|
+
}
|
|
2401
|
+
for (const { pr, sha, count, routine } of prShaEvaluations.values()) {
|
|
2402
|
+
if (count >= TOKEN_ANOMALY_THRESHOLDS.RE_REVIEW_THRASH_THRESHOLD) {
|
|
2403
|
+
anomalies.push({
|
|
2404
|
+
type: "unilateral_rereview_thrash",
|
|
2405
|
+
severity: "WARNING",
|
|
2406
|
+
routine,
|
|
2407
|
+
message: `Unilateral Re-Review Thrash on PR #${pr}: Evaluated ${count} times on identical commit SHA ${sha.slice(0, 7)} without intervening commits.`,
|
|
2408
|
+
remediation: `Enforce persistent review commit state and failure cooldowns (e.g. daemon.json review state cache).`
|
|
2409
|
+
});
|
|
2410
|
+
}
|
|
2411
|
+
}
|
|
2412
|
+
for (const [routineName, r] of Object.entries(telemetry.byRoutine)) {
|
|
2413
|
+
if (r.runCount >= 3) {
|
|
2414
|
+
const tokenLimitFailures = (telemetry.events || []).filter(
|
|
2415
|
+
(e) => e.routine === routineName && e.failureCategory === "token_limit"
|
|
2416
|
+
).length;
|
|
2417
|
+
const failPct = tokenLimitFailures / r.runCount * 100;
|
|
2418
|
+
if (failPct > TOKEN_ANOMALY_THRESHOLDS.ITERATION_EXHAUSTION_PERCENT) {
|
|
2419
|
+
anomalies.push({
|
|
2420
|
+
type: "iteration_exhaustion",
|
|
2421
|
+
severity: "WARNING",
|
|
2422
|
+
routine: routineName,
|
|
2423
|
+
message: `Iteration Ceiling Exhaustion on '${routineName}': ${failPct.toFixed(1)}% of runs terminate at max iteration/token limit.`,
|
|
2424
|
+
remediation: `Decompose tasks vertically into smaller slices and tighten pre-ready self-audits.`
|
|
2425
|
+
});
|
|
2426
|
+
}
|
|
2427
|
+
}
|
|
2428
|
+
}
|
|
2429
|
+
if (options.actualQuota?.available) {
|
|
2430
|
+
const geminiWeekly = options.actualQuota.geminiWeeklyRemainingPct;
|
|
2431
|
+
const gemini5h = options.actualQuota.gemini5hRemainingPct;
|
|
2432
|
+
if (geminiWeekly !== void 0 && geminiWeekly < TOKEN_ANOMALY_THRESHOLDS.PLAN_QUOTA_WEEKLY_WARNING_PERCENT) {
|
|
2433
|
+
anomalies.push({
|
|
2434
|
+
type: "plan_quota_velocity",
|
|
2435
|
+
severity: geminiWeekly < TOKEN_ANOMALY_THRESHOLDS.PLAN_QUOTA_WEEKLY_CRITICAL_PERCENT ? "CRITICAL" : "WARNING",
|
|
2436
|
+
message: `Plan Quota Burn Velocity: Gemini weekly plan quota is at ${geminiWeekly.toFixed(1)}% remaining (<${TOKEN_ANOMALY_THRESHOLDS.PLAN_QUOTA_WEEKLY_WARNING_PERCENT}% threshold).`,
|
|
2437
|
+
remediation: `Downgrade auxiliary routines ('peer-review', 'optimizer') to medium reasoning effort and standard Flash models (gemini-3.8-flash-medium).`
|
|
2438
|
+
});
|
|
2439
|
+
}
|
|
2440
|
+
if (gemini5h !== void 0 && gemini5h < TOKEN_ANOMALY_THRESHOLDS.PLAN_QUOTA_5H_CRITICAL_PERCENT) {
|
|
2441
|
+
anomalies.push({
|
|
2442
|
+
type: "plan_quota_velocity",
|
|
2443
|
+
severity: "CRITICAL",
|
|
2444
|
+
message: `Plan Quota Burn Velocity: Gemini 5h rolling plan quota is at ${gemini5h.toFixed(1)}% remaining (<${TOKEN_ANOMALY_THRESHOLDS.PLAN_QUOTA_5H_CRITICAL_PERCENT}% threshold).`,
|
|
2445
|
+
remediation: `Throttle active agent execution to prevent rate limit lockout during the current 5-hour window.`
|
|
2446
|
+
});
|
|
2447
|
+
}
|
|
2448
|
+
const claudeWeekly = options.actualQuota.claudeWeeklyRemainingPct;
|
|
2449
|
+
const claude5h = options.actualQuota.claude5hRemainingPct;
|
|
2450
|
+
if (claudeWeekly !== void 0 && claudeWeekly < TOKEN_ANOMALY_THRESHOLDS.PLAN_QUOTA_WEEKLY_WARNING_PERCENT) {
|
|
2451
|
+
anomalies.push({
|
|
2452
|
+
type: "plan_quota_velocity",
|
|
2453
|
+
severity: claudeWeekly < TOKEN_ANOMALY_THRESHOLDS.PLAN_QUOTA_WEEKLY_CRITICAL_PERCENT ? "CRITICAL" : "WARNING",
|
|
2454
|
+
message: `Plan Quota Burn Velocity: Claude/GPT weekly plan quota is at ${claudeWeekly.toFixed(1)}% remaining (<${TOKEN_ANOMALY_THRESHOLDS.PLAN_QUOTA_WEEKLY_WARNING_PERCENT}% threshold).`,
|
|
2455
|
+
remediation: `Conserve Claude/GPT quota for complex architectural reasoning; route standard tasks to Gemini Flash.`
|
|
2456
|
+
});
|
|
2457
|
+
}
|
|
2458
|
+
if (claude5h !== void 0 && claude5h < TOKEN_ANOMALY_THRESHOLDS.PLAN_QUOTA_5H_CRITICAL_PERCENT) {
|
|
2459
|
+
anomalies.push({
|
|
2460
|
+
type: "plan_quota_velocity",
|
|
2461
|
+
severity: "CRITICAL",
|
|
2462
|
+
message: `Plan Quota Burn Velocity: Claude/GPT 5h rolling plan quota is at ${claude5h.toFixed(1)}% remaining (<${TOKEN_ANOMALY_THRESHOLDS.PLAN_QUOTA_5H_CRITICAL_PERCENT}% threshold).`,
|
|
2463
|
+
remediation: `Throttle active agent execution to prevent Claude/GPT rate limit lockout during the current 5-hour window.`
|
|
2464
|
+
});
|
|
2465
|
+
}
|
|
2466
|
+
}
|
|
2467
|
+
return anomalies;
|
|
2468
|
+
}
|
|
2162
2469
|
function renderTelemetryDashboard(telemetry, options = {}) {
|
|
2163
2470
|
if (options.json) {
|
|
2164
2471
|
return JSON.stringify(telemetry, null, 2);
|
|
@@ -2187,8 +2494,9 @@ function renderTelemetryDashboard(telemetry, options = {}) {
|
|
|
2187
2494
|
lines.push("\n" + pc.bold("\u{1F916} Spend by Agent Routine:"));
|
|
2188
2495
|
for (const [routineName, r] of Object.entries(telemetry.byRoutine)) {
|
|
2189
2496
|
const costStr = pc.green(`$${r.totalEstimatedCost.toFixed(2)}`);
|
|
2497
|
+
const ioRatioStr = formatIoRatio(r.totalInputTokens, r.totalOutputTokens);
|
|
2190
2498
|
lines.push(
|
|
2191
|
-
` \u2022 ${pc.cyan(routineName.padEnd(30))} Runs: ${pc.bold(r.runCount.toString().padStart(2))} | Tokens: ${pc.bold(formatTokens(r.totalTokens).padStart(7))} | Cost: ${costStr.padStart(6)} | Avg Iter: ${r.avgIterationsUsed.toFixed(1)}`
|
|
2499
|
+
` \u2022 ${pc.cyan(routineName.padEnd(30))} Runs: ${pc.bold(r.runCount.toString().padStart(2))} | Tokens: ${pc.bold(formatTokens(r.totalTokens).padStart(7))} | I/O: ${ioRatioStr.padStart(5)} | Cost: ${costStr.padStart(6)} | Avg Iter: ${r.avgIterationsUsed.toFixed(1)}`
|
|
2192
2500
|
);
|
|
2193
2501
|
}
|
|
2194
2502
|
if (Object.keys(telemetry.byRepository).length > 0) {
|
|
@@ -2213,6 +2521,15 @@ function renderTelemetryDashboard(telemetry, options = {}) {
|
|
|
2213
2521
|
lines.push(
|
|
2214
2522
|
` \u2022 Est. Wasted Tokens Averted: ~${pc.bold(pc.green(formatTokens(amb.estimatedTokensSaved)))} tokens (avoided speculative builds)`
|
|
2215
2523
|
);
|
|
2524
|
+
const anomalies = detectTokenAnomalies(telemetry, { actualQuota: options.actualQuota });
|
|
2525
|
+
if (anomalies.length > 0) {
|
|
2526
|
+
lines.push("\n" + pc.bold(pc.yellow("\u{1F6A8} Detected Token & Pacing Anomalies:")));
|
|
2527
|
+
for (const anom of anomalies) {
|
|
2528
|
+
const badge = anom.severity === "CRITICAL" ? pc.red("[CRITICAL]") : pc.yellow("[WARNING]");
|
|
2529
|
+
lines.push(` ${badge} ${anom.message}`);
|
|
2530
|
+
lines.push(pc.gray(` \u21B3 Remediation: ${anom.remediation}`));
|
|
2531
|
+
}
|
|
2532
|
+
}
|
|
2216
2533
|
const failKeys = Object.keys(telemetry.failureCategories);
|
|
2217
2534
|
if (failKeys.length > 0) {
|
|
2218
2535
|
lines.push("\n" + pc.bold(pc.red("\u26A0\uFE0F Failure Categories Breakdown:")));
|
|
@@ -3176,29 +3493,37 @@ function renderTokenQuotaSection(options) {
|
|
|
3176
3493
|
const lines = [];
|
|
3177
3494
|
lines.push(border(`\u251C${horizontal}\u2524`));
|
|
3178
3495
|
if (hasUsage) {
|
|
3179
|
-
const quota = calculateTokenQuotaPercentages(total);
|
|
3180
3496
|
const breakdown = formatTokenBreakdown(options.usage, "prefix");
|
|
3181
|
-
const
|
|
3497
|
+
const inTok = options.usage?.inputTokens;
|
|
3498
|
+
const outTok = options.usage?.outputTokens;
|
|
3499
|
+
let ioStr = "";
|
|
3500
|
+
if (typeof inTok === "number" && typeof outTok === "number" && outTok > 0) {
|
|
3501
|
+
ioStr = ` \xB7 I/O: ${formatIoRatio(inTok, outTok)}`;
|
|
3502
|
+
}
|
|
3503
|
+
const tokenLine1 = ` Tokens: ${pc2.bold(total.toLocaleString())}${breakdown ? pc2.dim(` (${breakdown})`) : ""}${ioStr ? pc2.cyan(ioStr) : ""}`;
|
|
3182
3504
|
lines.push(border("\u2502") + tokenLine1 + " ".repeat(Math.max(1, width - 2 - stripAnsi(tokenLine1).length)) + border("\u2502"));
|
|
3183
|
-
|
|
3184
|
-
|
|
3185
|
-
|
|
3186
|
-
|
|
3187
|
-
|
|
3188
|
-
|
|
3189
|
-
const
|
|
3190
|
-
|
|
3191
|
-
|
|
3192
|
-
|
|
3193
|
-
|
|
3194
|
-
|
|
3505
|
+
if (!hasPlan) {
|
|
3506
|
+
const quota = calculateTokenQuotaPercentages(total);
|
|
3507
|
+
const tokenLine2 = ` \u21B3 ${pc2.cyan(quota.pctOf5hLimit.toFixed(2) + "% of 5h limit")} \xB7 ${pc2.cyan(quota.pctOfWeeklyLimit.toFixed(2) + "% of weekly limit")}`;
|
|
3508
|
+
lines.push(border("\u2502") + tokenLine2 + " ".repeat(Math.max(1, width - 2 - stripAnsi(tokenLine2).length)) + border("\u2502"));
|
|
3509
|
+
if (options.repoRoot) {
|
|
3510
|
+
try {
|
|
3511
|
+
const rolling = getRollingWindowTokenUsage(options.repoRoot);
|
|
3512
|
+
if (rolling.windowTokens > 0 || rolling.weeklyTokens > 0) {
|
|
3513
|
+
const badge5h = formatQuotaStatusBadge(rolling.windowStatus);
|
|
3514
|
+
const badgeWeekly = formatQuotaStatusBadge(rolling.weeklyStatus);
|
|
3515
|
+
const rollingLine1 = ` Rolling 5h: ${rolling.windowTokens.toLocaleString()} / ${rolling.windowLimit.toLocaleString()} tokens (${rolling.windowPercentage.toFixed(1)}% \xB7 ${badge5h})`;
|
|
3516
|
+
lines.push(border("\u2502") + rollingLine1 + " ".repeat(Math.max(1, width - 2 - stripAnsi(rollingLine1).length)) + border("\u2502"));
|
|
3517
|
+
const rollingLine2 = ` Rolling 7d: ${rolling.weeklyTokens.toLocaleString()} / ${rolling.weeklyLimit.toLocaleString()} tokens (${rolling.weeklyPercentage.toFixed(1)}% \xB7 ${badgeWeekly})`;
|
|
3518
|
+
lines.push(border("\u2502") + rollingLine2 + " ".repeat(Math.max(1, width - 2 - stripAnsi(rollingLine2).length)) + border("\u2502"));
|
|
3519
|
+
}
|
|
3520
|
+
} catch {
|
|
3195
3521
|
}
|
|
3196
|
-
} catch {
|
|
3197
3522
|
}
|
|
3198
3523
|
}
|
|
3199
3524
|
}
|
|
3200
3525
|
if (hasPlan && plan) {
|
|
3201
|
-
const summary = formatPlanQuotaSummary(plan);
|
|
3526
|
+
const summary = formatPlanQuotaSummary(plan, { hideUnused: true });
|
|
3202
3527
|
if (summary && summary !== "Plan Quota: Unavailable") {
|
|
3203
3528
|
const summaryLines = summary.split("\n");
|
|
3204
3529
|
for (let i = 0; i < summaryLines.length; i++) {
|
|
@@ -6161,6 +6486,10 @@ var KeyboardController = class {
|
|
|
6161
6486
|
this.options.onToggleVerbose?.();
|
|
6162
6487
|
return;
|
|
6163
6488
|
}
|
|
6489
|
+
if (str === "b" || str === "B" || keyName === "b") {
|
|
6490
|
+
this.options.onToggleFullBurn?.();
|
|
6491
|
+
return;
|
|
6492
|
+
}
|
|
6164
6493
|
if (str === "l" || str === "L" || keyName === "l") {
|
|
6165
6494
|
this.options.onTailLog?.();
|
|
6166
6495
|
return;
|
|
@@ -6352,6 +6681,7 @@ function printKeybindingCheatSheet() {
|
|
|
6352
6681
|
console.log(` ${pc13.bold(pc13.green("p"))} Pause / resume automated polling intervals`);
|
|
6353
6682
|
console.log(` ${pc13.bold(pc13.green("s"))} Print current daemon status summary card`);
|
|
6354
6683
|
console.log(` ${pc13.bold(pc13.green("v"))} Toggle verbose streaming logging live`);
|
|
6684
|
+
console.log(` ${pc13.bold(pc13.green("b"))} Toggle full burn mode (override <20% plan quota floor)`);
|
|
6355
6685
|
console.log(` ${pc13.bold(pc13.green("l"))} Tail recent lines from .jonah-fleet/daemon.log`);
|
|
6356
6686
|
console.log(` ${pc13.bold(pc13.green("w"))} Inspect active worktrees and clean stale ones`);
|
|
6357
6687
|
console.log(` ${pc13.bold(pc13.green("q"))} Graceful shutdown (waits for active routine to finish)`);
|
|
@@ -6385,6 +6715,11 @@ function printDaemonStatusSummary(options) {
|
|
|
6385
6715
|
` Verbose Mode: ${verbose ? pc13.green("ENABLED (streaming tokens)") : pc13.gray("DISABLED (compact spinner)")}`
|
|
6386
6716
|
);
|
|
6387
6717
|
}
|
|
6718
|
+
if (state.fullBurn !== void 0) {
|
|
6719
|
+
console.log(
|
|
6720
|
+
` Full Burn: ${state.fullBurn ? pc13.red(pc13.bold("ENABLED (quota floor bypassed)")) : pc13.green("DISABLED (stops at <20% quota)")}`
|
|
6721
|
+
);
|
|
6722
|
+
}
|
|
6388
6723
|
if (pendingRoutine) {
|
|
6389
6724
|
console.log(` Queued Routine: ${pc13.cyan(pc13.bold(pendingRoutine))}`);
|
|
6390
6725
|
}
|
|
@@ -6443,10 +6778,11 @@ function formatDaemonStatusLine(options = {}) {
|
|
|
6443
6778
|
const columns = options.columns !== void 0 ? options.columns : process.stderr.columns || 80;
|
|
6444
6779
|
const maxCols = Math.max(20, (columns || 80) - 2);
|
|
6445
6780
|
const includeTip = columns >= 55;
|
|
6781
|
+
const burnBadge = options.fullBurn ? pc13.red(" [FULL BURN]") : "";
|
|
6446
6782
|
let core;
|
|
6447
6783
|
if (options.isPaused) {
|
|
6448
6784
|
const queueStr = options.pendingRoutine ? pc13.cyan(` [Queued: ${options.pendingRoutine}]`) : "";
|
|
6449
|
-
core = `${pc13.dim("[" + timeString + "]")} \u23F8 ${pc13.yellow("PAUSED")}${queueStr}`;
|
|
6785
|
+
core = `${pc13.dim("[" + timeString + "]")} \u23F8 ${pc13.yellow("PAUSED")}${burnBadge}${queueStr}`;
|
|
6450
6786
|
} else {
|
|
6451
6787
|
const nextCheck = options.nextCheckTime !== void 0 ? options.nextCheckTime : nowMs;
|
|
6452
6788
|
const diffMs = Math.max(0, nextCheck - nowMs);
|
|
@@ -6456,7 +6792,7 @@ function formatDaemonStatusLine(options = {}) {
|
|
|
6456
6792
|
const timeStr = `${mins}m ${secs < 10 ? "0" : ""}${secs}s`;
|
|
6457
6793
|
const prStr = options.lastOpenPRCount !== void 0 ? ` (${options.lastOpenPRCount} ready PRs)` : "";
|
|
6458
6794
|
const queueStr = options.pendingRoutine ? pc13.cyan(` [Queued: ${options.pendingRoutine}]`) : "";
|
|
6459
|
-
core = `${pc13.dim("[" + timeString + "]")} \u26A1 ${pc13.dim("Watchdog Idle \xB7 Next check in " + timeStr + prStr)}${queueStr}`;
|
|
6795
|
+
core = `${pc13.dim("[" + timeString + "]")} \u26A1 ${pc13.dim("Watchdog Idle \xB7 Next check in " + timeStr + prStr)}${burnBadge}${queueStr}`;
|
|
6460
6796
|
}
|
|
6461
6797
|
if (!includeTip) {
|
|
6462
6798
|
return truncateAnsi(core, maxCols);
|
|
@@ -6514,6 +6850,46 @@ function isDaemonRunning(repoRoot) {
|
|
|
6514
6850
|
return false;
|
|
6515
6851
|
}
|
|
6516
6852
|
}
|
|
6853
|
+
function isFullBurnEnabled(options = {}, state, repoRoot) {
|
|
6854
|
+
if (options.fullBurn === true || state?.fullBurn === true) return true;
|
|
6855
|
+
if (process.env.JONAH_FLEET_FULL_BURN === "true" || process.env.JONAH_FLEET_FULL_BURN === "1" || process.env.FULL_BURN === "true" || process.env.FULL_BURN === "1") {
|
|
6856
|
+
return true;
|
|
6857
|
+
}
|
|
6858
|
+
if (repoRoot) {
|
|
6859
|
+
try {
|
|
6860
|
+
const manifest = loadManifest(repoRoot);
|
|
6861
|
+
if (manifest?.budgets?.fullBurn === true || manifest?.daemon?.fullBurn === true) {
|
|
6862
|
+
return true;
|
|
6863
|
+
}
|
|
6864
|
+
} catch {
|
|
6865
|
+
}
|
|
6866
|
+
}
|
|
6867
|
+
return false;
|
|
6868
|
+
}
|
|
6869
|
+
async function checkDaemonQuotaGuard(repoRoot, options = {}, state, cachedQuota) {
|
|
6870
|
+
if (isFullBurnEnabled(options, state, repoRoot)) {
|
|
6871
|
+
return { shouldStop: false };
|
|
6872
|
+
}
|
|
6873
|
+
let quota = cachedQuota;
|
|
6874
|
+
if (!quota) {
|
|
6875
|
+
if (options.getPlanQuota) {
|
|
6876
|
+
quota = await options.getPlanQuota(repoRoot);
|
|
6877
|
+
} else {
|
|
6878
|
+
quota = await fetchActualPlanQuota({ repoRoot });
|
|
6879
|
+
}
|
|
6880
|
+
}
|
|
6881
|
+
if (!quota || !quota.available) {
|
|
6882
|
+
return { shouldStop: false };
|
|
6883
|
+
}
|
|
6884
|
+
const depletion = checkPlanQuotaDepletion(quota, {
|
|
6885
|
+
thresholdPct: options.quotaThresholdPct ?? 20,
|
|
6886
|
+
model: options.model
|
|
6887
|
+
});
|
|
6888
|
+
if (depletion.depleted) {
|
|
6889
|
+
return { shouldStop: true, depletion };
|
|
6890
|
+
}
|
|
6891
|
+
return { shouldStop: false };
|
|
6892
|
+
}
|
|
6517
6893
|
function isBotLogin(login) {
|
|
6518
6894
|
if (!login) return false;
|
|
6519
6895
|
const l = login.toLowerCase().trim();
|
|
@@ -6835,9 +7211,16 @@ async function startBackgroundDaemon(repoRoot, options = {}) {
|
|
|
6835
7211
|
const existing = readDaemonState(repoRoot);
|
|
6836
7212
|
throw new Error(`Daemon is already running with PID ${existing?.pid}`);
|
|
6837
7213
|
}
|
|
7214
|
+
const guard = await checkDaemonQuotaGuard(repoRoot, options);
|
|
7215
|
+
if (guard.shouldStop) {
|
|
7216
|
+
throw new Error(
|
|
7217
|
+
`Plan quota is below 20% (${guard.depletion?.message}). Daemon stopped to protect compute budget. Run with --full-burn to bypass this guard.`
|
|
7218
|
+
);
|
|
7219
|
+
}
|
|
6838
7220
|
const reviewInterval = options.reviewInterval || 3;
|
|
6839
7221
|
const autoworkInterval = options.autoworkInterval || options.interval || 30;
|
|
6840
7222
|
const routines = options.routines || ["peer-review", "autowork"];
|
|
7223
|
+
const fullBurn = isFullBurnEnabled(options, void 0, repoRoot);
|
|
6841
7224
|
const logFilePath = path15.join(repoRoot, ".jonah-fleet", "daemon.log");
|
|
6842
7225
|
fs15.mkdirSync(path15.dirname(logFilePath), { recursive: true });
|
|
6843
7226
|
const logFd = fs15.openSync(logFilePath, "a");
|
|
@@ -6858,11 +7241,14 @@ async function startBackgroundDaemon(repoRoot, options = {}) {
|
|
|
6858
7241
|
if (options.verbose) {
|
|
6859
7242
|
args.push("--verbose");
|
|
6860
7243
|
}
|
|
7244
|
+
if (fullBurn) {
|
|
7245
|
+
args.push("--full-burn");
|
|
7246
|
+
}
|
|
6861
7247
|
const child = spawn2(process.execPath, [cliPath, ...args], {
|
|
6862
7248
|
cwd: repoRoot,
|
|
6863
7249
|
detached: true,
|
|
6864
7250
|
stdio: ["ignore", logFd, logFd],
|
|
6865
|
-
env: { ...process.env, JONAH_FLEET_DAEMON: "true" }
|
|
7251
|
+
env: { ...process.env, JONAH_FLEET_DAEMON: "true", ...fullBurn ? { JONAH_FLEET_FULL_BURN: "true" } : {} }
|
|
6866
7252
|
});
|
|
6867
7253
|
child.unref();
|
|
6868
7254
|
const state = {
|
|
@@ -6871,7 +7257,8 @@ async function startBackgroundDaemon(repoRoot, options = {}) {
|
|
|
6871
7257
|
reviewIntervalMinutes: reviewInterval,
|
|
6872
7258
|
autoworkIntervalMinutes: autoworkInterval,
|
|
6873
7259
|
routines,
|
|
6874
|
-
status: "idle"
|
|
7260
|
+
status: "idle",
|
|
7261
|
+
fullBurn
|
|
6875
7262
|
};
|
|
6876
7263
|
writeDaemonState(repoRoot, state);
|
|
6877
7264
|
return state;
|
|
@@ -7009,9 +7396,22 @@ async function drainReviewQueue(drainOptions) {
|
|
|
7009
7396
|
runRoutine = runLocalRoutine,
|
|
7010
7397
|
onAttempted,
|
|
7011
7398
|
failureCooldowns,
|
|
7012
|
-
onQuotaExhausted
|
|
7399
|
+
onQuotaExhausted,
|
|
7400
|
+
onQuotaDepleted
|
|
7013
7401
|
} = drainOptions;
|
|
7014
7402
|
if (isStopping()) return;
|
|
7403
|
+
const guard = await checkDaemonQuotaGuard(repoRoot, options, state);
|
|
7404
|
+
if (guard.shouldStop) {
|
|
7405
|
+
if (clearTicker) clearTicker();
|
|
7406
|
+
console.warn(
|
|
7407
|
+
pc14.red(
|
|
7408
|
+
`
|
|
7409
|
+
\u{1F6D1} Peer Review Watchdog stopped: Plan quota is below 20% (${guard.depletion?.message}).`
|
|
7410
|
+
)
|
|
7411
|
+
);
|
|
7412
|
+
onQuotaDepleted?.(guard.depletion);
|
|
7413
|
+
return;
|
|
7414
|
+
}
|
|
7015
7415
|
if (state) {
|
|
7016
7416
|
state.lastReviewCheckAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
7017
7417
|
writeDaemonState(repoRoot, state);
|
|
@@ -7055,6 +7455,18 @@ async function drainReviewQueue(drainOptions) {
|
|
|
7055
7455
|
const currentPR = candidatePRs[0];
|
|
7056
7456
|
const targetLabel = formatTargetLabel(`PR #${currentPR.number}`, currentPR.title);
|
|
7057
7457
|
let targetPRStr = targetLabel;
|
|
7458
|
+
const loopGuard = await checkDaemonQuotaGuard(repoRoot, options, state);
|
|
7459
|
+
if (loopGuard.shouldStop) {
|
|
7460
|
+
if (clearTicker) clearTicker();
|
|
7461
|
+
console.warn(
|
|
7462
|
+
pc14.red(
|
|
7463
|
+
`
|
|
7464
|
+
\u{1F6D1} Peer Review Watchdog stopped: Plan quota is below 20% (${loopGuard.depletion?.message}).`
|
|
7465
|
+
)
|
|
7466
|
+
);
|
|
7467
|
+
onQuotaDepleted?.(loopGuard.depletion);
|
|
7468
|
+
break;
|
|
7469
|
+
}
|
|
7058
7470
|
try {
|
|
7059
7471
|
if (clearTicker) clearTicker();
|
|
7060
7472
|
if (state) {
|
|
@@ -7088,6 +7500,20 @@ async function drainReviewQueue(drainOptions) {
|
|
|
7088
7500
|
}
|
|
7089
7501
|
}
|
|
7090
7502
|
});
|
|
7503
|
+
if (result.planQuota) {
|
|
7504
|
+
const postGuard = await checkDaemonQuotaGuard(repoRoot, options, state, result.planQuota);
|
|
7505
|
+
if (postGuard.shouldStop) {
|
|
7506
|
+
if (clearTicker) clearTicker();
|
|
7507
|
+
console.warn(
|
|
7508
|
+
pc14.red(
|
|
7509
|
+
`
|
|
7510
|
+
\u{1F6D1} Peer Review Watchdog stopped: Plan quota dropped below 20% after routine (${postGuard.depletion?.message}).`
|
|
7511
|
+
)
|
|
7512
|
+
);
|
|
7513
|
+
onQuotaDepleted?.(postGuard.depletion);
|
|
7514
|
+
break;
|
|
7515
|
+
}
|
|
7516
|
+
}
|
|
7091
7517
|
const activeTargetStr = targetPRStr;
|
|
7092
7518
|
const match = activeTargetStr?.match(/PR\s*#?([0-9]+)/i);
|
|
7093
7519
|
const prNum = match ? parseInt(match[1], 10) : currentPR.number;
|
|
@@ -7200,6 +7626,18 @@ async function performAutoworkScan(scanOptions) {
|
|
|
7200
7626
|
onDiagnosticCard
|
|
7201
7627
|
} = scanOptions;
|
|
7202
7628
|
if (isStopping()) return { executed: false, reason: "stopping" };
|
|
7629
|
+
const initialGuard = await checkDaemonQuotaGuard(repoRoot, options, state);
|
|
7630
|
+
if (initialGuard.shouldStop) {
|
|
7631
|
+
if (clearTicker) clearTicker();
|
|
7632
|
+
console.warn(
|
|
7633
|
+
pc14.red(
|
|
7634
|
+
`
|
|
7635
|
+
\u{1F6D1} Autowork Backlog Scan stopped: Plan quota depleted (<20% remaining). ${initialGuard.depletion?.message}`
|
|
7636
|
+
)
|
|
7637
|
+
);
|
|
7638
|
+
scanOptions.onQuotaDepleted?.(initialGuard.depletion);
|
|
7639
|
+
return { executed: false, reason: "quota_depleted" };
|
|
7640
|
+
}
|
|
7203
7641
|
const routines = options.routines || ["peer-review", "autowork"];
|
|
7204
7642
|
if (routines.includes("peer-review")) {
|
|
7205
7643
|
const pendingPRs = (await getPRs(repoRoot)).length;
|
|
@@ -7220,7 +7658,8 @@ async function performAutoworkScan(scanOptions) {
|
|
|
7220
7658
|
runRoutine,
|
|
7221
7659
|
onAttempted: scanOptions.onAttempted,
|
|
7222
7660
|
failureCooldowns: scanOptions.failureCooldowns,
|
|
7223
|
-
onQuotaExhausted: scanOptions.onQuotaExhausted
|
|
7661
|
+
onQuotaExhausted: scanOptions.onQuotaExhausted,
|
|
7662
|
+
onQuotaDepleted: scanOptions.onQuotaDepleted
|
|
7224
7663
|
});
|
|
7225
7664
|
const remainingPRs = (await getPRs(repoRoot)).length;
|
|
7226
7665
|
if (remainingPRs > 0) {
|
|
@@ -7270,6 +7709,20 @@ async function performAutoworkScan(scanOptions) {
|
|
|
7270
7709
|
}
|
|
7271
7710
|
});
|
|
7272
7711
|
recordDaemonSessionUsage(repoRoot, state, result.usage);
|
|
7712
|
+
if (result.planQuota) {
|
|
7713
|
+
const postRoutineGuard = await checkDaemonQuotaGuard(repoRoot, options, state, result.planQuota);
|
|
7714
|
+
if (postRoutineGuard.shouldStop) {
|
|
7715
|
+
if (clearTicker) clearTicker();
|
|
7716
|
+
console.warn(
|
|
7717
|
+
pc14.red(
|
|
7718
|
+
`
|
|
7719
|
+
\u{1F6D1} Autowork Scan stopped: Plan quota dropped below 20% after routine (${postRoutineGuard.depletion?.message}).`
|
|
7720
|
+
)
|
|
7721
|
+
);
|
|
7722
|
+
scanOptions.onQuotaDepleted?.(postRoutineGuard.depletion);
|
|
7723
|
+
return { executed: true, reason: "quota_depleted" };
|
|
7724
|
+
}
|
|
7725
|
+
}
|
|
7273
7726
|
const isQuota = result.quotaPaused || !result.success && detectQuotaExceeded(result.output || "", result.stderr || "").isQuota;
|
|
7274
7727
|
if (isQuota) {
|
|
7275
7728
|
const resetInfo = result.quotaResetInfo || detectQuotaExceeded(result.output || "", result.stderr || "").resetInfo;
|
|
@@ -7315,7 +7768,8 @@ function initializeDaemonState(repoRoot, options = {}) {
|
|
|
7315
7768
|
routines,
|
|
7316
7769
|
status: "idle",
|
|
7317
7770
|
evaluatedPRs: existingState?.evaluatedPRs,
|
|
7318
|
-
failureCooldowns: existingState?.failureCooldowns
|
|
7771
|
+
failureCooldowns: existingState?.failureCooldowns,
|
|
7772
|
+
fullBurn: isFullBurnEnabled(options, existingState, repoRoot)
|
|
7319
7773
|
};
|
|
7320
7774
|
writeDaemonState(repoRoot, state);
|
|
7321
7775
|
const failureCooldowns = /* @__PURE__ */ new Map();
|
|
@@ -7331,6 +7785,21 @@ async function runDaemonLoop(repoRoot, options = {}) {
|
|
|
7331
7785
|
const autoworkInterval = options.autoworkInterval || options.interval || 30;
|
|
7332
7786
|
const routines = options.routines || ["peer-review", "autowork"];
|
|
7333
7787
|
const { state, failureCooldowns } = initializeDaemonState(repoRoot, options);
|
|
7788
|
+
const initialGuard = await checkDaemonQuotaGuard(repoRoot, options, state);
|
|
7789
|
+
if (initialGuard.shouldStop) {
|
|
7790
|
+
console.warn(
|
|
7791
|
+
pc14.red(
|
|
7792
|
+
`
|
|
7793
|
+
\u{1F6D1} Daemon stopped: Plan quota is below 20% (${initialGuard.depletion?.message}).
|
|
7794
|
+
Remaining quota must stay >= 20% for 5h and 7days windows.
|
|
7795
|
+
To bypass this safety limit, start with --full-burn or set FULL_BURN=true.
|
|
7796
|
+
`
|
|
7797
|
+
)
|
|
7798
|
+
);
|
|
7799
|
+
clearDaemonState(repoRoot);
|
|
7800
|
+
return;
|
|
7801
|
+
}
|
|
7802
|
+
const isFullBurn = isFullBurnEnabled(options, state, repoRoot);
|
|
7334
7803
|
console.log(
|
|
7335
7804
|
renderFleetBanner({
|
|
7336
7805
|
command: "DAEMON",
|
|
@@ -7340,8 +7809,9 @@ async function runDaemonLoop(repoRoot, options = {}) {
|
|
|
7340
7809
|
{ label: "Review Watchdog", value: `Every ${reviewInterval}m (zero-token preflight)` },
|
|
7341
7810
|
{ label: "Autowork Scan", value: `Every ${autoworkInterval}m` },
|
|
7342
7811
|
{ label: "Routines", value: routines.join(", ") },
|
|
7812
|
+
{ label: "Full Burn", value: isFullBurn ? "ENABLED" : "DISABLED (stops at <20% quota)" },
|
|
7343
7813
|
{ label: "Target", value: repoRoot },
|
|
7344
|
-
{ label: "Hotkeys", value: "'r' review \xB7 'a' autowork \xB7 'p' pause \xB7 's' status \xB7 '?' help" }
|
|
7814
|
+
{ label: "Hotkeys", value: "'r' review \xB7 'a' autowork \xB7 'p' pause \xB7 's' status \xB7 'b' burn \xB7 '?' help" }
|
|
7345
7815
|
]
|
|
7346
7816
|
})
|
|
7347
7817
|
);
|
|
@@ -7430,7 +7900,10 @@ Stopping local agent daemon...`));
|
|
|
7430
7900
|
getPRs: getPRsFn,
|
|
7431
7901
|
runRoutine: runRoutineFn,
|
|
7432
7902
|
failureCooldowns,
|
|
7433
|
-
onQuotaExhausted: handleQuotaPause
|
|
7903
|
+
onQuotaExhausted: handleQuotaPause,
|
|
7904
|
+
onQuotaDepleted: async () => {
|
|
7905
|
+
await handleStop();
|
|
7906
|
+
}
|
|
7434
7907
|
});
|
|
7435
7908
|
try {
|
|
7436
7909
|
const prs = await getPRsFn(repoRoot);
|
|
@@ -7488,7 +7961,10 @@ Stopping local agent daemon...`));
|
|
|
7488
7961
|
getBacklog: options.getBacklog || getBacklogTriageReport,
|
|
7489
7962
|
runRoutine: runRoutineFn,
|
|
7490
7963
|
failureCooldowns,
|
|
7491
|
-
onQuotaExhausted: handleQuotaPause
|
|
7964
|
+
onQuotaExhausted: handleQuotaPause,
|
|
7965
|
+
onQuotaDepleted: async () => {
|
|
7966
|
+
await handleStop();
|
|
7967
|
+
}
|
|
7492
7968
|
});
|
|
7493
7969
|
try {
|
|
7494
7970
|
const prs = await getPRsFn(repoRoot);
|
|
@@ -7549,6 +8025,19 @@ Stopping local agent daemon...`));
|
|
|
7549
8025
|
};
|
|
7550
8026
|
const runTargetedReview = async (prNumber) => {
|
|
7551
8027
|
if (isStopping || isWorking) return;
|
|
8028
|
+
const guard = await checkDaemonQuotaGuard(repoRoot, options, state);
|
|
8029
|
+
if (guard.shouldStop) {
|
|
8030
|
+
clearTicker();
|
|
8031
|
+
console.warn(
|
|
8032
|
+
pc14.red(
|
|
8033
|
+
`
|
|
8034
|
+
\u{1F6D1} Cannot start targeted review: Plan quota is below 20% (${guard.depletion?.message}).
|
|
8035
|
+
Press 'b' to toggle full-burn mode if you wish to proceed.`
|
|
8036
|
+
)
|
|
8037
|
+
);
|
|
8038
|
+
updateTicker();
|
|
8039
|
+
return;
|
|
8040
|
+
}
|
|
7552
8041
|
try {
|
|
7553
8042
|
isWorking = true;
|
|
7554
8043
|
clearTicker();
|
|
@@ -7573,6 +8062,20 @@ Stopping local agent daemon...`));
|
|
|
7573
8062
|
writeDaemonState(repoRoot, state);
|
|
7574
8063
|
}
|
|
7575
8064
|
});
|
|
8065
|
+
if (result.planQuota) {
|
|
8066
|
+
const postGuard = await checkDaemonQuotaGuard(repoRoot, options, state, result.planQuota);
|
|
8067
|
+
if (postGuard.shouldStop) {
|
|
8068
|
+
clearTicker();
|
|
8069
|
+
console.warn(
|
|
8070
|
+
pc14.red(
|
|
8071
|
+
`
|
|
8072
|
+
\u{1F6D1} Daemon stopped after targeted review: Plan quota dropped below 20% (${postGuard.depletion?.message}).`
|
|
8073
|
+
)
|
|
8074
|
+
);
|
|
8075
|
+
await handleStop();
|
|
8076
|
+
return;
|
|
8077
|
+
}
|
|
8078
|
+
}
|
|
7576
8079
|
if (result.success) {
|
|
7577
8080
|
const outcome = result.outcome || detectPeerReviewOutcome({
|
|
7578
8081
|
output: result.output,
|
|
@@ -7616,6 +8119,19 @@ Stopping local agent daemon...`));
|
|
|
7616
8119
|
};
|
|
7617
8120
|
const runTargetedAutowork = async (issueNumber) => {
|
|
7618
8121
|
if (isStopping || isWorking) return;
|
|
8122
|
+
const guard = await checkDaemonQuotaGuard(repoRoot, options, state);
|
|
8123
|
+
if (guard.shouldStop) {
|
|
8124
|
+
clearTicker();
|
|
8125
|
+
console.warn(
|
|
8126
|
+
pc14.red(
|
|
8127
|
+
`
|
|
8128
|
+
\u{1F6D1} Cannot start targeted autowork: Plan quota is below 20% (${guard.depletion?.message}).
|
|
8129
|
+
Press 'b' to toggle full-burn mode if you wish to proceed.`
|
|
8130
|
+
)
|
|
8131
|
+
);
|
|
8132
|
+
updateTicker();
|
|
8133
|
+
return;
|
|
8134
|
+
}
|
|
7619
8135
|
try {
|
|
7620
8136
|
isWorking = true;
|
|
7621
8137
|
clearTicker();
|
|
@@ -7640,6 +8156,20 @@ Stopping local agent daemon...`));
|
|
|
7640
8156
|
writeDaemonState(repoRoot, state);
|
|
7641
8157
|
}
|
|
7642
8158
|
});
|
|
8159
|
+
if (result.planQuota) {
|
|
8160
|
+
const postGuard = await checkDaemonQuotaGuard(repoRoot, options, state, result.planQuota);
|
|
8161
|
+
if (postGuard.shouldStop) {
|
|
8162
|
+
clearTicker();
|
|
8163
|
+
console.warn(
|
|
8164
|
+
pc14.red(
|
|
8165
|
+
`
|
|
8166
|
+
\u{1F6D1} Daemon stopped after targeted autowork: Plan quota dropped below 20% (${postGuard.depletion?.message}).`
|
|
8167
|
+
)
|
|
8168
|
+
);
|
|
8169
|
+
await handleStop();
|
|
8170
|
+
return;
|
|
8171
|
+
}
|
|
8172
|
+
}
|
|
7643
8173
|
if (result.success) {
|
|
7644
8174
|
console.log(pc14.green(`\u2713 Targeted autowork on Issue #${issueNumber} completed successfully.
|
|
7645
8175
|
`));
|
|
@@ -7834,6 +8364,27 @@ ${pc14.cyan("Enter Issue # to work (Esc/Enter to cancel):")} `, {
|
|
|
7834
8364
|
}
|
|
7835
8365
|
updateTicker();
|
|
7836
8366
|
},
|
|
8367
|
+
onToggleFullBurn: () => {
|
|
8368
|
+
if (isStopping || isGracefulStopping) return;
|
|
8369
|
+
options.fullBurn = !isFullBurnEnabled(options, state, repoRoot);
|
|
8370
|
+
if (state) {
|
|
8371
|
+
state.fullBurn = options.fullBurn;
|
|
8372
|
+
writeDaemonState(repoRoot, state);
|
|
8373
|
+
}
|
|
8374
|
+
clearTicker();
|
|
8375
|
+
if (options.fullBurn) {
|
|
8376
|
+
console.log(
|
|
8377
|
+
pc14.red(`
|
|
8378
|
+
[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] \u{1F525} Full burn mode ENABLED (ignoring <20% quota floor).`)
|
|
8379
|
+
);
|
|
8380
|
+
} else {
|
|
8381
|
+
console.log(
|
|
8382
|
+
pc14.green(`
|
|
8383
|
+
[${(/* @__PURE__ */ new Date()).toLocaleTimeString()}] \u{1F6E1}\uFE0F Full burn mode DISABLED (daemon will stop if quota < 20%).`)
|
|
8384
|
+
);
|
|
8385
|
+
}
|
|
8386
|
+
updateTicker();
|
|
8387
|
+
},
|
|
7837
8388
|
onTailLog: () => {
|
|
7838
8389
|
if (isStopping || isGracefulStopping) return;
|
|
7839
8390
|
clearTicker();
|
|
@@ -7928,6 +8479,18 @@ ${pc14.cyan("Enter Issue # to work (Esc/Enter to cancel):")} `, {
|
|
|
7928
8479
|
isTicking = true;
|
|
7929
8480
|
try {
|
|
7930
8481
|
if (!isPaused) {
|
|
8482
|
+
const tickGuard = await checkDaemonQuotaGuard(repoRoot, options, state);
|
|
8483
|
+
if (tickGuard.shouldStop) {
|
|
8484
|
+
clearTicker();
|
|
8485
|
+
console.warn(
|
|
8486
|
+
pc14.red(
|
|
8487
|
+
`
|
|
8488
|
+
\u{1F6D1} Daemon stopped by quota guard: Plan quota is below 20% (${tickGuard.depletion?.message}). Stopping daemon...`
|
|
8489
|
+
)
|
|
8490
|
+
);
|
|
8491
|
+
await handleStop();
|
|
8492
|
+
return;
|
|
8493
|
+
}
|
|
7931
8494
|
const now = Date.now();
|
|
7932
8495
|
if (routines.includes("peer-review") && now >= nextReviewCheckTime) {
|
|
7933
8496
|
await performReviewDrain();
|
|
@@ -7963,7 +8526,8 @@ async function runDaemonCommand(action, options = {}) {
|
|
|
7963
8526
|
routines: options.routines ? options.routines.split(",").map((r) => r.trim()) : void 0,
|
|
7964
8527
|
model: options.model,
|
|
7965
8528
|
foreground: options.foreground,
|
|
7966
|
-
verbose: options.verbose
|
|
8529
|
+
verbose: options.verbose,
|
|
8530
|
+
fullBurn: options.fullBurn
|
|
7967
8531
|
};
|
|
7968
8532
|
if (act === "start") {
|
|
7969
8533
|
if (options.foreground) {
|
|
@@ -7987,6 +8551,7 @@ async function runDaemonCommand(action, options = {}) {
|
|
|
7987
8551
|
{ label: "Review Watchdog", value: `Every ${state2.reviewIntervalMinutes}m (zero-token preflight)` },
|
|
7988
8552
|
{ label: "Autowork Scan", value: `Every ${state2.autoworkIntervalMinutes}m` },
|
|
7989
8553
|
{ label: "Routines", value: state2.routines.join(", ") },
|
|
8554
|
+
{ label: "Full Burn", value: state2.fullBurn ? "ENABLED" : "DISABLED (stops at <20% quota)" },
|
|
7990
8555
|
{ label: "Log", value: ".jonah-fleet/daemon.log" }
|
|
7991
8556
|
]
|
|
7992
8557
|
})
|
|
@@ -8041,6 +8606,7 @@ Stopping background agent daemon (PID ${state2?.pid})...`));
|
|
|
8041
8606
|
details: [
|
|
8042
8607
|
{ label: "Status", value: running && state ? state.status.toUpperCase() : "STOPPED" },
|
|
8043
8608
|
...running && state ? [{ label: "PID", value: String(state.pid) }] : [],
|
|
8609
|
+
...running && state && state.fullBurn !== void 0 ? [{ label: "Full Burn", value: state.fullBurn ? "ENABLED" : "DISABLED" }] : [],
|
|
8044
8610
|
{ label: "Target", value: cwd }
|
|
8045
8611
|
]
|
|
8046
8612
|
})
|
|
@@ -8055,6 +8621,11 @@ Stopping background agent daemon (PID ${state2?.pid})...`));
|
|
|
8055
8621
|
console.log(` Peer Review Cadence: Every ${state.reviewIntervalMinutes} minutes (0-token fast preflight)`);
|
|
8056
8622
|
console.log(` Autowork Cadence: Every ${state.autoworkIntervalMinutes} minutes`);
|
|
8057
8623
|
console.log(` Routines: ${state.routines.join(", ")}`);
|
|
8624
|
+
if (state.fullBurn !== void 0) {
|
|
8625
|
+
console.log(
|
|
8626
|
+
` Full Burn: ${state.fullBurn ? pc15.red(pc15.bold("ENABLED (quota floor bypassed)")) : pc15.green("DISABLED (stops at <20% quota)")}`
|
|
8627
|
+
);
|
|
8628
|
+
}
|
|
8058
8629
|
let currentStateText = pc15.green("IDLE");
|
|
8059
8630
|
if (state.status === "working") {
|
|
8060
8631
|
const workingDesc = state.activeRoutine + (state.activeTarget ? ` (${pc15.bold(state.activeTarget)})` : "");
|
|
@@ -8225,7 +8796,7 @@ program.name("jonah-fleet").description("Manage autonomous agent fleet, prompt r
|
|
|
8225
8796
|
program.command("run <routine>").description("Run a specific prompt routine locally in an isolated git worktree").option("-i, --issue <number>", "Targeted issue number for autowork").option("-p, --pr <number>", "Targeted pull request number for peer-review").option("-m, --model <model>", "LLM model override (defaults to gemini-3.8-flash-high)").option("--timeout <duration>", "CLI execution print timeout (default: 30m)").option("--no-worktree", "Execute directly in current directory without creating a git worktree").option("--keep-worktree", "Preserve the git worktree after routine execution completes").option("-d, --dry-run", "Preview prompt and execution parameters without launching agent").option("-v, --verbose", "Stream raw agent tokens and logs directly to stdout").action(async (routine, options) => {
|
|
8226
8797
|
await runRoutineCommand(routine, options);
|
|
8227
8798
|
});
|
|
8228
|
-
program.command("daemon [action]").description("Manage background local worker daemon polling for unclaimed issues and pull requests").option("-i, --interval <minutes>", "Legacy global polling interval in minutes (default: 30)").option("--review-interval <minutes>", "Peer Review watchdog cadence in minutes (default: 3)").option("--autowork-interval <minutes>", "Autowork backlog cadence in minutes (default: 30)").option("-r, --routines <list>", "Comma-separated routines to run (default: peer-review,autowork)").option("-m, --model <model>", "LLM model override").option("--foreground", "Run daemon in foreground with live console logs").option("-v, --verbose", "Stream raw agent tokens and logs directly to stdout").action(async (action, options) => {
|
|
8799
|
+
program.command("daemon [action]").description("Manage background local worker daemon polling for unclaimed issues and pull requests").option("-i, --interval <minutes>", "Legacy global polling interval in minutes (default: 30)").option("--review-interval <minutes>", "Peer Review watchdog cadence in minutes (default: 3)").option("--autowork-interval <minutes>", "Autowork backlog cadence in minutes (default: 30)").option("-r, --routines <list>", "Comma-separated routines to run (default: peer-review,autowork)").option("-m, --model <model>", "LLM model override").option("--foreground", "Run daemon in foreground with live console logs").option("-v, --verbose", "Stream raw agent tokens and logs directly to stdout").option("--full-burn", "Run in full burn mode without stopping on <20% plan quota limit").action(async (action, options) => {
|
|
8229
8800
|
await runDaemonCommand(action, options);
|
|
8230
8801
|
});
|
|
8231
8802
|
program.command("labels [action]").description("Audit, prune, or provision repository labels for autonomous fleet taxonomy (audit | prune | provision)").option("-d, --dry-run", "Preview prunable labels without deleting them", false).option("-y, --yes", "Confirm automatic deletion of prunable labels", false).option("-r, --repo <repo>", "Target GitHub repository (defaults to current)").option("-j, --json", "Output results as JSON", false).action(async (action, options) => {
|