@event4u/agent-config 9.15.0 → 9.16.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/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +21 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/mcp/content.js +1 -1
- package/dist/cli/mcp/content.js.map +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +1 -1
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +1 -1
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +1 -1
- package/dist/mcp/registry-manifest.json +1 -1
- package/dist/mcp/server.json +2 -2
- package/dist/server/routes/workspace.js +1 -1
- package/dist/server/routes/workspace.js.map +1 -1
- package/dist/shared/userMd/utils.js +1 -1
- package/dist/shared/userMd/utils.js.map +1 -1
- package/dist/ui/assets/index-DmR0mbk4.js +26 -0
- package/dist/ui/assets/index-DmR0mbk4.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/package.json +4 -4
- package/src/config/agent-settings.template.yml +3 -1
- package/src/config/budget-routing.json +7 -0
- package/src/scripts/_lib/tier_budget_routing.ts +154 -28
- package/src/scripts/anchor_scoring_run.ts +1 -1
- package/src/scripts/bench_persona_placebo.ts +1 -1
- package/src/scripts/bench_quality_run.ts +1 -1
- package/src/scripts/check_cli_registry_budget_sync.ts +168 -0
- package/src/scripts/check_gate_coverage.ts +1 -1
- package/src/scripts/check_token_quality_golden.ts +1 -1
- package/src/scripts/cost/budget.mjs +18 -4
- package/src/scripts/release.ts +110 -7
- package/src/scripts/routing_doctor.ts +76 -1
- package/src/shared/userMd/utils.ts +1 -1
- package/dist/ui/assets/index-DJY_vQVn.js +0 -40
- package/dist/ui/assets/index-DJY_vQVn.js.map +0 -1
package/src/scripts/release.ts
CHANGED
|
@@ -634,13 +634,16 @@ export function _failed_checks_report(names: readonly string[]): string {
|
|
|
634
634
|
* A short grace period gives GitHub time to register workflow runs on a
|
|
635
635
|
* freshly-pushed branch.
|
|
636
636
|
*/
|
|
637
|
-
function watch_pr_checks(): void {
|
|
637
|
+
function watch_pr_checks(branch: string): void {
|
|
638
638
|
// time.sleep(5) — blocking grace period. Never reached on any test path.
|
|
639
639
|
const until = Date.now() + 5000;
|
|
640
640
|
while (Date.now() < until) {
|
|
641
641
|
// busy-wait stand-in for time.sleep(5) without an event-loop yield.
|
|
642
642
|
}
|
|
643
|
-
|
|
643
|
+
// The branch is passed explicitly — `gh` inferring the PR from HEAD
|
|
644
|
+
// resolved to `main` mid-run once (2026-08-03, 9.16.0 resume) and the
|
|
645
|
+
// whole release died on "no pull requests found for branch main".
|
|
646
|
+
const proc = spawnSync('gh', ['pr', 'checks', branch, '--watch'], {
|
|
644
647
|
cwd: REPO_ROOT,
|
|
645
648
|
encoding: 'utf-8',
|
|
646
649
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -667,7 +670,7 @@ function watch_pr_checks(): void {
|
|
|
667
670
|
if (output) {
|
|
668
671
|
process.stderr.write(output + '\n');
|
|
669
672
|
}
|
|
670
|
-
const summary = spawnSync('gh', ['pr', 'checks', '--json', 'name,bucket'], {
|
|
673
|
+
const summary = spawnSync('gh', ['pr', 'checks', branch, '--json', 'name,bucket'], {
|
|
671
674
|
cwd: REPO_ROOT,
|
|
672
675
|
encoding: 'utf-8',
|
|
673
676
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
@@ -679,6 +682,105 @@ function watch_pr_checks(): void {
|
|
|
679
682
|
die(`PR checks failed (exit ${returncode})`);
|
|
680
683
|
}
|
|
681
684
|
|
|
685
|
+
/**
|
|
686
|
+
* Push the release branch, integrating a remote that moved under us.
|
|
687
|
+
*
|
|
688
|
+
* A GitHub-side "Update branch" click (or a concurrent resume run) advances
|
|
689
|
+
* `origin/<branch>` while the local ref stays behind; a bare `git push` is
|
|
690
|
+
* then rejected with "fetch first" and the whole release run dies (measured
|
|
691
|
+
* 2026-08-03, 9.15.0). Same-branch commits are never ours to drop, so the
|
|
692
|
+
* recovery is merge-and-retry, never force-push.
|
|
693
|
+
*/
|
|
694
|
+
function push_release_branch(branch: string): void {
|
|
695
|
+
const first = run(['git', 'push', '-u', REMOTE, branch], { check: false });
|
|
696
|
+
if (first.returncode === 0) {
|
|
697
|
+
return;
|
|
698
|
+
}
|
|
699
|
+
process.stdout.write(`↻ ${REMOTE}/${branch} moved — integrating and retrying push\n`);
|
|
700
|
+
run(['git', 'fetch', REMOTE, branch]);
|
|
701
|
+
run(['git', 'merge', '--no-edit', `${REMOTE}/${branch}`]);
|
|
702
|
+
run(['git', 'push', '-u', REMOTE, branch]);
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
/** mergeStateStatus of the branch's PR ('' when the probe fails). */
|
|
706
|
+
function _pr_merge_state(branch: string): string {
|
|
707
|
+
const r = run(
|
|
708
|
+
['gh', 'pr', 'view', branch, '--json', 'mergeStateStatus', '--jq', '.mergeStateStatus'],
|
|
709
|
+
{
|
|
710
|
+
check: false,
|
|
711
|
+
capture: true,
|
|
712
|
+
},
|
|
713
|
+
);
|
|
714
|
+
return r.returncode === 0 ? r.stdout.trim() : '';
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
/**
|
|
718
|
+
* Bound on update-and-retry rounds in merge_release_pr. Three is deliberate:
|
|
719
|
+
* main receiving unrelated merges three times inside one checks-wait window
|
|
720
|
+
* means the queue is hot and a human should decide, not a loop.
|
|
721
|
+
*/
|
|
722
|
+
const _MERGE_UPDATE_ROUNDS = 3;
|
|
723
|
+
|
|
724
|
+
/**
|
|
725
|
+
* Merge the release PR, absorbing a moving base branch.
|
|
726
|
+
*
|
|
727
|
+
* Branch protection requires the head to be up to date with `main`, and
|
|
728
|
+
* `main` moves constantly (dependabot, unrelated merges) — so between the
|
|
729
|
+
* checks-wait and the merge the PR routinely flips to BEHIND and a bare
|
|
730
|
+
* `gh pr merge` dies with "the head branch is not up to date with the base
|
|
731
|
+
* branch" (measured 2026-08-03, 9.16.0). Recovery is mechanical and safe by
|
|
732
|
+
* construction: merging `origin/main` into `release/X.Y.Z` makes the fix
|
|
733
|
+
* files identical on both sides, so the release-PR diff stays inside the
|
|
734
|
+
* version-bump allowlist (docs/contracts/release-pr-gating.md § Mid-release
|
|
735
|
+
* fixes) — do it here instead of aborting the run.
|
|
736
|
+
*/
|
|
737
|
+
function merge_release_pr(branch: string, wait_for_checks: boolean): void {
|
|
738
|
+
if (git(['rev-parse', '--abbrev-ref', 'HEAD'], { capture: true }) !== branch) {
|
|
739
|
+
run(['git', 'checkout', branch]);
|
|
740
|
+
}
|
|
741
|
+
for (let round = 0; round <= _MERGE_UPDATE_ROUNDS; round++) {
|
|
742
|
+
if (_pr_merge_state(branch) !== 'BEHIND') {
|
|
743
|
+
const merged = run(['gh', 'pr', 'merge', branch, '--merge', '--delete-branch'], {
|
|
744
|
+
check: false,
|
|
745
|
+
});
|
|
746
|
+
if (merged.returncode === 0) {
|
|
747
|
+
return;
|
|
748
|
+
}
|
|
749
|
+
if (_pr_merge_state(branch) !== 'BEHIND') {
|
|
750
|
+
// Not the moving-base failure — surface it unchanged.
|
|
751
|
+
throw new CalledProcessError(merged.returncode, [
|
|
752
|
+
'gh',
|
|
753
|
+
'pr',
|
|
754
|
+
'merge',
|
|
755
|
+
branch,
|
|
756
|
+
'--merge',
|
|
757
|
+
'--delete-branch',
|
|
758
|
+
]);
|
|
759
|
+
}
|
|
760
|
+
// BEHIND appeared between the probe and the merge — fall through
|
|
761
|
+
// to the update below.
|
|
762
|
+
}
|
|
763
|
+
if (round === _MERGE_UPDATE_ROUNDS) {
|
|
764
|
+
break;
|
|
765
|
+
}
|
|
766
|
+
process.stdout.write(
|
|
767
|
+
`↻ head behind ${MAIN_BRANCH} (update round ${round + 1}/${_MERGE_UPDATE_ROUNDS}) — ` +
|
|
768
|
+
`merging ${REMOTE}/${MAIN_BRANCH} into ${branch} and re-running checks\n`,
|
|
769
|
+
);
|
|
770
|
+
run(['git', 'fetch', REMOTE, MAIN_BRANCH]);
|
|
771
|
+
run(['git', 'merge', '--no-edit', `${REMOTE}/${MAIN_BRANCH}`]);
|
|
772
|
+
push_release_branch(branch);
|
|
773
|
+
if (wait_for_checks) {
|
|
774
|
+
watch_pr_checks(branch);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
die(
|
|
778
|
+
`release PR is still behind ${MAIN_BRANCH} after ${_MERGE_UPDATE_ROUNDS} update round(s) — ` +
|
|
779
|
+
`${MAIN_BRANCH} is moving faster than the checks complete. ` +
|
|
780
|
+
'Re-run when the queue quiets down: task release -- --resume --yes',
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
|
|
682
784
|
function have(bin: string): boolean {
|
|
683
785
|
const res = spawnSync('which', [bin], { encoding: 'utf-8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
684
786
|
if (res.error) {
|
|
@@ -1596,9 +1698,10 @@ function execute(
|
|
|
1596
1698
|
_step(4, total, 'PR already merged — skip push');
|
|
1597
1699
|
} else {
|
|
1598
1700
|
// `git push -u` is naturally idempotent — it prints "Everything
|
|
1599
|
-
// up-to-date" when remote already matches.
|
|
1701
|
+
// up-to-date" when remote already matches. push_release_branch
|
|
1702
|
+
// additionally absorbs a remote that moved under us.
|
|
1600
1703
|
_step(4, total, `Push ${branch} to ${REMOTE}`);
|
|
1601
|
-
|
|
1704
|
+
push_release_branch(branch);
|
|
1602
1705
|
}
|
|
1603
1706
|
|
|
1604
1707
|
// ─── 5. PR ──────────────────────────────────────────────────────────────
|
|
@@ -1637,7 +1740,7 @@ function execute(
|
|
|
1637
1740
|
_step(6, total, 'PR already merged — skip checks wait');
|
|
1638
1741
|
} else if (wait_for_checks) {
|
|
1639
1742
|
_step(6, total, 'Wait for PR checks');
|
|
1640
|
-
watch_pr_checks();
|
|
1743
|
+
watch_pr_checks(branch);
|
|
1641
1744
|
} else {
|
|
1642
1745
|
_step(6, total, 'Skip waiting for checks (--no-wait)');
|
|
1643
1746
|
}
|
|
@@ -1647,7 +1750,7 @@ function execute(
|
|
|
1647
1750
|
_step(7, total, `PR #${pr_info!['number']} already merged — skip`);
|
|
1648
1751
|
} else {
|
|
1649
1752
|
_step(7, total, 'Merge pull request (merge commit) and delete branch');
|
|
1650
|
-
|
|
1753
|
+
merge_release_pr(branch, wait_for_checks);
|
|
1651
1754
|
}
|
|
1652
1755
|
|
|
1653
1756
|
// ─── 8. tag main + push tag ─────────────────────────────────────────────
|
|
@@ -119,6 +119,67 @@ export interface OrchestrationReport {
|
|
|
119
119
|
mode: string | null;
|
|
120
120
|
reason: string;
|
|
121
121
|
} | null;
|
|
122
|
+
/** Budget-routing delivery evidence (external review 2026-08-03, Finding 2). */
|
|
123
|
+
delivery: {
|
|
124
|
+
eligible_dispatches: number;
|
|
125
|
+
budget_evidence_lines: number;
|
|
126
|
+
warning: string;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Adherence ≠ delivery: budget routing binds via policy prose, so the
|
|
132
|
+
* doctor checks the DELIVERY evidence — orchestration audit lines that
|
|
133
|
+
* carry a tier decision. Eligible dispatches recorded while
|
|
134
|
+
* `budget_routing != off` but ZERO tier-carrying lines → WARN with the
|
|
135
|
+
* exact reason. Read-only over agents/runtime/state/audit/*.jsonl; no
|
|
136
|
+
* new state. This closes the same blind-spot class the session-canary
|
|
137
|
+
* incident exposed (policy present, delivery silently absent).
|
|
138
|
+
*/
|
|
139
|
+
export function check_budget_delivery(
|
|
140
|
+
workspace_root: string,
|
|
141
|
+
budget_routing: string,
|
|
142
|
+
): OrchestrationReport["delivery"] {
|
|
143
|
+
const out = { eligible_dispatches: 0, budget_evidence_lines: 0, warning: "" };
|
|
144
|
+
if (budget_routing === "off") {
|
|
145
|
+
return out;
|
|
146
|
+
}
|
|
147
|
+
const auditDir = path.join(workspace_root, "agents", "runtime", "state", "audit");
|
|
148
|
+
let files: string[] = [];
|
|
149
|
+
try {
|
|
150
|
+
files = fs.readdirSync(auditDir).filter((f) => f.endsWith(".jsonl"));
|
|
151
|
+
} catch {
|
|
152
|
+
return out; // no audit dir — nothing dispatched, nothing to warn about
|
|
153
|
+
}
|
|
154
|
+
for (const f of files) {
|
|
155
|
+
let text = "";
|
|
156
|
+
try {
|
|
157
|
+
text = fs.readFileSync(path.join(auditDir, f), "utf-8");
|
|
158
|
+
} catch {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
for (const line of text.split("\n")) {
|
|
162
|
+
if (!line.trim()) continue;
|
|
163
|
+
try {
|
|
164
|
+
const rec = JSON.parse(line) as Record<string, unknown>;
|
|
165
|
+
const spawns = Number(rec["spawn_count"] ?? 0);
|
|
166
|
+
if (!Number.isFinite(spawns) || spawns < 1) continue;
|
|
167
|
+
out.eligible_dispatches += 1;
|
|
168
|
+
if (typeof rec["tier"] === "string" && rec["tier"] !== "") {
|
|
169
|
+
out.budget_evidence_lines += 1;
|
|
170
|
+
}
|
|
171
|
+
} catch {
|
|
172
|
+
// non-record line — audit files are shared JSONL surfaces
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
if (out.eligible_dispatches > 0 && out.budget_evidence_lines === 0) {
|
|
177
|
+
out.warning =
|
|
178
|
+
`budget_routing=${budget_routing} is policy-bound but shows no delivery evidence: ` +
|
|
179
|
+
`${out.eligible_dispatches} orchestration dispatch(es) recorded, 0 carry a tier decision — ` +
|
|
180
|
+
`the relation may not be running (adherence != delivery)`;
|
|
181
|
+
}
|
|
182
|
+
return out;
|
|
122
183
|
}
|
|
123
184
|
|
|
124
185
|
export interface DoctorReport {
|
|
@@ -160,8 +221,14 @@ export function collect_orchestration(
|
|
|
160
221
|
const cost_budgets = (cost["budgets"] ?? {}) as Record<string, unknown>;
|
|
161
222
|
const tracking_dir = path.join(workspace_root, "agents", "cost-tracking");
|
|
162
223
|
const ledger_present = fs.existsSync(path.join(tracking_dir, "sessions.jsonl"));
|
|
224
|
+
// YAML 1.1 trap: an unquoted `off` parses as boolean false — coerce it
|
|
225
|
+
// back to the enum string so the check honors the user's intent.
|
|
163
226
|
const budget_routing =
|
|
164
|
-
|
|
227
|
+
sub["budget_routing"] === false
|
|
228
|
+
? "off"
|
|
229
|
+
: typeof sub["budget_routing"] === "string"
|
|
230
|
+
? (sub["budget_routing"] as string)
|
|
231
|
+
: "ask";
|
|
165
232
|
const per_tier = (cost_budgets["per_tier"] ?? {}) as Record<string, unknown>;
|
|
166
233
|
const cooldowns = readCooldowns(tracking_dir);
|
|
167
234
|
const tier_budgets: OrchestrationReport["tier_budgets"] = {};
|
|
@@ -197,6 +264,7 @@ export function collect_orchestration(
|
|
|
197
264
|
cost_budgets,
|
|
198
265
|
ledger_present,
|
|
199
266
|
sample,
|
|
267
|
+
delivery: check_budget_delivery(workspace_root, budget_routing),
|
|
200
268
|
};
|
|
201
269
|
}
|
|
202
270
|
|
|
@@ -407,6 +475,13 @@ function _render(report: DoctorReport): string {
|
|
|
407
475
|
return `${t}=${cap}${cool}`;
|
|
408
476
|
});
|
|
409
477
|
lines.push(` tier budgets (rolling-24h): ${tierBits.join(" · ")}`);
|
|
478
|
+
if (o.delivery.warning) {
|
|
479
|
+
lines.push(` ⚠️ ${o.delivery.warning}`);
|
|
480
|
+
} else if (o.delivery.eligible_dispatches > 0) {
|
|
481
|
+
lines.push(
|
|
482
|
+
` delivery evidence: ${o.delivery.budget_evidence_lines}/${o.delivery.eligible_dispatches} dispatches carry a tier decision`,
|
|
483
|
+
);
|
|
484
|
+
}
|
|
410
485
|
lines.push(` activation probe: ${o.activation.action} — ${o.activation.reason}`);
|
|
411
486
|
if (o.sample) {
|
|
412
487
|
lines.push(
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
* Drop once no consumer ships the legacy file.
|
|
24
24
|
*/
|
|
25
25
|
|
|
26
|
-
import yaml from 'js-yaml';
|
|
26
|
+
import * as yaml from 'js-yaml';
|
|
27
27
|
|
|
28
28
|
/** Frontmatter fence used by the legacy `.agent-user.md` format. */
|
|
29
29
|
const FRONTMATTER_FENCE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
|