@event4u/agent-config 14.20.0 → 14.21.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 (100) hide show
  1. package/.augment-plugin/marketplace.json +3 -3
  2. package/.augment-plugin/plugin.json +1 -1
  3. package/.claude-plugin/marketplace.json +1 -1
  4. package/CHANGELOG.md +243 -0
  5. package/README.md +1 -1
  6. package/agents/templates/.ai-council.yml.example +10 -0
  7. package/dist/agent-src/commands/analyze/inbox.md +1 -1
  8. package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +247 -32
  9. package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
  10. package/dist/agent-src/commands/analyze.md +12 -7
  11. package/dist/agent-src/commands/feature/plan.md +55 -16
  12. package/dist/agent-src/commands/feature/refactor.md +8 -3
  13. package/dist/agent-src/commands/humanize.md +48 -4
  14. package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
  15. package/dist/agent-src/contexts/execution/host-capability-manifest.md +4 -1
  16. package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
  17. package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
  18. package/dist/agent-src/rules/evaluator-independence.md +4 -4
  19. package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
  20. package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
  21. package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
  22. package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
  23. package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
  24. package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
  25. package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
  26. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  27. package/dist/agent-src/templates/features.md +5 -0
  28. package/dist/agent-src/templates/roadmaps.md +15 -0
  29. package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
  30. package/dist/cli-delegate/{chunk-PI2GU3DP.js → chunk-2TAQ2K4V.js} +4 -1
  31. package/dist/cli-delegate/{chunk-C6IPZRDA.js → chunk-643Z7FIC.js} +1 -1
  32. package/dist/cli-delegate/{chunk-NUMQHLS5.js → chunk-J3RFNKG4.js} +21 -2
  33. package/dist/cli-delegate/cmd_conformance.js +17 -3
  34. package/dist/cli-delegate/cmd_doctor.js +3 -3
  35. package/dist/cli-delegate/cmd_export.js +2 -2
  36. package/dist/cli-delegate/cmd_handoff.js +1 -1
  37. package/dist/cli-delegate/cmd_prune.js +2 -2
  38. package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
  39. package/dist/cli-delegate/cmd_sync.js +2 -2
  40. package/dist/cli-delegate/cmd_uninstall.js +2 -2
  41. package/dist/cli-delegate/cmd_validate.js +2 -2
  42. package/dist/discovery/deprecation-report.md +1 -1
  43. package/dist/discovery/discovery-manifest.json +56 -25
  44. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  45. package/dist/discovery/discovery-manifest.summary.md +3 -3
  46. package/dist/discovery/dormancy-report.md +1 -1
  47. package/dist/discovery/orphan-report.md +1 -1
  48. package/dist/discovery/packs.json +7 -6
  49. package/dist/discovery/trust-report.md +2 -2
  50. package/dist/discovery/workspaces.json +6 -5
  51. package/dist/hooks/dispatch.js +479 -241
  52. package/dist/install/install.mjs +4 -1
  53. package/dist/mcp/registry-manifest.json +2 -2
  54. package/dist/mcp/server.json +2 -2
  55. package/dist/mcp/server.mjs +313 -63
  56. package/dist/scripts/ai_council/content_ceiling.js +63 -0
  57. package/dist/scripts/ai_council/seat_policy.js +106 -0
  58. package/dist/scripts/ai_council/transport_resolver.js +21 -0
  59. package/llms.txt +14 -14
  60. package/package.json +1 -1
  61. package/src/config/gate-violation-baselines.json +3 -3
  62. package/src/config/release-gate-locality.yml +4 -3
  63. package/src/scripts/_lib/blocked_by_marker.ts +189 -0
  64. package/src/scripts/_lib/host_capability.ts +38 -1
  65. package/src/scripts/_lib/release_highlights.ts +19 -211
  66. package/src/scripts/_lib/release_material.ts +27 -77
  67. package/src/scripts/_lib/structured_ask.ts +117 -0
  68. package/src/scripts/_lib/tty_prompt.ts +76 -0
  69. package/src/scripts/ai_council/api_transport.ts +163 -0
  70. package/src/scripts/ai_council/clients.ts +58 -63
  71. package/src/scripts/ai_council/config.ts +13 -0
  72. package/src/scripts/ai_council/content_ceiling.ts +79 -0
  73. package/src/scripts/ai_council/jury_aggregate.ts +178 -0
  74. package/src/scripts/ai_council/seat_policy.ts +137 -0
  75. package/src/scripts/ai_council/status_surface.ts +79 -0
  76. package/src/scripts/ai_council/transport_resolver.ts +59 -1
  77. package/src/scripts/ai_tells_rules.ts +446 -5
  78. package/src/scripts/ask_block_census.ts +499 -0
  79. package/src/scripts/bench_humanizer_eval.ts +264 -40
  80. package/src/scripts/bench_prose_tells_fp.ts +198 -0
  81. package/src/scripts/check_branch_work_committed.ts +234 -0
  82. package/src/scripts/check_pack_size.ts +1 -1
  83. package/src/scripts/check_release_highlights.ts +0 -157
  84. package/src/scripts/council_cli.ts +18 -27
  85. package/src/scripts/detect_ai_tells.ts +142 -20
  86. package/src/scripts/harvest_reference_tokens.ts +698 -0
  87. package/src/scripts/hook_manifest.json +1 -1
  88. package/src/scripts/hook_manifest.yaml +31 -1
  89. package/src/scripts/hooks/concern_registry.ts +2 -0
  90. package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
  91. package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
  92. package/src/scripts/install-hooks.sh +52 -0
  93. package/src/scripts/lint_roadmap_blockers.ts +51 -1
  94. package/src/scripts/migrate_command_suggestions.ts +1 -1
  95. package/src/scripts/probe_unblocked_ask.ts +135 -2
  96. package/src/scripts/release.ts +26 -71
  97. package/src/scripts/release_drill.ts +6 -10
  98. package/src/scripts/release_env.ts +1 -0
  99. package/src/scripts/release_publication.ts +34 -29
  100. package/src/scripts/self_review_gate.ts +409 -15
