@event4u/agent-config 9.29.0 → 9.30.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 (122) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +108 -7
  3. package/README.md +1 -0
  4. package/dist/agent-src/commands/analyze/inbox.md +22 -1
  5. package/dist/agent-src/commands/explain-run.md +31 -1
  6. package/dist/agent-src/commands/team/delegate.md +1 -0
  7. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +4 -1
  8. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +17 -0
  9. package/dist/agent-src/contexts/execution/subagent-response-contract.md +11 -1
  10. package/dist/agent-src/rules/preservation-guard.md +9 -0
  11. package/dist/agent-src/scripts/roadmap_gates.ts +312 -0
  12. package/dist/agent-src/scripts/update_roadmap_progress.ts +72 -4
  13. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +10 -0
  14. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  15. package/dist/cli/registry.js +2 -0
  16. package/dist/cli/registry.js.map +1 -1
  17. package/dist/cli-delegate/{chunk-OJM3MBJM.js → chunk-BTJNRUX3.js} +21 -34
  18. package/dist/cli-delegate/{chunk-MMXA5GY7.js → chunk-DDUNNFYX.js} +14 -0
  19. package/dist/cli-delegate/{chunk-NZ5N2XQL.js → chunk-EQY4FLJB.js} +1 -1
  20. package/dist/cli-delegate/{chunk-RCIA67XN.js → chunk-IYTGIG2Y.js} +44 -1
  21. package/dist/cli-delegate/chunk-MMJL4TIX.js +18 -0
  22. package/dist/cli-delegate/chunk-O3CO5BFB.js +161 -0
  23. package/dist/cli-delegate/{chunk-REKCMYOU.js → chunk-PFBV6XRT.js} +36 -20
  24. package/dist/cli-delegate/chunk-RVGBH4XK.js +142 -0
  25. package/dist/cli-delegate/cmd_conformance.js +2798 -95
  26. package/dist/cli-delegate/cmd_doctor.js +2 -1
  27. package/dist/cli-delegate/cmd_export.js +2 -2
  28. package/dist/cli-delegate/cmd_handoff.js +86 -6
  29. package/dist/cli-delegate/cmd_prune.js +2 -2
  30. package/dist/cli-delegate/cmd_route_audit.js +2 -1
  31. package/dist/cli-delegate/cmd_session_recycle.js +177 -0
  32. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  33. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  34. package/dist/cli-delegate/cmd_settings_set.js +2 -2
  35. package/dist/cli-delegate/cmd_sync.js +2 -2
  36. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  37. package/dist/cli-delegate/cmd_validate.js +2 -2
  38. package/dist/discovery/deprecation-report.md +1 -1
  39. package/dist/discovery/discovery-manifest.json +7 -7
  40. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  41. package/dist/discovery/discovery-manifest.summary.md +1 -1
  42. package/dist/discovery/orphan-report.md +1 -1
  43. package/dist/discovery/packs.json +2 -2
  44. package/dist/discovery/trust-report.md +1 -1
  45. package/dist/discovery/workspaces.json +2 -2
  46. package/dist/hooks/dispatch.js +1904 -575
  47. package/dist/install/install.mjs +14 -0
  48. package/dist/mcp/registry-manifest.json +2 -2
  49. package/dist/mcp/server.json +2 -2
  50. package/dist/mcp/server.mjs +3760 -726
  51. package/dist/server/schemas/settings.js +6 -0
  52. package/dist/server/schemas/settings.js.map +1 -1
  53. package/dist/ui/assets/index-B5qGYOJo.js +26 -0
  54. package/dist/ui/assets/index-B5qGYOJo.js.map +1 -0
  55. package/dist/ui/index.html +1 -1
  56. package/docs/contracts/settings-classes.md +21 -5
  57. package/package.json +1 -1
  58. package/src/agent-src/scripts/roadmap_gates.ts +312 -0
  59. package/src/agent-src/scripts/update_roadmap_progress.ts +72 -4
  60. package/src/config/agent-settings.template.yml +36 -0
  61. package/src/config/dispatch-economy-metrics.json +61 -0
  62. package/src/config/evaluator-budgets.json +4 -4
  63. package/src/config/hook-token-budget.json +91 -0
  64. package/src/config/recycle-threshold-budget.json +12 -0
  65. package/src/config/rule-enforcement-baseline.json +86 -0
  66. package/src/scripts/_cli/cmd_conformance.ts +17 -2
  67. package/src/scripts/_cli/cmd_session_recycle.ts +226 -0
  68. package/src/scripts/_dispatch.bash +23 -0
  69. package/src/scripts/_lib/judgment_ladder.ts +254 -2
  70. package/src/scripts/_lib/orchestration_record.ts +41 -10
  71. package/src/scripts/_lib/recycle_envelope_paths.ts +35 -0
  72. package/src/scripts/_lib/self_repair.ts +194 -0
  73. package/src/scripts/_lib/session_eol.ts +230 -0
  74. package/src/scripts/_lib/session_role.ts +42 -0
  75. package/src/scripts/_lib/subagent_capsule.ts +159 -0
  76. package/src/scripts/_lib/subagent_response.ts +75 -0
  77. package/src/scripts/ai_council/clients.ts +50 -1
  78. package/src/scripts/ask_transport.ts +221 -0
  79. package/src/scripts/bench_hook_injection.ts +234 -0
  80. package/src/scripts/check_adr_frontmatter.ts +84 -40
  81. package/src/scripts/check_branch_freshness.ts +1 -0
  82. package/src/scripts/check_corpus_staleness.ts +18 -4
  83. package/src/scripts/check_detector_corpus.ts +17 -0
  84. package/src/scripts/check_gitignore_freshness.ts +42 -8
  85. package/src/scripts/check_hook_bundle_freshness.ts +20 -0
  86. package/src/scripts/check_kernel_prefix_stability.ts +1 -0
  87. package/src/scripts/check_standing_rule_delivery.ts +73 -32
  88. package/src/scripts/check_static_layer_stability.ts +154 -0
  89. package/src/scripts/check_suppression_hygiene.ts +48 -0
  90. package/src/scripts/dispatch_economy_report.ts +448 -0
  91. package/src/scripts/dispatch_r2_reviewer.ts +15 -2
  92. package/src/scripts/explain_run.ts +206 -4
  93. package/src/scripts/generate_settings_reference.ts +1 -0
  94. package/src/scripts/handoff_context_hook.ts +112 -4
  95. package/src/scripts/hook_manifest.yaml +117 -10
  96. package/src/scripts/hooks/concern_registry.ts +8 -0
  97. package/src/scripts/hooks/delegation_nudge_hook.ts +40 -3
  98. package/src/scripts/hooks/dispatch_hook.ts +39 -2
  99. package/src/scripts/hooks/edit_shape_hook.ts +303 -0
  100. package/src/scripts/hooks/orchestration_record_hook.ts +19 -1
  101. package/src/scripts/hooks/reread_guard_hook.ts +314 -0
  102. package/src/scripts/hooks/rtk_wrap_hook.ts +133 -14
  103. package/src/scripts/hooks/session_eol_hook.ts +268 -0
  104. package/src/scripts/hooks/turn_end_gate_hook.ts +507 -0
  105. package/src/scripts/install-hooks.sh +1 -1
  106. package/src/scripts/language_mirror_hook.ts +89 -0
  107. package/src/scripts/lint_decision_impact.ts +14 -1
  108. package/src/scripts/lint_hook_manifest.ts +82 -0
  109. package/src/scripts/lint_readme_serial_comma.ts +1 -0
  110. package/src/scripts/lint_rule_enforcement_declaration.ts +347 -0
  111. package/src/scripts/measure_turn_end_gate.ts +194 -0
  112. package/src/scripts/orchestration_record.ts +4 -1
  113. package/src/scripts/rebuild_hook_bundle.ts +1 -0
  114. package/src/scripts/report_conformance_funnel.ts +284 -0
  115. package/src/scripts/report_imperative_density.ts +1 -0
  116. package/src/scripts/routing_doctor.ts +97 -0
  117. package/src/scripts/self_repair_cli.ts +51 -3
  118. package/src/scripts/session_eol_report.ts +261 -0
  119. package/src/server/schemas/settings.ts +14 -0
  120. package/src/templates/compact-instructions.md +40 -0
  121. package/dist/ui/assets/index-JDAF00bC.js +0 -26
  122. package/dist/ui/assets/index-JDAF00bC.js.map +0 -1
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * session-eol — Stop-slot session end-of-life instrument + recycle advisory
4
+ * (road-to-token-economy-recycling Phases 1.1 / 3.2 / 4.2).
5
+ *
6
+ * Phase 1.1 (record-only): per session, maintain counts-only end-of-life
7
+ * state — final main-chain context size in parsed tokens (via
8
+ * `_lib/session_eol.ts` → `cc_transcript.billableInputTokens`), turn count,
9
+ * and every observed compaction event — under
10
+ * `agents/runtime/state/session-eol/<sha256(session)>.json`. Incremental:
11
+ * each Stop reads only the transcript bytes appended since the last scan.
12
+ *
13
+ * Phase 3.2 (advisory, once per session): past the committed recycle
14
+ * threshold (`src/config/recycle-threshold-budget.json`), inject ONE line
15
+ * advising `session:recycle` — the F2 once-per-session pattern of
16
+ * `end_review_nudge_hook.ts`. Absent/unreadable config or transcript is
17
+ * SILENCE, never a block (fail-open); hooks cannot inject `/clear`, so the
18
+ * recycle action itself stays advisory-carried by design (roadmap 5.1).
19
+ *
20
+ * Phase 4.2 (read surface): every Stop also overwrites
21
+ * `agents/runtime/state/context-fill.json` with the machine-readable fill
22
+ * level + threshold state — display substrate for an external statusline;
23
+ * carries counts only, and this hook's behaviour is identical whether or
24
+ * not anything reads it.
25
+ *
26
+ * Never blocks: exit 0 on every silent path; the advisory reports at
27
+ * exit 2 ({decision:"warn", additional_context}) which `host_semantics`
28
+ * reduces to a non-blocking warn on claude. `AGENT_CONFIG_REPLAY=1` → no-op.
29
+ */
30
+
31
+ import * as crypto from 'node:crypto';
32
+ import * as fs from 'node:fs';
33
+ import * as path from 'node:path';
34
+ import process from 'node:process';
35
+ import { fileURLToPath, pathToFileURL } from 'node:url';
36
+
37
+ import recycleThresholdConfig from '../../config/recycle-threshold-budget.json';
38
+
39
+ import {
40
+ emptyCounters,
41
+ readNewCompleteLines,
42
+ scanEolSlice,
43
+ type EolCounters,
44
+ } from '../_lib/session_eol.js';
45
+ import { unwrap, type JsonObject, type JsonValue } from './envelope.js';
46
+ import { readHookStdin } from './hook_stdin.js';
47
+ import { atomic_write_json, is_replay_mode } from './state_io.js';
48
+ import { isSafeTranscriptPath } from './end_review_nudge_hook.js';
49
+
50
+ const EXIT_WARN = 2;
51
+
52
+ /**
53
+ * Per-process threshold override — the test seam and the emergency off
54
+ * switch (`0` or any non-positive value disables the advisory lane while
55
+ * recording continues). The committed value lives in
56
+ * `src/config/recycle-threshold-budget.json` and is statically imported so
57
+ * the hook bundle carries it into consumers — one threshold, one constant.
58
+ */
59
+ export const THRESHOLD_OVERRIDE_ENV = 'AGENT_RECYCLE_THRESHOLD_TOKENS';
60
+
61
+ /** Machine-readable fill-level surface (Phase 4.2). Counts only, overwritten per Stop. */
62
+ export const CONTEXT_FILL_REL = path.join('agents', 'runtime', 'state', 'context-fill.json');
63
+
64
+ export interface SessionEolState {
65
+ schema_version: 1;
66
+ counters: EolCounters;
67
+ /** ISO stamp when the recycle advisory fired for this session (F2), or null. */
68
+ advisory_fired_at: string | null;
69
+ updated_at: string;
70
+ }
71
+
72
+ function str(value: JsonValue | undefined): string {
73
+ return typeof value === 'string' ? value : '';
74
+ }
75
+
76
+ function stateDir(workspaceRoot: string): string {
77
+ return path.join(workspaceRoot, 'agents', 'runtime', 'state', 'session-eol');
78
+ }
79
+
80
+ export function stateFile(workspaceRoot: string, sessionKey: string): string {
81
+ return path.join(stateDir(workspaceRoot), `${sessionKey}.json`);
82
+ }
83
+
84
+ /** Same derivation as `end_review_nudge_hook.deriveSessionKey` — hashed, never raw. */
85
+ export function deriveSessionKey(envelope: JsonObject, payload: JsonObject): string {
86
+ const raw =
87
+ str(envelope['session_id'] as JsonValue | undefined) ||
88
+ str((payload['transcript_path'] ?? payload['transcriptPath']) as JsonValue | undefined);
89
+ return crypto.createHash('sha256').update(raw || 'unknown-session').digest('hex');
90
+ }
91
+
92
+ /** Load prior per-session state; any read/shape error reads as "fresh session". */
93
+ export function readState(file: string): SessionEolState {
94
+ try {
95
+ const parsed = JSON.parse(fs.readFileSync(file, 'utf-8')) as Partial<SessionEolState>;
96
+ if (
97
+ parsed &&
98
+ typeof parsed === 'object' &&
99
+ parsed.schema_version === 1 &&
100
+ parsed.counters &&
101
+ typeof parsed.counters.scanned_bytes === 'number'
102
+ ) {
103
+ return {
104
+ schema_version: 1,
105
+ counters: parsed.counters,
106
+ advisory_fired_at:
107
+ typeof parsed.advisory_fired_at === 'string' ? parsed.advisory_fired_at : null,
108
+ updated_at: typeof parsed.updated_at === 'string' ? parsed.updated_at : '',
109
+ };
110
+ }
111
+ } catch {
112
+ // fall through — fresh state
113
+ }
114
+ return {
115
+ schema_version: 1,
116
+ counters: emptyCounters(),
117
+ advisory_fired_at: null,
118
+ updated_at: '',
119
+ };
120
+ }
121
+
122
+ /**
123
+ * Resolve the recycle threshold (Phase 3.1): the per-process override wins
124
+ * (tests; `0` = advisory lane off), else the committed constant from
125
+ * `recycle-threshold-budget.json`. Returns `null` when the lane is
126
+ * disabled or the value is not a positive number — recording continues
127
+ * either way (fail-open, never a block).
128
+ */
129
+ export function readThresholdTokens(): number | null {
130
+ const override = process.env[THRESHOLD_OVERRIDE_ENV];
131
+ if (override !== undefined) {
132
+ const value = Number(override);
133
+ return Number.isFinite(value) && value > 0 ? value : null;
134
+ }
135
+ const value = (recycleThresholdConfig as Record<string, unknown>)['recycle_threshold_tokens'];
136
+ return typeof value === 'number' && Number.isFinite(value) && value > 0 ? value : null;
137
+ }
138
+
139
+ /** The exactly-one advisory line injected past threshold (Phase 3.2). */
140
+ export function buildAdvisoryLine(tokens: number, threshold: number): string {
141
+ return (
142
+ `context past recycle threshold (${tokens.toLocaleString('en-US')} of ` +
143
+ `${threshold.toLocaleString('en-US')} tokens): run \`agent-config session:recycle\` to ` +
144
+ `write the recycle envelope, then /clear — the successor session resumes from the ` +
145
+ `envelope at session_start (road-to-token-economy-recycling)`
146
+ );
147
+ }
148
+
149
+ /** Overwrite the Phase 4.2 read surface. Counts only; failures are swallowed. */
150
+ function writeContextFill(
151
+ workspaceRoot: string,
152
+ tokens: number | null,
153
+ threshold: number | null,
154
+ ts: string,
155
+ ): void {
156
+ try {
157
+ atomic_write_json(path.join(workspaceRoot, CONTEXT_FILL_REL), {
158
+ schema_version: 1,
159
+ final_context_tokens: tokens,
160
+ recycle_threshold_tokens: threshold,
161
+ past_threshold: tokens !== null && threshold !== null && tokens >= threshold,
162
+ updated_at: ts,
163
+ });
164
+ } catch {
165
+ // a state-write failure must never block the Stop path
166
+ }
167
+ }
168
+
169
+ export function main(): number {
170
+ const [envelope, payload] = unwrap(readHookStdin(), 'claude');
171
+
172
+ const event = String(envelope['event'] ?? '');
173
+ if (event !== '' && event !== 'stop') {
174
+ return 0;
175
+ }
176
+ if (is_replay_mode()) {
177
+ return 0; // replay fixtures never mutate session state
178
+ }
179
+
180
+ const workspaceRoot = String(envelope['workspace_root'] ?? '').trim() || process.cwd();
181
+
182
+ const transcriptPath = str(
183
+ (payload['transcript_path'] ?? payload['transcriptPath']) as JsonValue | undefined,
184
+ ).trim();
185
+ if (!transcriptPath || !isSafeTranscriptPath(transcriptPath)) {
186
+ return 0; // no readable ledger — silence, never a block
187
+ }
188
+
189
+ const sessionKey = deriveSessionKey(envelope, payload);
190
+ const file = stateFile(workspaceRoot, sessionKey);
191
+ const state = readState(file);
192
+
193
+ let counters = state.counters;
194
+ const read = readNewCompleteLines(transcriptPath, counters.scanned_bytes);
195
+ if (read.truncated) {
196
+ const full = readNewCompleteLines(transcriptPath, 0);
197
+ counters = scanEolSlice(full.text, emptyCounters());
198
+ counters.scanned_bytes = full.nextOffset;
199
+ } else if (read.text.length > 0) {
200
+ counters = scanEolSlice(read.text, counters);
201
+ counters.scanned_bytes = read.nextOffset;
202
+ } else {
203
+ counters = { ...counters, scanned_bytes: read.nextOffset };
204
+ }
205
+
206
+ const now = new Date().toISOString();
207
+ const threshold = readThresholdTokens();
208
+ const tokens = counters.final_context_tokens;
209
+
210
+ const shouldAdvise =
211
+ threshold !== null &&
212
+ tokens !== null &&
213
+ tokens >= threshold &&
214
+ state.advisory_fired_at === null;
215
+
216
+ const nextState: SessionEolState = {
217
+ schema_version: 1,
218
+ counters,
219
+ advisory_fired_at: shouldAdvise ? now : state.advisory_fired_at,
220
+ updated_at: now,
221
+ };
222
+ try {
223
+ atomic_write_json(file, nextState);
224
+ } catch {
225
+ // recording is best-effort; never block the Stop path
226
+ }
227
+
228
+ writeContextFill(workspaceRoot, tokens, threshold, now);
229
+
230
+ if (shouldAdvise && threshold !== null && tokens !== null) {
231
+ process.stdout.write(
232
+ `${JSON.stringify({
233
+ decision: 'warn',
234
+ reason: `session-eol: context ${tokens} >= recycle threshold ${threshold}`,
235
+ additional_context: buildAdvisoryLine(tokens, threshold),
236
+ })}\n`,
237
+ );
238
+ return EXIT_WARN;
239
+ }
240
+ return 0;
241
+ }
242
+
243
+ // Bundle-safety: never auto-run when inlined into an esbuild bundle, where
244
+ // every module shares the bundle's `import.meta.url` (see cmd_migrate.ts).
245
+ declare const __AGENT_CONFIG_BUNDLE__: boolean | undefined;
246
+ function _isCliEntry(): boolean {
247
+ if (typeof __AGENT_CONFIG_BUNDLE__ !== 'undefined' && __AGENT_CONFIG_BUNDLE__) {
248
+ return false;
249
+ }
250
+ if (process.argv[1] === undefined) {
251
+ return false;
252
+ }
253
+ const argvUrl = pathToFileURL(path.resolve(process.argv[1])).href;
254
+ if (import.meta.url === argvUrl) {
255
+ return true;
256
+ }
257
+ try {
258
+ const here = fs.realpathSync(fileURLToPath(import.meta.url));
259
+ const argv = fs.realpathSync(path.resolve(process.argv[1]));
260
+ return here === argv;
261
+ } catch {
262
+ return false;
263
+ }
264
+ }
265
+
266
+ if (_isCliEntry()) {
267
+ process.exit(main());
268
+ }