@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +165 -237
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +34 -4
- package/dist/agent-src/commands/pr/merge.md +9 -5
- package/dist/agent-src/commands/review/changes.md +95 -14
- package/dist/agent-src/commands/roadmap/process-full.md +2 -2
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +77 -0
- package/dist/agent-src/contexts/execution/roadmap-execution-contract.md +36 -0
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +80 -0
- package/dist/agent-src/rules/untrusted-input-defense.md +9 -3
- package/dist/agent-src/skills/code-review/evals/evals.json +87 -46
- package/dist/agent-src/skills/humanizer/SKILL.md +39 -0
- package/dist/agent-src/skills/humanizer/evals/strip_fixtures.json +57 -0
- package/dist/agent-src/skills/humanizer/references/fixtures.md +54 -0
- package/dist/agent-src/skills/judge-spec-compliance/SKILL.md +154 -0
- package/dist/agent-src/skills/judge-synthesis/SKILL.md +45 -0
- package/dist/agent-src/skills/roadmap-writing/SKILL.md +18 -18
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +8 -0
- package/dist/cli/registry.js +2 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-MMSGVZKV.js → chunk-77GR3VD4.js} +65 -40
- package/dist/cli-delegate/{chunk-Q26HT4QT.js → chunk-E46KPN6K.js} +251 -150
- package/dist/cli-delegate/{chunk-EEJUMCDZ.js → chunk-PLSG6UL5.js} +1 -1
- package/dist/cli-delegate/cmd_conformance.js +89 -13
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_handoff.js +2 -2
- package/dist/cli-delegate/cmd_memory_get.js +1 -1
- package/dist/cli-delegate/cmd_route_audit.js +1 -1
- package/dist/cli-delegate/cmd_settings_get.js +1 -1
- package/dist/cli-delegate/cmd_settings_set.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +37 -15
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -3
- package/dist/discovery/dormancy-report.md +1 -1
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +6 -5
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +5 -4
- package/dist/hooks/dispatch.js +562 -204
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +808 -331
- package/dist/scripts/_lib/claude_plugin.js +162 -0
- package/dist/scripts/_lib/claude_plugin.js.map +1 -0
- package/dist/scripts/_lib/environment_detector.js +397 -0
- package/dist/scripts/_lib/environment_detector.js.map +1 -0
- package/dist/scripts/_lib/spawn_env.js +129 -0
- package/dist/scripts/_lib/spawn_env.js.map +1 -0
- package/dist/scripts/ai_council/modes.js +199 -0
- package/dist/scripts/ai_council/modes.js.map +1 -0
- package/dist/scripts/ai_council/transport_resolver.js +418 -0
- package/dist/scripts/ai_council/transport_resolver.js.map +1 -0
- package/dist/server/routes/wizard.js +3 -2
- package/dist/server/routes/wizard.js.map +1 -1
- package/dist/ui/assets/index-Cxgy63kz.js +26 -0
- package/dist/ui/assets/index-Cxgy63kz.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/docs/guidelines/agent-infra/traceability-field-mechanics.md +50 -0
- package/docs/guidelines/agent-infra/untrusted-input-spotlighting.md +17 -0
- package/package.json +7 -3
- package/src/config/estate-count-budget.json +13 -744
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-coverage.yml +247 -9
- package/src/config/gate-violation-baselines.json +72 -72
- package/src/config/publish-surface.json +60 -0
- package/src/config/requirements-trace-budget.json +32 -0
- package/src/config/rule-stub-ceilings.json +8 -1
- package/src/scripts/_dispatch.bash +7 -0
- package/src/scripts/_lib/base_tree.ts +175 -0
- package/src/scripts/_lib/billing_grant.ts +165 -0
- package/src/scripts/_lib/billing_grant_cli.ts +123 -0
- package/src/scripts/_lib/council_fallback_posture.ts +53 -6
- package/src/scripts/_lib/council_fallback_wiring.ts +61 -1
- package/src/scripts/_lib/host_capability.ts +59 -5
- package/src/scripts/_lib/quota_parked.ts +133 -0
- package/src/scripts/_lib/retrieval_sanitize.ts +62 -0
- package/src/scripts/_lib/review_telemetry.ts +196 -0
- package/src/scripts/_lib/subagent_response.ts +92 -0
- package/src/scripts/adr/regenerate_index.ts +14 -7
- package/src/scripts/ai_council/clients.ts +12 -1
- package/src/scripts/ai_council/config.ts +72 -115
- package/src/scripts/ai_council/fallback_config.ts +19 -7
- package/src/scripts/ai_council/mid_flight_fallback.ts +88 -7
- package/src/scripts/ai_council/orchestrator.ts +2 -2
- package/src/scripts/ai_council/py_format.ts +124 -0
- package/src/scripts/ai_council/transport_resolver.ts +65 -2
- package/src/scripts/audit_adr_coverage.ts +33 -6
- package/src/scripts/check_council_pin_staleness.ts +300 -0
- package/src/scripts/check_estate_count.ts +420 -293
- package/src/scripts/check_gate_coverage.ts +91 -0
- package/src/scripts/check_no_stub_inventory_table.ts +292 -0
- package/src/scripts/check_pack_size.ts +234 -1
- package/src/scripts/check_publish_surface.ts +297 -0
- package/src/scripts/check_requirements_trace.ts +378 -0
- package/src/scripts/check_secret_leak.ts +166 -12
- package/src/scripts/code_graph/cli.ts +44 -5
- package/src/scripts/code_graph/extract.ts +87 -0
- package/src/scripts/council_cli.ts +12 -16
- package/src/scripts/detect_ai_tells.ts +87 -0
- package/src/scripts/dispatch_r2_reviewer.ts +42 -0
- package/src/scripts/generate_capabilities_index.ts +41 -0
- package/src/scripts/git_authorization_hook.ts +54 -4
- package/src/scripts/hooks/block_unauthorized_git.ts +19 -1
- package/src/scripts/hooks/run_continuation_hook.ts +3 -1
- package/src/scripts/hooks/subagent_ledger_hook.ts +76 -11
- package/src/scripts/injection_scan_hook.ts +120 -10
- package/src/scripts/lint_adr_sweep_routing.ts +365 -0
- package/src/scripts/lint_workflow_security.ts +53 -10
- package/src/scripts/lint_workflow_security_allowlist.json +2 -18
- package/src/scripts/report_envelope_rate.ts +166 -0
- package/src/scripts/review_axis_report.ts +218 -0
- package/src/scripts/run_supervise.ts +46 -1
- package/src/scripts/schemas/evals.schema.json +5 -0
- package/src/scripts/sync_pr_branch.ts +14 -7
- package/dist/cli/commands/commands.candidates.test.js +0 -257
- package/dist/cli/commands/commands.candidates.test.js.map +0 -1
- package/dist/cli/commands/profiles.test.js +0 -88
- package/dist/cli/commands/profiles.test.js.map +0 -1
- package/dist/cli/commands/recordTriggerEval.test.js +0 -113
- package/dist/cli/commands/recordTriggerEval.test.js.map +0 -1
- package/dist/cli/configRoot.test.js +0 -97
- package/dist/cli/configRoot.test.js.map +0 -1
- package/dist/cli/mcp/content.test.js +0 -54
- package/dist/cli/mcp/content.test.js.map +0 -1
- package/dist/cli/mcp/dispatch.test.js +0 -108
- package/dist/cli/mcp/dispatch.test.js.map +0 -1
- package/dist/server/writeRoot.test.js +0 -91
- package/dist/server/writeRoot.test.js.map +0 -1
- package/dist/shared/capabilities.test.js +0 -39
- package/dist/shared/capabilities.test.js.map +0 -1
- package/dist/ui/assets/index-C1pBRm1f.js +0 -26
- package/dist/ui/assets/index-C1pBRm1f.js.map +0 -1
- /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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"}
|