@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +108 -7
- package/README.md +1 -0
- package/dist/agent-src/commands/analyze/inbox.md +22 -1
- package/dist/agent-src/commands/explain-run.md +31 -1
- package/dist/agent-src/commands/team/delegate.md +1 -0
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +4 -1
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +17 -0
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +11 -1
- package/dist/agent-src/rules/preservation-guard.md +9 -0
- package/dist/agent-src/scripts/roadmap_gates.ts +312 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +72 -4
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +10 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/cli/registry.js +2 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-OJM3MBJM.js → chunk-BTJNRUX3.js} +21 -34
- package/dist/cli-delegate/{chunk-MMXA5GY7.js → chunk-DDUNNFYX.js} +14 -0
- package/dist/cli-delegate/{chunk-NZ5N2XQL.js → chunk-EQY4FLJB.js} +1 -1
- package/dist/cli-delegate/{chunk-RCIA67XN.js → chunk-IYTGIG2Y.js} +44 -1
- package/dist/cli-delegate/chunk-MMJL4TIX.js +18 -0
- package/dist/cli-delegate/chunk-O3CO5BFB.js +161 -0
- package/dist/cli-delegate/{chunk-REKCMYOU.js → chunk-PFBV6XRT.js} +36 -20
- package/dist/cli-delegate/chunk-RVGBH4XK.js +142 -0
- package/dist/cli-delegate/cmd_conformance.js +2798 -95
- package/dist/cli-delegate/cmd_doctor.js +2 -1
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +86 -6
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_route_audit.js +2 -1
- package/dist/cli-delegate/cmd_session_recycle.js +177 -0
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_settings_set.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +7 -7
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +2 -2
- package/dist/discovery/trust-report.md +1 -1
- package/dist/discovery/workspaces.json +2 -2
- package/dist/hooks/dispatch.js +1904 -575
- package/dist/install/install.mjs +14 -0
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +3760 -726
- package/dist/server/schemas/settings.js +6 -0
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/ui/assets/index-B5qGYOJo.js +26 -0
- package/dist/ui/assets/index-B5qGYOJo.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/docs/contracts/settings-classes.md +21 -5
- package/package.json +1 -1
- package/src/agent-src/scripts/roadmap_gates.ts +312 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +72 -4
- package/src/config/agent-settings.template.yml +36 -0
- package/src/config/dispatch-economy-metrics.json +61 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/hook-token-budget.json +91 -0
- package/src/config/recycle-threshold-budget.json +12 -0
- package/src/config/rule-enforcement-baseline.json +86 -0
- package/src/scripts/_cli/cmd_conformance.ts +17 -2
- package/src/scripts/_cli/cmd_session_recycle.ts +226 -0
- package/src/scripts/_dispatch.bash +23 -0
- package/src/scripts/_lib/judgment_ladder.ts +254 -2
- package/src/scripts/_lib/orchestration_record.ts +41 -10
- package/src/scripts/_lib/recycle_envelope_paths.ts +35 -0
- package/src/scripts/_lib/self_repair.ts +194 -0
- package/src/scripts/_lib/session_eol.ts +230 -0
- package/src/scripts/_lib/session_role.ts +42 -0
- package/src/scripts/_lib/subagent_capsule.ts +159 -0
- package/src/scripts/_lib/subagent_response.ts +75 -0
- package/src/scripts/ai_council/clients.ts +50 -1
- package/src/scripts/ask_transport.ts +221 -0
- package/src/scripts/bench_hook_injection.ts +234 -0
- package/src/scripts/check_adr_frontmatter.ts +84 -40
- package/src/scripts/check_branch_freshness.ts +1 -0
- package/src/scripts/check_corpus_staleness.ts +18 -4
- package/src/scripts/check_detector_corpus.ts +17 -0
- package/src/scripts/check_gitignore_freshness.ts +42 -8
- package/src/scripts/check_hook_bundle_freshness.ts +20 -0
- package/src/scripts/check_kernel_prefix_stability.ts +1 -0
- package/src/scripts/check_standing_rule_delivery.ts +73 -32
- package/src/scripts/check_static_layer_stability.ts +154 -0
- package/src/scripts/check_suppression_hygiene.ts +48 -0
- package/src/scripts/dispatch_economy_report.ts +448 -0
- package/src/scripts/dispatch_r2_reviewer.ts +15 -2
- package/src/scripts/explain_run.ts +206 -4
- package/src/scripts/generate_settings_reference.ts +1 -0
- package/src/scripts/handoff_context_hook.ts +112 -4
- package/src/scripts/hook_manifest.yaml +117 -10
- package/src/scripts/hooks/concern_registry.ts +8 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +40 -3
- package/src/scripts/hooks/dispatch_hook.ts +39 -2
- package/src/scripts/hooks/edit_shape_hook.ts +303 -0
- package/src/scripts/hooks/orchestration_record_hook.ts +19 -1
- package/src/scripts/hooks/reread_guard_hook.ts +314 -0
- package/src/scripts/hooks/rtk_wrap_hook.ts +133 -14
- package/src/scripts/hooks/session_eol_hook.ts +268 -0
- package/src/scripts/hooks/turn_end_gate_hook.ts +507 -0
- package/src/scripts/install-hooks.sh +1 -1
- package/src/scripts/language_mirror_hook.ts +89 -0
- package/src/scripts/lint_decision_impact.ts +14 -1
- package/src/scripts/lint_hook_manifest.ts +82 -0
- package/src/scripts/lint_readme_serial_comma.ts +1 -0
- package/src/scripts/lint_rule_enforcement_declaration.ts +347 -0
- package/src/scripts/measure_turn_end_gate.ts +194 -0
- package/src/scripts/orchestration_record.ts +4 -1
- package/src/scripts/rebuild_hook_bundle.ts +1 -0
- package/src/scripts/report_conformance_funnel.ts +284 -0
- package/src/scripts/report_imperative_density.ts +1 -0
- package/src/scripts/routing_doctor.ts +97 -0
- package/src/scripts/self_repair_cli.ts +51 -3
- package/src/scripts/session_eol_report.ts +261 -0
- package/src/server/schemas/settings.ts +14 -0
- package/src/templates/compact-instructions.md +40 -0
- package/dist/ui/assets/index-JDAF00bC.js +0 -26
- 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
|
+
}
|