@event4u/agent-config 14.8.0 → 14.9.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 (136) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/CHANGELOG.md +165 -237
  3. package/README.md +1 -1
  4. package/agents/templates/.ai-council.yml.example +34 -4
  5. package/dist/agent-src/commands/pr/merge.md +9 -5
  6. package/dist/agent-src/commands/review/changes.md +95 -14
  7. package/dist/agent-src/commands/roadmap/process-full.md +2 -2
  8. package/dist/agent-src/contexts/execution/host-capability-manifest.md +77 -0
  9. package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +36 -0
  10. package/dist/agent-src/contexts/execution/subagent-response-contract.md +80 -0
  11. package/dist/agent-src/rules/untrusted-input-defense.md +9 -3
  12. package/dist/agent-src/skills/code-review/evals/evals.json +87 -46
  13. package/dist/agent-src/skills/humanizer/SKILL.md +39 -0
  14. package/dist/agent-src/skills/humanizer/evals/strip_fixtures.json +57 -0
  15. package/dist/agent-src/skills/humanizer/references/fixtures.md +54 -0
  16. package/dist/agent-src/skills/judge-spec-compliance/SKILL.md +154 -0
  17. package/dist/agent-src/skills/judge-synthesis/SKILL.md +45 -0
  18. package/dist/agent-src/skills/roadmap-writing/SKILL.md +18 -18
  19. package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
  20. package/dist/agent-src/templates/roadmaps.md +8 -0
  21. package/dist/cli/registry.js +2 -0
  22. package/dist/cli/registry.js.map +1 -1
  23. package/dist/cli-delegate/{chunk-MMSGVZKV.js → chunk-77GR3VD4.js} +65 -40
  24. package/dist/cli-delegate/{chunk-Q26HT4QT.js → chunk-E46KPN6K.js} +251 -150
  25. package/dist/cli-delegate/{chunk-EEJUMCDZ.js → chunk-PLSG6UL5.js} +1 -1
  26. package/dist/cli-delegate/cmd_conformance.js +89 -13
  27. package/dist/cli-delegate/cmd_doctor.js +3 -3
  28. package/dist/cli-delegate/cmd_handoff.js +2 -2
  29. package/dist/cli-delegate/cmd_memory_get.js +1 -1
  30. package/dist/cli-delegate/cmd_route_audit.js +1 -1
  31. package/dist/cli-delegate/cmd_settings_get.js +1 -1
  32. package/dist/cli-delegate/cmd_settings_set.js +1 -1
  33. package/dist/discovery/deprecation-report.md +1 -1
  34. package/dist/discovery/discovery-manifest.json +37 -15
  35. package/dist/discovery/discovery-manifest.json.sha256 +1 -1
  36. package/dist/discovery/discovery-manifest.summary.md +3 -3
  37. package/dist/discovery/dormancy-report.md +1 -1
  38. package/dist/discovery/orphan-report.md +1 -1
  39. package/dist/discovery/packs.json +6 -5
  40. package/dist/discovery/trust-report.md +2 -2
  41. package/dist/discovery/workspaces.json +5 -4
  42. package/dist/hooks/dispatch.js +562 -204
  43. package/dist/mcp/registry-manifest.json +3 -3
  44. package/dist/mcp/server.json +2 -2
  45. package/dist/mcp/server.mjs +808 -331
  46. package/dist/scripts/_lib/claude_plugin.js +162 -0
  47. package/dist/scripts/_lib/claude_plugin.js.map +1 -0
  48. package/dist/scripts/_lib/environment_detector.js +397 -0
  49. package/dist/scripts/_lib/environment_detector.js.map +1 -0
  50. package/dist/scripts/_lib/spawn_env.js +129 -0
  51. package/dist/scripts/_lib/spawn_env.js.map +1 -0
  52. package/dist/scripts/ai_council/modes.js +199 -0
  53. package/dist/scripts/ai_council/modes.js.map +1 -0
  54. package/dist/scripts/ai_council/transport_resolver.js +418 -0
  55. package/dist/scripts/ai_council/transport_resolver.js.map +1 -0
  56. package/dist/server/routes/wizard.js +3 -2
  57. package/dist/server/routes/wizard.js.map +1 -1
  58. package/dist/ui/assets/index-Cxgy63kz.js +26 -0
  59. package/dist/ui/assets/index-Cxgy63kz.js.map +1 -0
  60. package/dist/ui/index.html +1 -1
  61. package/docs/guidelines/agent-infra/traceability-field-mechanics.md +50 -0
  62. package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +17 -0
  63. package/package.json +7 -3
  64. package/src/config/estate-count-budget.json +13 -744
  65. package/src/config/evaluator-budgets.json +4 -4
  66. package/src/config/gate-coverage.yml +247 -9
  67. package/src/config/gate-violation-baselines.json +72 -72
  68. package/src/config/publish-surface.json +60 -0
  69. package/src/config/requirements-trace-budget.json +32 -0
  70. package/src/config/rule-stub-ceilings.json +8 -1
  71. package/src/scripts/_dispatch.bash +7 -0
  72. package/src/scripts/_lib/base_tree.ts +175 -0
  73. package/src/scripts/_lib/billing_grant.ts +165 -0
  74. package/src/scripts/_lib/billing_grant_cli.ts +123 -0
  75. package/src/scripts/_lib/council_fallback_posture.ts +53 -6
  76. package/src/scripts/_lib/council_fallback_wiring.ts +61 -1
  77. package/src/scripts/_lib/host_capability.ts +59 -5
  78. package/src/scripts/_lib/quota_parked.ts +133 -0
  79. package/src/scripts/_lib/retrieval_sanitize.ts +62 -0
  80. package/src/scripts/_lib/review_telemetry.ts +196 -0
  81. package/src/scripts/_lib/subagent_response.ts +92 -0
  82. package/src/scripts/adr/regenerate_index.ts +14 -7
  83. package/src/scripts/ai_council/clients.ts +12 -1
  84. package/src/scripts/ai_council/config.ts +72 -115
  85. package/src/scripts/ai_council/fallback_config.ts +19 -7
  86. package/src/scripts/ai_council/mid_flight_fallback.ts +88 -7
  87. package/src/scripts/ai_council/orchestrator.ts +2 -2
  88. package/src/scripts/ai_council/py_format.ts +124 -0
  89. package/src/scripts/ai_council/transport_resolver.ts +65 -2
  90. package/src/scripts/audit_adr_coverage.ts +33 -6
  91. package/src/scripts/check_council_pin_staleness.ts +300 -0
  92. package/src/scripts/check_estate_count.ts +420 -293
  93. package/src/scripts/check_gate_coverage.ts +91 -0
  94. package/src/scripts/check_no_stub_inventory_table.ts +292 -0
  95. package/src/scripts/check_pack_size.ts +234 -1
  96. package/src/scripts/check_publish_surface.ts +297 -0
  97. package/src/scripts/check_requirements_trace.ts +378 -0
  98. package/src/scripts/check_secret_leak.ts +166 -12
  99. package/src/scripts/code_graph/cli.ts +44 -5
  100. package/src/scripts/code_graph/extract.ts +87 -0
  101. package/src/scripts/council_cli.ts +12 -16
  102. package/src/scripts/detect_ai_tells.ts +87 -0
  103. package/src/scripts/dispatch_r2_reviewer.ts +42 -0
  104. package/src/scripts/generate_capabilities_index.ts +41 -0
  105. package/src/scripts/git_authorization_hook.ts +54 -4
  106. package/src/scripts/hooks/block_unauthorized_git.ts +19 -1
  107. package/src/scripts/hooks/run_continuation_hook.ts +3 -1
  108. package/src/scripts/hooks/subagent_ledger_hook.ts +76 -11
  109. package/src/scripts/injection_scan_hook.ts +120 -10
  110. package/src/scripts/lint_adr_sweep_routing.ts +365 -0
  111. package/src/scripts/lint_workflow_security.ts +53 -10
  112. package/src/scripts/lint_workflow_security_allowlist.json +2 -18
  113. package/src/scripts/report_envelope_rate.ts +166 -0
  114. package/src/scripts/review_axis_report.ts +218 -0
  115. package/src/scripts/run_supervise.ts +46 -1
  116. package/src/scripts/schemas/evals.schema.json +5 -0
  117. package/src/scripts/sync_pr_branch.ts +14 -7
  118. package/dist/cli/commands/commands.candidates.test.js +0 -257
  119. package/dist/cli/commands/commands.candidates.test.js.map +0 -1
  120. package/dist/cli/commands/profiles.test.js +0 -88
  121. package/dist/cli/commands/profiles.test.js.map +0 -1
  122. package/dist/cli/commands/recordTriggerEval.test.js +0 -113
  123. package/dist/cli/commands/recordTriggerEval.test.js.map +0 -1
  124. package/dist/cli/configRoot.test.js +0 -97
  125. package/dist/cli/configRoot.test.js.map +0 -1
  126. package/dist/cli/mcp/content.test.js +0 -54
  127. package/dist/cli/mcp/content.test.js.map +0 -1
  128. package/dist/cli/mcp/dispatch.test.js +0 -108
  129. package/dist/cli/mcp/dispatch.test.js.map +0 -1
  130. package/dist/server/writeRoot.test.js +0 -91
  131. package/dist/server/writeRoot.test.js.map +0 -1
  132. package/dist/shared/capabilities.test.js +0 -39
  133. package/dist/shared/capabilities.test.js.map +0 -1
  134. package/dist/ui/assets/index-C1pBRm1f.js +0 -26
  135. package/dist/ui/assets/index-C1pBRm1f.js.map +0 -1
  136. /package/dist/cli-delegate/{chunk-N7CDDGI3.js → chunk-CYH5SP6M.js} +0 -0
