@event4u/agent-config 9.28.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.
Files changed (147) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/AGENTS.md +6 -6
  3. package/CHANGELOG.md +66 -0
  4. package/README.md +1 -1
  5. package/agents/templates/.ai-council.yml.example +55 -24
  6. package/dist/agent-src/commands/cost/report.md +2 -2
  7. package/dist/agent-src/commands/team/adversarial.md +8 -7
  8. package/dist/agent-src/commands/team/delegate.md +13 -12
  9. package/dist/agent-src/commands/team/review.md +9 -8
  10. package/dist/agent-src/commands/team/status.md +9 -8
  11. package/dist/agent-src/commands/team.md +29 -19
  12. package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +88 -2
  13. package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +29 -21
  14. package/dist/agent-src/contexts/execution/host-capability-manifest.md +26 -15
  15. package/dist/agent-src/contexts/execution/orchestration-telemetry.md +18 -0
  16. package/dist/agent-src/contexts/execution/subagent-response-contract.md +54 -0
  17. package/dist/agent-src/contexts/execution/subagent-routing.md +13 -11
  18. package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +25 -0
  19. package/dist/agent-src/contexts/execution/subagent-steering.md +6 -3
  20. package/dist/agent-src/rules/delegation-policy.md +11 -7
  21. package/dist/agent-src/rules/settings-ask-protocol.md +8 -7
  22. package/dist/agent-src/rules/token-budget-discipline.md +1 -1
  23. package/dist/agent-src/skills/forensics-report/SKILL.md +114 -0
  24. package/dist/agent-src/skills/forensics-report/evals/triggers.json +17 -0
  25. package/dist/agent-src/skills/premortem/SKILL.md +30 -1
  26. package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +2 -1
  27. package/dist/agent-src/skills/subagent-orchestration/SKILL.md +7 -5
  28. package/dist/agent-src/skills/subagent-orchestration/schemas/subagent-status.json +61 -3
  29. package/dist/agent-src/skills/test-case-discovery/SKILL.md +1 -1
  30. package/dist/agent-src/templates/AGENTS.md +4 -4
  31. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  32. package/dist/agent-src/templates/roadmaps.md +23 -0
  33. package/dist/cli-delegate/{chunk-22GCIJ2O.js → chunk-66OVWGB5.js} +1 -1
  34. package/dist/cli-delegate/{chunk-GB2FSUEE.js → chunk-LCFJIQ4J.js} +32 -0
  35. package/dist/cli-delegate/{chunk-VSYT7OHO.js → chunk-LVRYBKTI.js} +31 -2
  36. package/dist/cli-delegate/{chunk-DVRS2KVR.js → chunk-MMXA5GY7.js} +10 -17
  37. package/dist/cli-delegate/{chunk-JXARCGVN.js → chunk-MP3JT3IU.js} +1 -1
  38. package/dist/cli-delegate/{chunk-PQSAQKHW.js → chunk-NZ5N2XQL.js} +3 -3
  39. package/dist/cli-delegate/{chunk-7OMQCH4V.js → chunk-OJM3MBJM.js} +1 -1
  40. package/dist/cli-delegate/{chunk-2BE5I2MN.js → chunk-RCIA67XN.js} +111 -90
  41. package/dist/cli-delegate/cmd_analyze_session.js +1 -1
  42. package/dist/cli-delegate/cmd_brand_status.js +1 -1
  43. package/dist/cli-delegate/cmd_conformance.js +6 -43
  44. package/dist/cli-delegate/cmd_doctor.js +9 -5
  45. package/dist/cli-delegate/cmd_explain.js +2 -2
  46. package/dist/cli-delegate/cmd_export.js +4 -4
  47. package/dist/cli-delegate/cmd_handoff.js +3 -3
  48. package/dist/cli-delegate/cmd_mcp_available.js +1 -1
  49. package/dist/cli-delegate/cmd_migrate.js +2 -2
  50. package/dist/cli-delegate/cmd_packs_active.js +2 -2
  51. package/dist/cli-delegate/cmd_prune.js +4 -4
  52. package/dist/cli-delegate/cmd_route_audit.js +2 -2
  53. package/dist/cli-delegate/cmd_route_explain.js +1 -1
  54. package/dist/cli-delegate/cmd_settings_get.js +1 -7
  55. package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
  56. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  57. package/dist/cli-delegate/cmd_sync.js +4 -4
  58. package/dist/cli-delegate/cmd_uninstall.js +4 -4
  59. package/dist/cli-delegate/cmd_update.js +1 -1
  60. package/dist/cli-delegate/cmd_upgrade.js +1 -1
  61. package/dist/cli-delegate/cmd_validate.js +4 -4
  62. package/dist/cli-delegate/cmd_versions.js +1 -1
  63. package/dist/discovery/deprecation-report.md +1 -1
  64. package/dist/discovery/discovery-manifest.json +69 -20
  65. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  66. package/dist/discovery/discovery-manifest.summary.md +3 -2
  67. package/dist/discovery/orphan-report.md +1 -1
  68. package/dist/discovery/packs.json +39 -2
  69. package/dist/discovery/trust-report.md +2 -2
  70. package/dist/discovery/workspaces.json +12 -4
  71. package/dist/hooks/dispatch.js +373 -35
  72. package/dist/install/install.mjs +42 -17
  73. package/dist/mcp/registry-manifest.json +2 -2
  74. package/dist/mcp/server.json +2 -2
  75. package/dist/mcp/server.mjs +651 -262
  76. package/dist/router.json +1 -1
  77. package/dist/scripts/_lib/agent_settings.js +54 -0
  78. package/dist/scripts/_lib/agent_settings.js.map +1 -1
  79. package/dist/scripts/condense.js +147 -2
  80. package/dist/scripts/condense.js.map +1 -1
  81. package/dist/server/schemas/settings.js +6 -7
  82. package/dist/server/schemas/settings.js.map +1 -1
  83. package/dist/shared/settingsCarveOut.js +0 -9
  84. package/dist/shared/settingsCarveOut.js.map +1 -1
  85. package/dist/shared/settingsClasses.js +11 -9
  86. package/dist/shared/settingsClasses.js.map +1 -1
  87. package/dist/ui/assets/{index-DmR0mbk4.js → index-JDAF00bC.js} +2 -2
  88. package/dist/ui/assets/{index-DmR0mbk4.js.map → index-JDAF00bC.js.map} +1 -1
  89. package/dist/ui/index.html +1 -1
  90. package/docs/contracts/settings-classes.md +39 -9
  91. package/package.json +1 -1
  92. package/src/agent-src/templates/AGENTS.md +4 -4
  93. package/src/config/agent-settings.template.yml +52 -66
  94. package/src/config/discovery/packs.yml +11 -0
  95. package/src/config/discovery/workspaces.yml +1 -1
  96. package/src/config/gate-violation-baselines.json +3 -3
  97. package/src/scripts/_cli/cmd_conformance.ts +14 -42
  98. package/src/scripts/_cli/cmd_doctor.ts +65 -48
  99. package/src/scripts/_lib/agent_settings.ts +58 -0
  100. package/src/scripts/_lib/auto_dispatch.corpus.test.ts +1 -1
  101. package/src/scripts/_lib/auto_dispatch.ts +23 -18
  102. package/src/scripts/_lib/capsule_trigger.ts +124 -0
  103. package/src/scripts/_lib/host_capability.ts +63 -6
  104. package/src/scripts/_lib/judgment_ladder.ts +382 -0
  105. package/src/scripts/_lib/orchestration_gate.ts +8 -0
  106. package/src/scripts/_lib/orchestration_record.ts +37 -0
  107. package/src/scripts/_lib/orchestration_savings.ts +1 -1
  108. package/src/scripts/_lib/self_repair.ts +160 -13
  109. package/src/scripts/_lib/self_repair_store.ts +28 -0
  110. package/src/scripts/_lib/subagent_capsule.ts +182 -0
  111. package/src/scripts/_lib/subagent_response.ts +19 -0
  112. package/src/scripts/_lib/subagent_spawn.ts +13 -0
  113. package/src/scripts/_lib/subagent_steering.ts +9 -4
  114. package/src/scripts/_lib/tier_budget_routing.ts +6 -1
  115. package/src/scripts/_lib/worker_budget.ts +37 -2
  116. package/src/scripts/ai_council/clients.ts +76 -54
  117. package/src/scripts/ai_council/config.ts +94 -6
  118. package/src/scripts/ai_council/handoff.ts +113 -0
  119. package/src/scripts/ai_council/modes.ts +19 -4
  120. package/src/scripts/ai_council/orchestrator.ts +105 -0
  121. package/src/scripts/ai_council/quorum.ts +84 -0
  122. package/src/scripts/ai_council/session.ts +44 -0
  123. package/src/scripts/ai_council/transport_resolver.ts +127 -1
  124. package/src/scripts/ai_team/availability.ts +110 -0
  125. package/src/scripts/ai_team/config.ts +34 -14
  126. package/src/scripts/ai_team/team_dispatch.ts +71 -29
  127. package/src/scripts/bench_critic_protocol.ts +396 -0
  128. package/src/scripts/check_detector_corpus.ts +202 -0
  129. package/src/scripts/check_hook_bundle_freshness.ts +4 -1
  130. package/src/scripts/condense.ts +162 -2
  131. package/src/scripts/council_cli.ts +401 -25
  132. package/src/scripts/forensics_report.ts +333 -0
  133. package/src/scripts/hooks/delegation_nudge_hook.ts +69 -35
  134. package/src/scripts/lint_discovery_vocabulary.ts +2 -0
  135. package/src/scripts/lint_never_silent.ts +276 -0
  136. package/src/scripts/lint_no_activation_gates.ts +271 -0
  137. package/src/scripts/orchestration_record.ts +8 -0
  138. package/src/scripts/rebuild_hook_bundle.ts +177 -0
  139. package/src/scripts/release.ts +74 -2
  140. package/src/scripts/routing_doctor.ts +21 -36
  141. package/src/scripts/score_skill_selection.ts +19 -5
  142. package/src/scripts/self_repair_cli.ts +287 -57
  143. package/src/scripts/team_events_spike.ts +271 -0
  144. package/src/scripts/team_review_gate_hook.ts +31 -9
  145. package/src/server/schemas/settings.ts +10 -17
  146. package/src/shared/settingsCarveOut.ts +0 -10
  147. package/src/shared/settingsClasses.ts +11 -9
