jonah-fleet 1.12.0 → 1.14.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 +16 -5
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +356 -4
- package/dist/lib/installer.d.ts.map +1 -1
- package/dist/lib/labels.d.ts.map +1 -1
- package/dist/lib/loop-guard.d.ts +80 -0
- package/dist/lib/loop-guard.d.ts.map +1 -0
- package/dist/lib/presets.d.ts +5 -0
- package/dist/lib/presets.d.ts.map +1 -1
- package/dist/lib/runner.d.ts +10 -1
- package/dist/lib/runner.d.ts.map +1 -1
- package/package.json +1 -1
- package/schema.json +26 -0
- package/templates/docs/LESSONS.template.md +7 -0
- package/templates/prompts/ORCHESTRATION.md +26 -0
- package/templates/prompts/analytics-review.md +41 -0
- package/templates/prompts/autowork.md +26 -5
- package/templates/prompts/issues-housekeeping.md +11 -1
- package/templates/prompts/optimizer.md +3 -0
- package/templates/prompts/peer-review.md +1 -0
- package/templates/scripts/run-with-loop-guard.js +439 -0
- package/templates/skills/triage/SKILL.md +19 -14
- package/templates/workflows/analytics-review-cron.yml +313 -0
- package/templates/workflows/autowork-cron.yml +59 -4
- package/templates/workflows/dependency-check-cron.yml +64 -1
- package/templates/workflows/design-review-cron.yml +59 -4
- package/templates/workflows/issues-housekeeping-cron.yml +64 -1
- package/templates/workflows/prompt-optimizer-cron.yml +59 -4
- package/templates/workflows/trigger-autowork-manual.yml +59 -4
- package/templates/workflows/trigger-autowork-on-bug.yml +59 -4
- package/templates/workflows/trigger-autowork-on-merge.yml +59 -4
- package/templates/workflows/trigger-review-routine.yml +68 -4
package/CHANGELOG.md
CHANGED
|
@@ -5,19 +5,30 @@ All notable changes to `jonah-fleet` will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
-
## [1.
|
|
8
|
+
## [1.14.0](https://github.com/juliendurandeu/jonah-fleet/compare/v1.13.0...v1.14.0) (2026-09-19)
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
### Features
|
|
12
12
|
|
|
13
|
-
* **
|
|
13
|
+
* **workflows:** handle LLM quota limits with graceful pause (status:quota-paused) ([#240](https://github.com/juliendurandeu/jonah-fleet/issues/240)) ([98bb176](https://github.com/juliendurandeu/jonah-fleet/commit/98bb176f0b29462ab9de6baf5d0f102fee78b0ba))
|
|
14
14
|
|
|
15
|
+
## [Unreleased] - 2026-09-19
|
|
15
16
|
|
|
16
|
-
###
|
|
17
|
+
### Features
|
|
18
|
+
|
|
19
|
+
* **workflows:** intercept LLM quota limit errors (`Individual quota reached`), record reset timeline, label tracking issue `status:quota-paused`, and pause routine execution gracefully with exit code 0 to prevent broken Action builds and wasted runner minutes.
|
|
20
|
+
* **labels:** introduce `status:quota-paused` to core fleet label definitions.
|
|
21
|
+
|
|
22
|
+
## [1.13.0](https://github.com/juliendurandeu/jonah-fleet/compare/v1.12.0...v1.13.0) (2026-09-19)
|
|
17
23
|
|
|
18
|
-
* **runner:** prevent premature routine termination on async background tasks in headless mode ([6641b2b](https://github.com/juliendurandeu/jonah-fleet/commit/6641b2b2e46ab363b73d490aac1144a33a1c69f9))
|
|
19
24
|
|
|
20
|
-
|
|
25
|
+
### Features
|
|
26
|
+
|
|
27
|
+
* **guard:** implement CLI runner loop-guard and circuit-breaker wrapper ([#222](https://github.com/juliendurandeu/jonah-fleet/issues/222)) ([bedacc0](https://github.com/juliendurandeu/jonah-fleet/commit/bedacc0d9bc2a36276f58901ffa2f2b06e797016))
|
|
28
|
+
* **memory:** introduce opt-in LESSONS.md operational memory tier ([#207](https://github.com/juliendurandeu/jonah-fleet/issues/207)) ([#219](https://github.com/juliendurandeu/jonah-fleet/issues/219)) ([13f74fa](https://github.com/juliendurandeu/jonah-fleet/commit/13f74fa2acc78554bba486b2bf77521106265f03))
|
|
29
|
+
* **orchestration:** standardize 'Why I believe this' structured human escalation card ([#229](https://github.com/juliendurandeu/jonah-fleet/issues/229)) ([997e1d2](https://github.com/juliendurandeu/jonah-fleet/commit/997e1d20d7d0a3970ecca9f209841db745f99979))
|
|
30
|
+
|
|
31
|
+
## [1.12.0](https://github.com/juliendurandeu/jonah-fleet/compare/v1.11.0...v1.12.0) (2026-09-18)
|
|
21
32
|
|
|
22
33
|
### Bug Fixes
|
|
23
34
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export
|
|
1
|
+
export * from './lib/loop-guard.js';
|
|
2
2
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AA6IA,cAAc,qBAAqB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -122,7 +122,7 @@ var ROUTINE_TO_WORKFLOW_MAP = {
|
|
|
122
122
|
"issues-housekeeping": ["issues-housekeeping-cron.yml"],
|
|
123
123
|
"dependency-update-security-check": ["dependency-check-cron.yml"],
|
|
124
124
|
"product-planning": [],
|
|
125
|
-
"analytics-review": [],
|
|
125
|
+
"analytics-review": ["analytics-review-cron.yml"],
|
|
126
126
|
"design-review": ["design-review-cron.yml"]
|
|
127
127
|
};
|
|
128
128
|
var FLEET_VERSION = "1.8.0";
|
|
@@ -608,6 +608,22 @@ function installFleet(targetDir, manifest, options = {}) {
|
|
|
608
608
|
}
|
|
609
609
|
}
|
|
610
610
|
}
|
|
611
|
+
const targetScriptsDir = path3.join(targetDir, ".github/scripts");
|
|
612
|
+
fs3.mkdirSync(targetScriptsDir, { recursive: true });
|
|
613
|
+
const baseScripts = ["run-with-loop-guard.js"];
|
|
614
|
+
for (const file of baseScripts) {
|
|
615
|
+
const src = path3.join(templatesDir, "scripts", file);
|
|
616
|
+
const dest = path3.join(targetScriptsDir, file);
|
|
617
|
+
if (fs3.existsSync(src)) {
|
|
618
|
+
if (!fs3.existsSync(dest) || options.force) {
|
|
619
|
+
fs3.copyFileSync(src, dest);
|
|
620
|
+
try {
|
|
621
|
+
fs3.chmodSync(dest, 493);
|
|
622
|
+
} catch {
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
}
|
|
611
627
|
for (const [routineName, isEnabled] of Object.entries(manifest.routines)) {
|
|
612
628
|
if (!isEnabled) continue;
|
|
613
629
|
const promptFile = `${routineName}.md`;
|
|
@@ -672,6 +688,17 @@ function installFleet(targetDir, manifest, options = {}) {
|
|
|
672
688
|
result.docsInstalled.push("AGENTS.md");
|
|
673
689
|
}
|
|
674
690
|
}
|
|
691
|
+
const lessonsEnabled = manifest.lessons === true || typeof manifest.lessons === "object" && manifest.lessons?.enabled;
|
|
692
|
+
if (lessonsEnabled) {
|
|
693
|
+
const lessonsPath = path3.join(targetDir, "LESSONS.md");
|
|
694
|
+
if (!fs3.existsSync(lessonsPath) || options.force) {
|
|
695
|
+
const lessonsSrc = path3.join(templatesDir, "docs/LESSONS.template.md");
|
|
696
|
+
if (fs3.existsSync(lessonsSrc)) {
|
|
697
|
+
fs3.copyFileSync(lessonsSrc, lessonsPath);
|
|
698
|
+
result.docsInstalled.push("LESSONS.md");
|
|
699
|
+
}
|
|
700
|
+
}
|
|
701
|
+
}
|
|
675
702
|
return result;
|
|
676
703
|
}
|
|
677
704
|
|
|
@@ -1321,6 +1348,7 @@ var FLEET_CORE_LABELS = [
|
|
|
1321
1348
|
{ name: "status:running", color: "fbca04", description: "Routine execution in progress" },
|
|
1322
1349
|
{ name: "status:success", color: "0e8a16", description: "Routine execution succeeded" },
|
|
1323
1350
|
{ name: "status:failure", color: "d93f0b", description: "Routine execution failed" },
|
|
1351
|
+
{ name: "status:quota-paused", color: "e4e669", description: "Routine execution paused due to API quota limits" },
|
|
1324
1352
|
{ name: "needs-attention", color: "e11d48", description: "Requires maintainer triage" },
|
|
1325
1353
|
{ name: "runner:github-actions", color: "1f883d", description: "Executed via GitHub Actions" },
|
|
1326
1354
|
{ name: "runner:local", color: "bfd4f2", description: "Executed via local machine daemon" },
|
|
@@ -3495,6 +3523,223 @@ var TerminalSpinner = class {
|
|
|
3495
3523
|
}
|
|
3496
3524
|
};
|
|
3497
3525
|
|
|
3526
|
+
// src/lib/loop-guard.ts
|
|
3527
|
+
import crypto from "crypto";
|
|
3528
|
+
function canonicalStringify(value) {
|
|
3529
|
+
if (value === null || value === void 0) {
|
|
3530
|
+
return "";
|
|
3531
|
+
}
|
|
3532
|
+
if (typeof value !== "object") {
|
|
3533
|
+
return JSON.stringify(value);
|
|
3534
|
+
}
|
|
3535
|
+
if (Array.isArray(value)) {
|
|
3536
|
+
return "[" + value.map(canonicalStringify).join(",") + "]";
|
|
3537
|
+
}
|
|
3538
|
+
const keys = Object.keys(value).sort();
|
|
3539
|
+
const entries = keys.map((k) => `${JSON.stringify(k)}:${canonicalStringify(value[k])}`);
|
|
3540
|
+
return "{" + entries.join(",") + "}";
|
|
3541
|
+
}
|
|
3542
|
+
function computeActionHash(toolName, args) {
|
|
3543
|
+
const canonicalArgs = canonicalStringify(args);
|
|
3544
|
+
return crypto.createHash("sha256").update(`${toolName}:${canonicalArgs}`).digest("hex");
|
|
3545
|
+
}
|
|
3546
|
+
var LoopGuard = class {
|
|
3547
|
+
repetitionThreshold;
|
|
3548
|
+
pingPongThreshold;
|
|
3549
|
+
consecutiveErrorThreshold;
|
|
3550
|
+
slidingWindowSize;
|
|
3551
|
+
onTrip;
|
|
3552
|
+
history = [];
|
|
3553
|
+
lastErrorHash = null;
|
|
3554
|
+
consecutiveErrorCount = 0;
|
|
3555
|
+
trippedResult = null;
|
|
3556
|
+
constructor(options = {}) {
|
|
3557
|
+
this.repetitionThreshold = options.repetitionThreshold ?? 5;
|
|
3558
|
+
this.pingPongThreshold = options.pingPongThreshold ?? 3;
|
|
3559
|
+
this.consecutiveErrorThreshold = options.consecutiveErrorThreshold ?? 2;
|
|
3560
|
+
this.slidingWindowSize = options.slidingWindowSize ?? 20;
|
|
3561
|
+
this.onTrip = options.onTrip;
|
|
3562
|
+
}
|
|
3563
|
+
setOnTrip(callback) {
|
|
3564
|
+
this.onTrip = callback;
|
|
3565
|
+
}
|
|
3566
|
+
isTripped() {
|
|
3567
|
+
return this.trippedResult !== null;
|
|
3568
|
+
}
|
|
3569
|
+
getTrip() {
|
|
3570
|
+
return this.trippedResult;
|
|
3571
|
+
}
|
|
3572
|
+
getHistory() {
|
|
3573
|
+
return [...this.history];
|
|
3574
|
+
}
|
|
3575
|
+
reset() {
|
|
3576
|
+
this.history = [];
|
|
3577
|
+
this.lastErrorHash = null;
|
|
3578
|
+
this.consecutiveErrorCount = 0;
|
|
3579
|
+
this.trippedResult = null;
|
|
3580
|
+
}
|
|
3581
|
+
/**
|
|
3582
|
+
* Records an action occurrence and evaluates repetition, ping-pong, and error guards.
|
|
3583
|
+
*/
|
|
3584
|
+
recordAction(toolName, args, isError = false) {
|
|
3585
|
+
if (this.trippedResult) {
|
|
3586
|
+
return this.trippedResult;
|
|
3587
|
+
}
|
|
3588
|
+
const hash = computeActionHash(toolName, args);
|
|
3589
|
+
const record = {
|
|
3590
|
+
toolName,
|
|
3591
|
+
args,
|
|
3592
|
+
hash,
|
|
3593
|
+
isError,
|
|
3594
|
+
timestamp: Date.now()
|
|
3595
|
+
};
|
|
3596
|
+
if (isError) {
|
|
3597
|
+
if (this.lastErrorHash === hash) {
|
|
3598
|
+
this.consecutiveErrorCount++;
|
|
3599
|
+
} else {
|
|
3600
|
+
this.lastErrorHash = hash;
|
|
3601
|
+
this.consecutiveErrorCount = 1;
|
|
3602
|
+
}
|
|
3603
|
+
if (this.consecutiveErrorCount >= this.consecutiveErrorThreshold) {
|
|
3604
|
+
return this.triggerTrip({
|
|
3605
|
+
reason: "consecutive_errors",
|
|
3606
|
+
message: `Tool circuit breaker tripped: ${this.consecutiveErrorCount} consecutive identical tool errors for '${toolName}' with unchanged arguments.`,
|
|
3607
|
+
toolName,
|
|
3608
|
+
actionHash: hash,
|
|
3609
|
+
count: this.consecutiveErrorCount
|
|
3610
|
+
});
|
|
3611
|
+
}
|
|
3612
|
+
} else {
|
|
3613
|
+
this.lastErrorHash = null;
|
|
3614
|
+
this.consecutiveErrorCount = 0;
|
|
3615
|
+
}
|
|
3616
|
+
this.history.push(record);
|
|
3617
|
+
const windowStart = Math.max(0, this.history.length - this.slidingWindowSize);
|
|
3618
|
+
const currentWindow = this.history.slice(windowStart);
|
|
3619
|
+
let repetitionCount = 0;
|
|
3620
|
+
for (const item of currentWindow) {
|
|
3621
|
+
if (item.hash === hash) {
|
|
3622
|
+
repetitionCount++;
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
if (repetitionCount >= this.repetitionThreshold) {
|
|
3626
|
+
return this.triggerTrip({
|
|
3627
|
+
reason: "repetition",
|
|
3628
|
+
message: `Action repetition loop detected: tool '${toolName}' called ${repetitionCount} times with identical parameters within sliding window of ${this.slidingWindowSize}.`,
|
|
3629
|
+
toolName,
|
|
3630
|
+
actionHash: hash,
|
|
3631
|
+
count: repetitionCount
|
|
3632
|
+
});
|
|
3633
|
+
}
|
|
3634
|
+
const requiredPingPongLength = this.pingPongThreshold * 2;
|
|
3635
|
+
if (this.history.length >= requiredPingPongLength) {
|
|
3636
|
+
const pingPongSlice = this.history.slice(-requiredPingPongLength);
|
|
3637
|
+
const hashA = pingPongSlice[0].hash;
|
|
3638
|
+
const hashB = pingPongSlice[1].hash;
|
|
3639
|
+
if (hashA !== hashB) {
|
|
3640
|
+
let isPingPong = true;
|
|
3641
|
+
for (let i = 0; i < requiredPingPongLength; i++) {
|
|
3642
|
+
const expectedHash = i % 2 === 0 ? hashA : hashB;
|
|
3643
|
+
if (pingPongSlice[i].hash !== expectedHash) {
|
|
3644
|
+
isPingPong = false;
|
|
3645
|
+
break;
|
|
3646
|
+
}
|
|
3647
|
+
}
|
|
3648
|
+
if (isPingPong) {
|
|
3649
|
+
return this.triggerTrip({
|
|
3650
|
+
reason: "ping_pong",
|
|
3651
|
+
message: `Alternating ping-pong action loop detected: ${this.pingPongThreshold} consecutive alternating cycles between tools (A-B-A-B-A-B).`,
|
|
3652
|
+
toolName,
|
|
3653
|
+
actionHash: hash,
|
|
3654
|
+
count: this.pingPongThreshold
|
|
3655
|
+
});
|
|
3656
|
+
}
|
|
3657
|
+
}
|
|
3658
|
+
}
|
|
3659
|
+
return null;
|
|
3660
|
+
}
|
|
3661
|
+
/**
|
|
3662
|
+
* Feeds a single line of output (stream-json or raw text) into the guard.
|
|
3663
|
+
*/
|
|
3664
|
+
feedLine(line) {
|
|
3665
|
+
if (!line || !line.trim()) return null;
|
|
3666
|
+
try {
|
|
3667
|
+
const parsed = JSON.parse(line.trim());
|
|
3668
|
+
if (parsed && typeof parsed === "object" && parsed.event === "step_update") {
|
|
3669
|
+
const su = parsed.step_update;
|
|
3670
|
+
if (su && su.step_type === "tool") {
|
|
3671
|
+
const toolName = su.tool_name || su.tool_info?.name || "unknown";
|
|
3672
|
+
const params = su.tool_info?.parameters;
|
|
3673
|
+
if (su.state === "ACTIVE") {
|
|
3674
|
+
return this.recordAction(toolName, params, false);
|
|
3675
|
+
}
|
|
3676
|
+
if (su.state === "ERROR") {
|
|
3677
|
+
return this.recordAction(toolName, params, true);
|
|
3678
|
+
}
|
|
3679
|
+
}
|
|
3680
|
+
}
|
|
3681
|
+
} catch {
|
|
3682
|
+
}
|
|
3683
|
+
return null;
|
|
3684
|
+
}
|
|
3685
|
+
triggerTrip(trip) {
|
|
3686
|
+
this.trippedResult = trip;
|
|
3687
|
+
if (this.onTrip) {
|
|
3688
|
+
try {
|
|
3689
|
+
this.onTrip(trip);
|
|
3690
|
+
} catch {
|
|
3691
|
+
}
|
|
3692
|
+
}
|
|
3693
|
+
return trip;
|
|
3694
|
+
}
|
|
3695
|
+
};
|
|
3696
|
+
function formatLoopGuardFailureCard(options) {
|
|
3697
|
+
const serverUrl = options.serverUrl || process.env.GITHUB_SERVER_URL || "https://github.com";
|
|
3698
|
+
const repository = options.repository || process.env.GITHUB_REPOSITORY || "";
|
|
3699
|
+
const runId = options.runId || process.env.GITHUB_RUN_ID || "";
|
|
3700
|
+
const logUrl = runId && repository ? `${serverUrl}/${repository}/actions/runs/${runId}` : "";
|
|
3701
|
+
const lines = [
|
|
3702
|
+
`### \u274C Milestone: Run Interrupted / Failed`,
|
|
3703
|
+
`- **Routine**: \`${options.routine}\``,
|
|
3704
|
+
`- **Status**: Loop guard circuit breaker tripped (\`loop_circuit_breaker\`)`,
|
|
3705
|
+
`- **Root Cause Category**: \`loop_circuit_breaker\``,
|
|
3706
|
+
`- **Trigger**: \`${options.trip.reason}\``,
|
|
3707
|
+
`- **Reason**: ${options.trip.message}`,
|
|
3708
|
+
`- **Action**: Process terminated to prevent runaway token burn`
|
|
3709
|
+
];
|
|
3710
|
+
if (logUrl) {
|
|
3711
|
+
lines.push(`- **Action Log**: [View Run Logs](${logUrl})`);
|
|
3712
|
+
}
|
|
3713
|
+
return lines.join("\n");
|
|
3714
|
+
}
|
|
3715
|
+
function formatLoopGuardReport(options) {
|
|
3716
|
+
return [
|
|
3717
|
+
`# Run Report`,
|
|
3718
|
+
``,
|
|
3719
|
+
`## Result`,
|
|
3720
|
+
`FAILURE`,
|
|
3721
|
+
``,
|
|
3722
|
+
`## Summary`,
|
|
3723
|
+
`| Metric | Value |`,
|
|
3724
|
+
`|---|---|`,
|
|
3725
|
+
`| Routine | \`${options.routine}\` |`,
|
|
3726
|
+
`| Timestamp | \`${options.timestamp}\` |`,
|
|
3727
|
+
`| Result | \`FAILURE\` |`,
|
|
3728
|
+
`| Category | \`loop_circuit_breaker\` |`,
|
|
3729
|
+
`| Trigger | \`${options.trip.reason}\` |`,
|
|
3730
|
+
`| Tool | \`${options.trip.toolName}\` |`,
|
|
3731
|
+
`| Action Hash | \`${options.trip.actionHash}\` |`,
|
|
3732
|
+
``,
|
|
3733
|
+
`### Failure Details`,
|
|
3734
|
+
`The autonomous execution was halted by the deterministic loop-guard wrapper.`,
|
|
3735
|
+
`- **Category**: \`loop_circuit_breaker\``,
|
|
3736
|
+
`- **Trigger**: \`${options.trip.reason}\``,
|
|
3737
|
+
`- **Tool**: \`${options.trip.toolName}\``,
|
|
3738
|
+
`- **Action Hash**: \`${options.trip.actionHash}\``,
|
|
3739
|
+
`- **Message**: ${options.trip.message}`
|
|
3740
|
+
].join("\n");
|
|
3741
|
+
}
|
|
3742
|
+
|
|
3498
3743
|
// src/lib/runner.ts
|
|
3499
3744
|
import pc10 from "picocolors";
|
|
3500
3745
|
var execFileAsync4 = promisify4(execFile4);
|
|
@@ -3699,8 +3944,56 @@ async function tryPostLocalRunMilestoneAsync(cwd, issueNumber, cardContent) {
|
|
|
3699
3944
|
}
|
|
3700
3945
|
}
|
|
3701
3946
|
}
|
|
3702
|
-
function
|
|
3947
|
+
function detectQuotaExceeded(output, stderr) {
|
|
3948
|
+
const combined = `${output}
|
|
3949
|
+
${stderr}`;
|
|
3950
|
+
if (/individual quota reached|quota reached/i.test(combined)) {
|
|
3951
|
+
const match = combined.match(/Resets in ([^.\n]+)/i);
|
|
3952
|
+
return {
|
|
3953
|
+
isQuota: true,
|
|
3954
|
+
resetInfo: match ? `Resets in ${match[1].trim()}` : void 0
|
|
3955
|
+
};
|
|
3956
|
+
}
|
|
3957
|
+
return { isQuota: false };
|
|
3958
|
+
}
|
|
3959
|
+
function tryReconcileLocalRunIssue(cwd, issueNumber, reportContent, exitCode, hostname, routine, quotaInfo) {
|
|
3703
3960
|
try {
|
|
3961
|
+
if (quotaInfo?.isQuota) {
|
|
3962
|
+
const resetStr = quotaInfo.resetInfo || "Resets in future window";
|
|
3963
|
+
const quotaCard = [
|
|
3964
|
+
`### \u23F8\uFE0F Milestone: Routine Execution Quota-Paused`,
|
|
3965
|
+
`- **Routine**: \`${routine || "local-routine"}\``,
|
|
3966
|
+
`- **Status**: LLM quota reached (\`${resetStr}\`)`,
|
|
3967
|
+
`- **Runner**: \`local (${hostname})\``,
|
|
3968
|
+
`- **Next Action**: Routine execution paused cleanly. Work will resume on next sweep after quota resets.`
|
|
3969
|
+
].join("\n");
|
|
3970
|
+
tryPostLocalRunMilestone(cwd, issueNumber, quotaCard);
|
|
3971
|
+
const finalBody2 = `${reportContent}
|
|
3972
|
+
|
|
3973
|
+
---
|
|
3974
|
+
_Generated by Jonah Fleet local runner on ${hostname}._`;
|
|
3975
|
+
const tmpFile2 = path12.join(os2.tmpdir(), `jonah-fleet-run-${issueNumber}-${Date.now()}.md`);
|
|
3976
|
+
fs12.writeFileSync(tmpFile2, finalBody2, "utf8");
|
|
3977
|
+
try {
|
|
3978
|
+
execSync2(`gh issue edit ${issueNumber} --body-file ${JSON.stringify(tmpFile2)}`, {
|
|
3979
|
+
cwd,
|
|
3980
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
3981
|
+
});
|
|
3982
|
+
execSync2(`gh issue edit ${issueNumber} --add-label "status:quota-paused" --remove-label "status:running"`, {
|
|
3983
|
+
cwd,
|
|
3984
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
3985
|
+
});
|
|
3986
|
+
execSync2(`gh issue close ${issueNumber} --reason completed`, {
|
|
3987
|
+
cwd,
|
|
3988
|
+
stdio: ["ignore", "ignore", "ignore"]
|
|
3989
|
+
});
|
|
3990
|
+
} finally {
|
|
3991
|
+
if (fs12.existsSync(tmpFile2)) {
|
|
3992
|
+
fs12.unlinkSync(tmpFile2);
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
return;
|
|
3996
|
+
}
|
|
3704
3997
|
if (exitCode !== 0) {
|
|
3705
3998
|
const interruptionCard = formatInterruptionCard({
|
|
3706
3999
|
routine: routine || "local-routine",
|
|
@@ -4044,6 +4337,33 @@ Timeout: ${printTimeout}`,
|
|
|
4044
4337
|
});
|
|
4045
4338
|
}
|
|
4046
4339
|
};
|
|
4340
|
+
let loopGuardTrip = null;
|
|
4341
|
+
let activeChild = null;
|
|
4342
|
+
const loopGuard = new LoopGuard({
|
|
4343
|
+
repetitionThreshold: 5,
|
|
4344
|
+
pingPongThreshold: 3,
|
|
4345
|
+
consecutiveErrorThreshold: 2,
|
|
4346
|
+
slidingWindowSize: 20,
|
|
4347
|
+
onTrip: (trip) => {
|
|
4348
|
+
loopGuardTrip = trip;
|
|
4349
|
+
spinner?.stop();
|
|
4350
|
+
if (activeChild && !activeChild.killed) {
|
|
4351
|
+
try {
|
|
4352
|
+
activeChild.kill("SIGTERM");
|
|
4353
|
+
} catch {
|
|
4354
|
+
}
|
|
4355
|
+
const killTimer = setTimeout(() => {
|
|
4356
|
+
try {
|
|
4357
|
+
if (activeChild && !activeChild.killed) {
|
|
4358
|
+
activeChild.kill("SIGKILL");
|
|
4359
|
+
}
|
|
4360
|
+
} catch {
|
|
4361
|
+
}
|
|
4362
|
+
}, 3e3);
|
|
4363
|
+
killTimer.unref();
|
|
4364
|
+
}
|
|
4365
|
+
}
|
|
4366
|
+
});
|
|
4047
4367
|
const stdoutParser = new LineBufferedStreamParser((line) => {
|
|
4048
4368
|
const event = parseStreamJsonEvent(line);
|
|
4049
4369
|
if (event) {
|
|
@@ -4053,6 +4373,7 @@ Timeout: ${printTimeout}`,
|
|
|
4053
4373
|
const toolName = su.tool_name || su.tool_info?.name || "unknown";
|
|
4054
4374
|
const toolParams = su.tool_info?.parameters;
|
|
4055
4375
|
if (su.state === "ACTIVE") {
|
|
4376
|
+
loopGuard.recordAction(toolName, toolParams, false);
|
|
4056
4377
|
const actionDesc = formatActionDescription(toolName, toolParams);
|
|
4057
4378
|
lastActionDesc = actionDesc;
|
|
4058
4379
|
if (spinner) {
|
|
@@ -4065,6 +4386,9 @@ Timeout: ${printTimeout}`,
|
|
|
4065
4386
|
const formatted = formatVerboseEvent(event);
|
|
4066
4387
|
if (formatted) console.log(formatted);
|
|
4067
4388
|
}
|
|
4389
|
+
} else if (su.state === "ERROR") {
|
|
4390
|
+
loopGuard.recordAction(toolName, toolParams, true);
|
|
4391
|
+
lastActionDesc = null;
|
|
4068
4392
|
} else if (su.state === "DONE") {
|
|
4069
4393
|
lastActionDesc = null;
|
|
4070
4394
|
if (su.tool_info?.output) {
|
|
@@ -4168,6 +4492,7 @@ Timeout: ${printTimeout}`,
|
|
|
4168
4492
|
env: childEnv,
|
|
4169
4493
|
stdio: ["inherit", "pipe", "pipe"]
|
|
4170
4494
|
});
|
|
4495
|
+
activeChild = child;
|
|
4171
4496
|
child.stdout?.on("data", (data) => {
|
|
4172
4497
|
processChunk(data.toString(), false);
|
|
4173
4498
|
});
|
|
@@ -4204,7 +4529,26 @@ Timeout: ${printTimeout}`,
|
|
|
4204
4529
|
exitCode = 1;
|
|
4205
4530
|
}
|
|
4206
4531
|
}
|
|
4207
|
-
if (
|
|
4532
|
+
if (loopGuardTrip) {
|
|
4533
|
+
exitCode = 1;
|
|
4534
|
+
reportContent = formatLoopGuardReport({
|
|
4535
|
+
routine,
|
|
4536
|
+
timestamp,
|
|
4537
|
+
trip: loopGuardTrip
|
|
4538
|
+
});
|
|
4539
|
+
try {
|
|
4540
|
+
fs12.writeFileSync(executionReportPath, reportContent, "utf8");
|
|
4541
|
+
fs12.writeFileSync(targetReportPath, reportContent, "utf8");
|
|
4542
|
+
} catch {
|
|
4543
|
+
}
|
|
4544
|
+
if (routineIssueNumber) {
|
|
4545
|
+
const failureCard = formatLoopGuardFailureCard({
|
|
4546
|
+
routine,
|
|
4547
|
+
trip: loopGuardTrip
|
|
4548
|
+
});
|
|
4549
|
+
tryPostLocalRunMilestone(targetDir, routineIssueNumber, failureCard);
|
|
4550
|
+
}
|
|
4551
|
+
} else if (!reportContent) {
|
|
4208
4552
|
reportContent = formatFallbackRunReport({
|
|
4209
4553
|
routine,
|
|
4210
4554
|
timestamp,
|
|
@@ -4243,8 +4587,9 @@ Timeout: ${printTimeout}`,
|
|
|
4243
4587
|
);
|
|
4244
4588
|
} catch {
|
|
4245
4589
|
}
|
|
4590
|
+
const quotaInfo = detectQuotaExceeded(output, accumulatedStderr);
|
|
4246
4591
|
if (routineIssueNumber) {
|
|
4247
|
-
tryReconcileLocalRunIssue(targetDir, routineIssueNumber, reportContent, exitCode, hostname, routine);
|
|
4592
|
+
tryReconcileLocalRunIssue(targetDir, routineIssueNumber, reportContent, exitCode, hostname, routine, quotaInfo);
|
|
4248
4593
|
}
|
|
4249
4594
|
if (options.showCard !== false && !options.verbose) {
|
|
4250
4595
|
const prMatch = targetLabel.match(/PR\s*#?(\d+)/i);
|
|
@@ -5919,3 +6264,10 @@ program.command("telemetry [repos...]").description("Aggregate and report fleet-
|
|
|
5919
6264
|
await runTelemetry({ ...options, repos, action });
|
|
5920
6265
|
});
|
|
5921
6266
|
program.parse(process.argv);
|
|
6267
|
+
export {
|
|
6268
|
+
LoopGuard,
|
|
6269
|
+
canonicalStringify,
|
|
6270
|
+
computeActionHash,
|
|
6271
|
+
formatLoopGuardFailureCard,
|
|
6272
|
+
formatLoopGuardReport
|
|
6273
|
+
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/lib/installer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAA2B,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,aAAa,EAAyC,MAAM,eAAe,CAAC;AAKrF,wBAAgB,eAAe,IAAI,MAAM,CAOxC;AAED,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGtE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,QAAQ,EAAE,aAAa,EACvB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,GAAG,SAAS,CAqBpB;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,GAAE,cAAmB,GAAG,aAAa,
|
|
1
|
+
{"version":3,"file":"installer.d.ts","sourceRoot":"","sources":["../../src/lib/installer.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,aAAa,EAA2B,MAAM,cAAc,CAAC;AACtE,OAAO,EAAE,aAAa,EAAyC,MAAM,eAAe,CAAC;AAKrF,wBAAgB,eAAe,IAAI,MAAM,CAOxC;AAED,MAAM,WAAW,aAAa;IAC5B,gBAAgB,EAAE,MAAM,EAAE,CAAC;IAC3B,kBAAkB,EAAE,MAAM,EAAE,CAAC;IAC7B,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAMlF;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAGtE;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,YAAY,EAAE,MAAM,EACpB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,QAAQ,EAAE,aAAa,EACvB,WAAW,CAAC,EAAE,MAAM,GACnB,MAAM,GAAG,SAAS,CAqBpB;AAED,wBAAgB,YAAY,CAAC,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,OAAO,GAAE,cAAmB,GAAG,aAAa,CAyIpH"}
|
package/dist/lib/labels.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/lib/labels.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqB,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAGjE,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,qBAAqB,EAAE,MAAM,CAAC;IAC9B,sBAAsB,EAAE,MAAM,CAAC;CAChC;AAED,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,sBAAsB,GAAG,YAAY,GAAG,UAAU,CAAC;AAE1F,MAAM,WAAW,mBAAoB,SAAQ,aAAa;IACxD,QAAQ,EAAE,aAAa,CAAC;CACzB;AAED,MAAM,WAAW,gBAAgB;IAC/B,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,kBAAkB,EAAE,mBAAmB,EAAE,CAAC;IAC1C,UAAU,EAAE,mBAAmB,EAAE,CAAC;IAClC,QAAQ,EAAE,mBAAmB,EAAE,CAAC;IAChC,GAAG,EAAE,mBAAmB,EAAE,CAAC;CAC5B;AAED,eAAO,MAAM,gCAAgC,EAAE,MAAM,EAqBpD,CAAC;AAEF,wBAAgB,gBAAgB,CAC9B,SAAS,EAAE,MAAM,EACjB,iBAAiB,GAAE,MAAM,EAAqC,GAC7D,OAAO,CAcT;AAED,wBAAgB,cAAc,CAC5B,MAAM,EAAE,aAAa,EAAE,EACvB,iBAAiB,GAAE,MAAM,EAAqC,GAC7D,gBAAgB,CAyClB;AAED,wBAAsB,eAAe,CACnC,cAAc,CAAC,EAAE,MAAM,EACvB,GAAG,GAAE,MAAsB,EAC3B,QAAQ,GAAE,UAA8B,GACvC,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAiCD,wBAAsB,eAAe,CACnC,cAAc,CAAC,EAAE,MAAM,EACvB,QAAQ,GAAE,UAA8B,EACxC,GAAG,GAAE,MAAsB,GAC1B,OAAO,CAAC,aAAa,EAAE,CAAC,CA6D1B;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,GAAG,CAAC,EAAE,OAAO,CAAC;IACd,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,iBAAiB,CAAC,EAAE,MAAM,EAAE,CAAC;IAC7B,QAAQ,CAAC,EAAE,UAAU,CAAC;CACvB;AAED,MAAM,WAAW,iBAAiB;IAChC,IAAI,EAAE,MAAM,CAAC;IACb,UAAU,EAAE,gBAAgB,CAAC;IAC7B,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAChD,MAAM,EAAE,OAAO,CAAC;CACjB;AAED,wBAAsB,WAAW,CAAC,OAAO,GAAE,kBAAuB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CA6C9F;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,eAAO,MAAM,iBAAiB,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"labels.d.ts","sourceRoot":"","sources":["../../src/lib/labels.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
export type LoopGuardTripReason = 'repetition' | 'ping_pong' | 'consecutive_errors';
|
|
2
|
+
export interface LoopGuardTrip {
|
|
3
|
+
reason: LoopGuardTripReason;
|
|
4
|
+
message: string;
|
|
5
|
+
toolName: string;
|
|
6
|
+
actionHash: string;
|
|
7
|
+
count: number;
|
|
8
|
+
}
|
|
9
|
+
export interface ActionRecord {
|
|
10
|
+
toolName: string;
|
|
11
|
+
args: any;
|
|
12
|
+
hash: string;
|
|
13
|
+
isError: boolean;
|
|
14
|
+
timestamp: number;
|
|
15
|
+
}
|
|
16
|
+
export interface LoopGuardOptions {
|
|
17
|
+
repetitionThreshold?: number;
|
|
18
|
+
pingPongThreshold?: number;
|
|
19
|
+
consecutiveErrorThreshold?: number;
|
|
20
|
+
slidingWindowSize?: number;
|
|
21
|
+
onTrip?: (trip: LoopGuardTrip) => void;
|
|
22
|
+
}
|
|
23
|
+
/**
|
|
24
|
+
* Deterministically stringifies an object by recursively sorting its keys.
|
|
25
|
+
*/
|
|
26
|
+
export declare function canonicalStringify(value: any): string;
|
|
27
|
+
/**
|
|
28
|
+
* Computes a deterministic SHA-256 action hash: SHA-256(tool_name + canonicalString(args))
|
|
29
|
+
*/
|
|
30
|
+
export declare function computeActionHash(toolName: string, args: any): string;
|
|
31
|
+
/**
|
|
32
|
+
* Deterministic Loop Guard & Circuit Breaker monitoring tool calls and execution events.
|
|
33
|
+
*/
|
|
34
|
+
export declare class LoopGuard {
|
|
35
|
+
private repetitionThreshold;
|
|
36
|
+
private pingPongThreshold;
|
|
37
|
+
private consecutiveErrorThreshold;
|
|
38
|
+
private slidingWindowSize;
|
|
39
|
+
private onTrip?;
|
|
40
|
+
private history;
|
|
41
|
+
private lastErrorHash;
|
|
42
|
+
private consecutiveErrorCount;
|
|
43
|
+
private trippedResult;
|
|
44
|
+
constructor(options?: LoopGuardOptions);
|
|
45
|
+
setOnTrip(callback: (trip: LoopGuardTrip) => void): void;
|
|
46
|
+
isTripped(): boolean;
|
|
47
|
+
getTrip(): LoopGuardTrip | null;
|
|
48
|
+
getHistory(): ActionRecord[];
|
|
49
|
+
reset(): void;
|
|
50
|
+
/**
|
|
51
|
+
* Records an action occurrence and evaluates repetition, ping-pong, and error guards.
|
|
52
|
+
*/
|
|
53
|
+
recordAction(toolName: string, args: any, isError?: boolean): LoopGuardTrip | null;
|
|
54
|
+
/**
|
|
55
|
+
* Feeds a single line of output (stream-json or raw text) into the guard.
|
|
56
|
+
*/
|
|
57
|
+
feedLine(line: string): LoopGuardTrip | null;
|
|
58
|
+
private triggerTrip;
|
|
59
|
+
}
|
|
60
|
+
export interface LoopGuardFailureCardOptions {
|
|
61
|
+
routine: string;
|
|
62
|
+
trip: LoopGuardTrip;
|
|
63
|
+
runId?: string;
|
|
64
|
+
serverUrl?: string;
|
|
65
|
+
repository?: string;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Formats a structured 5-point milestone interruption card on circuit breaker trip.
|
|
69
|
+
*/
|
|
70
|
+
export declare function formatLoopGuardFailureCard(options: LoopGuardFailureCardOptions): string;
|
|
71
|
+
export interface LoopGuardReportOptions {
|
|
72
|
+
routine: string;
|
|
73
|
+
timestamp: string;
|
|
74
|
+
trip: LoopGuardTrip;
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Formats a markdown run report for .jonah-fleet/run-report.md when loop guard terminates a run.
|
|
78
|
+
*/
|
|
79
|
+
export declare function formatLoopGuardReport(options: LoopGuardReportOptions): string;
|
|
80
|
+
//# sourceMappingURL=loop-guard.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"loop-guard.d.ts","sourceRoot":"","sources":["../../src/lib/loop-guard.ts"],"names":[],"mappings":"AAEA,MAAM,MAAM,mBAAmB,GAAG,YAAY,GAAG,WAAW,GAAG,oBAAoB,CAAC;AAEpF,MAAM,WAAW,aAAa;IAC5B,MAAM,EAAE,mBAAmB,CAAC;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,GAAG,CAAC;IACV,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,yBAAyB,CAAC,EAAE,MAAM,CAAC;IACnC,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,MAAM,CAAC,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,CAAC;CACxC;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,CAarD;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,GAAG,MAAM,CAGrE;AAED;;GAEG;AACH,qBAAa,SAAS;IACpB,OAAO,CAAC,mBAAmB,CAAS;IACpC,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,yBAAyB,CAAS;IAC1C,OAAO,CAAC,iBAAiB,CAAS;IAClC,OAAO,CAAC,MAAM,CAAC,CAAgC;IAE/C,OAAO,CAAC,OAAO,CAAsB;IACrC,OAAO,CAAC,aAAa,CAAuB;IAC5C,OAAO,CAAC,qBAAqB,CAAa;IAC1C,OAAO,CAAC,aAAa,CAA8B;IAEnD,YAAY,OAAO,GAAE,gBAAqB,EAMzC;IAEM,SAAS,CAAC,QAAQ,EAAE,CAAC,IAAI,EAAE,aAAa,KAAK,IAAI,GAAG,IAAI,CAE9D;IAEM,SAAS,IAAI,OAAO,CAE1B;IAEM,OAAO,IAAI,aAAa,GAAG,IAAI,CAErC;IAEM,UAAU,IAAI,YAAY,EAAE,CAElC;IAEM,KAAK,IAAI,IAAI,CAKnB;IAED;;OAEG;IACI,YAAY,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,OAAO,GAAE,OAAe,GAAG,aAAa,GAAG,IAAI,CA6F/F;IAED;;OAEG;IACI,QAAQ,CAAC,IAAI,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI,CAwBlD;IAED,OAAO,CAAC,WAAW;CAWpB;AAED,MAAM,WAAW,2BAA2B;IAC1C,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,aAAa,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,0BAA0B,CAAC,OAAO,EAAE,2BAA2B,GAAG,MAAM,CAqBvF;AAED,MAAM,WAAW,sBAAsB;IACrC,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,EAAE,aAAa,CAAC;CACrB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,MAAM,CA0B7E"}
|
package/dist/lib/presets.d.ts
CHANGED
|
@@ -80,6 +80,11 @@ export interface FleetManifest {
|
|
|
80
80
|
labels?: {
|
|
81
81
|
protected?: string[];
|
|
82
82
|
};
|
|
83
|
+
lessons?: boolean | LessonsConfig;
|
|
84
|
+
}
|
|
85
|
+
export interface LessonsConfig {
|
|
86
|
+
enabled?: boolean;
|
|
87
|
+
maxEntries?: number;
|
|
83
88
|
}
|
|
84
89
|
export declare const DEFAULT_ROUTINE_MODELS: Record<string, string>;
|
|
85
90
|
export declare const DEFAULT_ROUTINE_TIMEOUTS: Record<string, number>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/lib/presets.ts"],"names":[],"mappings":"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
|
|
1
|
+
{"version":3,"file":"presets.d.ts","sourceRoot":"","sources":["../../src/lib/presets.ts"],"names":[],"mappings":"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"}
|
package/dist/lib/runner.d.ts
CHANGED
|
@@ -145,12 +145,21 @@ export declare function tryPostLocalRunMilestone(cwd: string, issueNumber: numbe
|
|
|
145
145
|
* Fails gracefully if offline or unauthenticated.
|
|
146
146
|
*/
|
|
147
147
|
export declare function tryPostLocalRunMilestoneAsync(cwd: string, issueNumber: number, cardContent: string): Promise<boolean>;
|
|
148
|
+
export interface QuotaDetectionResult {
|
|
149
|
+
isQuota: boolean;
|
|
150
|
+
resetInfo?: string;
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Detects whether routine runner stdout/stderr failed due to LLM quota exhaustion.
|
|
154
|
+
*/
|
|
155
|
+
export declare function detectQuotaExceeded(output: string, stderr: string): QuotaDetectionResult;
|
|
148
156
|
/**
|
|
149
157
|
* Reconciles a GitHub issue for local routine execution.
|
|
150
158
|
* Closes the issue if success, leaves it open with needs-attention if failure.
|
|
159
|
+
* Handles quota-paused routines gracefully without marking as failure.
|
|
151
160
|
* Fails gracefully if offline or unauthenticated.
|
|
152
161
|
*/
|
|
153
|
-
export declare function tryReconcileLocalRunIssue(cwd: string, issueNumber: number, reportContent: string, exitCode: number, hostname: string, routine?: string): void;
|
|
162
|
+
export declare function tryReconcileLocalRunIssue(cwd: string, issueNumber: number, reportContent: string, exitCode: number, hostname: string, routine?: string, quotaInfo?: QuotaDetectionResult): void;
|
|
154
163
|
/**
|
|
155
164
|
* Asynchronously marks an interrupted local routine run as failed and posts an interruption card.
|
|
156
165
|
* Leaves the issue open with status:failure,needs-attention for maintainer triage.
|