@@ -0,0 +1,129 @@
1
+ /**
2
+ * Hardened environment for spawned subprocesses
3
+ * (road-to-runtime-security-hardening Phase 1).
4
+ *
5
+ * agent-config shells out to external CLIs (the AI-council transport spawns
6
+ * `codex` / `claude` / `gemini`; hooks spawn `git`). By default `spawnSync`
7
+ * inherits the full parent `process.env`, so any attacker-influenced env var in
8
+ * the parent becomes a code-execution vector the moment the child (or a `git`
9
+ * it invokes internally) runs:
10
+ *
11
+ * - dynamic-loader preload/search-path (CWE-426 untrusted search path):
12
+ * LD_PRELOAD, LD_LIBRARY_PATH, DYLD_INSERT_LIBRARIES, DYLD_LIBRARY_PATH,
13
+ * GCONV_PATH — load attacker code into any spawned ELF/Mach-O.
14
+ * - git command-injection (CWE-88): GIT_EXTERNAL_DIFF, GIT_SSH,
15
+ * GIT_SSH_COMMAND, GIT_PROXY_COMMAND, any GIT_*_COMMAND, GIT_PAGER, PAGER —
16
+ * git runs these as shell commands (`core.pager` / external-diff RCE).
17
+ * - git config-injection (CWE-88): the GIT_CONFIG* family (GIT_CONFIG,
18
+ * GIT_CONFIG_GLOBAL/_SYSTEM, and inline GIT_CONFIG_COUNT / GIT_CONFIG_KEY_<n>
19
+ * / GIT_CONFIG_VALUE_<n>) sets ANY git config — `core.fsmonitor` runs shell
20
+ * on every `git status`; GIT_ALTERNATE_OBJECT_DIRECTORIES / HOSTALIASES
21
+ * redirect object stores / hostname resolution.
22
+ * - runtime auto-exec hooks: NODE_OPTIONS (`--require attacker.js`),
23
+ * BASH_ENV / ENV (sourced by non-interactive shells), PYTHONPATH /
24
+ * PYTHONSTARTUP / PYTHONINSPECT, PERL5OPT / PERL5LIB, RUBYOPT.
25
+ * - IFS — classic shell field-splitting hijack.
26
+ *
27
+ * This is the Least-Agency the `tool-safety` rule preaches, applied to our own
28
+ * transport. We scrub by *family* (prefix + exact name), NOT via an allowlist:
29
+ * the CLIs legitimately need arbitrary env (ANTHROPIC_API_KEY, XDG config
30
+ * paths, proxy settings), so an allowlist would break them and rot as new
31
+ * good vars appear. Deny-by-family removes the known execution vectors and
32
+ * preserves everything else. This is standard, publicly-documented OWASP/CWE
33
+ * knowledge — independently implemented here.
34
+ */
35
+ /** Exact env-var names removed unconditionally. */
36
+ const DENY_EXACT = new Set([
37
+ // dynamic loader
38
+ 'LD_PRELOAD',
39
+ 'LD_LIBRARY_PATH',
40
+ 'LD_AUDIT',
41
+ 'DYLD_INSERT_LIBRARIES',
42
+ 'DYLD_LIBRARY_PATH',
43
+ 'DYLD_FRAMEWORK_PATH',
44
+ 'GCONV_PATH',
45
+ // git command-injection
46
+ 'GIT_EXTERNAL_DIFF',
47
+ 'GIT_SSH',
48
+ 'GIT_SSH_COMMAND',
49
+ 'GIT_PROXY_COMMAND',
50
+ 'GIT_PAGER',
51
+ 'PAGER',
52
+ // git config-injection: GIT_ALTERNATE_OBJECT_DIRECTORIES can point git at
53
+ // attacker-controlled object stores; the GIT_CONFIG* family is handled by
54
+ // prefix below (it is a general arbitrary-config-injection primitive).
55
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES',
56
+ // git path-redirection: GIT_DIR / GIT_INDEX_FILE / GIT_NAMESPACE point a
57
+ // child's git at an attacker-controlled repo, index file, or ref namespace
58
+ // — ordinary git ops then read and write attacker state (low severity,
59
+ // same family as GIT_ALTERNATE_OBJECT_DIRECTORIES above).
60
+ 'GIT_DIR',
61
+ 'GIT_INDEX_FILE',
62
+ 'GIT_NAMESPACE',
63
+ // resolver hijack: HOSTALIASES remaps hostnames via an attacker file,
64
+ // redirecting any network fetch the child performs (CWE-427-adjacent).
65
+ 'HOSTALIASES',
66
+ // runtime auto-exec hooks
67
+ 'NODE_OPTIONS',
68
+ 'BASH_ENV',
69
+ 'ENV',
70
+ 'PYTHONPATH',
71
+ 'PYTHONSTARTUP',
72
+ 'PYTHONINSPECT',
73
+ 'PERL5OPT',
74
+ 'PERL5LIB',
75
+ 'RUBYOPT',
76
+ // shell field-splitting
77
+ 'IFS',
78
+ ]);
79
+ /**
80
+ * Prefix families removed by pattern. `LD_` / `DYLD_` cover loader variants we
81
+ * did not enumerate; `GIT_*_COMMAND` covers every git config-command hook; the
82
+ * `GIT_CONFIG` family (GIT_CONFIG, GIT_CONFIG_GLOBAL, GIT_CONFIG_SYSTEM, and the
83
+ * inline GIT_CONFIG_COUNT / GIT_CONFIG_KEY_<n> / GIT_CONFIG_VALUE_<n> trio) is a
84
+ * general arbitrary-config-injection primitive — an attacker can set ANY git
85
+ * config key, e.g. `core.fsmonitor` / `core.pager` / `core.sshCommand` /
86
+ * `core.hooksPath` / `alias.*`, all of which git executes as shell on ordinary
87
+ * operations (`git status` alone runs `core.fsmonitor`). The plain `GIT_*_COMMAND`
88
+ * check missed it (no `_COMMAND` suffix), so it is denied by its own prefix.
89
+ * (GIT_ASKPASS is intentionally NOT denied — it is a path, not a shell command,
90
+ * and legitimate credential helpers need it; the *_COMMAND family is the
91
+ * shell-executed one.)
92
+ */
93
+ function isDeniedByFamily(name) {
94
+ if (name.startsWith('LD_') || name.startsWith('DYLD_'))
95
+ return true;
96
+ if (name.startsWith('GIT_') && name.endsWith('_COMMAND'))
97
+ return true;
98
+ if (name === 'GIT_CONFIG' || name.startsWith('GIT_CONFIG_'))
99
+ return true;
100
+ return false;
101
+ }
102
+ /**
103
+ * Return a copy of the current environment with code-execution-injection
104
+ * vectors scrubbed, then `overrides` applied. Pass the result as
105
+ * `spawnSync`/`spawn`'s `env:` option.
106
+ *
107
+ * @param overrides values to set AFTER scrubbing (e.g. a scoped PATH or a
108
+ * package-root marker). Overrides win over both the inherited env and the
109
+ * deny-list, so a caller can deliberately re-supply a scrubbed var if it
110
+ * genuinely needs one — that is an explicit, reviewable decision at the call
111
+ * site, never an ambient inheritance.
112
+ */
113
+ export function hardenedSpawnEnv(overrides = {}) {
114
+ const out = {};
115
+ for (const [key, value] of Object.entries(process.env)) {
116
+ if (value === undefined)
117
+ continue;
118
+ if (DENY_EXACT.has(key))
119
+ continue;
120
+ if (isDeniedByFamily(key))
121
+ continue;
122
+ out[key] = value;
123
+ }
124
+ for (const [key, value] of Object.entries(overrides)) {
125
+ out[key] = value;
126
+ }
127
+ return out;
128
+ }
129
+ //# sourceMappingURL=spawn_env.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spawn_env.js","sourceRoot":"","sources":["../../../src/scripts/_lib/spawn_env.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AAEH,mDAAmD;AACnD,MAAM,UAAU,GAAwB,IAAI,GAAG,CAAC;IAC5C,iBAAiB;IACjB,YAAY;IACZ,iBAAiB;IACjB,UAAU;IACV,uBAAuB;IACvB,mBAAmB;IACnB,qBAAqB;IACrB,YAAY;IACZ,wBAAwB;IACxB,mBAAmB;IACnB,SAAS;IACT,iBAAiB;IACjB,mBAAmB;IACnB,WAAW;IACX,OAAO;IACP,0EAA0E;IAC1E,0EAA0E;IAC1E,uEAAuE;IACvE,kCAAkC;IAClC,yEAAyE;IACzE,2EAA2E;IAC3E,uEAAuE;IACvE,0DAA0D;IAC1D,SAAS;IACT,gBAAgB;IAChB,eAAe;IACf,sEAAsE;IACtE,uEAAuE;IACvE,aAAa;IACb,0BAA0B;IAC1B,cAAc;IACd,UAAU;IACV,KAAK;IACL,YAAY;IACZ,eAAe;IACf,eAAe;IACf,UAAU;IACV,UAAU;IACV,SAAS;IACT,wBAAwB;IACxB,KAAK;CACR,CAAC,CAAC;AAEH;;;;;;;;;;;;;GAaG;AACH,SAAS,gBAAgB,CAAC,IAAY;IAClC,IAAI,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACpE,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;QAAE,OAAO,IAAI,CAAC;IACtE,IAAI,IAAI,KAAK,YAAY,IAAI,IAAI,CAAC,UAAU,CAAC,aAAa,CAAC;QAAE,OAAO,IAAI,CAAC;IACzE,OAAO,KAAK,CAAC;AACjB,CAAC;AAED;;;;;;;;;;GAUG;AACH,MAAM,UAAU,gBAAgB,CAC5B,YAAoC,EAAE;IAEtC,MAAM,GAAG,GAAsB,EAAE,CAAC;IAClC,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACrD,IAAI,KAAK,KAAK,SAAS;YAAE,SAAS;QAClC,IAAI,UAAU,CAAC,GAAG,CAAC,GAAG,CAAC;YAAE,SAAS;QAClC,IAAI,gBAAgB,CAAC,GAAG,CAAC;YAAE,SAAS;QACpC,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACrB,CAAC;IACD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACnD,GAAG,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;IACrB,CAAC;IACD,OAAO,GAAG,CAAC;AACf,CAAC"}
@@ -0,0 +1,199 @@
1
+ /**
2
+ * Mode resolution for council members (Phase 2b).
3
+ *
4
+ * Ported from the retired Python `src/scripts/ai_council/modes.py` (ADR-200 —
5
+ * Python→TS migration, Phase 1). Pure resolver — never touches the
6
+ * filesystem or environment. Callers pass in already-loaded values from
7
+ * `.agent-settings.yml`.
8
+ *
9
+ * Each council member runs in exactly one transport mode per invocation:
10
+ *
11
+ * - `api` — direct SDK call against the provider's API (billable).
12
+ * - `manual` — copy-paste loop with the user as transport (free).
13
+ * - `cli` — shell out to a locally-installed provider CLI under the
14
+ * user's subscription auth. `billable=False`.
15
+ * - `auto` — resolve per provider per invocation (cli → api → unavailable).
16
+ * Accepted here as a VALUE; the concrete transport is chosen by
17
+ * `transport_resolver.ts`, which owns the chain and the billing
18
+ * classification. `manual` is never part of the auto chain.
19
+ *
20
+ * Resolution precedence — first non-empty wins:
21
+ *
22
+ * 1. Invocation flag e.g. `/council mode:manual`
23
+ * 2. Per-member setting `ai_council.members.<name>.mode`
24
+ * 3. Global setting `ai_council.mode` OR `ai_council.defaults.mode`
25
+ * (see `resolve_global_mode` — both shapes reach
26
+ * this resolver, and they must agree)
27
+ * 4. Built-in default `manual`
28
+ *
29
+ * ## Two distinct defaults — the reconciliation (road-to-zero-ceremony-detection)
30
+ *
31
+ * The contract used to describe a single fallback (`"api"`) while this module
32
+ * implemented `manual`. Both were right about different layers, and the docs
33
+ * conflated them. They are now named separately:
34
+ *
35
+ * - **Loader default** — when a config file omits `defaults.mode` entirely,
36
+ * `config.ts::_build_defaults` fills `auto` (road-to-always-on-orchestration
37
+ * Phase 3.1 — CLI-first is now the owner-set transport doctrine; before this
38
+ * flip the loader default was `api`). This is what every real config
39
+ * observes, because the loader always populates the key.
40
+ * - **Built-in fallback** — when NO layer supplies a mode at all (a settings
41
+ * dict handed straight to the resolver, no config file involved), the answer
42
+ * is `manual`, below. That is the fail-closed direction the no-silent-spend
43
+ * Iron Law requires: a caller who supplied no transport preference has not
44
+ * asked to spend money, so the free transport wins.
45
+ *
46
+ * ### Why `DEFAULT_MODE` did NOT flip to `auto` alongside the loader default
47
+ *
48
+ * `auto` still resolves to a paying rung (the api fallback) when no CLI is
49
+ * usable — it is a routing rule, not a free transport. `DEFAULT_MODE` is only
50
+ * reached by a caller that bypasses the config loader entirely (no
51
+ * `.ai-council.yml`, a hand-built settings dict handed straight to
52
+ * `resolve_mode`/`resolve_modes`). Flipping it to `auto` would let such a
53
+ * caller spend money on a config that never asked for `auto` in the first
54
+ * place — the same no-silent-spend violation the built-in fallback exists to
55
+ * prevent. Every real, file-backed config always has `defaults.mode`
56
+ * populated by the loader (now `auto`), so `DEFAULT_MODE` is never consulted
57
+ * on that path — verified in modes.test.ts and config.test.ts.
58
+ */
59
+ export const VALID_MODES = new Set(['api', 'manual', 'cli', 'auto']);
60
+ /**
61
+ * Built-in fallback when no layer supplies a mode. `manual` — free, no key,
62
+ * no spend. NOT the same as the loader's `defaults.mode` default (`auto`
63
+ * since road-to-always-on-orchestration Phase 3.1); see the module header.
64
+ */
65
+ export const DEFAULT_MODE = 'manual';
66
+ /** Raised when a configured / invoked mode is not in `VALID_MODES`. */
67
+ export class InvalidModeError extends Error {
68
+ constructor(message) {
69
+ super(message);
70
+ this.name = 'InvalidModeError';
71
+ }
72
+ }
73
+ /** Mirror Python `sorted(VALID_MODES)` for error messages: ['api','cli','manual']. */
74
+ function _sortedValidModes() {
75
+ return Array.from(VALID_MODES).sort();
76
+ }
77
+ function _normalise(value) {
78
+ if (value === null || value === undefined) {
79
+ return null;
80
+ }
81
+ if (typeof value !== 'string') {
82
+ return null;
83
+ }
84
+ const s = value.trim().toLowerCase();
85
+ return s || null;
86
+ }
87
+ function _validate(mode, source) {
88
+ if (mode === null) {
89
+ return null;
90
+ }
91
+ if (!VALID_MODES.has(mode)) {
92
+ throw new InvalidModeError(`${source} requested mode=${_pyRepr(mode)}; ` +
93
+ `expected one of: ${_pyReprList(_sortedValidModes())}`);
94
+ }
95
+ return mode;
96
+ }
97
+ /** Python repr() for a string: single-quoted. */
98
+ function _pyRepr(s) {
99
+ return `'${s}'`;
100
+ }
101
+ /** Python repr() for a list of strings: ['a', 'b']. */
102
+ function _pyReprList(items) {
103
+ return `[${items.map((i) => _pyRepr(i)).join(', ')}]`;
104
+ }
105
+ /**
106
+ * Extract the global transport mode from an `ai_council` block, accepting BOTH
107
+ * shapes the block legitimately arrives in.
108
+ *
109
+ * `council_cli.ts::_synthesize_ai_council_block` FLATTENS the loader's
110
+ * `defaults.mode` onto a top-level `mode` key, so the normal path presents the
111
+ * flat shape. But `build_members` is on the exported surface, and callers that
112
+ * hand it a RAW `.ai-council.yml`-shaped dict present the nested shape. Reading
113
+ * only the flat key silently dropped the configured default on that path and
114
+ * fell through to `DEFAULT_MODE`.
115
+ *
116
+ * Flat wins when both are present — it is the synthesized, already-resolved
117
+ * value. Returns `null` when neither shape carries a usable string, letting the
118
+ * precedence chain continue to the built-in.
119
+ */
120
+ export function resolve_global_mode(aiBlock) {
121
+ if (aiBlock === null || aiBlock === undefined) {
122
+ return null;
123
+ }
124
+ const flat = _normalise(_settingsGet(aiBlock, 'mode'));
125
+ if (flat !== null) {
126
+ return flat;
127
+ }
128
+ const defaults = _settingsGet(aiBlock, 'defaults');
129
+ if (defaults === null || defaults === undefined) {
130
+ return null;
131
+ }
132
+ if (!(defaults instanceof Map) && typeof defaults !== 'object') {
133
+ return null;
134
+ }
135
+ if (Array.isArray(defaults)) {
136
+ return null;
137
+ }
138
+ return _normalise(_settingsGet(defaults, 'mode'));
139
+ }
140
+ function _settingsGet(settings, key) {
141
+ if (settings === null || settings === undefined) {
142
+ return undefined;
143
+ }
144
+ if (settings instanceof Map) {
145
+ return settings.get(key);
146
+ }
147
+ return settings[key];
148
+ }
149
+ /**
150
+ * Resolve the effective transport mode for one member.
151
+ *
152
+ * Returns one of `VALID_MODES`. Never `null`.
153
+ *
154
+ * Throws `InvalidModeError` if any non-empty layer requests a mode not
155
+ * in `VALID_MODES`. The earliest layer (highest priority) is checked
156
+ * first; later layers are not validated when an earlier one already won.
157
+ */
158
+ export function resolve_mode(memberName, opts = {}) {
159
+ const invocationMode = opts.invocationMode ?? null;
160
+ const memberSettings = opts.memberSettings ?? null;
161
+ const globalMode = opts.globalMode ?? null;
162
+ const inv = _validate(_normalise(invocationMode), `/council mode= for ${_pyRepr(memberName)}`);
163
+ if (inv !== null) {
164
+ return inv;
165
+ }
166
+ let memberModeRaw = null;
167
+ if (memberSettings !== null) {
168
+ const v = _settingsGet(memberSettings, 'mode');
169
+ memberModeRaw = v === undefined ? null : v;
170
+ }
171
+ const member = _validate(_normalise(memberModeRaw), `ai_council.members.${memberName}.mode`);
172
+ if (member !== null) {
173
+ return member;
174
+ }
175
+ const glob = _validate(_normalise(globalMode), 'ai_council.mode');
176
+ if (glob !== null) {
177
+ return glob;
178
+ }
179
+ return DEFAULT_MODE;
180
+ }
181
+ /**
182
+ * Resolve modes for a batch of members. Convenience wrapper.
183
+ *
184
+ * `membersSettings` is the full `ai_council.members` mapping; each
185
+ * member's sub-dict is forwarded to `resolve_mode()`.
186
+ */
187
+ export function resolve_modes(memberNames, opts = {}) {
188
+ const out = {};
189
+ const settings = opts.membersSettings ?? {};
190
+ for (const name of memberNames) {
191
+ out[name] = resolve_mode(name, {
192
+ invocationMode: opts.invocationMode ?? null,
193
+ memberSettings: _settingsGet(settings, name) ?? null,
194
+ globalMode: opts.globalMode ?? null,
195
+ });
196
+ }
197
+ return out;
198
+ }
199
+ //# sourceMappingURL=modes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"modes.js","sourceRoot":"","sources":["../../../src/scripts/ai_council/modes.ts"],"names":[],"mappings":"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"}