@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
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
read_lockfile
|
|
4
|
+
} from "./chunk-T7L2QK3K.js";
|
|
5
|
+
|
|
6
|
+
// src/scripts/_lib/turn_end_refusals.ts
|
|
7
|
+
import * as crypto from "node:crypto";
|
|
8
|
+
import * as fs from "node:fs";
|
|
9
|
+
import * as path from "node:path";
|
|
10
|
+
var DETECTOR_IDS = [
|
|
11
|
+
"promissory",
|
|
12
|
+
"language",
|
|
13
|
+
"verification",
|
|
14
|
+
"completion"
|
|
15
|
+
];
|
|
16
|
+
var REFUSAL_STATE_REL = path.join("agents", "runtime", "state", "turn-end-gate");
|
|
17
|
+
function refusalStateDir(workspaceRoot) {
|
|
18
|
+
return path.join(workspaceRoot, REFUSAL_STATE_REL);
|
|
19
|
+
}
|
|
20
|
+
function deriveSessionKey(sessionId) {
|
|
21
|
+
return crypto.createHash("sha256").update(sessionId).digest("hex").slice(0, 32);
|
|
22
|
+
}
|
|
23
|
+
function sessionRefusalFile(workspaceRoot, sessionKey) {
|
|
24
|
+
return path.join(refusalStateDir(workspaceRoot), `${sessionKey}.json`);
|
|
25
|
+
}
|
|
26
|
+
function readSessionCounts(workspaceRoot, sessionId) {
|
|
27
|
+
try {
|
|
28
|
+
const raw = fs.readFileSync(
|
|
29
|
+
sessionRefusalFile(workspaceRoot, deriveSessionKey(sessionId)),
|
|
30
|
+
"utf-8"
|
|
31
|
+
);
|
|
32
|
+
const rec = parseRecord(raw);
|
|
33
|
+
return rec === null ? null : countsOf(rec);
|
|
34
|
+
} catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function emptyCounts() {
|
|
39
|
+
return { promissory: 0, language: 0, verification: 0, completion: 0 };
|
|
40
|
+
}
|
|
41
|
+
function isDetector(v) {
|
|
42
|
+
return typeof v === "string" && DETECTOR_IDS.includes(v);
|
|
43
|
+
}
|
|
44
|
+
function parseRecord(raw) {
|
|
45
|
+
let decoded;
|
|
46
|
+
try {
|
|
47
|
+
decoded = JSON.parse(raw);
|
|
48
|
+
} catch {
|
|
49
|
+
return null;
|
|
50
|
+
}
|
|
51
|
+
if (typeof decoded !== "object" || decoded === null || Array.isArray(decoded)) return null;
|
|
52
|
+
const o = decoded;
|
|
53
|
+
if (typeof o["refused_at"] !== "string") return null;
|
|
54
|
+
if (typeof o["refused_turn"] !== "number") return null;
|
|
55
|
+
if (!isDetector(o["detector"])) return null;
|
|
56
|
+
const rec = {
|
|
57
|
+
refused_at: o["refused_at"],
|
|
58
|
+
refused_turn: o["refused_turn"],
|
|
59
|
+
detector: o["detector"]
|
|
60
|
+
};
|
|
61
|
+
if (typeof o["first_refused_at"] === "string") rec.first_refused_at = o["first_refused_at"];
|
|
62
|
+
if (typeof o["agent_config_version"] === "string") {
|
|
63
|
+
rec.agent_config_version = o["agent_config_version"];
|
|
64
|
+
}
|
|
65
|
+
const counts = o["counts"];
|
|
66
|
+
if (typeof counts === "object" && counts !== null && !Array.isArray(counts)) {
|
|
67
|
+
const parsed = {};
|
|
68
|
+
for (const id of DETECTOR_IDS) {
|
|
69
|
+
const n = counts[id];
|
|
70
|
+
if (typeof n === "number" && Number.isFinite(n) && n >= 0) parsed[id] = n;
|
|
71
|
+
}
|
|
72
|
+
rec.counts = parsed;
|
|
73
|
+
}
|
|
74
|
+
return rec;
|
|
75
|
+
}
|
|
76
|
+
function countsOf(rec) {
|
|
77
|
+
const out = emptyCounts();
|
|
78
|
+
if (rec.counts === void 0) {
|
|
79
|
+
out[rec.detector] = 1;
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
let any = false;
|
|
83
|
+
for (const id of DETECTOR_IDS) {
|
|
84
|
+
const n = rec.counts[id];
|
|
85
|
+
if (typeof n === "number" && n > 0) {
|
|
86
|
+
out[id] = n;
|
|
87
|
+
any = true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
if (!any) out[rec.detector] = 1;
|
|
91
|
+
return out;
|
|
92
|
+
}
|
|
93
|
+
function foldRefusal(prev, input) {
|
|
94
|
+
const counts = prev === null ? emptyCounts() : countsOf(prev);
|
|
95
|
+
for (const d of input.detectors) counts[d] += 1;
|
|
96
|
+
const primary = input.detectors[0] ?? prev?.detector ?? "verification";
|
|
97
|
+
const rec = {
|
|
98
|
+
refused_at: input.at,
|
|
99
|
+
refused_turn: input.turnOrdinal,
|
|
100
|
+
detector: primary,
|
|
101
|
+
first_refused_at: prev?.first_refused_at ?? prev?.refused_at ?? input.at,
|
|
102
|
+
counts
|
|
103
|
+
};
|
|
104
|
+
if (input.version !== void 0) rec.agent_config_version = input.version;
|
|
105
|
+
else if (prev?.agent_config_version !== void 0) {
|
|
106
|
+
rec.agent_config_version = prev.agent_config_version;
|
|
107
|
+
}
|
|
108
|
+
return rec;
|
|
109
|
+
}
|
|
110
|
+
function readInstallBoundary() {
|
|
111
|
+
try {
|
|
112
|
+
const lock = read_lockfile();
|
|
113
|
+
if (lock === null) return { version: null, installed_at: null };
|
|
114
|
+
return {
|
|
115
|
+
version: lock.agent_config_version ?? null,
|
|
116
|
+
installed_at: lock.installed_at ?? null
|
|
117
|
+
};
|
|
118
|
+
} catch {
|
|
119
|
+
return { version: null, installed_at: null };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
var REFUSAL_STATE_MAX_AGE_DAYS = 90;
|
|
123
|
+
function pruneAgedRefusalState(workspaceRoot, opts = {}) {
|
|
124
|
+
const maxAgeDays = opts.maxAgeDays ?? REFUSAL_STATE_MAX_AGE_DAYS;
|
|
125
|
+
const now = opts.now ?? /* @__PURE__ */ new Date();
|
|
126
|
+
const dir = refusalStateDir(workspaceRoot);
|
|
127
|
+
const result = { scanned: 0, pruned: 0, kept: 0 };
|
|
128
|
+
let entries;
|
|
129
|
+
try {
|
|
130
|
+
entries = fs.readdirSync(dir);
|
|
131
|
+
} catch {
|
|
132
|
+
return result;
|
|
133
|
+
}
|
|
134
|
+
const cutoffMs = now.getTime() - maxAgeDays * 24 * 60 * 60 * 1e3;
|
|
135
|
+
for (const name of entries) {
|
|
136
|
+
if (!name.endsWith(".json")) continue;
|
|
137
|
+
result.scanned += 1;
|
|
138
|
+
const file = path.join(dir, name);
|
|
139
|
+
let rec = null;
|
|
140
|
+
try {
|
|
141
|
+
rec = parseRecord(fs.readFileSync(file, "utf-8"));
|
|
142
|
+
} catch {
|
|
143
|
+
rec = null;
|
|
144
|
+
}
|
|
145
|
+
if (rec === null) {
|
|
146
|
+
result.kept += 1;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
const at = Date.parse(rec.refused_at);
|
|
150
|
+
if (!Number.isFinite(at) || at >= cutoffMs) {
|
|
151
|
+
result.kept += 1;
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
try {
|
|
155
|
+
fs.unlinkSync(file);
|
|
156
|
+
result.pruned += 1;
|
|
157
|
+
} catch {
|
|
158
|
+
result.kept += 1;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
export {
|
|
165
|
+
deriveSessionKey,
|
|
166
|
+
sessionRefusalFile,
|
|
167
|
+
readSessionCounts,
|
|
168
|
+
parseRecord,
|
|
169
|
+
foldRefusal,
|
|
170
|
+
readInstallBoundary,
|
|
171
|
+
pruneAgedRefusalState
|
|
172
|
+
};
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
|
|
2
|
+
import {
|
|
3
|
+
statePathFor
|
|
4
|
+
} from "./chunk-JZ4NR2TW.js";
|
|
2
5
|
import {
|
|
3
6
|
git_common_dir
|
|
4
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-6EOCNQAM.js";
|
|
5
8
|
|
|
6
9
|
// src/scripts/_lib/envelope_grounding.ts
|
|
7
10
|
import { execFileSync } from "node:child_process";
|
|
@@ -78,10 +81,10 @@ function collectRepoAnchor(root) {
|
|
|
78
81
|
head: git(root, ["rev-parse", "HEAD"])
|
|
79
82
|
};
|
|
80
83
|
}
|
|
81
|
-
|
|
82
|
-
|
|
84
|
+
function readLastVerify(root, session_id) {
|
|
85
|
+
if (typeof session_id !== "string" || session_id.trim() === "") return null;
|
|
83
86
|
try {
|
|
84
|
-
const raw = fs.readFileSync(path.join(root,
|
|
87
|
+
const raw = fs.readFileSync(path.join(root, statePathFor(session_id)), "utf-8");
|
|
85
88
|
const state = JSON.parse(raw);
|
|
86
89
|
const lv = state["last_verification"];
|
|
87
90
|
if (typeof lv !== "object" || lv === null || Array.isArray(lv)) return null;
|
|
@@ -94,11 +97,11 @@ function readLastVerify(root) {
|
|
|
94
97
|
return null;
|
|
95
98
|
}
|
|
96
99
|
}
|
|
97
|
-
function collectGrounding(root) {
|
|
100
|
+
function collectGrounding(root, session_id) {
|
|
98
101
|
const anchor = collectRepoAnchor(root);
|
|
99
102
|
const porcelain = git(root, ["status", "--porcelain"]);
|
|
100
103
|
if (porcelain === null) {
|
|
101
|
-
return { ...anchor, uncommitted_paths: [], status_summary: null, last_verify: readLastVerify(root) };
|
|
104
|
+
return { ...anchor, uncommitted_paths: [], status_summary: null, last_verify: readLastVerify(root, session_id) };
|
|
102
105
|
}
|
|
103
106
|
const lines = porcelain.split("\n").filter((l) => l.trim().length > 0);
|
|
104
107
|
const paths = lines.map((l) => l.slice(3).trim()).filter((p) => p.length > 0 && p.length <= MAX_FIELD_CHARS).slice(0, MAX_PATHS);
|
|
@@ -106,7 +109,7 @@ function collectGrounding(root) {
|
|
|
106
109
|
...anchor,
|
|
107
110
|
uncommitted_paths: paths,
|
|
108
111
|
status_summary: lines.length === 0 ? "clean working tree" : `${lines.length} uncommitted path(s)${lines.length > MAX_PATHS ? `, first ${MAX_PATHS} listed` : ""}`,
|
|
109
|
-
last_verify: readLastVerify(root)
|
|
112
|
+
last_verify: readLastVerify(root, session_id)
|
|
110
113
|
};
|
|
111
114
|
}
|
|
112
115
|
function describeDrift(envelope, actual) {
|
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
|
|
2
|
+
|
|
3
|
+
// src/scripts/hooks/state_io.ts
|
|
4
|
+
import { createHash } from "node:crypto";
|
|
5
|
+
import fs from "node:fs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
var LOCK_BASENAME = ".dispatcher.lock";
|
|
8
|
+
var REPLAY_ENV_VAR = "AGENT_CONFIG_REPLAY";
|
|
9
|
+
function is_replay_mode() {
|
|
10
|
+
return (process.env[REPLAY_ENV_VAR] ?? "").trim() === "1";
|
|
11
|
+
}
|
|
12
|
+
function _lock_path(state_dir) {
|
|
13
|
+
return path.join(state_dir, LOCK_BASENAME);
|
|
14
|
+
}
|
|
15
|
+
function _target_lock_path(target_path) {
|
|
16
|
+
return `${target_path}.lock`;
|
|
17
|
+
}
|
|
18
|
+
function _py_json_dumps(value, indent) {
|
|
19
|
+
const pad = " ".repeat(indent);
|
|
20
|
+
const escapeString = (s) => {
|
|
21
|
+
let out = '"';
|
|
22
|
+
for (const ch of s) {
|
|
23
|
+
const code = ch.codePointAt(0);
|
|
24
|
+
switch (ch) {
|
|
25
|
+
case '"':
|
|
26
|
+
out += '\\"';
|
|
27
|
+
break;
|
|
28
|
+
case "\\":
|
|
29
|
+
out += "\\\\";
|
|
30
|
+
break;
|
|
31
|
+
case "\n":
|
|
32
|
+
out += "\\n";
|
|
33
|
+
break;
|
|
34
|
+
case "\r":
|
|
35
|
+
out += "\\r";
|
|
36
|
+
break;
|
|
37
|
+
case " ":
|
|
38
|
+
out += "\\t";
|
|
39
|
+
break;
|
|
40
|
+
case "\b":
|
|
41
|
+
out += "\\b";
|
|
42
|
+
break;
|
|
43
|
+
case "\f":
|
|
44
|
+
out += "\\f";
|
|
45
|
+
break;
|
|
46
|
+
default:
|
|
47
|
+
if (code < 32 || code > 126) {
|
|
48
|
+
if (code > 65535) {
|
|
49
|
+
const c = code - 65536;
|
|
50
|
+
const hi = 55296 + (c >> 10);
|
|
51
|
+
const lo = 56320 + (c & 1023);
|
|
52
|
+
out += "\\u" + hi.toString(16).padStart(4, "0") + "\\u" + lo.toString(16).padStart(4, "0");
|
|
53
|
+
} else {
|
|
54
|
+
out += "\\u" + code.toString(16).padStart(4, "0");
|
|
55
|
+
}
|
|
56
|
+
} else {
|
|
57
|
+
out += ch;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
return out + '"';
|
|
62
|
+
};
|
|
63
|
+
const render = (v, depth) => {
|
|
64
|
+
if (v === null || v === void 0) {
|
|
65
|
+
return "null";
|
|
66
|
+
}
|
|
67
|
+
if (typeof v === "boolean") {
|
|
68
|
+
return v ? "true" : "false";
|
|
69
|
+
}
|
|
70
|
+
if (typeof v === "number") {
|
|
71
|
+
if (!Number.isFinite(v)) {
|
|
72
|
+
if (Number.isNaN(v)) return "NaN";
|
|
73
|
+
return v > 0 ? "Infinity" : "-Infinity";
|
|
74
|
+
}
|
|
75
|
+
return String(v);
|
|
76
|
+
}
|
|
77
|
+
if (typeof v === "string") {
|
|
78
|
+
return escapeString(v);
|
|
79
|
+
}
|
|
80
|
+
const curPad = pad.repeat(depth + 1);
|
|
81
|
+
const closePad = pad.repeat(depth);
|
|
82
|
+
if (Array.isArray(v)) {
|
|
83
|
+
if (v.length === 0) {
|
|
84
|
+
return "[]";
|
|
85
|
+
}
|
|
86
|
+
const items = v.map((item) => curPad + render(item, depth + 1));
|
|
87
|
+
return "[\n" + items.join(",\n") + "\n" + closePad + "]";
|
|
88
|
+
}
|
|
89
|
+
if (typeof v === "object") {
|
|
90
|
+
const obj = v;
|
|
91
|
+
const keys = Object.keys(obj);
|
|
92
|
+
if (keys.length === 0) {
|
|
93
|
+
return "{}";
|
|
94
|
+
}
|
|
95
|
+
const items = keys.map(
|
|
96
|
+
(k) => curPad + escapeString(k) + ": " + render(obj[k], depth + 1)
|
|
97
|
+
);
|
|
98
|
+
return "{\n" + items.join(",\n") + "\n" + closePad + "}";
|
|
99
|
+
}
|
|
100
|
+
throw new TypeError(`Object of type ${typeof v} is not JSON serializable`);
|
|
101
|
+
};
|
|
102
|
+
return render(value, 0);
|
|
103
|
+
}
|
|
104
|
+
function atomic_write_json(target, payload, options = {}) {
|
|
105
|
+
if (is_replay_mode()) {
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const indent = options.indent ?? 2;
|
|
109
|
+
const targetPath = path.resolve(target);
|
|
110
|
+
const state_dir = path.dirname(targetPath);
|
|
111
|
+
fs.mkdirSync(state_dir, { recursive: true });
|
|
112
|
+
const body = _py_json_dumps(payload, indent) + "\n";
|
|
113
|
+
_atomic_write_text(targetPath, body);
|
|
114
|
+
}
|
|
115
|
+
function atomic_write_text(target, text) {
|
|
116
|
+
if (is_replay_mode()) {
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
const targetPath = path.resolve(target);
|
|
120
|
+
const state_dir = path.dirname(targetPath);
|
|
121
|
+
fs.mkdirSync(state_dir, { recursive: true });
|
|
122
|
+
_atomic_write_text(targetPath, text);
|
|
123
|
+
}
|
|
124
|
+
var LOCK_STALE_MS = 3e4;
|
|
125
|
+
var LOCK_ACQUIRE_DEADLINE_MS = 5e3;
|
|
126
|
+
var LOCK_SPIN_SLEEP_MS = 2;
|
|
127
|
+
function _sleep(ms) {
|
|
128
|
+
const sab = new Int32Array(new SharedArrayBuffer(4));
|
|
129
|
+
Atomics.wait(sab, 0, 0, ms);
|
|
130
|
+
}
|
|
131
|
+
function _acquire_lock(lock_path, options = {}) {
|
|
132
|
+
const blocking = options.blocking ?? true;
|
|
133
|
+
const fd = fs.openSync(lock_path, "a", 420);
|
|
134
|
+
const held = lock_path + ".held";
|
|
135
|
+
const give_up = () => {
|
|
136
|
+
try {
|
|
137
|
+
fs.closeSync(fd);
|
|
138
|
+
} catch {
|
|
139
|
+
}
|
|
140
|
+
return null;
|
|
141
|
+
};
|
|
142
|
+
const start = Date.now();
|
|
143
|
+
for (; ; ) {
|
|
144
|
+
try {
|
|
145
|
+
const heldFd = fs.openSync(held, "wx", 420);
|
|
146
|
+
fs.closeSync(heldFd);
|
|
147
|
+
return fd;
|
|
148
|
+
} catch (err) {
|
|
149
|
+
if (err.code !== "EEXIST") {
|
|
150
|
+
return fd;
|
|
151
|
+
}
|
|
152
|
+
let age_ms;
|
|
153
|
+
try {
|
|
154
|
+
age_ms = Date.now() - fs.statSync(held).mtimeMs;
|
|
155
|
+
} catch {
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
if (age_ms >= LOCK_STALE_MS) {
|
|
159
|
+
try {
|
|
160
|
+
fs.rmSync(held, { force: true });
|
|
161
|
+
} catch {
|
|
162
|
+
}
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (!blocking) {
|
|
166
|
+
return give_up();
|
|
167
|
+
}
|
|
168
|
+
if (Date.now() - start > LOCK_ACQUIRE_DEADLINE_MS) {
|
|
169
|
+
return give_up();
|
|
170
|
+
}
|
|
171
|
+
_sleep(LOCK_SPIN_SLEEP_MS);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
function _release_lock(fd, lock_path) {
|
|
176
|
+
try {
|
|
177
|
+
fs.closeSync(fd);
|
|
178
|
+
} catch {
|
|
179
|
+
}
|
|
180
|
+
try {
|
|
181
|
+
fs.rmSync(lock_path + ".held", { force: true });
|
|
182
|
+
} catch {
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
function _publish_text_locked(target, text) {
|
|
186
|
+
const tmp = target + `.tmp.${process.pid}`;
|
|
187
|
+
fs.writeFileSync(tmp, text, { encoding: "utf-8" });
|
|
188
|
+
fs.renameSync(tmp, target);
|
|
189
|
+
}
|
|
190
|
+
function _atomic_write_text(target, text) {
|
|
191
|
+
const lock_path = _lock_path(path.dirname(target));
|
|
192
|
+
const fd = _acquire_lock(lock_path);
|
|
193
|
+
if (fd === null) {
|
|
194
|
+
_publish_text_locked(target, text);
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
try {
|
|
198
|
+
_publish_text_locked(target, text);
|
|
199
|
+
} finally {
|
|
200
|
+
_release_lock(fd, lock_path);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
function has_stable_session_id(session_id) {
|
|
204
|
+
return typeof session_id === "string" && session_id.trim() !== "";
|
|
205
|
+
}
|
|
206
|
+
function session_state_file(dir, session_id) {
|
|
207
|
+
const digest = createHash("sha256").update(session_id, "utf8").digest("hex").slice(0, 32);
|
|
208
|
+
return path.join(dir, `${digest}.json`);
|
|
209
|
+
}
|
|
210
|
+
function restore_claimed_state(tomb, live, link = fs.linkSync) {
|
|
211
|
+
try {
|
|
212
|
+
link(tomb, live);
|
|
213
|
+
} catch (err) {
|
|
214
|
+
const code = err.code;
|
|
215
|
+
if (code !== "EEXIST" && code !== "ENOENT") {
|
|
216
|
+
if (!NO_HARD_LINK_CODES.has(code ?? "")) throw err;
|
|
217
|
+
if (!fs.existsSync(live)) {
|
|
218
|
+
fs.renameSync(tomb, live);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
fs.rmSync(tomb, { force: true });
|
|
224
|
+
}
|
|
225
|
+
var NO_HARD_LINK_CODES = /* @__PURE__ */ new Set(["EPERM", "ENOSYS", "EXDEV", "EOPNOTSUPP", "EMLINK"]);
|
|
226
|
+
function prune_stale_session_states(dir, now_ms, retention_days, mtime_of = (target) => fs.statSync(target).mtimeMs) {
|
|
227
|
+
if (is_replay_mode()) return 0;
|
|
228
|
+
const cutoff = now_ms - retention_days * 24 * 60 * 60 * 1e3;
|
|
229
|
+
let entries;
|
|
230
|
+
try {
|
|
231
|
+
entries = fs.readdirSync(dir);
|
|
232
|
+
} catch {
|
|
233
|
+
return 0;
|
|
234
|
+
}
|
|
235
|
+
let removed = 0;
|
|
236
|
+
let claim = 0;
|
|
237
|
+
for (const name of entries) {
|
|
238
|
+
const orphan = /^(.+\.json)\.\d+\.\d+\.tomb$/.exec(name);
|
|
239
|
+
const live_name = orphan?.[1];
|
|
240
|
+
if (live_name === void 0) continue;
|
|
241
|
+
const tomb = path.join(dir, name);
|
|
242
|
+
const live = path.join(dir, live_name);
|
|
243
|
+
try {
|
|
244
|
+
if (mtime_of(tomb) >= cutoff) {
|
|
245
|
+
restore_claimed_state(tomb, live);
|
|
246
|
+
continue;
|
|
247
|
+
}
|
|
248
|
+
fs.rmSync(tomb, { force: true });
|
|
249
|
+
removed += 1;
|
|
250
|
+
} catch {
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
for (const name of entries) {
|
|
254
|
+
if (!name.endsWith(".json")) continue;
|
|
255
|
+
const full = path.join(dir, name);
|
|
256
|
+
try {
|
|
257
|
+
if (mtime_of(full) >= cutoff) continue;
|
|
258
|
+
claim += 1;
|
|
259
|
+
const tomb = `${full}.${process.pid}.${claim}.tomb`;
|
|
260
|
+
fs.renameSync(full, tomb);
|
|
261
|
+
if (mtime_of(tomb) >= cutoff) {
|
|
262
|
+
restore_claimed_state(tomb, full);
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
fs.rmSync(tomb, { force: true });
|
|
266
|
+
for (const suffix of [".lock", ".lock.held"]) {
|
|
267
|
+
try {
|
|
268
|
+
fs.rmSync(`${full}${suffix}`, { force: true });
|
|
269
|
+
} catch {
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
removed += 1;
|
|
273
|
+
} catch {
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
return removed;
|
|
277
|
+
}
|
|
278
|
+
function prune_legacy_state_file(target) {
|
|
279
|
+
if (is_replay_mode()) return;
|
|
280
|
+
try {
|
|
281
|
+
if (fs.statSync(target).isFile()) {
|
|
282
|
+
fs.rmSync(target, { force: true });
|
|
283
|
+
}
|
|
284
|
+
} catch {
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
function owns_session_state(state, session_id) {
|
|
288
|
+
if (typeof state !== "object" || state === null || Array.isArray(state)) return false;
|
|
289
|
+
return state["session_id"] === session_id;
|
|
290
|
+
}
|
|
291
|
+
function update_json_under_lock(target, mutate, options = {}) {
|
|
292
|
+
if (is_replay_mode()) return "failed";
|
|
293
|
+
const targetPath = path.resolve(target);
|
|
294
|
+
const state_dir = path.dirname(targetPath);
|
|
295
|
+
try {
|
|
296
|
+
fs.mkdirSync(state_dir, { recursive: true });
|
|
297
|
+
} catch {
|
|
298
|
+
return "failed";
|
|
299
|
+
}
|
|
300
|
+
const lock_path = _target_lock_path(targetPath);
|
|
301
|
+
let fd;
|
|
302
|
+
try {
|
|
303
|
+
fd = _acquire_lock(lock_path, { blocking: options.blocking ?? true });
|
|
304
|
+
} catch {
|
|
305
|
+
return "failed";
|
|
306
|
+
}
|
|
307
|
+
if (fd === null) return "failed";
|
|
308
|
+
try {
|
|
309
|
+
let loaded = {};
|
|
310
|
+
try {
|
|
311
|
+
const raw = fs.readFileSync(targetPath, "utf-8");
|
|
312
|
+
const decoded = JSON.parse(raw);
|
|
313
|
+
if (typeof decoded === "object" && decoded !== null && !Array.isArray(decoded)) {
|
|
314
|
+
loaded = decoded;
|
|
315
|
+
}
|
|
316
|
+
} catch {
|
|
317
|
+
}
|
|
318
|
+
const next = mutate(loaded);
|
|
319
|
+
if (next === null) return "skipped";
|
|
320
|
+
_publish_text_locked(targetPath, _py_json_dumps(next, options.indent ?? 2) + "\n");
|
|
321
|
+
return "written";
|
|
322
|
+
} catch {
|
|
323
|
+
return "failed";
|
|
324
|
+
} finally {
|
|
325
|
+
_release_lock(fd, lock_path);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// src/scripts/_lib/git_common_dir.ts
|
|
330
|
+
import fs2 from "node:fs";
|
|
331
|
+
import path2 from "node:path";
|
|
332
|
+
function realpath_or_self(p) {
|
|
333
|
+
try {
|
|
334
|
+
return fs2.realpathSync(p);
|
|
335
|
+
} catch {
|
|
336
|
+
return p;
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
function git_dir(project_root) {
|
|
340
|
+
const dot_git = path2.join(project_root, ".git");
|
|
341
|
+
try {
|
|
342
|
+
const st = fs2.statSync(dot_git);
|
|
343
|
+
if (st.isDirectory()) {
|
|
344
|
+
return realpath_or_self(dot_git);
|
|
345
|
+
}
|
|
346
|
+
const m = fs2.readFileSync(dot_git, "utf-8").match(/^gitdir:\s*(.+)\s*$/m);
|
|
347
|
+
if (m === null) {
|
|
348
|
+
return null;
|
|
349
|
+
}
|
|
350
|
+
return realpath_or_self(path2.resolve(project_root, m[1].trim()));
|
|
351
|
+
} catch {
|
|
352
|
+
return null;
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
function current_branch(project_root) {
|
|
356
|
+
const gd = git_dir(project_root);
|
|
357
|
+
if (gd === null) return null;
|
|
358
|
+
try {
|
|
359
|
+
const head = fs2.readFileSync(path2.join(gd, "HEAD"), "utf-8").trim();
|
|
360
|
+
const m = head.match(/^ref:\s*refs\/heads\/(.+)$/);
|
|
361
|
+
return m === null ? null : m[1].trim();
|
|
362
|
+
} catch {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
function git_common_dir(project_root) {
|
|
367
|
+
const dot_git = path2.join(project_root, ".git");
|
|
368
|
+
try {
|
|
369
|
+
const st = fs2.statSync(dot_git);
|
|
370
|
+
if (st.isDirectory()) {
|
|
371
|
+
return realpath_or_self(dot_git);
|
|
372
|
+
}
|
|
373
|
+
const m = fs2.readFileSync(dot_git, "utf-8").match(/^gitdir:\s*(.+)\s*$/m);
|
|
374
|
+
if (m === null) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
const gitdir = path2.resolve(project_root, m[1].trim());
|
|
378
|
+
const commondir_file = path2.join(gitdir, "commondir");
|
|
379
|
+
if (fs2.existsSync(commondir_file)) {
|
|
380
|
+
const common = path2.resolve(gitdir, fs2.readFileSync(commondir_file, "utf-8").trim());
|
|
381
|
+
return realpath_or_self(common);
|
|
382
|
+
}
|
|
383
|
+
return realpath_or_self(gitdir);
|
|
384
|
+
} catch {
|
|
385
|
+
return null;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export {
|
|
390
|
+
is_replay_mode,
|
|
391
|
+
atomic_write_json,
|
|
392
|
+
atomic_write_text,
|
|
393
|
+
has_stable_session_id,
|
|
394
|
+
session_state_file,
|
|
395
|
+
prune_stale_session_states,
|
|
396
|
+
prune_legacy_state_file,
|
|
397
|
+
owns_session_state,
|
|
398
|
+
update_json_under_lock,
|
|
399
|
+
git_dir,
|
|
400
|
+
current_branch,
|
|
401
|
+
git_common_dir
|
|
402
|
+
};
|