@@ -11744,8 +11744,40 @@ function load_agent_settings(options = {}) {
11744
11744
  _deep_merge(merged, layer);
11745
11745
  }
11746
11746
  }
11747
+ _warn_removed_always_on_keys(merged);
11747
11748
  return merged;
11748
11749
  }
11750
+ var REMOVED_ALWAYS_ON_KEYS = [
11751
+ "subagents.enabled",
11752
+ "subagents.auto",
11753
+ "subagents.host_capabilities",
11754
+ "subagents.budget_routing",
11755
+ "ai_team.enabled"
11756
+ ];
11757
+ var _warnedRemovedAlwaysOnKeys = /* @__PURE__ */ new Set();
11758
+ function _readDottedSettingsPath(root, dotted) {
11759
+ let node = root;
11760
+ for (const part of dotted.split(".")) {
11761
+ if (typeof node !== "object" || node === null || Array.isArray(node)) {
11762
+ return void 0;
11763
+ }
11764
+ node = node[part];
11765
+ }
11766
+ return node;
11767
+ }
11768
+ function _warn_removed_always_on_keys(merged) {
11769
+ for (const key of REMOVED_ALWAYS_ON_KEYS) {
11770
+ if (_warnedRemovedAlwaysOnKeys.has(key)) {
11771
+ continue;
11772
+ }
11773
+ if (_readDottedSettingsPath(merged, key) === void 0) {
11774
+ continue;
11775
+ }
11776
+ _warnedRemovedAlwaysOnKeys.add(key);
11777
+ process.stderr.write(`${key} was removed (always-on orchestration); ignored.
11778
+ `);
11779
+ }
11780
+ }
11749
11781
  function _read_yaml(p) {
11750
11782
  if (!_is_file(p)) {
11751
11783
  return null;
@@ -14196,6 +14228,7 @@ function _build_config(raw, source_path) {
14196
14228
  const cli_call_budget = _build_cli_call_budget(
14197
14229
  _asDict(_getOr(raw, "cli_call_budget", {}))
14198
14230
  );
14231
+ const quorum = _build_quorum(_get(raw, "quorum", "majority"));
14199
14232
  const necessity_classifier = _build_necessity_classifier(
14200
14233
  _asDict(_getOr(raw, "necessity_classifier", {}))
14201
14234
  );
@@ -14221,6 +14254,7 @@ function _build_config(raw, source_path) {
14221
14254
  "debate_gates"
14222
14255
  );
14223
14256
  const restate = _build_restate(_asDict(_getOr(raw, "restate", {})), "restate");
14257
+ const critic_protocol = _build_critic_protocol(_getOr(raw, "critic_protocol", "legacy"));
14224
14258
  const decision_resolution = _build_decision_resolution(
14225
14259
  _asDict(_getOr(raw, "decision_resolution", {}))
14226
14260
  );
@@ -14242,6 +14276,7 @@ function _build_config(raw, source_path) {
14242
14276
  advisors,
14243
14277
  consensus_scoring: consensus,
14244
14278
  cli_call_budget,
14279
+ quorum,
14245
14280
  necessity_classifier,
14246
14281
  model_downgrade,
14247
14282
  debate,
@@ -14250,6 +14285,7 @@ function _build_config(raw, source_path) {
14250
14285
  chairman,
14251
14286
  debate_gates,
14252
14287
  restate,
14288
+ critic_protocol,
14253
14289
  decision_resolution,
14254
14290
  routing,
14255
14291
  low_impact,
@@ -14451,6 +14487,16 @@ function _build_restate(d, scope) {
14451
14487
  }
14452
14488
  return { enabled: Boolean(enabled4) };
14453
14489
  }
14490
+ var _VALID_CRITIC_PROTOCOLS = /* @__PURE__ */ new Set(["legacy", "load_bearing"]);
14491
+ function _build_critic_protocol(v) {
14492
+ if (v === void 0 || v === null) return "legacy";
14493
+ if (!(_isStr(v) && _VALID_CRITIC_PROTOCOLS.has(v))) {
14494
+ throw new CouncilConfigError(
14495
+ `\`critic_protocol\`=${_pyRepr3(v)} not in ${_sortedListRepr(_VALID_CRITIC_PROTOCOLS)}.`
14496
+ );
14497
+ }
14498
+ return v;
14499
+ }
14454
14500
  var _VALID_RESOLUTION_MODES = /* @__PURE__ */ new Set([
14455
14501
  "agent",
14456
14502
  "council",
@@ -14726,7 +14772,7 @@ function _build_defaults(d) {
14726
14772
  if (!_isDict(d)) {
14727
14773
  throw new CouncilConfigError("`defaults` must be a mapping.");
14728
14774
  }
14729
- const mode = _get(d, "mode", "api");
14775
+ const mode = _get(d, "mode", "auto");
14730
14776
  if (!(_isStr(mode) && _VALID_MODES.has(mode))) {
14731
14777
  throw new CouncilConfigError(
14732
14778
  `defaults.mode=${_pyRepr3(mode)} not in ${_sortedListRepr(_VALID_MODES)}.`
@@ -14888,7 +14934,7 @@ function _build_cost_budget(d) {
14888
14934
  }
14889
14935
  return cb;
14890
14936
  }
14891
- function _build_member(name, cfg, default_mode = "api") {
14937
+ function _build_member(name, cfg, default_mode = "auto") {
14892
14938
  if (!_VALID_PROVIDERS.has(name)) {
14893
14939
  throw new CouncilConfigError(
14894
14940
  `members.${name}: unknown provider; valid: ${_sortedListRepr(_VALID_PROVIDERS)}.`
@@ -15005,6 +15051,7 @@ function _build_member(name, cfg, default_mode = "api") {
15005
15051
  prompt_cache_ttl
15006
15052
  };
15007
15053
  }
15054
+ var _DEFAULT_CLI_CALLS_PER_DAY = 50;
15008
15055
  function _build_cli_call_budget(d) {
15009
15056
  if (!_isDict(d)) {
15010
15057
  throw new CouncilConfigError("`cli_call_budget` must be a mapping.");
@@ -15016,6 +15063,9 @@ function _build_cli_call_budget(d) {
15016
15063
  );
15017
15064
  }
15018
15065
  const caps = /* @__PURE__ */ new Map();
15066
+ for (const provider of _VALID_PROVIDERS) {
15067
+ caps.set(provider, _DEFAULT_CLI_CALLS_PER_DAY);
15068
+ }
15019
15069
  for (const [provider, value] of Object.entries(raw_caps)) {
15020
15070
  if (!_VALID_PROVIDERS.has(provider)) {
15021
15071
  throw new CouncilConfigError(
@@ -15043,6 +15093,17 @@ function _build_cli_call_budget(d) {
15043
15093
  }
15044
15094
  return { max_calls_per_day: caps, warn_at };
15045
15095
  }
15096
+ function _build_quorum(raw) {
15097
+ if (raw === "majority") {
15098
+ return "majority";
15099
+ }
15100
+ if (_isInt(raw) && !_isBool(raw) && raw >= 1) {
15101
+ return raw;
15102
+ }
15103
+ throw new CouncilConfigError(
15104
+ `\`quorum\`=${_pyRepr3(raw)} must be 'majority' or an integer >= 1 (got ${_pyTypeName(raw)}).`
15105
+ );
15106
+ }
15046
15107
  function _build_advisor(name, cfg) {
15047
15108
  if (!_isDict(cfg)) {
15048
15109
  throw new CouncilConfigError(`advisors.${name}: must be a mapping.`);
@@ -17998,7 +18059,6 @@ var AI_TEAM_REVIEW_GATE_DEFAULTS = Object.freeze({
17998
18059
  max_consecutive_blocks: 3
17999
18060
  });
18000
18061
  var AI_TEAM_DEFAULTS = Object.freeze({
18001
- enabled: false,
18002
18062
  model: "auto",
18003
18063
  allow_delegate: false,
18004
18064
  max_calls_per_day: 50,
@@ -18006,13 +18066,13 @@ var AI_TEAM_DEFAULTS = Object.freeze({
18006
18066
  review_gate: AI_TEAM_REVIEW_GATE_DEFAULTS
18007
18067
  });
18008
18068
  var _KNOWN_KEYS = /* @__PURE__ */ new Set([
18009
- "enabled",
18010
18069
  "model",
18011
18070
  "allow_delegate",
18012
18071
  "max_calls_per_day",
18013
18072
  "suppress_setup_hint",
18014
18073
  "review_gate"
18015
18074
  ]);
18075
+ var _DELETED_KEYS = /* @__PURE__ */ new Set(["enabled"]);
18016
18076
  var _KNOWN_REVIEW_GATE_KEYS = /* @__PURE__ */ new Set([
18017
18077
  "managed",
18018
18078
  "max_consecutive_blocks"
@@ -18079,13 +18139,15 @@ function build_ai_team_config(raw) {
18079
18139
  throw new TeamConfigError("`ai_team` must be a mapping.");
18080
18140
  }
18081
18141
  for (const key of Object.keys(raw)) {
18142
+ if (_DELETED_KEYS.has(key)) {
18143
+ continue;
18144
+ }
18082
18145
  if (!_KNOWN_KEYS.has(key)) {
18083
18146
  throw new TeamConfigError(
18084
18147
  `ai_team.${key}: unknown key; valid: [${[..._KNOWN_KEYS].map((k) => `'${k}'`).join(", ")}].`
18085
18148
  );
18086
18149
  }
18087
18150
  }
18088
- const enabled4 = _requireBool(raw, "enabled", AI_TEAM_DEFAULTS.enabled);
18089
18151
  const allow_delegate = _requireBool(raw, "allow_delegate", AI_TEAM_DEFAULTS.allow_delegate);
18090
18152
  const suppress_setup_hint = _requireBool(
18091
18153
  raw,
@@ -18113,7 +18175,7 @@ function build_ai_team_config(raw) {
18113
18175
  max_calls_per_day = value;
18114
18176
  }
18115
18177
  const review_gate = _build_review_gate("review_gate" in raw ? raw["review_gate"] : void 0);
18116
- return { enabled: enabled4, model, allow_delegate, max_calls_per_day, suppress_setup_hint, review_gate };
18178
+ return { model, allow_delegate, max_calls_per_day, suppress_setup_hint, review_gate };
18117
18179
  }
18118
18180
  function load_ai_team_config(opts = {}) {
18119
18181
  const settings = opts.settings ?? load_agent_settings({ cwd: opts.cwd ?? process.cwd() });
@@ -18449,7 +18511,7 @@ function main20() {
18449
18511
  } catch {
18450
18512
  config = AI_TEAM_DEFAULTS;
18451
18513
  }
18452
- if (!config.enabled || !config.review_gate.managed) {
18514
+ if (!config.review_gate.managed) {
18453
18515
  return 0;
18454
18516
  }
18455
18517
  let notice = null;
@@ -18919,7 +18981,10 @@ function sanitizeEvidence(raw) {
18919
18981
  return flat.length <= MAX_EVIDENCE ? flat : `${flat.slice(0, MAX_EVIDENCE - 1)}\u2026`;
18920
18982
  }
18921
18983
  var COMPLAINT_PATTERNS = [
18922
- /\bdu hast\b[^.!?]{0,60}\b(nicht|falsch|vergessen|ignoriert|übersehen)\b/i,
18984
+ // No \b before the alternation: JS \b is ASCII-only, so a leading umlaut
18985
+ // ("übersehen") never sits on a word boundary — the corpus gate's
18986
+ // near-miss class caught that as a dead alternation branch.
18987
+ /\bdu hast\b[^.!?]{0,60}(nicht|falsch|vergessen|ignoriert|übersehen)\b/i,
18923
18988
  /\bdu (ignorierst|übersiehst)\b/i,
18924
18989
  /\b(das|es) (war|ist) (aber )?falsch\b/i,
18925
18990
  /\bhat nicht (richtig|korrekt) (gearbeitet|funktioniert)\b/i,
@@ -18928,10 +18993,43 @@ var COMPLAINT_PATTERNS = [
18928
18993
  /\bthat'?s (wrong|not right|incorrect)\b/i,
18929
18994
  /\byou (worked|did (it|that)) (wrong|incorrectly)\b/i
18930
18995
  ];
18996
+ var COMPLAINT_EXONERATIONS = [
18997
+ /\bdu hast nicht zufällig\b/i,
18998
+ /\bhast du nicht zufällig\b/i,
18999
+ /\byou didn'?t (need|have) to\b/i,
19000
+ /\bit'?s fine\b/i,
19001
+ /\b(kein problem|passt schon|alles gut)\b/i,
19002
+ // Explicit absolution INSIDE the complaint span, which is why it has to be
19003
+ // its own entry rather than relying on a neighbouring pleasantry: in
19004
+ // "das ist fine, du hast nichts falsch gemacht" the matched span is
19005
+ // `du hast nichts falsch`, and the `it's fine` above sits outside the
19006
+ // ± EXONERATION_MARGIN window — correctly, since that margin exists to stop
19007
+ // a pleasantry in one clause muting a complaint in another. `nichts falsch`
19008
+ // is not a neighbouring pleasantry: it is the negation of the complaint
19009
+ // itself. Caught by the detector-corpus gate, which flagged this exact
19010
+ // string as a must-not-fire fixture that fired (PR #1231).
19011
+ /\bnichts falsch\b/i,
19012
+ /\bkein fehler\b/i
19013
+ ];
19014
+ var EXONERATION_MARGIN = 12;
19015
+ function spanExonerated(prompt, start, end) {
19016
+ for (const ex of COMPLAINT_EXONERATIONS) {
19017
+ const g = new RegExp(ex.source, ex.flags.includes("g") ? ex.flags : `${ex.flags}g`);
19018
+ let m;
19019
+ while ((m = g.exec(prompt)) !== null) {
19020
+ const s = m.index;
19021
+ const e = m.index + m[0].length;
19022
+ if (e >= start - EXONERATION_MARGIN && s <= end + EXONERATION_MARGIN) {
19023
+ return true;
19024
+ }
19025
+ }
19026
+ }
19027
+ return false;
19028
+ }
18931
19029
  function detectUserReport(prompt) {
18932
19030
  for (const re of COMPLAINT_PATTERNS) {
18933
19031
  const m = re.exec(prompt);
18934
- if (m !== null) {
19032
+ if (m !== null && !spanExonerated(prompt, m.index, m.index + m[0].length)) {
18935
19033
  return {
18936
19034
  defect_class: "user-reported",
18937
19035
  source: "user-reported",
@@ -18991,10 +19089,22 @@ function detectLanguageMirror(turn) {
18991
19089
  suggested_surface: `Reply opened in the language the turn was not pinned to (pinned: ${pinned}) \u2014 report which surface carried the pin and why it did not bind.`
18992
19090
  };
18993
19091
  }
19092
+ var DETECTOR_REGISTRY = [
19093
+ {
19094
+ defect_class: "user-reported",
19095
+ source: "user-reported",
19096
+ detect: (turn) => detectUserReport(turn.prompt)
19097
+ },
19098
+ { defect_class: "council-availability-claim", source: "self-detected", detect: detectCouncilClaim },
19099
+ { defect_class: "language-mirror", source: "self-detected", detect: detectLanguageMirror }
19100
+ ];
18994
19101
  function runDetectors(turn) {
18995
19102
  const out = [];
18996
- for (const d of [detectCouncilClaim, detectLanguageMirror]) {
18997
- const f = d(turn);
19103
+ for (const d of DETECTOR_REGISTRY) {
19104
+ if (d.source !== "self-detected") {
19105
+ continue;
19106
+ }
19107
+ const f = d.detect(turn);
18998
19108
  if (f !== null) {
18999
19109
  out.push(f);
19000
19110
  }
@@ -22824,6 +22934,7 @@ var BANDS = ["low", "medium", "high"];
22824
22934
  var PHASES = ["refine", "memory", "analyze", "plan", "implement", "test", "verify", "report"];
22825
22935
  var LOOKUP_CLASSES = ["definition", "references", "string-existence", "report-run"];
22826
22936
  var ROUTES_TAKEN = ["primitive", "subagent"];
22937
+ var TRIGGER_ARMS = ["watermark", "saturation", "tie"];
22827
22938
  var PAYLOAD_HASH_RE = /^[a-f0-9]{8,64}$/i;
22828
22939
  var ORIGIN_RE = /^[a-z0-9][a-z0-9-]{0,63}$/;
22829
22940
  function envelopeOutcome(d) {
@@ -22906,6 +23017,19 @@ function buildOrchestrationLine(input) {
22906
23017
  if (input.rules_carried != null && input.rules_used != null && input.rules_used > input.rules_carried) {
22907
23018
  errors.push("rules_used cannot exceed rules_carried (a worker cannot apply a rule it was not given)");
22908
23019
  }
23020
+ if (input.capsule_emitted !== void 0 && typeof input.capsule_emitted !== "boolean") {
23021
+ errors.push("capsule_emitted must be a boolean or omitted");
23022
+ }
23023
+ for (const [key, v] of [
23024
+ ["capsule_entries", input.capsule_entries],
23025
+ ["watermark_step", input.watermark_step],
23026
+ ["saturation_step", input.saturation_step]
23027
+ ]) {
23028
+ if (v != null && (!isInt(v) || v < 0)) errors.push(`${key} must be a non-negative integer count`);
23029
+ }
23030
+ if (input.trigger_arm_earlier != null && !TRIGGER_ARMS.includes(input.trigger_arm_earlier)) {
23031
+ errors.push(`trigger_arm_earlier must be one of ${TRIGGER_ARMS.join(" | ")} (or omitted / null when neither arm fired)`);
23032
+ }
22909
23033
  if (!input.ts) errors.push("ts (ISO-8601 UTC) is required");
22910
23034
  if (!input.id) errors.push("id (ULID or content hash) is required");
22911
23035
  if (errors.length) return { line: null, errors };
@@ -22937,7 +23061,13 @@ function buildOrchestrationLine(input) {
22937
23061
  cache_hit: input.cache_hit ?? null,
22938
23062
  origin: input.origin ?? null,
22939
23063
  rules_carried: input.rules_carried ?? null,
22940
- rules_used: input.rules_used ?? null
23064
+ rules_used: input.rules_used ?? null,
23065
+ // capsule shadow-measurement — readers ignore unknowns per audit-log-v1
23066
+ capsule_emitted: input.capsule_emitted ?? null,
23067
+ capsule_entries: input.capsule_entries ?? null,
23068
+ watermark_step: input.watermark_step ?? null,
23069
+ saturation_step: input.saturation_step ?? null,
23070
+ trigger_arm_earlier: input.trigger_arm_earlier ?? null
22941
23071
  };
22942
23072
  const line = {
22943
23073
  schema_version: 1,
@@ -23046,6 +23176,11 @@ function main37(argv = process.argv.slice(2)) {
23046
23176
  origin: str2(flags, "origin"),
23047
23177
  rules_carried: int(flags, "rules-carried"),
23048
23178
  rules_used: int(flags, "rules-used"),
23179
+ capsule_emitted: bool(flags, "capsule-emitted"),
23180
+ capsule_entries: int(flags, "capsule-entries"),
23181
+ watermark_step: int(flags, "watermark-step"),
23182
+ saturation_step: int(flags, "saturation-step"),
23183
+ trigger_arm_earlier: str2(flags, "trigger-arm-earlier"),
23049
23184
  phase: str2(flags, "phase"),
23050
23185
  outcome: str2(flags, "outcome"),
23051
23186
  confidence_band: str2(flags, "confidence-band"),
@@ -23228,12 +23363,15 @@ import path55 from "node:path";
23228
23363
  import { fileURLToPath as fileURLToPath39, pathToFileURL as pathToFileURL34 } from "node:url";
23229
23364
 
23230
23365
  // src/scripts/_lib/host_capability.ts
23366
+ import process10 from "node:process";
23231
23367
  var SAFE_DEFAULT = {
23232
23368
  schema_version: 1,
23233
23369
  subagent_spawn: false,
23234
23370
  parallel_spawn: false,
23235
23371
  status_polling: false,
23236
- separate_quota_pool: false
23372
+ separate_quota_pool: false,
23373
+ agent_teams: false,
23374
+ worker_respawn: false
23237
23375
  };
23238
23376
  function asBool(value) {
23239
23377
  return value === true;
@@ -23248,7 +23386,9 @@ function normalizeHostManifest(input) {
23248
23386
  subagent_spawn: asBool(src.subagent_spawn),
23249
23387
  parallel_spawn: asBool(src.parallel_spawn),
23250
23388
  status_polling: asBool(src.status_polling),
23251
- separate_quota_pool: asBool(src.separate_quota_pool)
23389
+ separate_quota_pool: asBool(src.separate_quota_pool),
23390
+ agent_teams: asBool(src.agent_teams),
23391
+ worker_respawn: asBool(src.worker_respawn)
23252
23392
  };
23253
23393
  }
23254
23394
  var HOST_CAPABILITY_REGISTRY = {
@@ -23264,6 +23404,12 @@ function resolveHostCapabilities(hostId, override) {
23264
23404
  }
23265
23405
  return { ...SAFE_DEFAULT, ...row, schema_version: 1 };
23266
23406
  }
23407
+ function probeHostCapabilities(hostId) {
23408
+ const base = resolveHostCapabilities(hostId);
23409
+ const flag = process10.env.CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS;
23410
+ const probedAgentTeams = typeof flag === "string" && flag !== "";
23411
+ return { ...base, agent_teams: base.agent_teams || probedAgentTeams };
23412
+ }
23267
23413
 
23268
23414
  // src/scripts/_lib/auto_dispatch.ts
23269
23415
  var SIZE_FLOOR = 1;
@@ -23271,8 +23417,7 @@ function inSession(reason) {
23271
23417
  return { delegable: false, action: "in-session", mode: null, reason };
23272
23418
  }
23273
23419
  function classifyTask(signals, activation) {
23274
- if (!activation.enabled) return inSession("subagents.enabled is false");
23275
- if (activation.auto === "off") return inSession("subagents.auto is off");
23420
+ if (activation.halted) return inSession("emergency.orchestration_halt is set");
23276
23421
  if (!activation.subagent_spawn) return inSession("host has no subagent_spawn primitive");
23277
23422
  if (!(signals.size_estimate > SIZE_FLOOR)) {
23278
23423
  return inSession(`task below size floor (${signals.size_estimate} <= ${SIZE_FLOOR})`);
@@ -23288,13 +23433,197 @@ function classifyTask(signals, activation) {
23288
23433
  reason = signals.parallelizable ? `declared parallelizable: ${signals.parallelizable}` : `independent slices (${slices})`;
23289
23434
  }
23290
23435
  if (mode === null) {
23291
- if (activation.auto === "ask") {
23292
- return { delegable: false, action: "ask", mode: null, reason: "no delegable signal \u2014 borderline, ask" };
23436
+ return { delegable: false, action: "ask", mode: null, reason: "no delegable signal \u2014 borderline, ask" };
23437
+ }
23438
+ return { delegable: true, action: "dispatch", mode, reason };
23439
+ }
23440
+ var LOOKUP_PATTERNS = [
23441
+ {
23442
+ cls: "definition",
23443
+ primitive: "code-graph-query",
23444
+ re: /\b(where\s+is\s+\S+\s+defined|definition\s+(location|of|site)|find\s+the\s+definition|locate\s+the\s+(class|enum|function|type|interface|symbol)|confirm\w*\s+\S+\s+definition)\b/i
23445
+ },
23446
+ {
23447
+ cls: "references",
23448
+ primitive: "code-graph-query",
23449
+ re: /\b(who\s+(calls|imports|uses|references)|call\s*sites?|import\s+(call\s*)?sites?|reverse\s+references|where\s+is\s+\S+\s+(used|imported|referenced|called)|all\s+(callers|usages|references)\s+of)\b/i
23450
+ },
23451
+ {
23452
+ cls: "string-existence",
23453
+ primitive: "fts-or-capped-grep",
23454
+ re: /\b(does\s+(the\s+)?string\s+.*\bexist|is\s+(the\s+)?string\s+.*\b(present|used)\b|prob\w+\s+(candidate\s+)?strings?|search\s+for\s+the\s+(exact\s+)?string|(grep|check)\s+(for\s+)?(the\s+)?(literal|string)\b|does\s+\S+\s+appear\s+anywhere)/i
23455
+ },
23456
+ {
23457
+ cls: "report-run",
23458
+ primitive: "script-run-rtk",
23459
+ re: /\b(run(ning)?\s+(the\s+)?\S*(report|check|lint\w*|coverage)\b|run(ning)?\s+(check|lint|report)_\w+|regenerate\s+the\s+\S+\s+report)/i
23460
+ }
23461
+ ];
23462
+ function classifyLookup(taskText) {
23463
+ for (const p of LOOKUP_PATTERNS) {
23464
+ if (p.re.test(taskText)) {
23465
+ return {
23466
+ lookup_class: p.cls,
23467
+ route: "primitive",
23468
+ primitive: p.primitive,
23469
+ reason: `lookup-class ${p.cls} \u2014 deterministic primitive, no spawn`
23470
+ };
23293
23471
  }
23294
- return inSession("no enumerated delegable signal matched");
23295
23472
  }
23296
- const action = activation.auto === "on" ? "dispatch" : "ask";
23297
- return { delegable: true, action, mode, reason };
23473
+ return {
23474
+ lookup_class: null,
23475
+ route: "escalate",
23476
+ primitive: null,
23477
+ reason: "not lookup-shaped \u2014 regular classification path (never down-guessed)"
23478
+ };
23479
+ }
23480
+
23481
+ // src/scripts/_lib/judgment_ladder.ts
23482
+ var RENAME_RE = /\brename\s+\S+\s+to\s+\S+\b/i;
23483
+ var CODEMOD_RE = /\bcodemod\b/i;
23484
+ var FORMATTER_RUN_RE = /\bauto-?format(?:ting)?\b|\breformat(?:ting)?\b|\brun(?:ning)?\s+prettier\b|\brun(?:ning)?\s+eslint\s*--fix\b|\bapply\s+eslint\s*--fix\b/i;
23485
+ var SEARCH_REPLACE_RE = /\bsearch-and-replace\b|\bfind-and-replace\b|\bbulk\s+rename\b/i;
23486
+ function detectMechanicalTransform(text) {
23487
+ const rename = RENAME_RE.exec(text);
23488
+ if (rename) {
23489
+ return { matched: true, reason: `rename signal ("${rename[0]}") \u2014 deterministic script, no spawn` };
23490
+ }
23491
+ const codemod = CODEMOD_RE.exec(text);
23492
+ if (codemod) {
23493
+ return { matched: true, reason: `codemod signal ("${codemod[0]}") \u2014 deterministic script, no spawn` };
23494
+ }
23495
+ const formatter = FORMATTER_RUN_RE.exec(text);
23496
+ if (formatter) {
23497
+ return { matched: true, reason: `formatter-run signal ("${formatter[0]}") \u2014 deterministic script, no spawn` };
23498
+ }
23499
+ const searchReplace = SEARCH_REPLACE_RE.exec(text);
23500
+ if (searchReplace) {
23501
+ return {
23502
+ matched: true,
23503
+ reason: `search-and-replace/bulk-rename signal ("${searchReplace[0]}") \u2014 deterministic script, no spawn`
23504
+ };
23505
+ }
23506
+ return { matched: false, reason: "no mechanical-transform signal" };
23507
+ }
23508
+ var SINGLE_SLICE_READHEAVY_RE = /\b(?:read|review|summarize|summarise|inventory|audit|investigate|research|inspect|dig into|look into)\b[^.!?\n]{0,80}\b(?:this file|the file|this codebase|the codebase|this transcript|the transcript|this log|the log|this document|the document|this diff|the diff)\b/i;
23509
+ function detectBoundedReadHeavySlice(text) {
23510
+ const m = SINGLE_SLICE_READHEAVY_RE.exec(text);
23511
+ return m ? {
23512
+ matched: true,
23513
+ reason: `single bounded read-heavy slice ("${m[0]}") \u2014 one lite-tier subagent instead of session-context burn`
23514
+ } : { matched: false, reason: "no single-slice read-heavy signal" };
23515
+ }
23516
+ var CROSS_LAYER_RE = /\bcross[- ]layer\b|\bcross[- ]cutting\b|\bback[- ]?end\s+and\s+front[- ]?end\b|\bfront[- ]?end\s+and\s+back[- ]?end\b/i;
23517
+ var REVIEW_WITH_CHALLENGE_RE = /\breview[- ]with[- ]challenge\b|\bchallenge[- ]and[- ]implement\b|\bimplementer\s+and\s+(?:a\s+)?(?:judge|reviewer)\s+(?:working\s+)?together\b|\bsecond\s+opinion\s+while\s+(?:building|implementing)\b/i;
23518
+ var SHARED_TASK_LIST_RE = /\bshared\s+(?:task\s+list|todo\s+list|backlog)\b|\bcoordinat(?:e|ed|ing)\s+(?:with|between|across)\s+(?:multiple\s+|several\s+)?(?:agents|workers|subagents|teammates)\b/i;
23519
+ function detectCommunicationNeed(text) {
23520
+ const crossLayer = CROSS_LAYER_RE.exec(text);
23521
+ if (crossLayer) {
23522
+ return { matched: true, reason: `cross-layer communication signal ("${crossLayer[0]}")` };
23523
+ }
23524
+ const reviewChallenge = REVIEW_WITH_CHALLENGE_RE.exec(text);
23525
+ if (reviewChallenge) {
23526
+ return { matched: true, reason: `review-with-challenge signal ("${reviewChallenge[0]}")` };
23527
+ }
23528
+ const sharedList = SHARED_TASK_LIST_RE.exec(text);
23529
+ if (sharedList) {
23530
+ return { matched: true, reason: `shared-task-list signal ("${sharedList[0]}")` };
23531
+ }
23532
+ return { matched: false, reason: "no communication-need signal" };
23533
+ }
23534
+ var DESIGN_DECISION_RE = /\bdesign\s+decision\b|\barchitectur(?:e|al)\s+decision\b|\b(?:record(?:ing)?|decide|deciding|decision|accept(?:ed|ing)?|approv(?:e|ed|ing|al)|challeng(?:e|ed|ing)|choos(?:e|ing)|reject(?:ed|ing)?)\b[^.!?\n]{0,40}\bADR\b|\bADR\b[^.!?\n]{0,40}\b(?:record(?:ing)?|decide|deciding|decision|accept(?:ed|ing)?|approv(?:e|ed|ing|al)|challeng(?:e|ed|ing)|choos(?:e|ing)|reject(?:ed|ing)?)\b/i;
23535
+ var SECURITY_DOWNGRADE_RE = /\bsecurity\s+downgrade\b|\bdowngrade\s+(?:the\s+)?security\b|\bsecurity\s+exception\b/i;
23536
+ var RELEASE_GATE_ESCALATION_RE = /\bgate\s+escalation\b|\bescalat(?:e|ion)\s+to\s+(?:the\s+)?council\b|\b(?:escalat(?:e|ing|ion)|contest(?:ed|ing)?|dispute|disputing|override|overriding|waiv(?:e|ing)|bypass(?:ed|ing)?|challeng(?:e|ed|ing))\b[^.!?\n]{0,40}\brelease[- ]gate\b|\brelease[- ]gate\b[^.!?\n]{0,40}\b(?:escalat(?:e|ing|ion)|contest(?:ed|ing)?|dispute|disputing|override|overriding|waiv(?:e|ing)|bypass(?:ed|ing)?|challeng(?:e|ed|ing))\b/i;
23537
+ function detectContestedJudgment(text) {
23538
+ const design = DESIGN_DECISION_RE.exec(text);
23539
+ if (design) {
23540
+ return { matched: true, reason: `design-decision signal ("${design[0]}")` };
23541
+ }
23542
+ const security = SECURITY_DOWNGRADE_RE.exec(text);
23543
+ if (security) {
23544
+ return { matched: true, reason: `security-downgrade signal ("${security[0]}")` };
23545
+ }
23546
+ const releaseGate = RELEASE_GATE_ESCALATION_RE.exec(text);
23547
+ if (releaseGate) {
23548
+ return { matched: true, reason: `release-gate-escalation signal ("${releaseGate[0]}")` };
23549
+ }
23550
+ return { matched: false, reason: "no contested-judgment signal" };
23551
+ }
23552
+ function classifyLadder(inp) {
23553
+ if (inp.interactiveApprovalRequired === true) {
23554
+ return {
23555
+ rung: null,
23556
+ verdict: "in-session",
23557
+ reason: "interactive-approval-required \u2014 caller-stated, needs a human decision before anything may run"
23558
+ };
23559
+ }
23560
+ const lookup = classifyLookup(inp.taskText);
23561
+ if (lookup.route === "primitive") {
23562
+ return { rung: 0, verdict: "script", reason: `lookup-class ${lookup.lookup_class} \u2014 ${lookup.reason}` };
23563
+ }
23564
+ const mechanical = detectMechanicalTransform(inp.taskText);
23565
+ if (mechanical.matched) {
23566
+ return { rung: 0, verdict: "script", reason: mechanical.reason };
23567
+ }
23568
+ if (inp.insideSubagentSession === true) {
23569
+ return {
23570
+ rung: null,
23571
+ verdict: "in-session",
23572
+ reason: "recursive-dispatch guard: this classification runs inside a subagent/teammate session \u2014 rungs 1-4 resolve in-session"
23573
+ };
23574
+ }
23575
+ if (inp.activation.halted) {
23576
+ return { rung: null, verdict: "in-session", reason: "emergency.orchestration_halt is set" };
23577
+ }
23578
+ if (!inp.activation.subagent_spawn) {
23579
+ return { rung: null, verdict: "in-session", reason: "host has no subagent_spawn primitive" };
23580
+ }
23581
+ const contested = detectContestedJudgment(inp.taskText);
23582
+ if (contested.matched) {
23583
+ return { rung: 4, verdict: "council", reason: contested.reason };
23584
+ }
23585
+ const communication = detectCommunicationNeed(inp.taskText);
23586
+ if (communication.matched) {
23587
+ if (inp.agentTeams) {
23588
+ return { rung: 3, verdict: "team", reason: communication.reason };
23589
+ }
23590
+ const fallback = classifyTask(inp.signals, inp.activation);
23591
+ if (fallback.action === "dispatch" && fallback.mode !== null) {
23592
+ return {
23593
+ rung: 2,
23594
+ verdict: "subagent",
23595
+ mode: fallback.mode,
23596
+ degraded_from: 3,
23597
+ reason: `${communication.reason} \u2014 no agent_teams host capability, degraded to rung 2 (${fallback.reason})`
23598
+ };
23599
+ }
23600
+ return {
23601
+ rung: null,
23602
+ verdict: fallback.action === "ask" ? "ask" : "in-session",
23603
+ degraded_from: 3,
23604
+ reason: `${communication.reason} \u2014 no agent_teams host capability, degraded below rung 2 (${fallback.reason})`
23605
+ };
23606
+ }
23607
+ const classification = classifyTask(inp.signals, inp.activation);
23608
+ if (classification.action === "dispatch" && classification.mode !== null) {
23609
+ return { rung: 2, verdict: "subagent", mode: classification.mode, reason: classification.reason };
23610
+ }
23611
+ if (inp.signals.size_estimate <= SIZE_FLOOR) {
23612
+ return {
23613
+ rung: null,
23614
+ verdict: classification.action === "ask" ? "ask" : "in-session",
23615
+ reason: classification.reason
23616
+ };
23617
+ }
23618
+ const single = detectBoundedReadHeavySlice(inp.taskText);
23619
+ if (single.matched) {
23620
+ return { rung: 1, verdict: "subagent", mode: null, reason: single.reason };
23621
+ }
23622
+ return {
23623
+ rung: null,
23624
+ verdict: classification.action === "ask" ? "ask" : "in-session",
23625
+ reason: classification.reason
23626
+ };
23298
23627
  }
23299
23628
 
23300
23629
  // src/scripts/_lib/subagent_routing.ts
@@ -23403,16 +23732,16 @@ function extractTaskSignals(text) {
23403
23732
  }
23404
23733
  function resolveActivation(workspace_root, hostId) {
23405
23734
  const settings = load_agent_settings({ cwd: workspace_root });
23735
+ const emergency = settings["emergency"] ?? {};
23736
+ const halted = emergency["orchestration_halt"] === true;
23737
+ const host_manifest = probeHostCapabilities(hostId);
23406
23738
  const sub = settings["subagents"] ?? {};
23407
- const enabled4 = sub["enabled"] !== false;
23408
- const rawAuto = sub["auto"];
23409
- const auto = rawAuto === "on" || rawAuto === "off" ? rawAuto : "ask";
23410
- const host_manifest = resolveHostCapabilities(hostId, sub["host_capabilities"]);
23411
23739
  const downshift = sub["downshift"] !== false;
23412
23740
  return {
23413
- activation: { enabled: enabled4, auto, subagent_spawn: host_manifest.subagent_spawn },
23741
+ activation: { halted, subagent_spawn: host_manifest.subagent_spawn },
23414
23742
  downshift,
23415
- separate_quota_pool: host_manifest.separate_quota_pool
23743
+ separate_quota_pool: host_manifest.separate_quota_pool,
23744
+ agentTeams: host_manifest.agent_teams
23416
23745
  };
23417
23746
  }
23418
23747
  function recommendSliceTier(downshift, separate_quota_pool) {
@@ -23426,23 +23755,32 @@ function recommendSliceTier(downshift, separate_quota_pool) {
23426
23755
  });
23427
23756
  return decision.tier;
23428
23757
  }
23429
- function buildNudgeLine(classification, sliceCount, tier) {
23758
+ function buildNudgeLine(rung, classification, sliceCount, tier) {
23430
23759
  const unit = sliceCount === 1 ? "slice" : "slices";
23431
- return `<delegation-nudge>classifyTask verdict for this prompt: ${classification.mode} (${sliceCount} ${unit}, ${tier} tier recommended). Consider dispatching via subagent-orchestration instead of doing every slice in-session \u2014 ${classification.reason}.</delegation-nudge>`;
23760
+ const rungLabel = rung === null ? "rung-?" : `rung-${rung}`;
23761
+ const modeLabel = classification.mode ?? "single-slice";
23762
+ return `<delegation-nudge>${rungLabel}: dispatch ${modeLabel} (${sliceCount} ${unit}, ${tier} tier recommended). Consider dispatching via subagent-orchestration instead of doing every slice in-session \u2014 ${classification.reason}.</delegation-nudge>`;
23432
23763
  }
23433
23764
  function classifyPrompt(prompt, workspace_root, hostId) {
23434
23765
  try {
23435
23766
  const { signals, sliceCountForLine } = extractTaskSignals(prompt);
23436
- const { activation, downshift, separate_quota_pool } = resolveActivation(
23767
+ const { activation, downshift, separate_quota_pool, agentTeams } = resolveActivation(
23437
23768
  workspace_root,
23438
23769
  hostId
23439
23770
  );
23440
- const classification = classifyTask(signals, activation);
23441
- if (classification.mode === null) {
23771
+ const ladder = classifyLadder({ taskText: prompt, signals, activation, agentTeams });
23772
+ if (ladder.verdict !== "subagent") {
23442
23773
  return null;
23443
23774
  }
23444
23775
  const tier = recommendSliceTier(downshift, separate_quota_pool);
23445
- return { classification, sliceCount: sliceCountForLine, tier };
23776
+ const sliceCount = ladder.rung === 1 ? 1 : sliceCountForLine;
23777
+ const classification = {
23778
+ delegable: true,
23779
+ action: "dispatch",
23780
+ mode: ladder.mode ?? null,
23781
+ reason: ladder.reason
23782
+ };
23783
+ return { classification, sliceCount, tier, rung: ladder.rung };
23446
23784
  } catch {
23447
23785
  return null;
23448
23786
  }
@@ -23495,8 +23833,8 @@ function main39() {
23495
23833
  process.stdout.write(
23496
23834
  `${JSON.stringify({
23497
23835
  decision: "warn",
23498
- reason: `delegation-nudge: ${result.classification.mode} verdict (${result.sliceCount} slices, ${result.tier} tier)`,
23499
- additional_context: buildNudgeLine(result.classification, result.sliceCount, result.tier)
23836
+ reason: `delegation-nudge: rung-${result.rung} verdict (${result.sliceCount} slices, ${result.tier} tier)`,
23837
+ additional_context: buildNudgeLine(result.rung, result.classification, result.sliceCount, result.tier)
23500
23838
  })}
23501
23839
  `
23502
23840
  );