@event4u/agent-config 14.5.0 → 14.7.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 +384 -3
- package/README.md +1 -1
- package/dist/agent-src/commands/analyze/decision.md +7 -0
- package/dist/agent-src/commands/analyze/inbox.md +264 -10
- package/dist/agent-src/commands/analyze.md +1 -1
- package/dist/agent-src/commands/optimize/project.md +10 -0
- package/dist/agent-src/commands/roadmap/process-full.md +132 -4
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +31 -0
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +161 -1
- package/dist/agent-src/rules/context-hygiene.md +2 -0
- package/dist/agent-src/rules/decision-revisit-gate.md +111 -7
- package/dist/agent-src/rules/fix-what-you-see.md +108 -0
- package/dist/agent-src/rules/no-roadmap-references.md +0 -5
- package/dist/agent-src/rules/recurring-criticism.md +110 -0
- package/dist/agent-src/rules/roadmap-progress-sync.md +1 -1
- package/dist/agent-src/rules/rule-type-governance.md +0 -2
- package/dist/agent-src/rules/self-repair-loop.md +17 -0
- package/dist/agent-src/rules/skill-quality.md +0 -2
- package/dist/agent-src/rules/source-confidentiality.md +37 -7
- package/dist/agent-src/scripts/archival_sweep.ts +117 -0
- package/dist/agent-src/scripts/archive_completed_roadmaps.ts +19 -3
- package/dist/agent-src/scripts/gate_budget.ts +282 -0
- package/dist/agent-src/scripts/gate_execute.ts +72 -31
- package/dist/agent-src/scripts/roadmap_gates.ts +74 -3
- package/dist/agent-src/scripts/update_roadmap_progress.ts +251 -7
- package/dist/agent-src/skills/adr-create/SKILL.md +25 -3
- package/dist/agent-src/skills/ai-council/SKILL.md +48 -807
- package/dist/agent-src/skills/ai-council/references/advanced-modes.md +320 -0
- package/dist/agent-src/skills/ai-council/references/cost-and-redaction.md +99 -0
- package/dist/agent-src/skills/ai-council/references/execution-modes.md +162 -0
- package/dist/agent-src/skills/ai-council/references/output-and-synthesis.md +125 -0
- package/dist/agent-src/skills/ai-council/references/procedure.md +143 -0
- package/dist/agent-src/skills/decision-review/SKILL.md +18 -0
- package/dist/agent-src/skills/quality-tools/SKILL.md +34 -326
- package/dist/agent-src/skills/quality-tools/references/js-ts-tools.md +113 -0
- package/dist/agent-src/skills/quality-tools/references/php-tools.md +222 -0
- package/dist/agent-src/skills/roadmap-management/SKILL.md +15 -409
- package/dist/agent-src/skills/roadmap-management/references/archival.md +226 -0
- package/dist/agent-src/skills/roadmap-management/references/authoring.md +107 -0
- package/dist/agent-src/skills/roadmap-management/references/dashboard.md +47 -0
- package/dist/agent-src/skills/roadmap-management/references/lifecycle.md +58 -0
- package/dist/agent-src/skills/skill-writing/SKILL.md +34 -570
- package/dist/agent-src/skills/skill-writing/references/procedure.md +275 -0
- package/dist/agent-src/skills/skill-writing/references/read-only-scripts.md +18 -0
- package/dist/agent-src/skills/skill-writing/references/section-patterns.md +298 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +45 -2
- package/dist/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
- package/dist/agent-src/templates/scripts/telemetry/remote.ts +160 -7
- package/dist/agent-src/templates/scripts/telemetry/transport.ts +169 -0
- package/dist/cli/registry.js +1 -1
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-2GNWGMAL.js +172 -0
- package/dist/cli-delegate/{chunk-RLN73PR5.js → chunk-4VR4ORQ6.js} +10 -7
- package/dist/cli-delegate/chunk-6EOCNQAM.js +402 -0
- package/dist/cli-delegate/{chunk-XURJ4AQC.js → chunk-74E3TKWV.js} +381 -280
- package/dist/cli-delegate/{chunk-3UCJWMMP.js → chunk-7F3VYSM2.js} +4 -4
- package/dist/cli-delegate/{chunk-BKO2AZ7Z.js → chunk-B2ERU2VA.js} +1 -1
- package/dist/cli-delegate/{chunk-NXFWFAYV.js → chunk-G23AT6GD.js} +1 -0
- package/dist/cli-delegate/{chunk-63WSP6YZ.js → chunk-GJ5BVHXM.js} +72 -165
- package/dist/cli-delegate/{chunk-ZPSXJTLI.js → chunk-JZ4NR2TW.js} +353 -1
- package/dist/cli-delegate/chunk-MWRD5YOH.js +100 -0
- package/dist/cli-delegate/{chunk-GRRPEPRN.js → chunk-T7L2QK3K.js} +24 -5
- package/dist/cli-delegate/{chunk-EVTY7ISR.js → chunk-YM4GZDJG.js} +9 -1
- package/dist/cli-delegate/cmd_conformance.js +459 -709
- package/dist/cli-delegate/cmd_doctor.js +6 -6
- package/dist/cli-delegate/cmd_explain.js +1 -1
- package/dist/cli-delegate/cmd_export.js +7 -6
- package/dist/cli-delegate/cmd_handoff.js +6 -6
- package/dist/cli-delegate/cmd_packs_active.js +1 -1
- package/dist/cli-delegate/cmd_preflight.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +7 -6
- package/dist/cli-delegate/cmd_session_recycle.js +840 -30
- package/dist/cli-delegate/cmd_settings_migrate.js +7 -6
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/cli-delegate/cmd_sync.js +7 -6
- package/dist/cli-delegate/cmd_uninstall.js +9 -7
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +7 -6
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +72 -29
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -4
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +10 -8
- package/dist/discovery/trust-report.md +3 -3
- package/dist/discovery/workspaces.json +8 -6
- package/dist/hooks/dispatch.js +3127 -2022
- package/dist/install/claudePathsPlan.js +187 -0
- package/dist/install/claudePathsPlan.js.map +1 -0
- package/dist/install/hostLayerFingerprint.js +136 -0
- package/dist/install/hostLayerFingerprint.js.map +1 -0
- package/dist/install/install.mjs +553 -425
- package/dist/install/partitionEligibility.js +276 -0
- package/dist/install/partitionEligibility.js.map +1 -0
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +1744 -1191
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_src.js +41 -0
- package/dist/scripts/_lib/agent_src.js.map +1 -1
- package/dist/scripts/_lib/install_layout.js +83 -0
- package/dist/scripts/_lib/install_layout.js.map +1 -0
- package/dist/scripts/_lib/installed_lock.js +444 -0
- package/dist/scripts/_lib/installed_lock.js.map +1 -0
- package/dist/scripts/condense.js +13 -164
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +4 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/docs/contracts/settings-classes.md +12 -6
- package/docs/guidelines/agent-infra/agent-interaction-and-decision-quality.md +8 -3
- package/docs/guidelines/agent-infra/context-hygiene-mechanics.md +26 -0
- package/docs/guidelines/agent-infra/engineering-memory-data-format.md +49 -0
- package/docs/guidelines/agent-infra/memory-access.md +17 -0
- package/docs/guidelines/agent-infra/recurring-criticism-mechanics.md +96 -0
- package/package.json +1 -1
- package/src/agent-src/scripts/archival_sweep.ts +117 -0
- package/src/agent-src/scripts/archive_completed_roadmaps.ts +19 -3
- package/src/agent-src/scripts/gate_budget.ts +282 -0
- package/src/agent-src/scripts/gate_execute.ts +72 -31
- package/src/agent-src/scripts/roadmap_gates.ts +74 -3
- package/src/agent-src/scripts/update_roadmap_progress.ts +251 -7
- package/src/agent-src/templates/scripts/telemetry/flush_sender.mjs +163 -0
- package/src/agent-src/templates/scripts/telemetry/remote.ts +160 -7
- package/src/agent-src/templates/scripts/telemetry/transport.ts +169 -0
- package/src/config/agent-settings.template.yml +27 -2
- package/src/config/ci-local-parity.yml +18 -0
- package/src/config/estate-count-budget.json +367 -3
- package/src/config/gate-violation-baselines.json +41 -41
- package/src/config/pack-size-budget.json +3 -2
- package/src/config/rule-activation-census.json +14 -6
- package/src/config/rule-stub-ceilings.json +19 -6
- package/src/install/claudePathsPlan.ts +196 -0
- package/src/install/hostLayerFingerprint.ts +145 -0
- package/src/install/partitionEligibility.ts +325 -0
- package/src/scripts/_cli/cmd_session_recycle.ts +9 -1
- package/src/scripts/_dispatch.bash +6 -1
- package/src/scripts/_lib/ac_heading.ts +30 -0
- package/src/scripts/_lib/agent_src.ts +42 -0
- package/src/scripts/_lib/bench_ab_pinned_repo.ts +162 -0
- package/src/scripts/_lib/bench_ab_workspace.ts +10 -1
- package/src/scripts/_lib/envelope_grounding.ts +38 -8
- package/src/scripts/_lib/installed_lock.ts +50 -5
- package/src/scripts/_lib/layer_overlap_notice.ts +85 -0
- package/src/scripts/_lib/md_section.ts +129 -0
- package/src/scripts/_lib/scope_guard.sh +86 -4
- package/src/scripts/_lib/self_repair.ts +19 -0
- package/src/scripts/_lib/self_repair_class_b.ts +171 -0
- package/src/scripts/_lib/subagent_capsule.ts +81 -1
- package/src/scripts/adr/regenerate_index.ts +13 -2
- package/src/scripts/adr_cite_check.ts +424 -0
- package/src/scripts/ai_council/cli_help.ts +55 -0
- package/src/scripts/before_complete_hook.ts +153 -28
- package/src/scripts/bench_ab_v2_complexity.ts +27 -7
- package/src/scripts/check_adr_frontmatter.ts +187 -1
- package/src/scripts/check_references.ts +60 -0
- package/src/scripts/check_release_published.ts +32 -11
- package/src/scripts/check_single_delivery.ts +517 -0
- package/src/scripts/ci_settle.ts +170 -0
- package/src/scripts/condense.ts +20 -170
- package/src/scripts/council_cli.ts +3 -3
- package/src/scripts/dispatch_r2_reviewer.ts +6 -14
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +77 -14
- package/src/scripts/hooks/block_no_verify.ts +61 -10
- package/src/scripts/hooks/block_unauthorized_git.ts +1 -1
- package/src/scripts/hooks/concern_registry.ts +4 -0
- package/src/scripts/hooks/rtk_wrap_hook.ts +8 -3
- package/src/scripts/hooks/run_continuation_hook.ts +887 -68
- package/src/scripts/hooks/ship_diff_volume_hook.ts +81 -15
- package/src/scripts/hooks/source_first_gate_hook.ts +464 -0
- package/src/scripts/hooks/state_io.ts +782 -20
- package/src/scripts/hooks/subagent_ledger_hook.ts +44 -15
- package/src/scripts/hooks/telemetry_flush_hook.ts +139 -0
- package/src/scripts/hooks/telemetry_self_repair.ts +128 -0
- package/src/scripts/hooks/telemetry_usage_hook.ts +22 -4
- package/src/scripts/hooks/turn_end_gate_hook.ts +81 -15
- package/src/scripts/install.sh +12 -2
- package/src/scripts/install.ts +16 -11
- package/src/scripts/language_mirror_hook.ts +561 -25
- package/src/scripts/lint_framework_leakage_allowlist.json +2 -2
- package/src/scripts/lint_plan_risk_register.ts +4 -3
- package/src/scripts/lint_skill_router_head.ts +4 -4
- package/src/scripts/mcp_server/tools.ts +2 -2
- package/src/scripts/memory_consolidation.ts +189 -0
- package/src/scripts/memory_eviction.ts +384 -0
- package/src/scripts/new_roadmap.ts +136 -0
- package/src/scripts/pack_mcp_content.ts +20 -5
- package/src/scripts/release.ts +37 -38
- package/src/scripts/report_layer_overlap.ts +78 -0
- package/src/scripts/report_memory_pointers.ts +622 -0
- package/src/scripts/self_repair_hook.ts +37 -5
- package/src/scripts/session_register_hook.ts +115 -15
- package/src/scripts/skill_usage_collect.ts +11 -1
- package/src/scripts/skill_usage_report.ts +278 -8
- package/src/scripts/sync_pr_branch.ts +7 -0
- package/src/server/schemas/settings.ts +8 -0
- package/dist/cli-delegate/chunk-EJITJBJX.js +0 -172
- package/dist/cli-delegate/chunk-PZWRPRWP.js +0 -38
- /package/dist/cli-delegate/{chunk-RYNADUFG.js → chunk-LWTYTX6F.js} +0 -0
|
@@ -1,4 +1,14 @@
|
|
|
1
1
|
import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
readHookStdin
|
|
4
|
+
} from "./chunk-F7DQWYR5.js";
|
|
5
|
+
import {
|
|
6
|
+
has_stable_session_id,
|
|
7
|
+
prune_legacy_state_file,
|
|
8
|
+
prune_stale_session_states,
|
|
9
|
+
session_state_file,
|
|
10
|
+
update_json_under_lock
|
|
11
|
+
} from "./chunk-6EOCNQAM.js";
|
|
2
12
|
|
|
3
13
|
// src/scripts/_lib/secret_detector.ts
|
|
4
14
|
var ENTROPY_MIN_TOKEN_LENGTH = 20;
|
|
@@ -213,6 +223,20 @@ var CAPSULE_SCHEMA_VERSION = 3;
|
|
|
213
223
|
function isShortLine(value, max) {
|
|
214
224
|
return typeof value === "string" && value.length > 0 && value.length <= max && !value.includes("\n");
|
|
215
225
|
}
|
|
226
|
+
function isPathRef(value) {
|
|
227
|
+
return typeof value === "string" && value.length > 0 && !/\s/.test(value);
|
|
228
|
+
}
|
|
229
|
+
function checkPathRefList(errors, field, value, max, required) {
|
|
230
|
+
checkList(errors, field, value, max, required);
|
|
231
|
+
if (!Array.isArray(value)) return;
|
|
232
|
+
if (value.some((x) => !isShortLine(x, max))) return;
|
|
233
|
+
const bad = value.filter((x) => !isPathRef(x));
|
|
234
|
+
if (bad.length === 0) return;
|
|
235
|
+
const shown = bad.slice(0, 3).map((x) => JSON.stringify(String(x))).join(", ");
|
|
236
|
+
errors.push(
|
|
237
|
+
`${field} carries ${bad.length} entr${bad.length === 1 ? "y" : "ies"} that are prose, not path refs: ${shown}${bad.length > 3 ? ", \u2026" : ""} \u2014 write the path (src/generated/api.ts, ../other-worktree/) or drop the entry; a sentence naming a file cannot be compared against a write target, and being comparable is the whole reason this is a field rather than a line of prose`
|
|
238
|
+
);
|
|
239
|
+
}
|
|
216
240
|
function checkList(errors, field, value, max, required) {
|
|
217
241
|
if (value === void 0) {
|
|
218
242
|
if (required) errors.push(`${field} is required`);
|
|
@@ -402,7 +426,7 @@ function validateRecycleEnvelope(input) {
|
|
|
402
426
|
checkList(errors, "decisions", e["decisions"], MAX_LINE_CHARS, false);
|
|
403
427
|
errors.push(...decisionTagErrors(e["decisions"]));
|
|
404
428
|
checkList(errors, "constraints", e["constraints"], MAX_LINE_CHARS, false);
|
|
405
|
-
|
|
429
|
+
checkPathRefList(errors, "do_not_touch", e["do_not_touch"], MAX_REF_CHARS, false);
|
|
406
430
|
checkList(errors, "open_worker_envelopes", e["open_worker_envelopes"], MAX_REF_CHARS, false);
|
|
407
431
|
checkList(errors, "artifact_paths", e["artifact_paths"], MAX_REF_CHARS, false);
|
|
408
432
|
checkList(errors, "suggested_skills", e["suggested_skills"], MAX_REF_CHARS, false);
|
|
@@ -455,7 +479,335 @@ function validateAssumption(input, label = "assumption") {
|
|
|
455
479
|
return errors;
|
|
456
480
|
}
|
|
457
481
|
|
|
482
|
+
// src/scripts/before_complete_hook.ts
|
|
483
|
+
import fs from "node:fs";
|
|
484
|
+
import path from "node:path";
|
|
485
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
486
|
+
var STATE_FILE = path.join("agents", "state", "verify-before-complete.json");
|
|
487
|
+
var STATE_DIR = path.join("agents", "state", "verify-before-complete");
|
|
488
|
+
var STATE_RETENTION_DAYS = 7;
|
|
489
|
+
function statePathFor(session_id) {
|
|
490
|
+
return session_state_file(STATE_DIR, session_id);
|
|
491
|
+
}
|
|
492
|
+
var COMMAND_TOOLS = /* @__PURE__ */ new Set([
|
|
493
|
+
"launch-process",
|
|
494
|
+
"launch_process",
|
|
495
|
+
// Augment
|
|
496
|
+
"Bash",
|
|
497
|
+
"BashTool",
|
|
498
|
+
// Claude Code
|
|
499
|
+
"run-process",
|
|
500
|
+
"runProcess",
|
|
501
|
+
// variants
|
|
502
|
+
"shell",
|
|
503
|
+
"execute_shell",
|
|
504
|
+
// generic / Cline
|
|
505
|
+
"RunShellCommand"
|
|
506
|
+
// Cursor
|
|
507
|
+
]);
|
|
508
|
+
var _VERIFICATION_RE = new RegExp(
|
|
509
|
+
"(?:^|[\\s;&|`(])(task\\s+(?:ci|test|tests|lint|check|qa|phpstan|rector|ecs|pest|pytest)|(?:\\./|\\.venv/bin/|vendor/bin/)?(?:pest|phpunit|phpstan|psalm|rector|ecs)\\b|(?:python3?|\\.venv/bin/python3?)\\s+-m\\s+pytest|pytest\\b|(?:npm|pnpm|yarn|bun)\\s+(?:run\\s+)?(?:test|check|lint|typecheck|tsc)|cargo\\s+(?:test|check|clippy)|go\\s+test|make\\s+(?:test|check|lint)|composer\\s+(?:test|check|lint|phpstan)|(?:php\\s+)?artisan\\s+test)",
|
|
510
|
+
"i"
|
|
511
|
+
);
|
|
512
|
+
function _now() {
|
|
513
|
+
return (/* @__PURE__ */ new Date()).toISOString().replace(/\.\d{3}Z$/, "+00:00");
|
|
514
|
+
}
|
|
515
|
+
function _empty_state() {
|
|
516
|
+
return {
|
|
517
|
+
schema_version: 1,
|
|
518
|
+
session_id: "",
|
|
519
|
+
turn_started_at: null,
|
|
520
|
+
last_verification: null,
|
|
521
|
+
verifications_this_turn: 0,
|
|
522
|
+
verifications_this_session: 0,
|
|
523
|
+
last_stop_at: null,
|
|
524
|
+
verified_this_turn: false,
|
|
525
|
+
ci_saw_pending: false,
|
|
526
|
+
nonevidence_this_turn: 0,
|
|
527
|
+
// Round 7 § Phase 1 — the last CI read, SESSION-scoped on purpose. Every
|
|
528
|
+
// other counter here is turn-scoped because a stale POSITIVE would wrongly
|
|
529
|
+
// vouch ("I verified"). This one is a NEGATIVE ("CI was not settled"), and a
|
|
530
|
+
// stale negative only ever refuses more often — so surviving the turn
|
|
531
|
+
// boundary preserves the freshness invariant rather than bypassing it. It
|
|
532
|
+
// has to survive it: the measured failure is a completion claim in a LATER
|
|
533
|
+
// turn than the poll it rests on.
|
|
534
|
+
ci_last: null,
|
|
535
|
+
checked_at: _now()
|
|
536
|
+
};
|
|
537
|
+
}
|
|
538
|
+
function _extract_command(payload) {
|
|
539
|
+
const toolRaw = payload["tool_name"] || payload["toolName"] || payload["tool"];
|
|
540
|
+
if (!(typeof toolRaw === "string" && COMMAND_TOOLS.has(toolRaw))) {
|
|
541
|
+
return [typeof toolRaw === "string" ? toolRaw : null, null];
|
|
542
|
+
}
|
|
543
|
+
const tool = toolRaw;
|
|
544
|
+
const ti = payload["tool_input"];
|
|
545
|
+
if (typeof ti === "object" && ti !== null && !Array.isArray(ti)) {
|
|
546
|
+
for (const key of ["command", "cmd", "shell_command"]) {
|
|
547
|
+
const v = ti[key];
|
|
548
|
+
if (typeof v === "string" && v) {
|
|
549
|
+
return [tool, v];
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
for (const key of ["command", "cmd"]) {
|
|
554
|
+
const v = payload[key];
|
|
555
|
+
if (typeof v === "string" && v) {
|
|
556
|
+
return [tool, v];
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
return [tool, null];
|
|
560
|
+
}
|
|
561
|
+
function _is_verification(command) {
|
|
562
|
+
return _VERIFICATION_RE.test(command) || isCiPoll(command);
|
|
563
|
+
}
|
|
564
|
+
var _VACUOUS_PATTERNS = [
|
|
565
|
+
// gh pr checks / gh run — no checks registered at all.
|
|
566
|
+
/\b0\s+(?:checks?|runs?)\b/i,
|
|
567
|
+
// Test runners reporting an empty run.
|
|
568
|
+
/\bno tests? (?:ran|found|to run|were run)\b/i,
|
|
569
|
+
/\bNo test files found\b/i,
|
|
570
|
+
/\b0\s+pass(?:ed|ing)?\b[\s,|·]*\b0\s+fail(?:ed|ing|ures)?\b/i,
|
|
571
|
+
/\bTests?\s+0\s+passed\b/i,
|
|
572
|
+
/\bRan 0 tests?\b/i,
|
|
573
|
+
// Linters / scanners with an empty target set.
|
|
574
|
+
/\bno files? (?:matched|to (?:lint|check|scan)|found)\b/i,
|
|
575
|
+
/\b0\s+files?\s+(?:checked|scanned|linted)\b/i,
|
|
576
|
+
/\bnothing to check\b/i
|
|
577
|
+
];
|
|
578
|
+
function isVacuousOutput(output) {
|
|
579
|
+
const text = output.trim();
|
|
580
|
+
if (!text) {
|
|
581
|
+
return false;
|
|
582
|
+
}
|
|
583
|
+
const lines = text.split("\n").filter((l) => l.trim());
|
|
584
|
+
return lines.every((l) => _VACUOUS_PATTERNS.some((re) => re.test(l)));
|
|
585
|
+
}
|
|
586
|
+
var _CI_POLL_RE = /\bgh\s+(?:pr\s+checks|run\s+(?:watch|list|view))\b/i;
|
|
587
|
+
function isCiPoll(command) {
|
|
588
|
+
return _CI_POLL_RE.test(command);
|
|
589
|
+
}
|
|
590
|
+
function pendingCount(output) {
|
|
591
|
+
const m = /(\d+)\s+(?:checks? )?pending\b/i.exec(output) ?? /\bpending[:=]\s*(\d+)/i.exec(output);
|
|
592
|
+
if (m?.[1] !== void 0) {
|
|
593
|
+
return Number(m[1]);
|
|
594
|
+
}
|
|
595
|
+
const rows = output.match(/^\S.*\b(pending|in_progress|queued)\b/gim);
|
|
596
|
+
if (rows) {
|
|
597
|
+
return rows.length;
|
|
598
|
+
}
|
|
599
|
+
if (/\bno checks reported\b/i.test(output)) {
|
|
600
|
+
return null;
|
|
601
|
+
}
|
|
602
|
+
if (/^\S+\s+(pass|fail|skipping|successful|failing)\b/im.test(output)) {
|
|
603
|
+
return 0;
|
|
604
|
+
}
|
|
605
|
+
return null;
|
|
606
|
+
}
|
|
607
|
+
function _extract_output(payload) {
|
|
608
|
+
for (const key of ["tool_response", "toolResponse", "output", "stdout", "result"]) {
|
|
609
|
+
const v = payload[key];
|
|
610
|
+
if (typeof v === "string") {
|
|
611
|
+
return v;
|
|
612
|
+
}
|
|
613
|
+
if (v !== null && typeof v === "object") {
|
|
614
|
+
return JSON.stringify(v);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
return null;
|
|
618
|
+
}
|
|
619
|
+
function _reset_turn(state, session_id) {
|
|
620
|
+
state["session_id"] = session_id || state["session_id"] || "";
|
|
621
|
+
state["turn_started_at"] = _now();
|
|
622
|
+
state["verifications_this_turn"] = 0;
|
|
623
|
+
state["verified_this_turn"] = false;
|
|
624
|
+
state["ci_saw_pending"] = false;
|
|
625
|
+
state["nonevidence_this_turn"] = 0;
|
|
626
|
+
return state;
|
|
627
|
+
}
|
|
628
|
+
function _asInt(v) {
|
|
629
|
+
if (!v) {
|
|
630
|
+
return 0;
|
|
631
|
+
}
|
|
632
|
+
if (typeof v === "number") {
|
|
633
|
+
return Math.trunc(v);
|
|
634
|
+
}
|
|
635
|
+
const n = Number(v);
|
|
636
|
+
return Number.isFinite(n) ? Math.trunc(n) : 0;
|
|
637
|
+
}
|
|
638
|
+
function _update(state, event, envelope) {
|
|
639
|
+
const session_id = envelope["session_id"] || state["session_id"] || "";
|
|
640
|
+
if (session_id && session_id !== state["session_id"]) {
|
|
641
|
+
state["session_id"] = session_id;
|
|
642
|
+
state["verifications_this_session"] = 0;
|
|
643
|
+
state["ci_last"] = null;
|
|
644
|
+
state = _reset_turn(state, session_id);
|
|
645
|
+
}
|
|
646
|
+
let payload = envelope["payload"];
|
|
647
|
+
if (!(typeof payload === "object" && payload !== null && !Array.isArray(payload))) {
|
|
648
|
+
payload = {};
|
|
649
|
+
}
|
|
650
|
+
const pl = payload;
|
|
651
|
+
if (event === "session_start" || event === "user_prompt_submit") {
|
|
652
|
+
state = _reset_turn(state, session_id);
|
|
653
|
+
} else if (event === "post_tool_use") {
|
|
654
|
+
const [tool, cmd] = _extract_command(pl);
|
|
655
|
+
if (cmd && _is_verification(cmd)) {
|
|
656
|
+
const output = _extract_output(pl);
|
|
657
|
+
const vacuous = output !== null && isVacuousOutput(output);
|
|
658
|
+
let counts = !vacuous;
|
|
659
|
+
if (isCiPoll(cmd)) {
|
|
660
|
+
const pending = output === null ? null : pendingCount(output);
|
|
661
|
+
if (pending !== null && pending > 0) {
|
|
662
|
+
state["ci_saw_pending"] = true;
|
|
663
|
+
counts = false;
|
|
664
|
+
} else if (pending === 0) {
|
|
665
|
+
counts = !vacuous && state["ci_saw_pending"] === true;
|
|
666
|
+
} else {
|
|
667
|
+
counts = false;
|
|
668
|
+
}
|
|
669
|
+
state["ci_last"] = {
|
|
670
|
+
at: _now(),
|
|
671
|
+
command: cmd.slice(0, 512),
|
|
672
|
+
pending,
|
|
673
|
+
settled: pending === 0 && counts === true
|
|
674
|
+
};
|
|
675
|
+
}
|
|
676
|
+
state["last_verification"] = {
|
|
677
|
+
command: cmd.slice(0, 512),
|
|
678
|
+
tool,
|
|
679
|
+
at: _now(),
|
|
680
|
+
platform: envelope["platform"] || "",
|
|
681
|
+
vacuous,
|
|
682
|
+
counted: counts
|
|
683
|
+
};
|
|
684
|
+
if (counts) {
|
|
685
|
+
state["verifications_this_turn"] = _asInt(state["verifications_this_turn"]) + 1;
|
|
686
|
+
state["verifications_this_session"] = _asInt(state["verifications_this_session"]) + 1;
|
|
687
|
+
state["verified_this_turn"] = true;
|
|
688
|
+
} else {
|
|
689
|
+
state["nonevidence_this_turn"] = _asInt(state["nonevidence_this_turn"]) + 1;
|
|
690
|
+
}
|
|
691
|
+
}
|
|
692
|
+
} else if (event === "stop") {
|
|
693
|
+
state["last_stop_at"] = _now();
|
|
694
|
+
}
|
|
695
|
+
state["checked_at"] = _now();
|
|
696
|
+
return state;
|
|
697
|
+
}
|
|
698
|
+
function run(stdin_text, options) {
|
|
699
|
+
const { consumer_root } = options;
|
|
700
|
+
const verbose = options.verbose ?? false;
|
|
701
|
+
let envelope = {};
|
|
702
|
+
if (stdin_text.trim()) {
|
|
703
|
+
try {
|
|
704
|
+
const decoded = JSON.parse(stdin_text);
|
|
705
|
+
if (typeof decoded === "object" && decoded !== null && !Array.isArray(decoded)) {
|
|
706
|
+
envelope = decoded;
|
|
707
|
+
}
|
|
708
|
+
} catch {
|
|
709
|
+
envelope = {};
|
|
710
|
+
}
|
|
711
|
+
}
|
|
712
|
+
const event = envelope["event"] || "";
|
|
713
|
+
const session_id = typeof envelope["session_id"] === "string" ? envelope["session_id"] : "";
|
|
714
|
+
if (!has_stable_session_id(session_id)) {
|
|
715
|
+
if (verbose) {
|
|
716
|
+
process.stderr.write(
|
|
717
|
+
"verify-before-complete-hook: no session_id \u2014 running stateless, nothing recorded\n"
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
return 0;
|
|
721
|
+
}
|
|
722
|
+
const target = path.join(consumer_root, statePathFor(session_id));
|
|
723
|
+
let state = {};
|
|
724
|
+
const outcome = update_json_under_lock(target, (loaded) => {
|
|
725
|
+
state = _update({ ..._empty_state(), ...loaded }, event, envelope);
|
|
726
|
+
return state;
|
|
727
|
+
});
|
|
728
|
+
if (outcome === "failed") {
|
|
729
|
+
if (verbose) {
|
|
730
|
+
process.stderr.write("verify-before-complete-hook: state write failed\n");
|
|
731
|
+
}
|
|
732
|
+
return 0;
|
|
733
|
+
}
|
|
734
|
+
if (event === "session_start" || event === "user_prompt_submit") {
|
|
735
|
+
prune_legacy_state_file(path.join(consumer_root, STATE_FILE));
|
|
736
|
+
prune_stale_session_states(
|
|
737
|
+
path.join(consumer_root, STATE_DIR),
|
|
738
|
+
Date.now(),
|
|
739
|
+
STATE_RETENTION_DAYS
|
|
740
|
+
);
|
|
741
|
+
}
|
|
742
|
+
if (verbose) {
|
|
743
|
+
process.stderr.write(
|
|
744
|
+
`verify-before-complete-hook: event=${event} verified_this_turn=${pyRepr(state["verified_this_turn"])} verifications_this_turn=${pyRepr(state["verifications_this_turn"])}
|
|
745
|
+
`
|
|
746
|
+
);
|
|
747
|
+
}
|
|
748
|
+
return 0;
|
|
749
|
+
}
|
|
750
|
+
function pyRepr(value) {
|
|
751
|
+
if (value === null || value === void 0) {
|
|
752
|
+
return "None";
|
|
753
|
+
}
|
|
754
|
+
if (typeof value === "boolean") {
|
|
755
|
+
return value ? "True" : "False";
|
|
756
|
+
}
|
|
757
|
+
return String(value);
|
|
758
|
+
}
|
|
759
|
+
function parse_args(argv) {
|
|
760
|
+
let platform = "generic";
|
|
761
|
+
let verbose = false;
|
|
762
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
763
|
+
const arg = argv[i];
|
|
764
|
+
if (arg === "--platform") {
|
|
765
|
+
platform = argv[i + 1] ?? "generic";
|
|
766
|
+
i += 1;
|
|
767
|
+
} else if (arg !== void 0 && arg.startsWith("--platform=")) {
|
|
768
|
+
platform = arg.slice("--platform=".length);
|
|
769
|
+
} else if (arg === "--verbose") {
|
|
770
|
+
verbose = true;
|
|
771
|
+
}
|
|
772
|
+
}
|
|
773
|
+
return { platform, verbose };
|
|
774
|
+
}
|
|
775
|
+
function _readStdin() {
|
|
776
|
+
return readHookStdin();
|
|
777
|
+
}
|
|
778
|
+
function main(argv) {
|
|
779
|
+
const args = parse_args(argv ?? process.argv.slice(2));
|
|
780
|
+
return run(_readStdin(), { consumer_root: process.cwd(), verbose: args.verbose });
|
|
781
|
+
}
|
|
782
|
+
function _isCliEntry() {
|
|
783
|
+
if (true) {
|
|
784
|
+
return false;
|
|
785
|
+
}
|
|
786
|
+
if (process.argv[1] === void 0) {
|
|
787
|
+
return false;
|
|
788
|
+
}
|
|
789
|
+
const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
|
|
790
|
+
if (import.meta.url === argvUrl) {
|
|
791
|
+
return true;
|
|
792
|
+
}
|
|
793
|
+
try {
|
|
794
|
+
const here = fs.realpathSync(fileURLToPath(import.meta.url));
|
|
795
|
+
const argv = fs.realpathSync(path.resolve(process.argv[1]));
|
|
796
|
+
return here === argv;
|
|
797
|
+
} catch {
|
|
798
|
+
return false;
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
var isCliEntry = _isCliEntry();
|
|
802
|
+
if (isCliEntry) {
|
|
803
|
+
process.exit(main(process.argv.slice(2)));
|
|
804
|
+
}
|
|
805
|
+
|
|
458
806
|
export {
|
|
807
|
+
statePathFor,
|
|
808
|
+
isVacuousOutput,
|
|
809
|
+
isCiPoll,
|
|
810
|
+
pendingCount,
|
|
459
811
|
CAPSULE_SCHEMA_VERSION,
|
|
460
812
|
scanEnvelopeDirectives,
|
|
461
813
|
wrapAsPriorSessionData,
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/scripts/_lib/fs_atomic.ts
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { randomBytes } from "node:crypto";
|
|
7
|
+
function _normalize_encoding(encoding) {
|
|
8
|
+
const compact = encoding.toLowerCase().replace(/[-_\s]/g, "");
|
|
9
|
+
const candidates = [encoding, compact];
|
|
10
|
+
if (compact === "latin1" || compact === "iso88591") candidates.push("latin1");
|
|
11
|
+
if (compact === "utf8") candidates.push("utf8");
|
|
12
|
+
if (compact === "utf16le" || compact === "utf16") candidates.push("utf16le");
|
|
13
|
+
if (compact === "usascii") candidates.push("ascii");
|
|
14
|
+
for (const c of candidates) {
|
|
15
|
+
if (Buffer.isEncoding(c)) return c;
|
|
16
|
+
}
|
|
17
|
+
throw new Error(`unknown encoding: ${encoding}`);
|
|
18
|
+
}
|
|
19
|
+
function write_atomic(p, data, options = {}) {
|
|
20
|
+
const encoding = options.encoding ?? "utf-8";
|
|
21
|
+
const target = path.normalize(p);
|
|
22
|
+
const parent = path.dirname(target);
|
|
23
|
+
fs.mkdirSync(parent, { recursive: true });
|
|
24
|
+
let payload;
|
|
25
|
+
if (typeof data === "string") {
|
|
26
|
+
payload = Buffer.from(data, _normalize_encoding(encoding));
|
|
27
|
+
} else if (data instanceof Uint8Array) {
|
|
28
|
+
payload = Buffer.from(data);
|
|
29
|
+
} else {
|
|
30
|
+
throw new TypeError(
|
|
31
|
+
`write_atomic: data must be str or bytes, got ${typeof data}`
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
let fd = null;
|
|
35
|
+
let tmp_path = "";
|
|
36
|
+
for (let attempt = 0; attempt < 32; attempt += 1) {
|
|
37
|
+
tmp_path = path.join(
|
|
38
|
+
parent,
|
|
39
|
+
`.${path.basename(target)}.tmp.${randomBytes(6).toString("hex")}`
|
|
40
|
+
);
|
|
41
|
+
try {
|
|
42
|
+
fd = fs.openSync(tmp_path, "wx", 384);
|
|
43
|
+
break;
|
|
44
|
+
} catch (err) {
|
|
45
|
+
if (err.code === "EEXIST") continue;
|
|
46
|
+
throw err;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
if (fd === null) {
|
|
50
|
+
throw new Error("write_atomic: could not create a unique temp file");
|
|
51
|
+
}
|
|
52
|
+
let closed = false;
|
|
53
|
+
try {
|
|
54
|
+
let offset = 0;
|
|
55
|
+
while (offset < payload.length) {
|
|
56
|
+
offset += fs.writeSync(fd, payload, offset, payload.length - offset);
|
|
57
|
+
}
|
|
58
|
+
try {
|
|
59
|
+
fs.fsyncSync(fd);
|
|
60
|
+
} catch {
|
|
61
|
+
}
|
|
62
|
+
fs.closeSync(fd);
|
|
63
|
+
closed = true;
|
|
64
|
+
fs.renameSync(tmp_path, target);
|
|
65
|
+
} catch (err) {
|
|
66
|
+
if (!closed) {
|
|
67
|
+
try {
|
|
68
|
+
fs.closeSync(fd);
|
|
69
|
+
} catch {
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
try {
|
|
73
|
+
fs.unlinkSync(tmp_path);
|
|
74
|
+
} catch {
|
|
75
|
+
}
|
|
76
|
+
throw err;
|
|
77
|
+
}
|
|
78
|
+
_fsync_dir(parent);
|
|
79
|
+
return target;
|
|
80
|
+
}
|
|
81
|
+
function _fsync_dir(directory) {
|
|
82
|
+
let dir_fd;
|
|
83
|
+
try {
|
|
84
|
+
dir_fd = fs.openSync(directory, fs.constants.O_RDONLY);
|
|
85
|
+
} catch {
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
try {
|
|
90
|
+
fs.fsyncSync(dir_fd);
|
|
91
|
+
} catch {
|
|
92
|
+
}
|
|
93
|
+
} finally {
|
|
94
|
+
fs.closeSync(dir_fd);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export {
|
|
99
|
+
write_atomic
|
|
100
|
+
};
|
|
@@ -58,6 +58,7 @@ var _VERSION_RE = /^\s*agent_config_version\s*:\s*"?([^"\s]+)"?\s*$/;
|
|
|
58
58
|
var _SCHEMA_RE = /^\s*schema_version\s*:\s*(\d+)\s*$/;
|
|
59
59
|
var _LAYOUT_RE = /^\s*install_layout_version\s*:\s*(\d+)\s*$/;
|
|
60
60
|
var _INSTALLED_AT_RE = /^\s*installed_at\s*:\s*"?([^"\s]+)"?\s*$/;
|
|
61
|
+
var _FINGERPRINT_RE = /^\s*host_layer_fingerprint\s*:\s*"?([0-9a-f]{64})"?\s*$/;
|
|
61
62
|
var _TOOL_RE = /^\s*-\s*([A-Za-z0-9_\-.]+)\s*$/;
|
|
62
63
|
function expanduser(p) {
|
|
63
64
|
if (p === "~") {
|
|
@@ -123,6 +124,12 @@ function read_lockfile(path2) {
|
|
|
123
124
|
in_tools = false;
|
|
124
125
|
continue;
|
|
125
126
|
}
|
|
127
|
+
const fp_m = _FINGERPRINT_RE.exec(raw_line);
|
|
128
|
+
if (fp_m) {
|
|
129
|
+
data.host_layer_fingerprint = fp_m[1];
|
|
130
|
+
in_tools = false;
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
126
133
|
if (raw_line.trim().startsWith("tools:")) {
|
|
127
134
|
in_tools = true;
|
|
128
135
|
continue;
|
|
@@ -145,14 +152,17 @@ function splitlines(text) {
|
|
|
145
152
|
}
|
|
146
153
|
return parts;
|
|
147
154
|
}
|
|
148
|
-
function _render(version, tools, installed_at) {
|
|
155
|
+
function _render(version, tools, installed_at, fingerprint) {
|
|
149
156
|
const lines = [
|
|
150
157
|
`schema_version: ${SCHEMA_VERSION}`,
|
|
151
158
|
`install_layout_version: ${INSTALL_LAYOUT_VERSION}`,
|
|
152
159
|
`agent_config_version: "${version}"`,
|
|
153
|
-
`installed_at: "${installed_at}"
|
|
154
|
-
"tools:"
|
|
160
|
+
`installed_at: "${installed_at}"`
|
|
155
161
|
];
|
|
162
|
+
if (fingerprint) {
|
|
163
|
+
lines.push(`host_layer_fingerprint: "${fingerprint}"`);
|
|
164
|
+
}
|
|
165
|
+
lines.push("tools:");
|
|
156
166
|
for (const tool of tools) {
|
|
157
167
|
lines.push(` - ${tool}`);
|
|
158
168
|
}
|
|
@@ -162,7 +172,12 @@ function write_lockfile(version, tools, options = {}) {
|
|
|
162
172
|
const target = options.path ?? lockfile_path();
|
|
163
173
|
fs.mkdirSync(path.dirname(target), { recursive: true });
|
|
164
174
|
const stamp = strftime_iso_z(options.now ?? /* @__PURE__ */ new Date());
|
|
165
|
-
const rendered = _render(
|
|
175
|
+
const rendered = _render(
|
|
176
|
+
version,
|
|
177
|
+
sorted_unique(tools),
|
|
178
|
+
stamp,
|
|
179
|
+
options.host_layer_fingerprint ?? null
|
|
180
|
+
);
|
|
166
181
|
const parent = path.dirname(target);
|
|
167
182
|
const { fd, tmp_name } = mkstemp(parent, ".installed.lock.");
|
|
168
183
|
try {
|
|
@@ -218,7 +233,11 @@ function migrate_layout(options = {}) {
|
|
|
218
233
|
const version = existing.agent_config_version || current_package_version();
|
|
219
234
|
const tools = [...existing.tools ?? []];
|
|
220
235
|
const when = options.now ?? _parse_installed_at(existing.installed_at);
|
|
221
|
-
write_lockfile(version, tools, {
|
|
236
|
+
write_lockfile(version, tools, {
|
|
237
|
+
path: target,
|
|
238
|
+
now: when,
|
|
239
|
+
host_layer_fingerprint: existing.host_layer_fingerprint ?? null
|
|
240
|
+
});
|
|
222
241
|
return {
|
|
223
242
|
from: from_v,
|
|
224
243
|
to: INSTALL_LAYOUT_VERSION,
|
|
@@ -4230,7 +4230,15 @@ var settingsSchema = external_exports.object({
|
|
|
4230
4230
|
),
|
|
4231
4231
|
horizon_weeks: external_exports.number().int().min(0).default(0).describe(
|
|
4232
4232
|
'Optional planning horizon (weeks) the agent shows in roadmap framing ("next 4 weeks"). Set 0 to omit the horizon \u2014 most teams prefer to ship without a hardcoded window.'
|
|
4233
|
-
)
|
|
4233
|
+
),
|
|
4234
|
+
gate_budget: external_exports.object({
|
|
4235
|
+
max_cost_per_run_usd: external_exports.number().min(0).default(5).describe(
|
|
4236
|
+
"Per-run USD ceiling for a CLASS-1 roadmap blocker executed via `agent-config gates --execute`. A class-1 entry whose **Budget:** field states a larger figure renders its consent line instead of running. Bounds the size of an authorised spend; never supplies the authorisation \u2014 `--confirm` is still required on every class-1 run."
|
|
4237
|
+
),
|
|
4238
|
+
max_cost_per_rolling_7d_usd: external_exports.number().min(0).default(25).describe(
|
|
4239
|
+
"Rolling 7-day USD ceiling for class-1 gate execution, summed from the append-only receipt ledger at agents/runtime/state/gate-budget-ledger.jsonl. A run whose estimate would cross it renders instead of running. A per-run cap alone bounds one mistake, not a week of them, which is why option (a) of b-gate-budget-preauth carries two numbers."
|
|
4240
|
+
)
|
|
4241
|
+
}).default({ max_cost_per_run_usd: 5, max_cost_per_rolling_7d_usd: 25 })
|
|
4234
4242
|
}),
|
|
4235
4243
|
planning: external_exports.object({
|
|
4236
4244
|
challenge_on_create: external_exports.boolean().default(true).describe(
|