@event4u/agent-config 9.27.0 → 9.29.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/AGENTS.md +13 -12
- package/CHANGELOG.md +165 -0
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +55 -24
- package/dist/agent-src/commands/analyze/conformance.md +24 -0
- package/dist/agent-src/commands/cost/report.md +2 -2
- package/dist/agent-src/commands/team/adversarial.md +8 -7
- package/dist/agent-src/commands/team/delegate.md +13 -12
- package/dist/agent-src/commands/team/review.md +9 -8
- package/dist/agent-src/commands/team/status.md +9 -8
- package/dist/agent-src/commands/team.md +29 -19
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +88 -2
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +29 -21
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +26 -15
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +28 -3
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +54 -0
- package/dist/agent-src/contexts/execution/subagent-routing.md +13 -11
- package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +25 -0
- package/dist/agent-src/contexts/execution/subagent-steering.md +6 -3
- package/dist/agent-src/rules/artifact-engagement-recording.md +4 -1
- package/dist/agent-src/rules/autonomous-execution.md +1 -1
- package/dist/agent-src/rules/brand-source-of-truth.md +33 -6
- package/dist/agent-src/rules/context-hygiene.md +3 -1
- package/dist/agent-src/rules/council-availability.md +93 -0
- package/dist/agent-src/rules/cross-source-consistency.md +3 -0
- package/dist/agent-src/rules/delegation-policy.md +38 -10
- package/dist/agent-src/rules/design-fidelity.md +3 -0
- package/dist/agent-src/rules/evaluator-independence.md +2 -1
- package/dist/agent-src/rules/external-code-graph-interop.md +2 -1
- package/dist/agent-src/rules/finance-safety-floor.md +1 -1
- package/dist/agent-src/rules/git-history-discipline.md +1 -1
- package/dist/agent-src/rules/history-discipline.md +3 -1
- package/dist/agent-src/rules/legal-safety-floor.md +1 -1
- package/dist/agent-src/rules/no-pr-progress-comments.md +3 -1
- package/dist/agent-src/rules/onboarding-gate.md +3 -1
- package/dist/agent-src/rules/roadmap-ci-steps-policy.md +4 -1
- package/dist/agent-src/rules/role-mode-adherence.md +1 -1
- package/dist/agent-src/rules/scale-discipline.md +3 -1
- package/dist/agent-src/rules/self-repair-loop.md +103 -0
- package/dist/agent-src/rules/session-canary.md +2 -1
- package/dist/agent-src/rules/settings-ask-protocol.md +8 -7
- package/dist/agent-src/rules/strategy-safety-floor.md +1 -1
- package/dist/agent-src/rules/token-budget-discipline.md +4 -2
- package/dist/agent-src/rules/tool-safety.md +5 -1
- package/dist/agent-src/skills/adversarial-review/SKILL.md +1 -1
- package/dist/agent-src/skills/analysis-autonomous-mode/SKILL.md +1 -1
- package/dist/agent-src/skills/forensics-report/SKILL.md +114 -0
- package/dist/agent-src/skills/forensics-report/evals/triggers.json +17 -0
- package/dist/agent-src/skills/performance-analysis/SKILL.md +1 -1
- package/dist/agent-src/skills/persona-improvement/SKILL.md +1 -1
- package/dist/agent-src/skills/premortem/SKILL.md +30 -1
- package/dist/agent-src/skills/project-analyzer/SKILL.md +1 -1
- package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +2 -1
- package/dist/agent-src/skills/security-audit/SKILL.md +1 -1
- package/dist/agent-src/skills/sequential-thinking/SKILL.md +1 -1
- package/dist/agent-src/skills/skill-improvement-pipeline/SKILL.md +1 -1
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +7 -5
- package/dist/agent-src/skills/subagent-orchestration/schemas/subagent-status.json +61 -3
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +1 -1
- package/dist/agent-src/skills/universal-project-analysis/SKILL.md +1 -1
- package/dist/agent-src/templates/AGENTS.md +8 -12
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +23 -0
- package/dist/cli/registry.js +7 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/chunk-66OVWGB5.js +270 -0
- package/dist/cli-delegate/{chunk-HJ6DAJHR.js → chunk-LCFJIQ4J.js} +89 -7
- package/dist/cli-delegate/{chunk-VSYT7OHO.js → chunk-LVRYBKTI.js} +31 -2
- package/dist/cli-delegate/{chunk-DVRS2KVR.js → chunk-MMXA5GY7.js} +10 -17
- package/dist/cli-delegate/{chunk-WR6GTRHJ.js → chunk-MP3JT3IU.js} +3 -3
- package/dist/cli-delegate/{chunk-3KJIMFTB.js → chunk-NZ5N2XQL.js} +467 -305
- package/dist/cli-delegate/{chunk-AIIUU73N.js → chunk-OJM3MBJM.js} +1 -1
- package/dist/cli-delegate/{chunk-BCPIOAGV.js → chunk-RCIA67XN.js} +117 -96
- package/dist/cli-delegate/chunk-REKCMYOU.js +2991 -0
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +134 -0
- package/dist/cli-delegate/cmd_conformance.js +10 -47
- package/dist/cli-delegate/cmd_converge.js +3 -3
- package/dist/cli-delegate/cmd_doctor.js +10 -6
- package/dist/cli-delegate/cmd_explain.js +80 -330
- package/dist/cli-delegate/cmd_export.js +6 -6
- package/dist/cli-delegate/cmd_fleet.js +3 -3
- package/dist/cli-delegate/cmd_handoff.js +3 -3
- package/dist/cli-delegate/cmd_mcp_available.js +433 -0
- package/dist/cli-delegate/cmd_migrate.js +3 -3
- package/dist/cli-delegate/cmd_packs_active.js +153 -0
- package/dist/cli-delegate/cmd_preflight.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +6 -6
- package/dist/cli-delegate/cmd_route_audit.js +8 -8
- package/dist/cli-delegate/cmd_route_explain.js +4 -4
- package/dist/cli-delegate/cmd_settings_get.js +266 -0
- package/dist/cli-delegate/cmd_settings_migrate.js +6 -6
- package/dist/cli-delegate/cmd_settings_set.js +8 -2968
- package/dist/cli-delegate/cmd_sync.js +6 -6
- package/dist/cli-delegate/cmd_uninstall.js +6 -6
- package/dist/cli-delegate/cmd_update.js +4 -4
- package/dist/cli-delegate/cmd_upgrade.js +4 -4
- package/dist/cli-delegate/cmd_validate.js +6 -6
- package/dist/cli-delegate/cmd_versions.js +4 -4
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +164 -55
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +4 -3
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +44 -5
- package/dist/discovery/trust-report.md +11 -11
- package/dist/discovery/workspaces.json +16 -6
- package/dist/hooks/dispatch.js +9341 -6131
- package/dist/install/install.mjs +739 -524
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +777 -281
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +134 -15
- package/dist/scripts/_lib/agent_settings.js.map +1 -1
- package/dist/scripts/condense.js +147 -2
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +6 -7
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/settingsCarveOut.js +0 -9
- package/dist/shared/settingsCarveOut.js.map +1 -1
- package/dist/shared/settingsClasses.js +11 -9
- package/dist/shared/settingsClasses.js.map +1 -1
- package/dist/ui/assets/{index-DmR0mbk4.js → index-JDAF00bC.js} +2 -2
- package/dist/ui/assets/{index-DmR0mbk4.js.map → index-JDAF00bC.js.map} +1 -1
- package/dist/ui/index.html +1 -1
- package/docs/contracts/settings-classes.md +68 -17
- package/docs/guidelines/agent-infra/memory-access.md +14 -3
- package/package.json +1 -1
- package/src/agent-src/templates/AGENTS.md +8 -12
- package/src/config/agent-settings.template.yml +52 -66
- package/src/config/budgets.yml +29 -0
- package/src/config/discovery/packs.yml +11 -0
- package/src/config/discovery/workspaces.yml +1 -1
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +26 -0
- package/src/config/gate-violation-baselines.json +3 -3
- package/src/scripts/_cli/cmd_brand_status.ts +174 -0
- package/src/scripts/_cli/cmd_conformance.ts +14 -42
- package/src/scripts/_cli/cmd_doctor.ts +65 -48
- package/src/scripts/_cli/cmd_mcp_available.ts +266 -0
- package/src/scripts/_cli/cmd_packs_active.ts +225 -0
- package/src/scripts/_cli/cmd_settings_get.ts +324 -0
- package/src/scripts/_dispatch.bash +57 -0
- package/src/scripts/_lib/agent_settings.ts +140 -16
- package/src/scripts/_lib/auto_dispatch.corpus.test.ts +1 -1
- package/src/scripts/_lib/auto_dispatch.ts +23 -18
- package/src/scripts/_lib/capsule_trigger.ts +124 -0
- package/src/scripts/_lib/carrier_divergence.ts +135 -0
- package/src/scripts/_lib/host_capability.ts +144 -4
- package/src/scripts/_lib/judgment_ladder.ts +382 -0
- package/src/scripts/_lib/orchestration_gate.ts +8 -0
- package/src/scripts/_lib/orchestration_record.ts +37 -0
- package/src/scripts/_lib/orchestration_savings.ts +1 -1
- package/src/scripts/_lib/prompt_shape.ts +88 -0
- package/src/scripts/_lib/review_skipped_record.ts +102 -0
- package/src/scripts/_lib/rule_layer_overlap.ts +254 -0
- package/src/scripts/_lib/self_repair.ts +489 -0
- package/src/scripts/_lib/self_repair_store.ts +128 -0
- package/src/scripts/_lib/subagent_capsule.ts +182 -0
- package/src/scripts/_lib/subagent_response.ts +19 -0
- package/src/scripts/_lib/subagent_spawn.ts +13 -0
- package/src/scripts/_lib/subagent_steering.ts +9 -4
- package/src/scripts/_lib/tier_budget_routing.ts +6 -1
- package/src/scripts/_lib/transcript_entry.ts +80 -0
- package/src/scripts/_lib/worker_budget.ts +37 -2
- package/src/scripts/ai_council/clients.ts +76 -54
- package/src/scripts/ai_council/config.ts +94 -6
- package/src/scripts/ai_council/handoff.ts +113 -0
- package/src/scripts/ai_council/modes.ts +19 -4
- package/src/scripts/ai_council/orchestrator.ts +105 -0
- package/src/scripts/ai_council/quorum.ts +84 -0
- package/src/scripts/ai_council/session.ts +44 -0
- package/src/scripts/ai_council/transport_resolver.ts +127 -1
- package/src/scripts/ai_team/availability.ts +110 -0
- package/src/scripts/ai_team/config.ts +34 -14
- package/src/scripts/ai_team/team_dispatch.ts +71 -29
- package/src/scripts/bench_critic_protocol.ts +396 -0
- package/src/scripts/check_branch_freshness.ts +255 -0
- package/src/scripts/check_council_config_location.ts +125 -1
- package/src/scripts/check_detector_corpus.ts +202 -0
- package/src/scripts/check_hook_bundle_freshness.ts +4 -1
- package/src/scripts/check_standing_rule_delivery.ts +314 -0
- package/src/scripts/condense.ts +162 -2
- package/src/scripts/config/profiles.ts +13 -3
- package/src/scripts/conformance_scan.ts +272 -24
- package/src/scripts/council_availability_hook.ts +188 -0
- package/src/scripts/council_cli.ts +527 -32
- package/src/scripts/forensics_report.ts +333 -0
- package/src/scripts/hook_manifest.yaml +107 -26
- package/src/scripts/hooks/block_no_verify.ts +39 -2
- package/src/scripts/hooks/block_unauthorized_git.ts +156 -7
- package/src/scripts/hooks/concern_registry.ts +10 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +484 -0
- package/src/scripts/hooks/end_review_nudge_hook.ts +726 -0
- package/src/scripts/hooks/orchestration_record_hook.ts +342 -0
- package/src/scripts/install.ts +151 -0
- package/src/scripts/language_mirror_hook.ts +30 -37
- package/src/scripts/lint_discovery_vocabulary.ts +2 -0
- package/src/scripts/lint_never_silent.ts +276 -0
- package/src/scripts/lint_no_activation_gates.ts +271 -0
- package/src/scripts/measure_scope_dedup.ts +19 -13
- package/src/scripts/onboarding_gate_hook.ts +40 -1
- package/src/scripts/orchestration_record.ts +12 -1
- package/src/scripts/preamble_byte_census.ts +9 -20
- package/src/scripts/rebuild_hook_bundle.ts +177 -0
- package/src/scripts/release.ts +74 -2
- package/src/scripts/report_carrier_divergence.ts +364 -0
- package/src/scripts/report_skill_activation.ts +257 -0
- package/src/scripts/report_skill_obligation_violations.ts +579 -0
- package/src/scripts/routing_doctor.ts +95 -39
- package/src/scripts/score_skill_selection.ts +19 -5
- package/src/scripts/self_repair_cli.ts +470 -0
- package/src/scripts/self_repair_hook.ts +241 -0
- package/src/scripts/team_events_spike.ts +271 -0
- package/src/scripts/team_review_gate_hook.ts +31 -9
- package/src/server/schemas/settings.ts +10 -17
- package/src/shared/settingsCarveOut.ts +0 -10
- package/src/shared/settingsClasses.ts +11 -9
|
@@ -7,19 +7,24 @@
|
|
|
7
7
|
* Decides whether a task is delegable to subagents and which
|
|
8
8
|
* `subagent-orchestration` mode fits — with NO per-turn LLM meta-call.
|
|
9
9
|
*
|
|
10
|
+
* Always-on orchestration (road-to-always-on-orchestration Phase 1): there is
|
|
11
|
+
* no more `subagents.enabled` / `subagents.auto` setting gating this. The
|
|
12
|
+
* layer activates on any host reporting a subagent primitive, unless the one
|
|
13
|
+
* audited incident switch (`emergency.orchestration_halt`) is set.
|
|
14
|
+
*
|
|
10
15
|
* Contract highlights:
|
|
11
16
|
* - A task is delegable only on an enumerated structural signal.
|
|
12
17
|
* - Trivial tasks below the size floor never delegate.
|
|
13
|
-
* -
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
18
|
+
* - A matched signal always DISPATCHES (there is no more ask-before-dispatch
|
|
19
|
+
* mode); an AMBIGUOUS verdict (no enumerated signal matched) is always an
|
|
20
|
+
* `ask` VERDICT to the user, never a speculative spawn.
|
|
21
|
+
* - The activation gate (`halted`, `subagent_spawn`) runs first; any failure
|
|
22
|
+
* short-circuits to in-session.
|
|
17
23
|
*/
|
|
18
24
|
|
|
19
25
|
/** Minimum task-size estimate (exclusive) below which a task never delegates. */
|
|
20
26
|
export const SIZE_FLOOR = 1;
|
|
21
27
|
|
|
22
|
-
export type AutoMode = 'off' | 'ask' | 'on';
|
|
23
28
|
export type DispatchMode = 'do-in-steps' | 'do-in-parallel';
|
|
24
29
|
export type DispatchAction = 'dispatch' | 'ask' | 'in-session';
|
|
25
30
|
|
|
@@ -35,10 +40,14 @@ export interface TaskSignals {
|
|
|
35
40
|
size_estimate: number;
|
|
36
41
|
}
|
|
37
42
|
|
|
38
|
-
/**
|
|
43
|
+
/**
|
|
44
|
+
* Activation inputs resolved from the emergency incident switch + the
|
|
45
|
+
* host-capability manifest. No `enabled`/`auto` fields — always-on
|
|
46
|
+
* orchestration carries no per-layer setting to resolve them from.
|
|
47
|
+
*/
|
|
39
48
|
export interface ActivationInputs {
|
|
40
|
-
|
|
41
|
-
|
|
49
|
+
/** `emergency.orchestration_halt` — the one audited incident switch. */
|
|
50
|
+
halted: boolean;
|
|
42
51
|
subagent_spawn: boolean;
|
|
43
52
|
}
|
|
44
53
|
|
|
@@ -59,8 +68,7 @@ function inSession(reason: string): Classification {
|
|
|
59
68
|
*/
|
|
60
69
|
export function classifyTask(signals: TaskSignals, activation: ActivationInputs): Classification {
|
|
61
70
|
// ── Activation gate — any failure short-circuits to in-session ──
|
|
62
|
-
if (
|
|
63
|
-
if (activation.auto === 'off') return inSession('subagents.auto is off');
|
|
71
|
+
if (activation.halted) return inSession('emergency.orchestration_halt is set');
|
|
64
72
|
if (!activation.subagent_spawn) return inSession('host has no subagent_spawn primitive');
|
|
65
73
|
|
|
66
74
|
// ── Size floor — trivial tasks never delegate ──
|
|
@@ -82,16 +90,13 @@ export function classifyTask(signals: TaskSignals, activation: ActivationInputs)
|
|
|
82
90
|
}
|
|
83
91
|
|
|
84
92
|
if (mode === null) {
|
|
85
|
-
// No enumerated signal matched → ambiguous.
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
}
|
|
89
|
-
return inSession('no enumerated delegable signal matched');
|
|
93
|
+
// No enumerated signal matched → ambiguous. Always an `ask` VERDICT —
|
|
94
|
+
// there is no more setting under which this silently stays in-session.
|
|
95
|
+
return { delegable: false, action: 'ask', mode: null, reason: 'no delegable signal — borderline, ask' };
|
|
90
96
|
}
|
|
91
97
|
|
|
92
|
-
// Matched a signal
|
|
93
|
-
|
|
94
|
-
return { delegable: true, action, mode, reason };
|
|
98
|
+
// Matched a signal → always dispatch (always-on: no ask-before-dispatch mode).
|
|
99
|
+
return { delegable: true, action: 'dispatch', mode, reason };
|
|
95
100
|
}
|
|
96
101
|
|
|
97
102
|
// ── Lookup-class routing (L0 — road-to-lean-agent-init) ──
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Capsule-emission triggers — the two arms Phase 1 compares in shadow
|
|
3
|
+
* (road-to-worker-generation-recycling).
|
|
4
|
+
*
|
|
5
|
+
* Pure, no-I/O. Two candidate answers to "when should a worker hand off?":
|
|
6
|
+
*
|
|
7
|
+
* - **watermark** — cumulative tokens reach `CAPSULE_WATERMARK_FRACTION` of
|
|
8
|
+
* the stop-loss budget (`worker_budget.ts`). Cheap, already instrumented,
|
|
9
|
+
* and indifferent to whether the worker is still learning anything.
|
|
10
|
+
* - **saturation** — novelty per step, by term frequency, falls below a
|
|
11
|
+
* threshold. The hypothesis it encodes: a worker near its budget should
|
|
12
|
+
* have stopped EARLIER, not summarised harder — a worker that has stopped
|
|
13
|
+
* surfacing new terms is done discovering regardless of its token count.
|
|
14
|
+
*
|
|
15
|
+
* Deliberately term-frequency only. Embeddings would add a model dependency,
|
|
16
|
+
* a similarity threshold nobody can defend, and a second thing to explain when
|
|
17
|
+
* the arm loses — and the arm has to be able to lose cheaply for the shadow
|
|
18
|
+
* comparison to mean anything.
|
|
19
|
+
*
|
|
20
|
+
* NOTHING ACTS ON THESE. Phase 1 logs the step at which each trigger WOULD have
|
|
21
|
+
* fired and the worker runs to stop-loss exactly as before, so the measurement
|
|
22
|
+
* is not confounded by the mechanism it is measuring.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
import { CAPSULE_WATERMARK_FRACTION } from './worker_budget.js';
|
|
26
|
+
|
|
27
|
+
/** Trailing steps averaged before saturation can fire. */
|
|
28
|
+
export const SATURATION_WINDOW = 3;
|
|
29
|
+
/** Mean novelty below this over the window = saturated. */
|
|
30
|
+
export const SATURATION_THRESHOLD = 0.2;
|
|
31
|
+
|
|
32
|
+
/** One observed worker step. `terms` is whatever the step surfaced — paths, symbols, words. */
|
|
33
|
+
export interface StepObservation {
|
|
34
|
+
/** Tokens this step consumed (per-step, not cumulative). */
|
|
35
|
+
tokens: number;
|
|
36
|
+
/** Terms surfaced by this step. Normalised and deduped internally. */
|
|
37
|
+
terms: string[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface TriggerComparison {
|
|
41
|
+
/** Steps observed. */
|
|
42
|
+
steps: number;
|
|
43
|
+
/** 1-based step at which the token watermark was crossed; null = never. */
|
|
44
|
+
watermark_step: number | null;
|
|
45
|
+
/** 1-based step at which novelty saturated; null = never. */
|
|
46
|
+
saturation_step: number | null;
|
|
47
|
+
/** Per-step novelty ratio (new terms / distinct terms in that step). */
|
|
48
|
+
novelty: number[];
|
|
49
|
+
/** Cumulative tokens after each step. */
|
|
50
|
+
cumulative_tokens: number[];
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Lowercase, strip surrounding punctuation. No stemming — a stemmer is a second thing to defend. */
|
|
54
|
+
function normalise(term: string): string {
|
|
55
|
+
return term.toLowerCase().replace(/^[^a-z0-9]+|[^a-z0-9]+$/g, '');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function distinctTerms(terms: string[]): Set<string> {
|
|
59
|
+
const out = new Set<string>();
|
|
60
|
+
for (const raw of terms) {
|
|
61
|
+
if (typeof raw !== 'string') continue;
|
|
62
|
+
const t = normalise(raw);
|
|
63
|
+
if (t.length > 0) out.add(t);
|
|
64
|
+
}
|
|
65
|
+
return out;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Run both arms over an observed step sequence and report where each WOULD
|
|
70
|
+
* have fired. Both may be null (neither fired) — which is a publishable result,
|
|
71
|
+
* not a failure of the instrument.
|
|
72
|
+
*/
|
|
73
|
+
export function compareTriggers(steps: StepObservation[], budget: number): TriggerComparison {
|
|
74
|
+
const watermark = Math.floor(budget * CAPSULE_WATERMARK_FRACTION);
|
|
75
|
+
const seen = new Set<string>();
|
|
76
|
+
const novelty: number[] = [];
|
|
77
|
+
const cumulative_tokens: number[] = [];
|
|
78
|
+
let cumulative = 0;
|
|
79
|
+
let watermark_step: number | null = null;
|
|
80
|
+
let saturation_step: number | null = null;
|
|
81
|
+
|
|
82
|
+
for (const [i, step] of steps.entries()) {
|
|
83
|
+
cumulative += Number.isFinite(step.tokens) ? step.tokens : 0;
|
|
84
|
+
cumulative_tokens.push(cumulative);
|
|
85
|
+
if (watermark_step === null && budget > 0 && cumulative >= watermark) {
|
|
86
|
+
watermark_step = i + 1;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const terms = distinctTerms(step.terms ?? []);
|
|
90
|
+
let fresh = 0;
|
|
91
|
+
for (const t of terms) {
|
|
92
|
+
if (!seen.has(t)) {
|
|
93
|
+
fresh += 1;
|
|
94
|
+
seen.add(t);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
// A step that surfaced nothing is fully saturated by definition: it
|
|
98
|
+
// learned nothing. Scoring it as 1.0 ("all new") would let an idle
|
|
99
|
+
// worker look maximally productive.
|
|
100
|
+
novelty.push(terms.size === 0 ? 0 : fresh / terms.size);
|
|
101
|
+
|
|
102
|
+
if (saturation_step === null && novelty.length >= SATURATION_WINDOW) {
|
|
103
|
+
const window = novelty.slice(-SATURATION_WINDOW);
|
|
104
|
+
const mean = window.reduce((a, b) => a + b, 0) / window.length;
|
|
105
|
+
if (mean < SATURATION_THRESHOLD) saturation_step = i + 1;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return { steps: steps.length, watermark_step, saturation_step, novelty, cumulative_tokens };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Which arm fired first, for the paired comparison in `docs/CLAIMS.md`
|
|
114
|
+
* (`worker-capsule-trigger-arm`). `null` = neither fired; `'tie'` = same step.
|
|
115
|
+
*/
|
|
116
|
+
export function earlierArm(c: TriggerComparison): 'watermark' | 'saturation' | 'tie' | null {
|
|
117
|
+
const w = c.watermark_step;
|
|
118
|
+
const s = c.saturation_step;
|
|
119
|
+
if (w === null && s === null) return null;
|
|
120
|
+
if (w === null) return 'saturation';
|
|
121
|
+
if (s === null) return 'watermark';
|
|
122
|
+
if (w === s) return 'tie';
|
|
123
|
+
return w < s ? 'watermark' : 'saturation';
|
|
124
|
+
}
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* One definition of "these two carriers deliver the same rule" — shared by the
|
|
3
|
+
* C-3 reachability harness and the advisory divergence report.
|
|
4
|
+
*
|
|
5
|
+
* WHY THIS IS A LIBRARY AND NOT TWO COPIES. Two surfaces need the comparison
|
|
6
|
+
* and they ask different questions of it. `measure_scope_dedup` asks *how many*
|
|
7
|
+
* installed twins a byte-identity dedup could skip, anchored on the projection
|
|
8
|
+
* source. `report_carrier_divergence` asks *which* rules a reader must look at,
|
|
9
|
+
* anchored on the union of both carriers. The directory walks are legitimately
|
|
10
|
+
* different; the comparison is not, and a second copy of it is how "differs only
|
|
11
|
+
* in the ownership stamp" and "differs in body" drift into meaning two things.
|
|
12
|
+
*
|
|
13
|
+
* The comparison itself is the load-bearing part, so it is stated once:
|
|
14
|
+
*
|
|
15
|
+
* 1. bytes equal → `identical`
|
|
16
|
+
* 2. equal after removing the two
|
|
17
|
+
* installer ownership keys → `provenance-only`
|
|
18
|
+
* 3. otherwise → `body-diff`
|
|
19
|
+
*
|
|
20
|
+
* Class 2 exists because `install.ts` stamps `package:` and `source_path:` into
|
|
21
|
+
* every rule it installs while the in-repo projection stamps nothing, so a
|
|
22
|
+
* provenance-only difference survives even a perfect version alignment. Keeping
|
|
23
|
+
* it apart from class 3 is what turns a bare "0 identical twins" into an answer:
|
|
24
|
+
* `provenanceOnly == shared` means aligning versions would buy nothing, and
|
|
25
|
+
* `bodyDiff > 0` means the two carriers are delivering different text — the only
|
|
26
|
+
* class where a reader has to act.
|
|
27
|
+
*
|
|
28
|
+
* NOT A LICENCE TO DEDUP. Classifying a pair `provenance-only` says the bodies
|
|
29
|
+
* match; it does NOT say the rule is safe to drop from one carrier. Byte-identity
|
|
30
|
+
* remains the dedup predicate, deliberately — see
|
|
31
|
+
* `agents/settings/contexts/dedup-reachability-refusal.md`, which records that
|
|
32
|
+
* decision and its five reopen conditions. Nothing here relaxes it.
|
|
33
|
+
*/
|
|
34
|
+
import * as fs from 'node:fs';
|
|
35
|
+
import * as path from 'node:path';
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* The keys `install.ts` adds to every installed rule (`_set_key(fm_lines,
|
|
39
|
+
* 'package', …)` and `source_path` at install.ts:2733). Removing them is what
|
|
40
|
+
* separates an installer stamp from a content change.
|
|
41
|
+
*/
|
|
42
|
+
export const OWNERSHIP_KEYS = ['package', 'source_path'] as const;
|
|
43
|
+
|
|
44
|
+
export type PairVerdict = 'identical' | 'provenance-only' | 'body-diff';
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Drop the ownership stamp lines, wherever they appear in the FILE.
|
|
48
|
+
*
|
|
49
|
+
* Not scoped to the frontmatter, and the docstring says so because the scope is
|
|
50
|
+
* wider than the intent. Two reasons it stays this way: `install.ts` writes the
|
|
51
|
+
* keys without any guarantee about fence position, and `scope_dedup.test.ts`
|
|
52
|
+
* deliberately pins the fence-less shape (a stamp with no `---` around it) as the
|
|
53
|
+
* install condition. Residual risk, stated rather than hidden: a rule body that
|
|
54
|
+
* contains a line literally starting `package:` or `source_path:` — a YAML
|
|
55
|
+
* example, an indent-free config snippet — has that line removed from both sides,
|
|
56
|
+
* so a genuine difference confined to such a line would read `provenance-only`
|
|
57
|
+
* instead of `body-diff`. Measured over the shipped corpus: no rule body carries
|
|
58
|
+
* one. Re-check that before treating the class as impossible rather than absent.
|
|
59
|
+
*/
|
|
60
|
+
export function stripOwnershipKeys(text: string): string {
|
|
61
|
+
return text
|
|
62
|
+
.split('\n')
|
|
63
|
+
.filter((line) => !OWNERSHIP_KEYS.some((k) => line.startsWith(`${k}:`)))
|
|
64
|
+
.join('\n');
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Compare one rule's two copies.
|
|
69
|
+
*
|
|
70
|
+
* Takes buffers rather than paths: the callers already hold them, and a pure
|
|
71
|
+
* function is what lets both surfaces pin the same three-way split in tests
|
|
72
|
+
* without a filesystem.
|
|
73
|
+
*/
|
|
74
|
+
export function comparePair(a: Buffer, b: Buffer): PairVerdict {
|
|
75
|
+
if (a.equals(b)) {
|
|
76
|
+
return 'identical';
|
|
77
|
+
}
|
|
78
|
+
if (stripOwnershipKeys(a.toString('utf-8')) === stripOwnershipKeys(b.toString('utf-8'))) {
|
|
79
|
+
return 'provenance-only';
|
|
80
|
+
}
|
|
81
|
+
return 'body-diff';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
export interface RuleDirCensus {
|
|
85
|
+
files: number;
|
|
86
|
+
/**
|
|
87
|
+
* BYTES, not characters — `statSync().size`. The name is kept because every
|
|
88
|
+
* consumer of this figure (`preamble_byte_census`, `measure_scope_dedup`, the
|
|
89
|
+
* conformance scan) already divides it by 4 under that name, and one honest
|
|
90
|
+
* docstring beats a rename that would silently move three published baselines.
|
|
91
|
+
*
|
|
92
|
+
* The gap is measured, not assumed: over the 110-rule project tree, 406 502
|
|
93
|
+
* bytes against 402 823 characters — **0.91 %** overhead from the em-dashes,
|
|
94
|
+
* arrows and status glyphs the prose uses. So the published token estimate
|
|
95
|
+
* reads 101 626 where a character basis gives 100 706. Under 1 % and in the
|
|
96
|
+
* conservative direction for a payload figure, which is why it is documented
|
|
97
|
+
* rather than repaired; re-measure before quoting it as negligible on a corpus
|
|
98
|
+
* with more non-ASCII than this one.
|
|
99
|
+
*/
|
|
100
|
+
chars: number;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* How much rule text one carrier delivers.
|
|
105
|
+
*
|
|
106
|
+
* `statSync`, not `lstatSync`, on purpose: a project-scope entry is a symlink
|
|
107
|
+
* into `dist/`, and the bytes the HOST reads are the target's. Measuring the link
|
|
108
|
+
* would report ~50 bytes per rule and make the whole payload vanish.
|
|
109
|
+
*
|
|
110
|
+
* Moved here from `preamble_byte_census` when `conformance_scan` needed it:
|
|
111
|
+
* that module pulls `yaml` and the cold-start report, and importing a dependency
|
|
112
|
+
* chain into a bundled CLI path to reach fifteen lines of `statSync` is the wrong
|
|
113
|
+
* trade. `preamble_byte_census` re-exports this so its own callers are unchanged
|
|
114
|
+
* and there is still exactly one definition.
|
|
115
|
+
*/
|
|
116
|
+
export function censusRuleDir(dir: string): RuleDirCensus {
|
|
117
|
+
let files = 0;
|
|
118
|
+
let chars = 0;
|
|
119
|
+
let entries: string[];
|
|
120
|
+
try {
|
|
121
|
+
entries = fs.readdirSync(dir);
|
|
122
|
+
} catch {
|
|
123
|
+
return { files: 0, chars: 0 };
|
|
124
|
+
}
|
|
125
|
+
for (const name of entries) {
|
|
126
|
+
if (!name.endsWith('.md')) continue;
|
|
127
|
+
try {
|
|
128
|
+
chars += fs.statSync(path.join(dir, name)).size;
|
|
129
|
+
files += 1;
|
|
130
|
+
} catch {
|
|
131
|
+
// Unreadable entry — skip rather than fail the whole census.
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return { files, chars };
|
|
135
|
+
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Host-capability manifest normalizer.
|
|
2
|
+
* Host-capability manifest normalizer + committed capability registry.
|
|
3
3
|
*
|
|
4
|
-
* Pure, no-I/O helper that takes a partial / unknown object (
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* Pure, no-I/O helper that takes a partial / unknown object (an explicit
|
|
5
|
+
* caller-supplied override, or the agent's own host-knowledge object) and
|
|
6
|
+
* returns a fully-populated manifest.
|
|
7
7
|
*
|
|
8
8
|
* Contract — see `src/agent-src/contexts/execution/host-capability-manifest.md`:
|
|
9
9
|
*
|
|
@@ -11,7 +11,28 @@
|
|
|
11
11
|
* have no subagent primitive.
|
|
12
12
|
* - A missing or non-boolean field resolves to `false`, never `true`.
|
|
13
13
|
* - `schema_version` is always forced to `1`.
|
|
14
|
+
*
|
|
15
|
+
* `resolveHostCapabilities` (F5, road-to-orchestrator-discipline-carriers)
|
|
16
|
+
* adds a resolution order in front of the safe default so a fresh install on
|
|
17
|
+
* a KNOWN host does not ship delegation dead by default:
|
|
18
|
+
*
|
|
19
|
+
* explicit override (wins, whole-object, unchanged semantics) → committed
|
|
20
|
+
* registry row for the detected host (`HOST_CAPABILITY_REGISTRY`) →
|
|
21
|
+
* `SAFE_DEFAULT` (all false).
|
|
22
|
+
*
|
|
23
|
+
* The registry lists only OBSERVED capabilities for a host this repo has
|
|
24
|
+
* actually measured (see the roadmap's transcript evidence) — never a
|
|
25
|
+
* speculative entry for a host nobody has verified.
|
|
26
|
+
*
|
|
27
|
+
* `probeHostCapabilities` (road-to-always-on-orchestration Phase 1) is the
|
|
28
|
+
* production entry point as of this change: capability is a FACT about the
|
|
29
|
+
* host, never a settings decision, so the former `subagents.host_capabilities`
|
|
30
|
+
* settings override was deleted. `resolveHostCapabilities`'s `override`
|
|
31
|
+
* parameter stays for back-compat (tests, and any caller that genuinely has a
|
|
32
|
+
* host-knowledge object of its own) — no production caller passes a
|
|
33
|
+
* settings-derived value into it any more.
|
|
14
34
|
*/
|
|
35
|
+
import process from 'node:process';
|
|
15
36
|
|
|
16
37
|
/** Resolved host-capability manifest. All booleans, `schema_version` fixed at 1. */
|
|
17
38
|
export interface HostCapabilityManifest {
|
|
@@ -20,6 +41,27 @@ export interface HostCapabilityManifest {
|
|
|
20
41
|
parallel_spawn: boolean;
|
|
21
42
|
status_polling: boolean;
|
|
22
43
|
separate_quota_pool: boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Claude Code's experimental multi-instance Agent Teams primitive
|
|
46
|
+
* (`CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS`). Resolved ONLY by
|
|
47
|
+
* {@link probeHostCapabilities}'s live environment probe — the registry
|
|
48
|
+
* and `normalizeHostManifest` never infer it from a host id, because
|
|
49
|
+
* this repo has not observed the flag's shape on any host, only its
|
|
50
|
+
* documented existence.
|
|
51
|
+
*/
|
|
52
|
+
agent_teams: boolean;
|
|
53
|
+
/**
|
|
54
|
+
* The host can kill a running worker and spawn a fresh one that continues
|
|
55
|
+
* the SAME task mid-flight — the primitive worker-generation recycling
|
|
56
|
+
* needs (road-to-worker-generation-recycling, `blocker: host-worker-respawn`).
|
|
57
|
+
*
|
|
58
|
+
* `false` on every host today, deliberately: like `status_polling` and
|
|
59
|
+
* `separate_quota_pool`, this field is set `true` only once the capability
|
|
60
|
+
* is OBSERVED on a host, never by inference from the fact that spawning and
|
|
61
|
+
* killing both exist separately. A host without it degrades to today's
|
|
62
|
+
* stop-loss behaviour — loudly, never silently.
|
|
63
|
+
*/
|
|
64
|
+
worker_respawn: boolean;
|
|
23
65
|
}
|
|
24
66
|
|
|
25
67
|
/** Safe default — unknown host assumes no subagent primitive. */
|
|
@@ -29,6 +71,8 @@ const SAFE_DEFAULT: HostCapabilityManifest = {
|
|
|
29
71
|
parallel_spawn: false,
|
|
30
72
|
status_polling: false,
|
|
31
73
|
separate_quota_pool: false,
|
|
74
|
+
agent_teams: false,
|
|
75
|
+
worker_respawn: false,
|
|
32
76
|
};
|
|
33
77
|
|
|
34
78
|
/** Coerce one field: only a strict boolean `true` survives; everything else is `false`. */
|
|
@@ -55,5 +99,101 @@ export function normalizeHostManifest(input: unknown): HostCapabilityManifest {
|
|
|
55
99
|
parallel_spawn: asBool(src.parallel_spawn),
|
|
56
100
|
status_polling: asBool(src.status_polling),
|
|
57
101
|
separate_quota_pool: asBool(src.separate_quota_pool),
|
|
102
|
+
agent_teams: asBool(src.agent_teams),
|
|
103
|
+
worker_respawn: asBool(src.worker_respawn),
|
|
58
104
|
};
|
|
59
105
|
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Committed registry — known host identifiers → OBSERVED capability sets.
|
|
109
|
+
*
|
|
110
|
+
* Keyed by the platform identifier this repo already uses for the same host
|
|
111
|
+
* across the hook/dispatch layer (`--platform claude`, `platforms['claude']`
|
|
112
|
+
* in `claude_settings_hooks.ts`, `TOOL_IDS` in `probe_skill_registration.ts`)
|
|
113
|
+
* — NOT the separate `toolDetection.ts` binary-detection id (`'claude-code'`),
|
|
114
|
+
* which names a different namespace (installed-CLI presence, not the running
|
|
115
|
+
* host identity a hook/dispatch envelope carries).
|
|
116
|
+
*
|
|
117
|
+
* Each row is partial: an omitted field stays at the `SAFE_DEFAULT` `false`
|
|
118
|
+
* when the row is applied. Claude Code is the one host this roadmap's
|
|
119
|
+
* transcript evidence measured spawning and running subagents concurrently
|
|
120
|
+
* (`Agent` tool dispatch, parallel tool-use blocks); polling and a separate
|
|
121
|
+
* quota pool were never observed, so they stay `false` — add a field only
|
|
122
|
+
* once it is itself observed, never by inference.
|
|
123
|
+
*/
|
|
124
|
+
const HOST_CAPABILITY_REGISTRY: Readonly<Record<string, Partial<HostCapabilityManifest>>> = {
|
|
125
|
+
claude: { subagent_spawn: true, parallel_spawn: true },
|
|
126
|
+
};
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Resolve the effective host-capability manifest for `hostId`.
|
|
130
|
+
*
|
|
131
|
+
* Resolution order (highest wins), per
|
|
132
|
+
* `host-capability-manifest.md § Resolution`:
|
|
133
|
+
*
|
|
134
|
+
* 1. `override` — the `subagents.host_capabilities` settings value, when
|
|
135
|
+
* present as a non-null object. Normalized exactly as {@link normalizeHostManifest}
|
|
136
|
+
* always has: a WHOLE-OBJECT override, each field independently coerced,
|
|
137
|
+
* a field absent from the override object resolves to `false`. This is
|
|
138
|
+
* the pre-existing, unchanged behavior — a present override still wins
|
|
139
|
+
* outright over the registry row, it does not merge field-by-field with it.
|
|
140
|
+
* 2. The committed {@link HOST_CAPABILITY_REGISTRY} row for `hostId`, when no
|
|
141
|
+
* override is present and the host is known.
|
|
142
|
+
* 3. `SAFE_DEFAULT` — unknown `hostId`, or no override and no registry row.
|
|
143
|
+
*
|
|
144
|
+
* `hostId` is a caller-supplied identifier (e.g. the same string a hook
|
|
145
|
+
* envelope's `platform` field carries) — this function does no environment
|
|
146
|
+
* probing of its own; see `host-capability-manifest.md` for why resolution
|
|
147
|
+
* stays a per-session, agent/caller-supplied fact rather than a live probe.
|
|
148
|
+
*
|
|
149
|
+
* An array override does NOT count as present (F8, review): `typeof [] ===
|
|
150
|
+
* 'object'` in JS, so a bare `typeof override === 'object'` check let a
|
|
151
|
+
* stray array reach `normalizeHostManifest`, which reads named properties
|
|
152
|
+
* off it (`.subagent_spawn`, …) that an array never has — every field
|
|
153
|
+
* silently coerces to `false`, exactly like an explicit empty-object
|
|
154
|
+
* override, even though nobody supplied one. An array is not a manifest
|
|
155
|
+
* shape at all, so it is treated the same as `null`/non-object: fall
|
|
156
|
+
* through to the registry row.
|
|
157
|
+
*/
|
|
158
|
+
export function resolveHostCapabilities(
|
|
159
|
+
hostId: string | null | undefined,
|
|
160
|
+
override?: unknown,
|
|
161
|
+
): HostCapabilityManifest {
|
|
162
|
+
if (
|
|
163
|
+
override !== undefined &&
|
|
164
|
+
override !== null &&
|
|
165
|
+
typeof override === 'object' &&
|
|
166
|
+
!Array.isArray(override)
|
|
167
|
+
) {
|
|
168
|
+
return normalizeHostManifest(override);
|
|
169
|
+
}
|
|
170
|
+
const row =
|
|
171
|
+
hostId !== null && hostId !== undefined ? HOST_CAPABILITY_REGISTRY[hostId] : undefined;
|
|
172
|
+
if (row === undefined) {
|
|
173
|
+
return { ...SAFE_DEFAULT };
|
|
174
|
+
}
|
|
175
|
+
return { ...SAFE_DEFAULT, ...row, schema_version: 1 };
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Probe the effective host-capability manifest for `hostId` from OBSERVABLE
|
|
180
|
+
* FACTS ONLY (road-to-always-on-orchestration Phase 1) — the committed
|
|
181
|
+
* registry row for the host, merged with a live environment probe. This is
|
|
182
|
+
* the production entry point: capability is a fact about the host, never a
|
|
183
|
+
* settings decision, so there is no override parameter here (that stays on
|
|
184
|
+
* {@link resolveHostCapabilities} for back-compat callers only).
|
|
185
|
+
*
|
|
186
|
+
* The one probed fact today: `agent_teams` resolves `true` when
|
|
187
|
+
* `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS` is set (any non-empty value) in the
|
|
188
|
+
* process environment — the one host fact this repo can observe about Claude
|
|
189
|
+
* Code's documented-but-unshipped-by-default multi-instance Agent Teams
|
|
190
|
+
* primitive. A registry row can also carry `agent_teams: true` directly for a
|
|
191
|
+
* host where it is unconditionally available; the two sources OR together —
|
|
192
|
+
* a probe never turns a registry-granted capability back off.
|
|
193
|
+
*/
|
|
194
|
+
export function probeHostCapabilities(hostId: string | null | undefined): HostCapabilityManifest {
|
|
195
|
+
const base = resolveHostCapabilities(hostId);
|
|
196
|
+
const flag = process.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
|
|
197
|
+
const probedAgentTeams = typeof flag === 'string' && flag !== '';
|
|
198
|
+
return { ...base, agent_teams: base.agent_teams || probedAgentTeams };
|
|
199
|
+
}
|