@event4u/agent-config 17.0.0 → 17.1.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.
Files changed (119) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +104 -485
  5. package/README.md +2 -1
  6. package/dist/agent-src/contexts/execution/host-capability-manifest.md +5 -3
  7. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  8. package/dist/agent-src/templates/roadmaps.md +5 -1
  9. package/dist/cli-delegate/{chunk-IROAQ77A.js → chunk-3PK3VZHB.js} +107 -9
  10. package/dist/cli-delegate/{chunk-2SFOOFJJ.js → chunk-BZJ4Y3CI.js} +1 -1
  11. package/dist/cli-delegate/{chunk-CAL76ADI.js → chunk-CMZEKSNN.js} +52 -10
  12. package/dist/cli-delegate/{chunk-ESUNQ5XR.js → chunk-E3CFULNX.js} +22 -4
  13. package/dist/cli-delegate/{chunk-GOMILC3O.js → chunk-QBHEI5N4.js} +4 -3
  14. package/dist/cli-delegate/{chunk-SIOMDAKL.js → chunk-VYZGJLCA.js} +29 -39
  15. package/dist/cli-delegate/chunk-W5JXMF2T.js +77 -0
  16. package/dist/cli-delegate/{chunk-4SHNERBL.js → chunk-XIAPY44N.js} +1 -1
  17. package/dist/cli-delegate/{chunk-JHMMWNJ2.js → chunk-YHAPONWV.js} +1 -1
  18. package/dist/cli-delegate/cmd_conformance.js +9 -8
  19. package/dist/cli-delegate/cmd_doctor.js +4 -4
  20. package/dist/cli-delegate/cmd_doctor_neighbours.js +2 -2
  21. package/dist/cli-delegate/cmd_explain.js +1 -1
  22. package/dist/cli-delegate/cmd_export.js +6 -6
  23. package/dist/cli-delegate/cmd_fleet.js +3 -3
  24. package/dist/cli-delegate/cmd_preflight.js +3 -3
  25. package/dist/cli-delegate/cmd_prune.js +6 -6
  26. package/dist/cli-delegate/cmd_settings_migrate.js +6 -6
  27. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  28. package/dist/cli-delegate/cmd_sync.js +6 -6
  29. package/dist/cli-delegate/cmd_uninstall.js +6 -6
  30. package/dist/cli-delegate/cmd_update.js +9 -13
  31. package/dist/cli-delegate/cmd_validate.js +6 -6
  32. package/dist/discovery/deprecation-report.md +1 -1
  33. package/dist/discovery/discovery-manifest.json +4 -4
  34. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  35. package/dist/discovery/discovery-manifest.summary.md +1 -1
  36. package/dist/discovery/dormancy-report.md +1 -1
  37. package/dist/discovery/orphan-report.md +1 -1
  38. package/dist/discovery/packs.json +2 -2
  39. package/dist/discovery/trust-report.md +1 -1
  40. package/dist/discovery/workspaces.json +2 -2
  41. package/dist/hooks/dispatch.js +98 -74
  42. package/dist/hooks/dispatch.sha256 +1 -1
  43. package/dist/install/claudeRuleRewrite.js +10 -3
  44. package/dist/install/conflict.js +24 -2
  45. package/dist/install/conflictTracker.js +14 -3
  46. package/dist/install/install.mjs +222 -117
  47. package/dist/install/installThinLayer.js +1 -0
  48. package/dist/install/loggedWrites.js +142 -0
  49. package/dist/install/paths.js +12 -2
  50. package/dist/install/preserve.js +25 -4
  51. package/dist/install/txlog.js +10 -6
  52. package/dist/mcp/registry-manifest.json +2 -2
  53. package/dist/mcp/server.json +2 -2
  54. package/dist/mcp/server.mjs +845 -294
  55. package/dist/scripts/_lib/egress_rules.js +4 -1
  56. package/dist/scripts/_lib/identifier_redaction.js +35 -2
  57. package/dist/scripts/ai_council/seat_policy.js +7 -1
  58. package/dist/server/schemas/settings.js +1 -1
  59. package/package.json +1 -1
  60. package/src/config/ci-local-parity.yml +1 -1
  61. package/src/config/gate-coverage.yml +27 -7
  62. package/src/config/gate-violation-baselines.json +8 -2
  63. package/src/config/hook-bundle-budget.json +2 -2
  64. package/src/config/production-entries.json +18 -0
  65. package/src/config/release-gate-locality.yml +0 -7
  66. package/src/install/claudeRuleRewrite.ts +10 -1
  67. package/src/install/conflict.ts +24 -1
  68. package/src/install/conflictTracker.ts +18 -2
  69. package/src/install/installThinLayer.ts +6 -0
  70. package/src/install/loggedWrites.ts +148 -0
  71. package/src/install/paths.ts +11 -2
  72. package/src/install/preserve.ts +40 -4
  73. package/src/install/txlog.ts +10 -6
  74. package/src/scripts/_cli/cmd_conformance.ts +3 -2
  75. package/src/scripts/_cli/cmd_update.ts +15 -17
  76. package/src/scripts/_lib/council_settings_block.ts +9 -2
  77. package/src/scripts/_lib/egress_rules.ts +4 -1
  78. package/src/scripts/_lib/host_capability.ts +10 -5
  79. package/src/scripts/_lib/identifier_redaction.ts +38 -2
  80. package/src/scripts/_lib/price_freshness.ts +112 -0
  81. package/src/scripts/_lib/production_reach.ts +194 -0
  82. package/src/scripts/_lib/quota_parked.ts +17 -28
  83. package/src/scripts/_lib/release_findings_ingest.ts +13 -335
  84. package/src/scripts/_lib/tier_downshift_verdict.ts +81 -35
  85. package/src/scripts/ai_council/chairman.ts +5 -2
  86. package/src/scripts/ai_council/clients.ts +4 -3
  87. package/src/scripts/ai_council/free_hand_shadow.ts +24 -12
  88. package/src/scripts/ai_council/free_route_catalog.ts +142 -5
  89. package/src/scripts/ai_council/free_route_client.ts +4 -0
  90. package/src/scripts/ai_council/hand_payload.ts +115 -7
  91. package/src/scripts/ai_council/orchestrator.ts +7 -10
  92. package/src/scripts/ai_council/route_chooser.ts +1 -1
  93. package/src/scripts/ai_council/route_member.ts +66 -4
  94. package/src/scripts/ai_council/seat_policy.ts +8 -1
  95. package/src/scripts/ai_council/self_hosted_probe.ts +327 -0
  96. package/src/scripts/ai_council/status_surface.ts +2 -0
  97. package/src/scripts/ask_transport.ts +137 -15
  98. package/src/scripts/check_finding_dispositions.ts +25 -71
  99. package/src/scripts/check_release_pr_shape.ts +3 -3
  100. package/src/scripts/check_step_production_reach.ts +257 -0
  101. package/src/scripts/council_cli.ts +10 -8
  102. package/src/scripts/hook_manifest.json +1 -1
  103. package/src/scripts/hook_manifest.yaml +3 -0
  104. package/src/scripts/hooks/dispatch_hook.ts +2 -2
  105. package/src/scripts/hooks/payload_stub.ts +27 -0
  106. package/src/scripts/hooks/spawn_guard_shadow_hook.ts +29 -70
  107. package/src/scripts/hooks/subagent_ledger_hook.ts +80 -0
  108. package/src/scripts/install.ts +2 -2
  109. package/src/scripts/lint_decision_classes.ts +34 -0
  110. package/src/scripts/lint_hook_manifest.ts +24 -0
  111. package/src/scripts/lint_roadmap_blockers.ts +52 -3
  112. package/src/scripts/release.ts +5 -9
  113. package/src/scripts/release_drill.ts +11 -165
  114. package/src/scripts/release_publication.ts +0 -9
  115. package/src/scripts/report_production_reach.ts +35 -0
  116. package/src/scripts/worker_reuse_shadow_report.ts +399 -1
  117. package/src/server/schemas/settings.ts +1 -1
  118. package/dist/cli-delegate/chunk-A4QJ6ENT.js +0 -50
  119. /package/dist/cli-delegate/{chunk-E6XNBVE4.js → chunk-KR5ZIFPO.js} +0 -0