@@ -27914,6 +27914,7 @@ function normalizeHostManifest(input) {
27914
27914
  separate_quota_pool: asBool(src.separate_quota_pool),
27915
27915
  agent_teams: asBool(src.agent_teams),
27916
27916
  worker_respawn: asBool(src.worker_respawn),
27917
+ structured_ask: asBool(src.structured_ask),
27917
27918
  reads_project_mcp_config: asBool(src.reads_project_mcp_config),
27918
27919
  // Inverted coercion, matching the inverted default: only an explicit
27919
27920
  // `false` clears the residual, so an absent or malformed value keeps
@@ -27966,13 +27967,14 @@ var init_host_capability = __esm({
27966
27967
  separate_quota_pool: false,
27967
27968
  agent_teams: false,
27968
27969
  worker_respawn: false,
27970
+ structured_ask: false,
27969
27971
  reads_project_mcp_config: false,
27970
27972
  // `true` because the safe answer here is "there is still a step", not
27971
27973
  // "there is nothing to do" — see the field's own note.
27972
27974
  mcp_needs_manual_activation: true
27973
27975
  };
27974
27976
  HOST_CAPABILITY_REGISTRY = {
27975
- claude: { subagent_spawn: true, parallel_spawn: true }
27977
+ claude: { subagent_spawn: true, parallel_spawn: true, structured_ask: false }
27976
27978
  };
27977
27979
  CAPABILITY_FIELDS = [
27978
27980
  "subagent_spawn",
@@ -27981,6 +27983,7 @@ var init_host_capability = __esm({
27981
27983
  "separate_quota_pool",
27982
27984
  "agent_teams",
27983
27985
  "worker_respawn",
27986
+ "structured_ask",
27984
27987
  "reads_project_mcp_config",
27985
27988
  "mcp_needs_manual_activation"
27986
27989
  ];
@@ -28520,6 +28523,94 @@ var init_cli_agency_bounds = __esm({
28520
28523
  }
28521
28524
  });
28522
28525
 
28526
+ // src/scripts/ai_council/api_transport.ts
28527
+ import { spawnSync as spawnSync9 } from "node:child_process";
28528
+ function curlJsonPost(url, extraHeaders, body) {
28529
+ const args = ["-sS", "-X", "POST", url, "-H", "content-type: application/json"];
28530
+ for (const h of extraHeaders) {
28531
+ args.push("-H", h);
28532
+ }
28533
+ args.push("--connect-timeout", "30", "--max-time", "290");
28534
+ args.push("-w", "\n%{http_code}", "--data-binary", "@-");
28535
+ const r = spawnSync9("curl", args, {
28536
+ input: JSON.stringify(body),
28537
+ encoding: "utf8",
28538
+ maxBuffer: 64 * 1024 * 1024,
28539
+ timeout: 3e5
28540
+ });
28541
+ if (r.error) {
28542
+ throw new Error(`curl spawn failed: ${r.error.message}`);
28543
+ }
28544
+ if (r.status !== 0) {
28545
+ throw new Error(
28546
+ `curl exited ${r.status ?? "null"}: ${(r.stderr ?? "").toString().slice(0, 500)}`
28547
+ );
28548
+ }
28549
+ const out = (r.stdout ?? "").toString();
28550
+ const nl = out.lastIndexOf("\n");
28551
+ const httpCode = (nl >= 0 ? out.slice(nl + 1) : out).trim();
28552
+ const bodyText = nl >= 0 ? out.slice(0, nl) : out;
28553
+ if (!/^2\d\d$/.test(httpCode)) {
28554
+ throw new Error(`HTTP ${httpCode}: ${bodyText.slice(0, 500)}`);
28555
+ }
28556
+ return JSON.parse(bodyText);
28557
+ }
28558
+ function openAiCompatibleClient(baseUrl, apiKey, post = curlJsonPost) {
28559
+ const url = `${baseUrl.replace(/\/+$/, "")}/chat/completions`;
28560
+ return {
28561
+ chat: {
28562
+ completions: {
28563
+ create: (kwargs) => post(url, [`authorization: Bearer ${apiKey}`], kwargs)
28564
+ }
28565
+ }
28566
+ };
28567
+ }
28568
+ function geminiRestClient(apiKey, post = curlJsonPost, baseUrl = GEMINI_BASE_URL) {
28569
+ return {
28570
+ models: {
28571
+ generate_content: (kwargs) => {
28572
+ const model = String(kwargs["model"] ?? "");
28573
+ const cfg = kwargs["config"] ?? {};
28574
+ const url = `${baseUrl.replace(/\/+$/, "")}/models/${model}:generateContent`;
28575
+ const raw = post(
28576
+ url,
28577
+ [`x-goog-api-key: ${apiKey}`],
28578
+ {
28579
+ contents: [{ role: "user", parts: [{ text: String(kwargs["contents"] ?? "") }] }],
28580
+ generationConfig: { maxOutputTokens: cfg["max_output_tokens"] ?? null }
28581
+ }
28582
+ );
28583
+ return normaliseGeminiRest(raw);
28584
+ }
28585
+ }
28586
+ };
28587
+ }
28588
+ function normaliseGeminiRest(raw) {
28589
+ const env = raw ?? {};
28590
+ const candidates = Array.isArray(env["candidates"]) ? env["candidates"] : [];
28591
+ const first = candidates[0] ?? {};
28592
+ const content = first["content"] ?? {};
28593
+ const parts = Array.isArray(content["parts"]) ? content["parts"] : [];
28594
+ const text = parts.map((p) => String((p ?? {})["text"] ?? "")).join("");
28595
+ const usage = env["usageMetadata"] ?? {};
28596
+ const num = (v) => typeof v === "number" && Number.isFinite(v) ? v : 0;
28597
+ return {
28598
+ text,
28599
+ model_version: String(env["modelVersion"] ?? ""),
28600
+ usage_metadata: {
28601
+ prompt_token_count: num(usage["promptTokenCount"]),
28602
+ candidates_token_count: num(usage["candidatesTokenCount"])
28603
+ }
28604
+ };
28605
+ }
28606
+ var GEMINI_BASE_URL;
28607
+ var init_api_transport = __esm({
28608
+ "src/scripts/ai_council/api_transport.ts"() {
28609
+ "use strict";
28610
+ GEMINI_BASE_URL = "https://generativelanguage.googleapis.com/v1beta";
28611
+ }
28612
+ });
28613
+
28523
28614
  // src/scripts/_lib/session_role.ts
28524
28615
  function resolveSessionRole(env = process.env) {
28525
28616
  const raw = (env[SESSION_ROLE_ENV] ?? "").trim().toLowerCase();
@@ -29228,7 +29319,7 @@ __export(clients_exports, {
29228
29319
  record_cli_call: () => record_cli_call,
29229
29320
  reset_cli_call_counts: () => reset_cli_call_counts
29230
29321
  });
29231
- import { spawnSync as spawnSync9 } from "node:child_process";
29322
+ import { spawnSync as spawnSync10 } from "node:child_process";
29232
29323
  import * as fs56 from "node:fs";
29233
29324
  import * as os10 from "node:os";
29234
29325
  import * as path57 from "node:path";
@@ -29336,34 +29427,6 @@ function _getattr(obj, name, fallback) {
29336
29427
  }
29337
29428
  return fallback;
29338
29429
  }
29339
- function _curlJsonPost(url, extraHeaders, body) {
29340
- const args = ["-sS", "-X", "POST", url, "-H", "content-type: application/json"];
29341
- for (const h of extraHeaders) {
29342
- args.push("-H", h);
29343
- }
29344
- args.push("--connect-timeout", "30", "--max-time", "290");
29345
- args.push("-w", "\n%{http_code}", "--data-binary", "@-");
29346
- const r = spawnSync9("curl", args, {
29347
- input: JSON.stringify(body),
29348
- encoding: "utf8",
29349
- maxBuffer: 64 * 1024 * 1024,
29350
- timeout: 3e5
29351
- });
29352
- if (r.error) {
29353
- throw new Error(`curl spawn failed: ${r.error.message}`);
29354
- }
29355
- if (r.status !== 0) {
29356
- throw new Error(`curl exited ${r.status ?? "null"}: ${(r.stderr ?? "").toString().slice(0, 500)}`);
29357
- }
29358
- const out = (r.stdout ?? "").toString();
29359
- const nl = out.lastIndexOf("\n");
29360
- const httpCode = (nl >= 0 ? out.slice(nl + 1) : out).trim();
29361
- const bodyText = nl >= 0 ? out.slice(0, nl) : out;
29362
- if (!/^2\d\d$/.test(httpCode)) {
29363
- throw new Error(`HTTP ${httpCode}: ${bodyText.slice(0, 500)}`);
29364
- }
29365
- return JSON.parse(bodyText);
29366
- }
29367
29430
  function _cliCallsStatePath() {
29368
29431
  return write_target(CLI_CALLS_FILENAME);
29369
29432
  }
@@ -29725,6 +29788,7 @@ var init_clients = __esm({
29725
29788
  "use strict";
29726
29789
  init_spawn_env();
29727
29790
  init_cli_agency_bounds();
29791
+ init_api_transport();
29728
29792
  init_session_role();
29729
29793
  init_agent_settings();
29730
29794
  init_user_global_paths();
@@ -29864,7 +29928,7 @@ var init_clients = __esm({
29864
29928
  }
29865
29929
  this._client = {
29866
29930
  messages: {
29867
- create: (kwargs) => _curlJsonPost(
29931
+ create: (kwargs) => curlJsonPost(
29868
29932
  "https://api.anthropic.com/v1/messages",
29869
29933
  [`x-api-key: ${api_key}`, "anthropic-version: 2023-06-01"],
29870
29934
  kwargs
@@ -29988,7 +30052,7 @@ var init_clients = __esm({
29988
30052
  this._client = {
29989
30053
  chat: {
29990
30054
  completions: {
29991
- create: (kwargs) => _curlJsonPost(
30055
+ create: (kwargs) => curlJsonPost(
29992
30056
  "https://api.openai.com/v1/chat/completions",
29993
30057
  [`authorization: Bearer ${api_key}`],
29994
30058
  kwargs
@@ -30071,7 +30135,7 @@ ${user_prompt}` }
30071
30135
  "GeminiClient requires explicit api_key or injected client. Use `api_key_ref: env:GEMINI_API_KEY` in ~/.event4u/agent-config/settings/.ai-council.yml."
30072
30136
  );
30073
30137
  }
30074
- throw new Error("google-genai package not installed. `pip install google-genai`.");
30138
+ this._client = geminiRestClient(api_key, opts.transport ?? curlJsonPost);
30075
30139
  }
30076
30140
  ask(system_prompt, user_prompt, max_tokens = DEFAULT_MAX_TOKENS) {
30077
30141
  const t0 = _nowMs();
@@ -30133,7 +30197,11 @@ ${user_prompt}`;
30133
30197
  `${this.constructor.name} requires explicit api_key or injected client.`
30134
30198
  );
30135
30199
  }
30136
- throw new Error("openai package not installed. `pip install openai`.");
30200
+ this._client = openAiCompatibleClient(
30201
+ opts.base_url,
30202
+ api_key,
30203
+ opts.transport ?? curlJsonPost
30204
+ );
30137
30205
  }
30138
30206
  ask(system_prompt, user_prompt, max_tokens = DEFAULT_MAX_TOKENS) {
30139
30207
  const t0 = _nowMs();
@@ -30186,7 +30254,13 @@ ${user_prompt}`;
30186
30254
  name = "xai";
30187
30255
  base_url = XAI_BASE_URL;
30188
30256
  constructor(opts = {}) {
30189
- super({ model: opts.model ?? DEFAULT_XAI_MODEL, client: opts.client, api_key: opts.api_key });
30257
+ super({
30258
+ model: opts.model ?? DEFAULT_XAI_MODEL,
30259
+ client: opts.client,
30260
+ api_key: opts.api_key,
30261
+ transport: opts.transport,
30262
+ base_url: XAI_BASE_URL
30263
+ });
30190
30264
  }
30191
30265
  };
30192
30266
  PerplexityClient = class extends _OpenAICompatibleClient {
@@ -30196,7 +30270,9 @@ ${user_prompt}`;
30196
30270
  super({
30197
30271
  model: opts.model ?? DEFAULT_PERPLEXITY_MODEL,
30198
30272
  client: opts.client,
30199
- api_key: opts.api_key
30273
+ api_key: opts.api_key,
30274
+ transport: opts.transport,
30275
+ base_url: PERPLEXITY_BASE_URL
30200
30276
  });
30201
30277
  }
30202
30278
  };
@@ -30380,7 +30456,7 @@ ${user_prompt}`;
30380
30456
  if (stdinPayload !== null) {
30381
30457
  spawnOpts.input = stdinPayload;
30382
30458
  }
30383
- const r = spawnSync9(argv0, cmd.slice(1), spawnOpts);
30459
+ const r = spawnSync10(argv0, cmd.slice(1), spawnOpts);
30384
30460
  if (r.error) {
30385
30461
  const err = r.error;
30386
30462
  if (r.signal === "SIGTERM" || err.code === "ETIMEDOUT") {
@@ -31170,6 +31246,102 @@ Type your follow-up question, end with a line containing only: ${this._end_marke
31170
31246
  }
31171
31247
  });
31172
31248
 
31249
+ // src/scripts/ai_council/content_ceiling.ts
31250
+ function classifyContentClass(raw, fallback = DEFAULT_CONTENT_CLASS) {
31251
+ if (typeof raw === "string" && _SET.has(raw)) {
31252
+ return raw;
31253
+ }
31254
+ return fallback;
31255
+ }
31256
+ function admitsContent(seatCeiling, contentClass) {
31257
+ if (seatCeiling === "public-artifact" && contentClass === "project-content") {
31258
+ return {
31259
+ admitted: false,
31260
+ reason: "seat ceiling is 'public-artifact'; the payload is classified 'project-content' (consumer-supplied)"
31261
+ };
31262
+ }
31263
+ return { admitted: true, reason: null };
31264
+ }
31265
+ var CONTENT_CLASSES, DEFAULT_CONTENT_CLASS, DEFAULT_SEAT_CEILING, _SET;
31266
+ var init_content_ceiling = __esm({
31267
+ "src/scripts/ai_council/content_ceiling.ts"() {
31268
+ "use strict";
31269
+ CONTENT_CLASSES = ["public-artifact", "project-content"];
31270
+ DEFAULT_CONTENT_CLASS = "project-content";
31271
+ DEFAULT_SEAT_CEILING = "project-content";
31272
+ _SET = new Set(CONTENT_CLASSES);
31273
+ }
31274
+ });
31275
+
31276
+ // src/scripts/ai_council/seat_policy.ts
31277
+ function isPolicyExclusion(v) {
31278
+ return typeof v === "string" && _SET2.has(v);
31279
+ }
31280
+ function classifyPolicyExclusion(raw) {
31281
+ if (raw === void 0 || raw === null) {
31282
+ return null;
31283
+ }
31284
+ return isPolicyExclusion(raw) ? raw : "policy_unknown";
31285
+ }
31286
+ function policyExclusionReason(p, provider) {
31287
+ const who = provider === void 0 ? "this seat" : provider;
31288
+ switch (p) {
31289
+ case "policy_terms":
31290
+ return `${who} is excluded by policy: the route's terms forbid this use`;
31291
+ case "policy_privacy":
31292
+ return `${who} is excluded by policy: a privacy constraint forbids this route`;
31293
+ case "policy_unproven_cost":
31294
+ return `${who} is excluded by policy: the route's cost property is asserted, not proven`;
31295
+ case "policy_model_mismatch":
31296
+ return `${who} is excluded by policy: the route serves a model other than the one requested`;
31297
+ case "policy_content_ceiling":
31298
+ return `${who} is excluded by policy: the payload exceeds the seat's content ceiling`;
31299
+ case "policy_unknown":
31300
+ return `${who} is excluded by policy: an exclusion this build does not understand (failing closed)`;
31301
+ }
31302
+ }
31303
+ function parseSeatFields(name, raw) {
31304
+ const dr = raw["disabled_reason"];
31305
+ let disabled_reason = null;
31306
+ if (dr !== void 0 && dr !== null) {
31307
+ if (typeof dr !== "string" || dr.trim() === "") {
31308
+ return {
31309
+ error: `members.${name}.disabled_reason must be a non-empty string when set (got ${JSON.stringify(dr)}).`
31310
+ };
31311
+ }
31312
+ disabled_reason = dr.trim();
31313
+ }
31314
+ return {
31315
+ fields: {
31316
+ disabled_reason,
31317
+ policy_exclusion: classifyPolicyExclusion(raw["policy_exclusion"]),
31318
+ content_ceiling: classifyContentClass(raw["content_ceiling"], DEFAULT_SEAT_CEILING)
31319
+ }
31320
+ };
31321
+ }
31322
+ var POLICY_EXCLUSIONS, _SET2;
31323
+ var init_seat_policy = __esm({
31324
+ "src/scripts/ai_council/seat_policy.ts"() {
31325
+ "use strict";
31326
+ init_content_ceiling();
31327
+ POLICY_EXCLUSIONS = [
31328
+ /** The provider's terms forbid this use of the route. */
31329
+ "policy_terms",
31330
+ /** A privacy constraint forbids sending to this route. */
31331
+ "policy_privacy",
31332
+ /** The route's cost property is asserted rather than proven. */
31333
+ "policy_unproven_cost",
31334
+ /** The route serves a model other than the one requested. */
31335
+ "policy_model_mismatch",
31336
+ /** 2.2 — the payload's content class exceeds the seat's ceiling. */
31337
+ "policy_content_ceiling",
31338
+ /** The config named an exclusion this build does not understand. */
31339
+ "policy_unknown"
31340
+ ];
31341
+ _SET2 = new Set(POLICY_EXCLUSIONS);
31342
+ }
31343
+ });
31344
+
31173
31345
  // src/scripts/ai_council/fallback_config.ts
31174
31346
  function buildFallback(raw, d) {
31175
31347
  if (!d.isDict(raw)) return { api_on_quota: false };
@@ -32321,6 +32493,10 @@ function _build_member(name, cfg, default_mode = "auto", ignored = []) {
32321
32493
  }
32322
32494
  verified_at = coerced;
32323
32495
  }
32496
+ const seat = parseSeatFields(name, cfg);
32497
+ if ("error" in seat) {
32498
+ throw new CouncilConfigError(seat.error);
32499
+ }
32324
32500
  const prompt_cache_raw = _get(cfg, "prompt_cache", null);
32325
32501
  let prompt_cache_ttl = "5m";
32326
32502
  if (prompt_cache_raw !== null && prompt_cache_raw !== void 0 && !_isBool(prompt_cache_raw)) {
@@ -32348,7 +32524,8 @@ function _build_member(name, cfg, default_mode = "auto", ignored = []) {
32348
32524
  participate_low_impact: participate_raw,
32349
32525
  tier,
32350
32526
  prompt_cache_ttl,
32351
- verified_at
32527
+ verified_at,
32528
+ ...seat.fields
32352
32529
  };
32353
32530
  }
32354
32531
  function resolve_cli_call_caps(raw) {
@@ -32540,6 +32717,7 @@ var init_config = __esm({
32540
32717
  init_user_global_paths();
32541
32718
  init_cli_call_budget();
32542
32719
  init_clients();
32720
+ init_seat_policy();
32543
32721
  init_quorum();
32544
32722
  init_fallback_config();
32545
32723
  init_py_format();
@@ -32724,7 +32902,7 @@ var init_agentSwitchDetection = __esm({
32724
32902
  });
32725
32903
 
32726
32904
  // src/scripts/_lib/environment_detector.ts
32727
- import { spawnSync as spawnSync10 } from "node:child_process";
32905
+ import { spawnSync as spawnSync11 } from "node:child_process";
32728
32906
  import * as fs58 from "node:fs";
32729
32907
  import * as os12 from "node:os";
32730
32908
  import * as path59 from "node:path";
@@ -32752,7 +32930,7 @@ function homeOf(env) {
32752
32930
  function defaultProbeKeychain(service) {
32753
32931
  if (process.platform !== "darwin") return false;
32754
32932
  try {
32755
- const res = spawnSync10("security", ["find-generic-password", "-s", service], {
32933
+ const res = spawnSync11("security", ["find-generic-password", "-s", service], {
32756
32934
  timeout: VERSION_PROBE_TIMEOUT_MS,
32757
32935
  encoding: "utf-8",
32758
32936
  windowsHide: true,
@@ -32766,7 +32944,7 @@ function defaultProbeKeychain(service) {
32766
32944
  }
32767
32945
  function defaultProbeVersion(binary) {
32768
32946
  try {
32769
- const res = spawnSync10(binary, ["--version"], {
32947
+ const res = spawnSync11(binary, ["--version"], {
32770
32948
  timeout: VERSION_PROBE_TIMEOUT_MS,
32771
32949
  encoding: "utf-8",
32772
32950
  windowsHide: true,
@@ -33078,6 +33256,22 @@ function resolveTransport(opts) {
33078
33256
  const auth = strongestAuth(report2, provider);
33079
33257
  const authSource = auth?.source ?? null;
33080
33258
  const billing = classifyBilling(provider, authSource);
33259
+ const exclusion = opts.policyExclusion ?? null;
33260
+ if (exclusion !== null) {
33261
+ return unavailable(provider, billing, policyExclusionReason(exclusion, provider), exclusion);
33262
+ }
33263
+ const contentClass = opts.contentClass ?? null;
33264
+ if (contentClass !== null) {
33265
+ const verdict = admitsContent(opts.seatCeiling ?? DEFAULT_SEAT_CEILING, contentClass);
33266
+ if (!verdict.admitted) {
33267
+ return unavailable(
33268
+ provider,
33269
+ billing,
33270
+ `${provider}: ${verdict.reason ?? "content ceiling refused the payload"}`,
33271
+ "policy_content_ceiling"
33272
+ );
33273
+ }
33274
+ }
33081
33275
  if (mode === "manual") {
33082
33276
  return {
33083
33277
  available: true,
@@ -33156,7 +33350,10 @@ function resolveMemberTransport(opts) {
33156
33350
  // `exactOptionalPropertyTypes` rejects an explicit `undefined` for an
33157
33351
  // optional-boolean field, so the key is omitted entirely rather than
33158
33352
  // forwarded as `apiKeyPresent: undefined`.
33159
- ...opts.apiKeyPresent !== void 0 ? { apiKeyPresent: opts.apiKeyPresent } : {}
33353
+ ...opts.apiKeyPresent !== void 0 ? { apiKeyPresent: opts.apiKeyPresent } : {},
33354
+ policyExclusion: opts.policyExclusion ?? null,
33355
+ seatCeiling: opts.seatCeiling ?? null,
33356
+ contentClass: opts.contentClass ?? null
33160
33357
  });
33161
33358
  return { ...resolved, configuredMode };
33162
33359
  }
@@ -33254,6 +33451,8 @@ var init_transport_resolver = __esm({
33254
33451
  "use strict";
33255
33452
  init_environment_detector();
33256
33453
  init_modes();
33454
+ init_content_ceiling();
33455
+ init_seat_policy();
33257
33456
  VALID_TRANSPORT_MODES = /* @__PURE__ */ new Set([
33258
33457
  "api",
33259
33458
  "manual",
@@ -39431,7 +39630,7 @@ var init_envelope = __esm({
39431
39630
  });
39432
39631
 
39433
39632
  // src/scripts/hooks/end_review_nudge_hook.ts
39434
- import { spawnSync as spawnSync11 } from "node:child_process";
39633
+ import { spawnSync as spawnSync12 } from "node:child_process";
39435
39634
  import * as crypto7 from "node:crypto";
39436
39635
  import * as fs68 from "node:fs";
39437
39636
  import * as os15 from "node:os";
@@ -39477,7 +39676,7 @@ function nonDocMutatedLines(rows) {
39477
39676
  }
39478
39677
  function gitNumstatRows(cwd) {
39479
39678
  try {
39480
- const r = spawnSync11("git", ["diff", "--numstat", "HEAD"], {
39679
+ const r = spawnSync12("git", ["diff", "--numstat", "HEAD"], {
39481
39680
  cwd,
39482
39681
  encoding: "utf8",
39483
39682
  maxBuffer: 16 * 1024 * 1024
@@ -39490,7 +39689,7 @@ function gitNumstatRows(cwd) {
39490
39689
  }
39491
39690
  function untrackedNonDocFiles(cwd) {
39492
39691
  try {
39493
- const r = spawnSync11("git", ["ls-files", "--others", "--exclude-standard"], {
39692
+ const r = spawnSync12("git", ["ls-files", "--others", "--exclude-standard"], {
39494
39693
  cwd,
39495
39694
  encoding: "utf8",
39496
39695
  maxBuffer: 16 * 1024 * 1024
@@ -41429,6 +41628,12 @@ function buildNudgeLine(rung, classification, sliceCount, tier) {
41429
41628
  if (rung === 0.5) {
41430
41629
  return `<delegation-nudge>rung-0.5: ask, est. <1k tokens. This is a single bounded question \u2014 answer it via ONE completion (\`./scripts-run src/scripts/ask_transport "<question>"\`) instead of a subagent spawn (measured spawn floor: ~251k tokens) \u2014 ${classification.reason}.</delegation-nudge>`;
41431
41630
  }
41631
+ if (rung === 4) {
41632
+ return `<delegation-nudge>rung-4: council. This is contested judgment, not a slice to dispatch \u2014 route it to the council yourself via \`./scripts-run src/scripts/council_cli run --input <file>\` (check reachability first with \`agent-config council:status\`; the file path is the input, never an inline question). Not a spawn, and not run for you \u2014 ${classification.reason}.</delegation-nudge>`;
41633
+ }
41634
+ if (rung === 3) {
41635
+ return `<delegation-nudge>rung-3: team. These slices must see each other's state during the run, so a set of independent subagent spawns cannot express it \u2014 use the host teams primitive, or fall back to \`./scripts-run src/scripts/ask_transport "<question>"\` for a bounded sub-question. Not a spawn, and not run for you \u2014 ${classification.reason}.</delegation-nudge>`;
41636
+ }
41432
41637
  const modeLabel = classification.mode ?? "single-slice";
41433
41638
  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>`;
41434
41639
  }
@@ -41447,6 +41652,14 @@ function classifyPrompt(prompt, workspace_root, hostId) {
41447
41652
  agentTeams,
41448
41653
  insideSubagentSession: sessionRole !== "orchestrator"
41449
41654
  });
41655
+ if (ladder.verdict === "team" || ladder.verdict === "council") {
41656
+ return {
41657
+ classification: { delegable: false, action: "ask", mode: null, reason: ladder.reason },
41658
+ sliceCount: 0,
41659
+ tier: recommendSliceTier(downshift, separate_quota_pool),
41660
+ rung: ladder.rung
41661
+ };
41662
+ }
41450
41663
  if (ladder.verdict !== "subagent") {
41451
41664
  return null;
41452
41665
  }
@@ -43415,7 +43628,7 @@ import * as fs78 from "node:fs";
43415
43628
  import * as os21 from "node:os";
43416
43629
  import * as path80 from "node:path";
43417
43630
  import process22 from "node:process";
43418
- import { spawnSync as spawnSync12 } from "node:child_process";
43631
+ import { spawnSync as spawnSync13 } from "node:child_process";
43419
43632
  import { fileURLToPath as fileURLToPath45, pathToFileURL as pathToFileURL35 } from "node:url";
43420
43633
  function print3(s = "") {
43421
43634
  process22.stdout.write(`${s}
@@ -43572,7 +43785,7 @@ function _check_hook_dispatcher(projectRoot, packageRoot = PACKAGE_ROOT2, opts =
43572
43785
  ...event === "stop" ? { stop_reason: "conformance-smoke" } : { source: "startup" }
43573
43786
  });
43574
43787
  const argv = [...runner, "--platform", platform, "--event", event];
43575
- const proc = spawnSync12(argv[0], argv.slice(1), {
43788
+ const proc = spawnSync13(argv[0], argv.slice(1), {
43576
43789
  input: payload,
43577
43790
  cwd: projectRoot,
43578
43791
  encoding: "utf8",
@@ -47078,6 +47291,39 @@ var init_solo_dispatch = __esm({
47078
47291
  }
47079
47292
  });
47080
47293
 
47294
+ // src/scripts/ai_council/status_surface.ts
47295
+ function resolvedTransportFor(name, member, report2, contentClass) {
47296
+ return resolveMemberTransport({
47297
+ provider: name,
47298
+ report: report2 ?? detectEnvironment(),
47299
+ invocationMode: null,
47300
+ memberSettings: null,
47301
+ globalMode: "auto",
47302
+ binaryOverride: member.binary,
47303
+ policyExclusion: member.policy_exclusion ?? null,
47304
+ seatCeiling: member.content_ceiling ?? DEFAULT_SEAT_CEILING,
47305
+ contentClass: contentClass ?? null
47306
+ });
47307
+ }
47308
+ function disabledSeats(cfg) {
47309
+ if (cfg === null) {
47310
+ return {};
47311
+ }
47312
+ return Object.fromEntries(
47313
+ [...cfg.members.entries()].filter(([, m]) => !m.enabled).map(([n, m]) => [n, m.disabled_reason ?? NO_DISABLED_REASON])
47314
+ );
47315
+ }
47316
+ var NO_DISABLED_REASON;
47317
+ var init_status_surface = __esm({
47318
+ "src/scripts/ai_council/status_surface.ts"() {
47319
+ "use strict";
47320
+ init_environment_detector();
47321
+ init_content_ceiling();
47322
+ init_transport_resolver();
47323
+ NO_DISABLED_REASON = "disabled, no reason recorded";
47324
+ }
47325
+ });
47326
+
47081
47327
  // src/scripts/ai_council/quorum_wiring.ts
47082
47328
  function _quorum_setting_from(ai) {
47083
47329
  const raw = ai["quorum"];
@@ -54268,18 +54514,8 @@ function _emit_shadow_slo_banner() {
54268
54514
  return;
54269
54515
  }
54270
54516
  }
54271
- function _resolvedTransportFor(name, member, report2) {
54272
- return resolveMemberTransport({
54273
- provider: name,
54274
- report: report2 ?? detectEnvironment(),
54275
- invocationMode: null,
54276
- memberSettings: null,
54277
- globalMode: "auto",
54278
- binaryOverride: member.binary
54279
- });
54280
- }
54281
54517
  function _posture(name, member) {
54282
- const t = _resolvedTransportFor(name, member);
54518
+ const t = resolvedTransportFor(name, member);
54283
54519
  const keyless = {
54284
54520
  anthropic: () => Boolean(load_anthropic_key()),
54285
54521
  openai: () => Boolean(load_openai_key())
@@ -54311,7 +54547,7 @@ function cmd_status(args, opts = {}) {
54311
54547
  const configured = cfg !== null && cfg.enabled && enabledMembers.length > 0;
54312
54548
  const probeStore = readProbeStore(REPO_ROOT11);
54313
54549
  const qualifications = enabledMembers.map(
54314
- ([name, m]) => qualifySeat(name, _resolvedTransportFor(name, m), m.model, probeStore)
54550
+ ([name, m]) => qualifySeat(name, resolvedTransportFor(name, m), m.model, probeStore)
54315
54551
  );
54316
54552
  if (_getattr3(args, "json", false)) {
54317
54553
  _stdout2(
@@ -54326,14 +54562,17 @@ function cmd_status(args, opts = {}) {
54326
54562
  member_names: enabledMembers.map(([n]) => n),
54327
54563
  transports: Object.fromEntries(
54328
54564
  enabledMembers.map(([n, m]) => {
54329
- const t = _resolvedTransportFor(n, m);
54565
+ const t = resolvedTransportFor(n, m);
54330
54566
  return [
54331
54567
  n,
54332
54568
  {
54333
54569
  available: t.available,
54334
54570
  transport: t.available ? t.transport : null,
54335
54571
  billing: t.available ? t.billing : null,
54336
- reason: t.reason
54572
+ reason: t.reason,
54573
+ absent_reason: t.absentReason,
54574
+ policy_exclusion: m.policy_exclusion,
54575
+ content_ceiling: m.content_ceiling
54337
54576
  }
54338
54577
  ];
54339
54578
  })
@@ -54344,6 +54583,7 @@ function cmd_status(args, opts = {}) {
54344
54583
  enabledMembers.map(([n, m]) => [n, _posture(n, m)])
54345
54584
  )
54346
54585
  },
54586
+ disabled_seats: disabledSeats(cfg),
54347
54587
  ignored_transport_keys: cfg?.ignored_transport_keys ?? [],
54348
54588
  qualification: qualificationJson(qualifications),
54349
54589
  qualified_members: countableSeats(qualifications),
@@ -54380,10 +54620,20 @@ function cmd_status(args, opts = {}) {
54380
54620
  `);
54381
54621
  }
54382
54622
  for (const [name, member] of enabledMembers) {
54383
- const t = _resolvedTransportFor(name, member);
54623
+ const t = resolvedTransportFor(name, member);
54384
54624
  const billing = t.available ? ` \xB7 ${t.billing}` : "";
54385
54625
  const detail = t.available ? t.transport : `unavailable \u2014 ${t.reason ?? "no usable transport"}`;
54386
54626
  _stdout2(` transport ${name}: ${detail}${billing}
54627
+ `);
54628
+ if (!t.available && t.absentReason !== null && t.absentReason.startsWith("policy_")) {
54629
+ _stdout2(` policy ${name}: ${t.absentReason}
54630
+ `);
54631
+ }
54632
+ _stdout2(` content ceiling ${name}: ${member.content_ceiling}
54633
+ `);
54634
+ }
54635
+ for (const [name, reason] of Object.entries(disabledSeats(cfg))) {
54636
+ _stdout2(` disabled seat ${name}: ${reason}
54387
54637
  `);
54388
54638
  }
54389
54639
  for (const line of renderPostureLines(
@@ -55662,7 +55912,7 @@ var init_council_cli = __esm({
55662
55912
  init_solo_dispatch();
55663
55913
  init_modes();
55664
55914
  init_transport_resolver();
55665
- init_transport_resolver();
55915
+ init_status_surface();
55666
55916
  init_quorum();
55667
55917
  init_quorum_wiring();
55668
55918
  init_qualification();