package/README.md CHANGED
@@ -2,9 +2,10 @@
2
2
 
3
3
  # Agent Config — every claim machine-checked, including the artifact counts in these badges
4
4
 
5
- [![Smoke](https://github.com/event4u-app/agent-config/actions/workflows/smoke.yml/badge.svg)](https://github.com/event4u-app/agent-config/actions/workflows/smoke.yml) [![Public install smoke (3 OS × 2 Node)](https://github.com/event4u-app/agent-config/actions/workflows/smoke-public-install.yml/badge.svg)](https://github.com/event4u-app/agent-config/actions/workflows/smoke-public-install.yml) [![npm](https://img.shields.io/npm/v/@event4u/agent-config?style=flat-square&label=npm&color=orange)](https://www.npmjs.com/package/@event4u/agent-config) [![agent-config MCP server](https://glama.ai/mcp/servers/event4u-app/agent-config/badges/score.svg)](https://glama.ai/mcp/servers/event4u-app/agent-config) [![MCP Toplist](https://mcptoplist.com/badge/glama%2Fevent4u-app%2Fagent-config.svg)](https://mcptoplist.com/server/glama%2Fevent4u-app%2Fagent-config) [![downloads](https://img.shields.io/npm/dm/@event4u/agent-config?style=flat-square&label=downloads%2Fmonth&color=orange)](https://www.npmjs.com/package/@event4u/agent-config)
5
+ [![Smoke](https://github.com/event4u-app/agent-config/actions/workflows/smoke.yml/badge.svg)](https://github.com/event4u-app/agent-config/actions/workflows/smoke.yml) [![Public install smoke (3 OS × 2 Node)](https://github.com/event4u-app/agent-config/actions/workflows/smoke-public-install.yml/badge.svg)](https://github.com/event4u-app/agent-config/actions/workflows/smoke-public-install.yml) [![npm](https://img.shields.io/npm/v/@event4u/agent-config?style=flat-square&label=npm&color=orange)](https://www.npmjs.com/package/@event4u/agent-config) [![agent-config MCP server](https://glama.ai/mcp/servers/event4u-app/agent-config/badges/score.svg)](https://glama.ai/mcp/servers/event4u-app/agent-config) [![MCP Toplist](https://mcptoplist.com/badge/glama%2Fevent4u-app%2Fagent-config.svg)](https://mcptoplist.com/server/glama%2Fevent4u-app%2Fagent-config)
6
6
 
7
7
  [![Skills](https://img.shields.io/badge/Skills-301-orange?style=flat-square)](dist/agent-src/skills/) [![Rules](https://img.shields.io/badge/Rules-122-orange?style=flat-square)](dist/agent-src/rules/) [![Commands](https://img.shields.io/badge/Commands-209-orange?style=flat-square)](dist/agent-src/commands/) [![Guidelines](https://img.shields.io/badge/Guidelines-121-orange?style=flat-square)](docs/guidelines/) [![Personas](https://img.shields.io/badge/Personas-29-orange?style=flat-square)](dist/agent-src/personas/) [![Advisors](https://img.shields.io/badge/Advisors-5-orange?style=flat-square)](dist/agent-src/personas/advisors/) <!-- claim:published-artifact-counts -->
8
+ [![downloads](https://img.shields.io/npm/dm/@event4u/agent-config?style=flat-square&label=downloads%2Fmonth&color=orange)](https://www.npmjs.com/package/@event4u/agent-config)
8
9
 
9
10
  <sub>**How these are counted** — one canonical counter, `agent-config` → `update_counts --check`, re-derives all six from the tree and fails CI when one is off by one. Two bases are not what the linked directory shows, so they are stated here rather than left to be inferred: **Commands 209** counts every command file **recursively** (the linked directory holds 62 at its top level) and **Rules 122** counts the **source** rules while the linked projection holds 121 — one rule is dormant and is not projected. **Personas 29** excludes the directory README. Counts are of files and directories: none of them measures quality, activation or adoption. <!-- claim:published-artifact-counts --></sub>
10
11
 
@@ -37,7 +37,9 @@ host, never a configuration decision (always-on orchestration, Phase 1):
37
37
  2. A live environment probe merged on top: `agent_teams` resolves `true`
38
38
  when `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` is set in the process
39
39
  environment — the one host fact this repo can observe about Claude
40
- Code's experimental multi-instance Agent Teams feature.
40
+ Code's experimental multi-instance Agent Teams feature. On `claude` the
41
+ same flag also sets `worker_resume`: the by-name re-task was observed
42
+ there only under it (cold-start 1.4, 2.1.306; council 2026-10-22).
41
43
  3. `SAFE_DEFAULT` (all `false`) for an unrecognized host or an unset probe.
42
44
 
43
45
  `resolveHostCapabilities(hostId, override?)` still accepts an optional
@@ -92,7 +94,7 @@ SAFE DEGRADATION, NOT BECAUSE ANYTHING WAS MEASURED.
92
94
 
93
95
  The seven booleans look alike and are not. `HOST_CAPABILITY_REGISTRY` holds
94
96
  exactly one row today, so on every other host **all six** fields are the safe
95
- default — and `agent_teams` is the only field any live check touches. The name
97
+ default — and `agent_teams` (plus `worker_resume` on `claude`) is the only field any live check touches. The name
96
98
  `probeHostCapabilities` says "detected" about values that were mostly asserted
97
99
  at authoring time.
98
100
 
@@ -136,7 +138,7 @@ A prober that decides for itself what counts as evidence is the inference
136
138
  | `status_polling` | a **completed poll returning a state the parent did not already hold**. A poll that returns what the parent knew is not an observation of polling |
137
139
  | `separate_quota_pool` | a **child leg continuing across a parent quota condition** — the parent constrained, the child proceeding |
138
140
  | `worker_respawn` | **one task continuing across a killed and re-spawned worker, same task id.** Never inferred from the fact that spawning and killing both exist separately |
139
- | `worker_resume` | **a finished leg re-tasked by name whose next `SubagentStart` carries the same ledger `ref`, and whose first call in that run reads more than half its input from cache.** Never inferred from the host documenting a resume or message tool |
141
+ | `worker_resume` | **a finished leg re-tasked by name whose next `SubagentStart` carries the same ledger `ref`, and whose first call in that run reads more than half its input from cache.** Never inferred from the host documenting a resume or message tool. On `claude` the agent-teams flag stands in for this row: the by-name re-task was observed only under it (2.1.306), so the live probe sets the field from the flag (council 2026-10-22) |
140
142
  | `fork_shares_cache` | **a fork whose first call reads more than half its input from cache and whose tool list equals the parent's.** Never inferred from documentation |
141
143
  | `structured_ask` | **the host's delivered tool surface, in a real session, carrying (or not carrying) a question-picker tool.** The delivered surface is the observation — never the vendor's documentation, and never another host by analogy. A `false` written from an observed surface is "checked, absent"; omitting the field is "never looked" |
142
144
  | `reads_project_mcp_config` | **a server named only in the project-scope config appearing in that host's own tool list.** Never inferred from the host documenting MCP support, which does not say which scope it reads |
@@ -39,7 +39,7 @@ schema_version: 1
39
39
  # CI guard: a release bump of `package.json` must update this value
40
40
  # in lockstep — see scripts/check_template_pin_drift.ts (road-to-
41
41
  # portable-runtime-and-update-check P3.3).
42
- agent_config_version: "17.0.0"
42
+ agent_config_version: "17.1.0"
43
43
 
44
44
  # --- Project identity ---
45
45
  project:
@@ -731,7 +731,11 @@ that was never a judgement call.
731
731
  (ADR-268 § 10). `resolved by` is one of `evidence`, `agent`,
732
732
  `independent:<session or model>`, `council:<record>`, `team:<record>`,
733
733
  `owner` — the prefixed forms carry their record, because a resolver
734
- with no record is a claim rather than a resolution.
734
+ with no record is a claim rather than a resolution. The three locked
735
+ classes (`product-owned`, `business-owned`, `destructive-owned`) take
736
+ `owner` only; a council answer on one is recorded with `owner` and a
737
+ decision cell starting `proposed`. A resolved human-only blocker needs
738
+ a dated `owner` row naming it.
735
739
 
736
740
  `revisit if` names the condition that reopens the row. Execution reads
737
741
  the table **before any step**, and a closed decision is re-asked only
@@ -1,4 +1,7 @@
1
1
  import { createRequire as __acCreateRequire } from 'node:module'; const require = globalThis.require ?? __acCreateRequire(import.meta.url);
2
+ import {
3
+ getLogPath
4
+ } from "./chunk-W5JXMF2T.js";
2
5
  import {
3
6
  classifyOwnership
4
7
  } from "./chunk-J2SJZICO.js";
@@ -7,14 +10,21 @@ import {
7
10
  var SIDECAR_SUFFIX = ".agent-config.new";
8
11
  var EXIT_COMPLETED_WITH_CONFLICTS = 3;
9
12
  function isLedgerMigration(inputs) {
10
- if (!inputs.exists || inputs.force) return false;
13
+ if (!inputs.exists || inputs.force || inputs.loggedWrite === true) return false;
11
14
  if (inputs.ledgerListed !== true) return false;
12
15
  if (inputs.onDiskSha256 === null) return false;
13
16
  return classifyOwnership(inputs.recordedSha256, inputs.onDiskSha256) === "unknown";
14
17
  }
18
+ function logProofApplies(inputs) {
19
+ if (!inputs.exists || inputs.force || inputs.onDiskSha256 === null) return false;
20
+ const ownership = classifyOwnership(inputs.recordedSha256, inputs.onDiskSha256);
21
+ if (ownership === "recorded-modified") return inputs.recordedIsStaged === true;
22
+ return ownership === "unknown" && isLedgerMigration({ ...inputs, loggedWrite: false });
23
+ }
15
24
  function decideDeployWrite(inputs) {
16
25
  if (!inputs.exists) return "write";
17
26
  if (inputs.force) return "write";
27
+ if (inputs.loggedWrite === true && logProofApplies(inputs)) return "write";
18
28
  const ownership = classifyOwnership(inputs.recordedSha256, inputs.onDiskSha256);
19
29
  if (ownership === "recorded-modified") return "preserve";
20
30
  if (ownership === "unknown" && isLedgerMigration(inputs)) {
@@ -39,13 +49,97 @@ function foreignSidecarMessage(target, sidecar) {
39
49
  return `Refusing to replace ${sidecar}: it already exists with different content and this install cannot show it wrote it. Package content for ${target} was NOT staged. Move or delete that file and re-run, or rerun with --force to replace the managed file instead.`;
40
50
  }
41
51
 
52
+ // src/install/loggedWrites.ts
53
+ import { gunzipSync } from "node:zlib";
54
+ import { existsSync, readdirSync, readFileSync, realpathSync, statSync } from "node:fs";
55
+ import { basename, dirname, join, relative, resolve, sep } from "node:path";
56
+ var WRITE_MARKER = '"kind":"write"';
57
+ var fileCache = /* @__PURE__ */ new Map();
58
+ var dirRealpaths = /* @__PURE__ */ new Map();
59
+ function canonicalPath(p) {
60
+ const abs = resolve(p);
61
+ const dir = dirname(abs);
62
+ let real = dirRealpaths.get(dir);
63
+ if (real === void 0) {
64
+ try {
65
+ real = realpathSync(dir);
66
+ } catch {
67
+ real = dir;
68
+ }
69
+ dirRealpaths.set(dir, real);
70
+ }
71
+ return join(real, basename(abs));
72
+ }
73
+ function logFiles(logPath) {
74
+ const dir = dirname(logPath);
75
+ if (!existsSync(dir)) return [];
76
+ const stem = basename(logPath).replace(/\.jsonl$/, "");
77
+ try {
78
+ return readdirSync(dir).filter((n) => n.startsWith(stem) && (n.endsWith(".jsonl") || n.endsWith(".jsonl.gz"))).map((n) => join(dir, n)).sort();
79
+ } catch {
80
+ return [];
81
+ }
82
+ }
83
+ function writesIn(file) {
84
+ let stamp;
85
+ try {
86
+ const st = statSync(file);
87
+ stamp = `${st.size}:${Math.trunc(st.mtimeMs)}`;
88
+ } catch {
89
+ return [];
90
+ }
91
+ const cached = fileCache.get(file);
92
+ if (cached !== void 0 && cached.stamp === stamp) return cached.entries;
93
+ let text = "";
94
+ try {
95
+ const raw = readFileSync(file);
96
+ text = (file.endsWith(".gz") ? gunzipSync(raw) : raw).toString("utf8");
97
+ } catch {
98
+ text = "";
99
+ }
100
+ const entries = [];
101
+ for (const line of text.split("\n")) {
102
+ if (!line.includes(WRITE_MARKER)) continue;
103
+ let entry;
104
+ try {
105
+ entry = JSON.parse(line);
106
+ } catch {
107
+ continue;
108
+ }
109
+ if (typeof entry.path !== "string" || typeof entry.sha256 !== "string" || entry.sha256 === "") continue;
110
+ entries.push([canonicalPath(entry.path), entry.sha256]);
111
+ }
112
+ fileCache.set(file, { stamp, entries });
113
+ return entries;
114
+ }
115
+ function loggedWriteDigests(logPath = getLogPath()) {
116
+ const writes = /* @__PURE__ */ new Map();
117
+ for (const file of logFiles(logPath)) {
118
+ for (const [target, sha] of writesIn(file)) {
119
+ let set = writes.get(target);
120
+ if (set === void 0) writes.set(target, set = /* @__PURE__ */ new Set());
121
+ set.add(sha);
122
+ }
123
+ }
124
+ return writes;
125
+ }
126
+ function onlyDeployed(anchor, currentFiles, log) {
127
+ return (target) => {
128
+ const rel = relative(resolve(anchor), resolve(target)).split(sep).join("/");
129
+ if (currentFiles.has(rel)) log(target);
130
+ };
131
+ }
132
+ function wroteTheseBytes(target, onDiskSha256, writes) {
133
+ return writes.get(canonicalPath(target))?.has(onDiskSha256) === true;
134
+ }
135
+
42
136
  // src/install/plan.ts
43
137
  import { createHash } from "node:crypto";
44
- import { readFileSync, readdirSync, statSync } from "node:fs";
45
- import { join, relative, resolve } from "node:path";
138
+ import { readFileSync as readFileSync2, readdirSync as readdirSync2, statSync as statSync2 } from "node:fs";
139
+ import { join as join2, relative as relative2, resolve as resolve2 } from "node:path";
46
140
  function sha256File(path) {
47
141
  try {
48
- const data = readFileSync(path);
142
+ const data = readFileSync2(path);
49
143
  return createHash("sha256").update(data).digest("hex");
50
144
  } catch {
51
145
  return null;
@@ -66,15 +160,15 @@ function walkSourceTree(dir) {
66
160
  function walkInto(dir, out) {
67
161
  let entries;
68
162
  try {
69
- entries = readdirSync(dir).sort();
163
+ entries = readdirSync2(dir).sort();
70
164
  } catch {
71
165
  return;
72
166
  }
73
167
  for (const name of entries) {
74
- const full = join(dir, name);
168
+ const full = join2(dir, name);
75
169
  let isDir;
76
170
  try {
77
- isDir = statSync(full).isDirectory();
171
+ isDir = statSync2(full).isDirectory();
78
172
  } catch {
79
173
  continue;
80
174
  }
@@ -94,8 +188,8 @@ function buildInstallPlan(inputs) {
94
188
  files = files.filter(source.fileFilter);
95
189
  }
96
190
  for (const srcFile of files) {
97
- const rel = relative(source.srcDir, srcFile);
98
- const destPath = resolve(source.destDir, rel);
191
+ const rel = relative2(source.srcDir, srcFile);
192
+ const destPath = resolve2(source.destDir, rel);
99
193
  bucket.push(
100
194
  fileEntry(srcFile, source.kind, {
101
195
  hashContent: source.kind !== "bridge"
@@ -126,12 +220,16 @@ function buildInstallPlan(inputs) {
126
220
  export {
127
221
  EXIT_COMPLETED_WITH_CONFLICTS,
128
222
  isLedgerMigration,
223
+ logProofApplies,
129
224
  decideDeployWrite,
130
225
  sidecarPathFor,
131
226
  preservedFileMessage,
132
227
  migrationPreservedMessage,
133
228
  conflictSummaryMessage,
134
229
  foreignSidecarMessage,
230
+ loggedWriteDigests,
231
+ onlyDeployed,
232
+ wroteTheseBytes,
135
233
  sha256File,
136
234
  buildInstallPlan
137
235
  };
@@ -12,7 +12,7 @@ import {
12
12
  EGRESS_RULES,
13
13
  evaluateMatch,
14
14
  shannonEntropy
15
- } from "./chunk-E6XNBVE4.js";
15
+ } from "./chunk-KR5ZIFPO.js";
16
16
  import {
17
17
  SRC_SKILLS
18
18
  } from "./chunk-YCU3TIXH.js";
@@ -42,7 +42,7 @@ import {
42
42
  readUserPin,
43
43
  residualReport,
44
44
  runtimeRootFor
45
- } from "./chunk-GOMILC3O.js";
45
+ } from "./chunk-QBHEI5N4.js";
46
46
  import {
47
47
  LEGACY_ALL,
48
48
  excludedRuleBasenames,
@@ -89,7 +89,7 @@ import {
89
89
  readCanaryStatusLine,
90
90
  runCensus,
91
91
  summaryLine
92
- } from "./chunk-2SFOOFJJ.js";
92
+ } from "./chunk-BZJ4Y3CI.js";
93
93
  import {
94
94
  EGRESS_RULES,
95
95
  REVIEW_CREDENTIAL_ROWS,
@@ -101,7 +101,7 @@ import {
101
101
  replaceSpans,
102
102
  resolveOverlaps,
103
103
  vendorPrefix
104
- } from "./chunk-E6XNBVE4.js";
104
+ } from "./chunk-KR5ZIFPO.js";
105
105
  import {
106
106
  classifyFileState,
107
107
  classifyOwnership,
@@ -468,7 +468,7 @@ function censusRuleDir(dir) {
468
468
  }
469
469
 
470
470
  // src/scripts/cache_realization_report.ts
471
- import { execFileSync } from "node:child_process";
471
+ import { execFileSync as execFileSync2 } from "node:child_process";
472
472
  import * as fs7 from "node:fs";
473
473
  import { homedir as homedir2 } from "node:os";
474
474
  import * as path12 from "node:path";
@@ -809,6 +809,7 @@ if (process.argv[1] !== void 0) {
809
809
  }
810
810
 
811
811
  // src/scripts/hooks/subagent_ledger_hook.ts
812
+ import { execFileSync } from "node:child_process";
812
813
  import * as crypto from "node:crypto";
813
814
  import * as fs5 from "node:fs";
814
815
  import * as path10 from "node:path";
@@ -1069,6 +1070,41 @@ function appendLedgerLine(root, ts, line) {
1069
1070
  fs5.appendFileSync(file, `${JSON.stringify(line)}
1070
1071
  `, "utf8");
1071
1072
  }
1073
+ var INDEX_SUBDIR = "index";
1074
+ function writeIndexEntry(root, ts, key, value) {
1075
+ if (is_replay_mode()) return;
1076
+ const dir = path10.join(root, LEDGER_DIR, INDEX_SUBDIR, ts.slice(0, 7));
1077
+ fs5.mkdirSync(dir, { recursive: true });
1078
+ const tmp = path10.join(dir, `.${key}.${process.pid}.tmp`);
1079
+ try {
1080
+ fs5.writeFileSync(tmp, value, "utf8");
1081
+ fs5.renameSync(tmp, path10.join(dir, key));
1082
+ } finally {
1083
+ fs5.rmSync(tmp, { force: true });
1084
+ }
1085
+ }
1086
+ function headOf(dir) {
1087
+ try {
1088
+ const env = Object.fromEntries(Object.entries(process.env).filter(([k]) => !k.startsWith("GIT_")));
1089
+ return execFileSync("git", ["-C", dir, "rev-parse", "HEAD"], { encoding: "utf8", timeout: 2e3, stdio: ["ignore", "pipe", "ignore"], env }).trim() || null;
1090
+ } catch {
1091
+ return null;
1092
+ }
1093
+ }
1094
+ function stateStamp(cwd) {
1095
+ let epoch = null;
1096
+ try {
1097
+ epoch = fs5.statSync(fileURLToPath(import.meta.url)).mtimeMs;
1098
+ } catch {
1099
+ }
1100
+ return typeof cwd === "string" && cwd ? { head: headOf(cwd), cwd_key: refFor(cwd), epoch } : { head: null, cwd_key: null, epoch };
1101
+ }
1102
+ function stopIndexKey(sessionId, ref) {
1103
+ return refFor(`subagent-stop\0${sessionId}\0${ref}`);
1104
+ }
1105
+ function recordStopIndex(root, ts, sessionId, ref) {
1106
+ writeIndexEntry(root, ts, stopIndexKey(sessionId, ref), "");
1107
+ }
1072
1108
  var WALL_CLOCK_ARMS_MS = [
1073
1109
  { label: "5m", ms: 5 * 60 * 1e3 },
1074
1110
  { label: "15m", ms: 15 * 60 * 1e3 },
@@ -1236,8 +1272,13 @@ function handleStop(root, payload, nowIso, sessionId) {
1236
1272
  // Beside the count, never instead of it: the count says how wrong the
1237
1273
  // candidate was, this says whether it was aimed at the envelope.
1238
1274
  envelope_field_hits: parse3.field_hits,
1239
- concurrent_open: countOpen(root)
1275
+ concurrent_open: countOpen(root),
1276
+ ...stateStamp(payload["cwd"])
1240
1277
  });
1278
+ try {
1279
+ if (sessionId !== null) recordStopIndex(root, nowIso, sessionId, ref);
1280
+ } catch {
1281
+ }
1241
1282
  }
1242
1283
  function processEnvelope(envelope, consumerRoot) {
1243
1284
  try {
@@ -1827,7 +1868,7 @@ function encodeProjectPath(cwd) {
1827
1868
  }
1828
1869
  function listGitWorktrees(cwd) {
1829
1870
  try {
1830
- const out = execFileSync("git", ["worktree", "list", "--porcelain"], { cwd, encoding: "utf-8", timeout: 1e4 });
1871
+ const out = execFileSync2("git", ["worktree", "list", "--porcelain"], { cwd, encoding: "utf-8", timeout: 1e4 });
1831
1872
  const paths = [];
1832
1873
  for (const line of out.split("\n")) {
1833
1874
  if (line.startsWith("worktree ")) paths.push(line.slice("worktree ".length).trim());
@@ -1900,7 +1941,7 @@ function computeWorktreeFragmentation(projectsRoot, worktreePaths, maxAgeDays) {
1900
1941
  }
1901
1942
  function hostVersion() {
1902
1943
  try {
1903
- const out = execFileSync("claude", ["--version"], { encoding: "utf-8", timeout: 5e3 }).trim();
1944
+ const out = execFileSync2("claude", ["--version"], { encoding: "utf-8", timeout: 5e3 }).trim();
1904
1945
  return out.length > 0 ? out : "unknown";
1905
1946
  } catch {
1906
1947
  return "unknown";
@@ -8060,9 +8101,10 @@ var CliClient = class extends ExternalAIClient {
8060
8101
  _cli_calls_path;
8061
8102
  /** Attribution label for every booking this client makes. */
8062
8103
  _consumer;
8063
- /** The session the last answered call ran in; `resumeNext` continues it once, non-blind only. */
8104
+ /** The session the last answered call ran in; `resumeNext` continues it once, never while `blind`. */
8064
8105
  lastSession = null;
8065
8106
  resumeNext = null;
8107
+ blind = false;
8066
8108
  static _AUTH_FAILURE_PATTERNS = [
8067
8109
  "authentication",
8068
8110
  "unauthorized",
@@ -8435,7 +8477,7 @@ var AnthropicCliClient = class extends CliClient {
8435
8477
  void max_tokens;
8436
8478
  return [
8437
8479
  this.binary,
8438
- ...seatResumeArgs(this.name, this.resumeNext, { blind: false, now_ms: Date.now() }),
8480
+ ...seatResumeArgs(this.name, this.resumeNext, { blind: this.blind, now_ms: Date.now() }),
8439
8481
  "--print",
8440
8482
  // The seat never needs the user environment; only a version the light
8441
8483
  // launch was probed on gets the flag (`LIGHT_LAUNCH_PROBES`).
@@ -8538,7 +8580,7 @@ var OpenAICliClient = class extends CliClient {
8538
8580
  void user_prompt;
8539
8581
  void max_tokens;
8540
8582
  const modelArgs = this.model === OPENAI_CLI_VENDOR_DEFAULT ? [] : ["--model", this.model];
8541
- const resume = seatResumeArgs(this.name, this.resumeNext, { blind: false, now_ms: Date.now() });
8583
+ const resume = seatResumeArgs(this.name, this.resumeNext, { blind: this.blind, now_ms: Date.now() });
8542
8584
  const bound = resume.length === 0 ? boundFor("openai") : resumeBound(boundFor("openai"));
8543
8585
  return [this.binary, "exec", ...resume, "--json", "--skip-git-repo-check", ...bound, ...modelArgs, "-"];
8544
8586
  }
@@ -2,11 +2,15 @@ import { createRequire as __acCreateRequire } from 'node:module'; const require
2
2
  import {
3
3
  buildInstallPlan,
4
4
  isLedgerMigration,
5
- sha256File
6
- } from "./chunk-IROAQ77A.js";
5
+ logProofApplies,
6
+ loggedWriteDigests,
7
+ sha256File,
8
+ sidecarPathFor,
9
+ wroteTheseBytes
10
+ } from "./chunk-3PK3VZHB.js";
7
11
  import {
8
12
  detectToolPresence
9
- } from "./chunk-A4QJ6ENT.js";
13
+ } from "./chunk-W5JXMF2T.js";
10
14
  import {
11
15
  classifyOwnership,
12
16
  ledgerListedPaths,
@@ -23,10 +27,11 @@ import { existsSync } from "node:fs";
23
27
  function isJsonTarget(entry) {
24
28
  return entry.kind === "deployed" && entry.path.endsWith(".json");
25
29
  }
26
- function computeConflicts(plan, recordedOverride) {
30
+ function computeConflicts(plan, recordedOverride, loggedOverride) {
27
31
  if (plan.policy.force) return [];
28
32
  const recorded = recordedOverride ?? recordedHashesWithLedger(plan.root);
29
33
  let ledgerFiles = null;
34
+ let logged;
30
35
  const ledgerLists = (p) => {
31
36
  ledgerFiles ??= ledgerListedPaths();
32
37
  return ledgerFiles.has(p);
@@ -40,6 +45,19 @@ function computeConflicts(plan, recordedOverride) {
40
45
  const onDisk = sha256File(entry.path);
41
46
  const ownership = classifyOwnership(recorded.get(entry.path), onDisk);
42
47
  if (ownership === "recorded-unchanged") continue;
48
+ const recordedSha = recorded.get(entry.path);
49
+ const unproven = {
50
+ exists: true,
51
+ recordedSha256: recordedSha,
52
+ onDiskSha256: onDisk,
53
+ force: false,
54
+ ledgerListed: ledgerLists(entry.path),
55
+ recordedIsStaged: typeof recordedSha === "string" && recordedSha === recorded.get(sidecarPathFor(entry.path))
56
+ };
57
+ if (logProofApplies(unproven)) {
58
+ logged ??= loggedOverride ?? loggedWriteDigests();
59
+ if (wroteTheseBytes(entry.path, onDisk, logged)) continue;
60
+ }
43
61
  if (ownership === "unknown" && plan.policy.knownPaths.has(entry.path)) continue;
44
62
  const migration = ownership === "unknown" && isLedgerMigration({
45
63
  exists: true,
@@ -645,7 +645,8 @@ function probeHostCapabilities(hostId) {
645
645
  const base = resolveHostCapabilities(hostId);
646
646
  const flag = process2.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
647
647
  const probedAgentTeams = typeof flag === "string" && flag !== "";
648
- return { ...base, agent_teams: base.agent_teams || probedAgentTeams };
648
+ const probedResume = probedAgentTeams && hostId === "claude";
649
+ return { ...base, agent_teams: base.agent_teams || probedAgentTeams, worker_resume: base.worker_resume || probedResume };
649
650
  }
650
651
  var CAPABILITY_FIELDS = [
651
652
  "subagent_spawn",
@@ -667,9 +668,9 @@ function describeHostCapabilities(hostId) {
667
668
  const probedAgentTeams = typeof flag === "string" && flag !== "";
668
669
  const sources = {};
669
670
  for (const field of CAPABILITY_FIELDS) {
670
- if (row !== void 0 && typeof row[field] === "boolean") {
671
+ if (row !== void 0 && typeof row[field] === "boolean" && !(row[field] === false && manifest[field] === true)) {
671
672
  sources[field] = "registry";
672
- } else if (field === "agent_teams" && probedAgentTeams) {
673
+ } else if ((field === "agent_teams" || field === "worker_resume" && hostId === "claude") && probedAgentTeams) {
673
674
  sources[field] = "live-probe";
674
675
  } else {
675
676
  sources[field] = "default";
@@ -2,7 +2,7 @@ import { createRequire as __acCreateRequire } from 'node:module'; const require
2
2
  import {
3
3
  ZodFirstPartyTypeKind,
4
4
  settingsSchema
5
- } from "./chunk-4SHNERBL.js";
5
+ } from "./chunk-XIAPY44N.js";
6
6
  import {
7
7
  GLOBAL_DEPLOY_SOURCES,
8
8
  RULE_SOURCE_REL,
@@ -23,10 +23,14 @@ import {
23
23
  decideDeployWrite,
24
24
  foreignSidecarMessage,
25
25
  isLedgerMigration,
26
+ logProofApplies,
27
+ loggedWriteDigests,
26
28
  migrationPreservedMessage,
29
+ onlyDeployed,
27
30
  preservedFileMessage,
28
- sidecarPathFor
29
- } from "./chunk-IROAQ77A.js";
31
+ sidecarPathFor,
32
+ wroteTheseBytes
33
+ } from "./chunk-3PK3VZHB.js";
30
34
  import {
31
35
  main
32
36
  } from "./chunk-F7HLSW6P.js";
@@ -40,11 +44,12 @@ import {
40
44
  ruleBody,
41
45
  splitFrontmatter,
42
46
  split_frontmatter
43
- } from "./chunk-JHMMWNJ2.js";
47
+ } from "./chunk-YHAPONWV.js";
44
48
  import {
45
49
  SCOPE_DETECT_AI_DIRS,
46
- SCOPE_DETECT_MANIFESTS
47
- } from "./chunk-A4QJ6ENT.js";
50
+ SCOPE_DETECT_MANIFESTS,
51
+ getLogPath
52
+ } from "./chunk-W5JXMF2T.js";
48
53
  import {
49
54
  FINDER_NAME,
50
55
  _read_settings,
@@ -59,7 +64,7 @@ import {
59
64
  printResiduals,
60
65
  probeHostCapabilities,
61
66
  removeCodexFinder
62
- } from "./chunk-GOMILC3O.js";
67
+ } from "./chunk-QBHEI5N4.js";
63
68
  import {
64
69
  LEGACY_ALL,
65
70
  compute_active_pack_ids,
@@ -1296,6 +1301,7 @@ function createConflictTracker(host) {
1296
1301
  root: null,
1297
1302
  recorded: null,
1298
1303
  ledgerFiles: null,
1304
+ logged: null,
1299
1305
  preserved: [],
1300
1306
  migrated: /* @__PURE__ */ new Set(),
1301
1307
  staged: /* @__PURE__ */ new Map()
@@ -1394,6 +1400,7 @@ function createConflictTracker(host) {
1394
1400
  conflictState.root = root;
1395
1401
  conflictState.recorded = null;
1396
1402
  conflictState.ledgerFiles = null;
1403
+ conflictState.logged = null;
1397
1404
  conflictState.preserved = [];
1398
1405
  conflictState.migrated = /* @__PURE__ */ new Set();
1399
1406
  conflictState.staged = /* @__PURE__ */ new Map();
@@ -1406,13 +1413,17 @@ function createConflictTracker(host) {
1406
1413
  */
1407
1414
  resolve(target, force, would) {
1408
1415
  const exists = pathExists(target);
1409
- const base = {
1416
+ const recordedSha256 = exists ? recordedHashFor(target) : void 0;
1417
+ const unproven = {
1410
1418
  exists,
1411
- recordedSha256: exists ? recordedHashFor(target) : void 0,
1419
+ recordedSha256,
1412
1420
  onDiskSha256: exists ? sha256OfFile(target) : null,
1413
1421
  force,
1414
- ledgerListed: exists && ledgerLists(target)
1422
+ ledgerListed: exists && ledgerLists(target),
1423
+ recordedIsStaged: typeof recordedSha256 === "string" && recordedSha256 === recordedHashFor(sidecarPathFor(target))
1415
1424
  };
1425
+ const loggedWrite = logProofApplies(unproven) && wroteTheseBytes(target, unproven.onDiskSha256, conflictState.logged ??= loggedWriteDigests());
1426
+ const base = { ...unproven, loggedWrite };
1416
1427
  const migrating = isLedgerMigration(base);
1417
1428
  const decision = decideDeployWrite(
1418
1429
  migrating ? { ...base, packagedSha256: wouldWriteDigest(target, would) } : base
@@ -2460,8 +2471,8 @@ function _withPreservedKeys(rendered, original) {
2460
2471
  }
2461
2472
  return ["---", ...keep, "---", "", rendered].join("\n");
2462
2473
  }
2463
- function rewriteAndReport(rulesDir, quiet, info2, warn2, wrappers, preserved = /* @__PURE__ */ new Set()) {
2464
- const result = rewriteClaudeRules(rulesDir, preserved);
2474
+ function rewriteAndReport(rulesDir, quiet, info2, warn2, wrappers, preserved = /* @__PURE__ */ new Set(), onWrite) {
2475
+ const result = rewriteClaudeRules(rulesDir, preserved, onWrite);
2465
2476
  if (wrappers && !quiet) {
2466
2477
  if (wrappers.wrapped.length > 0) {
2467
2478
  info2(
@@ -2515,7 +2526,7 @@ function renderClaudeRule(sourceText) {
2515
2526
  lines.push(pyRstrip(body) + "\n");
2516
2527
  return { text: lines.join("\n"), globs: plan.globs, dropped: plan.dropped };
2517
2528
  }
2518
- function rewriteClaudeRules(rulesDir, preserved = /* @__PURE__ */ new Set()) {
2529
+ function rewriteClaudeRules(rulesDir, preserved = /* @__PURE__ */ new Set(), onWrite) {
2519
2530
  const result = { rewritten: 0, scoped: [], dropped: [], failed: [] };
2520
2531
  let entries;
2521
2532
  try {
@@ -2553,6 +2564,7 @@ function rewriteClaudeRules(rulesDir, preserved = /* @__PURE__ */ new Set()) {
2553
2564
  });
2554
2565
  continue;
2555
2566
  }
2567
+ onWrite?.(full);
2556
2568
  result.rewritten += 1;
2557
2569
  }
2558
2570
  const stem = name.slice(0, -3);
@@ -2898,6 +2910,7 @@ function thinInstalledRuleLayer(opts) {
2898
2910
  try {
2899
2911
  if (next !== installed) {
2900
2912
  fs16.writeFileSync(target, next, "utf-8");
2913
+ opts.onWrite?.(target);
2901
2914
  rewritten += 1;
2902
2915
  }
2903
2916
  thinned += 1;
@@ -4493,30 +4506,6 @@ function detect_module_roots(project_root) {
4493
4506
  return [...high, ...medium];
4494
4507
  }
4495
4508
 
4496
- // src/install/paths.ts
4497
- import { homedir as homedir8, tmpdir as tmpdir2 } from "node:os";
4498
- import { join as join18 } from "node:path";
4499
- var INSTALL_ROOT_SUBPATH = ".event4u/agent-config";
4500
- var INSTALL_LOG_FILENAME = "install-log.jsonl";
4501
- function resolveHome(home) {
4502
- if (home && home.length > 0) {
4503
- return home;
4504
- }
4505
- const fromOs = homedir8();
4506
- if (!fromOs) {
4507
- throw new Error(
4508
- "Cannot resolve home directory \u2014 both $HOME (POSIX) and $USERPROFILE (Windows) are unset."
4509
- );
4510
- }
4511
- return fromOs;
4512
- }
4513
- function getInstallRoot(home) {
4514
- return join18(resolveHome(home), INSTALL_ROOT_SUBPATH);
4515
- }
4516
- function getLogPath(home) {
4517
- return join18(getInstallRoot(home), INSTALL_LOG_FILENAME);
4518
- }
4519
-
4520
4509
  // src/scripts/install.ts
4521
4510
  var _HERE = fileURLToPath2(import.meta.url);
4522
4511
  var SystemExitError = class extends Error {
@@ -6600,7 +6589,8 @@ function _deploy_global_content(tools, force, package_root, lockfile_path2) {
6600
6589
  info,
6601
6590
  warn,
6602
6591
  res,
6603
- preserved_now
6592
+ preserved_now,
6593
+ onlyDeployed(anchor, current_files, (p) => _log_tx_entry("write", p))
6604
6594
  );
6605
6595
  deployClaudeCarrier({
6606
6596
  packageRoot: package_root,
@@ -6610,7 +6600,7 @@ function _deploy_global_content(tools, force, package_root, lockfile_path2) {
6610
6600
  warn,
6611
6601
  userPin: state.FULL_PIN,
6612
6602
  wantsThin: installerThinsHost(tool_id, { packageRoot: package_root }),
6613
- thin: (bodyLinkRoot) => describeThinInstalledLayer(thinInstalledRuleLayer({ rulesDir: path18.join(anchor, "rules"), packageRoot: package_root, preserved: preserved_now, bodyLinkRoot }))
6603
+ thin: (bodyLinkRoot) => describeThinInstalledLayer(thinInstalledRuleLayer({ rulesDir: path18.join(anchor, "rules"), packageRoot: package_root, preserved: preserved_now, bodyLinkRoot, onWrite: onlyDeployed(anchor, current_files, (p) => _log_tx_entry("write", p)) }))
6614
6604
  });
6615
6605
  if (!state.QUIET) for (const l of installReceiptBudgetLines(package_root, void 0, { thisDeploy: /* @__PURE__ */ new Map([[anchor, current_files]]) })) info(l);
6616
6606
  }