@event4u/agent-config 9.28.0 → 9.30.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +1 -1
- package/AGENTS.md +6 -6
- package/CHANGELOG.md +172 -5
- package/README.md +2 -1
- package/agents/templates/.ai-council.yml.example +55 -24
- package/dist/agent-src/commands/analyze/inbox.md +22 -1
- package/dist/agent-src/commands/cost/report.md +2 -2
- package/dist/agent-src/commands/explain-run.md +31 -1
- package/dist/agent-src/commands/team/adversarial.md +8 -7
- package/dist/agent-src/commands/team/delegate.md +14 -12
- package/dist/agent-src/commands/team/review.md +9 -8
- package/dist/agent-src/commands/team/status.md +9 -8
- package/dist/agent-src/commands/team.md +29 -19
- package/dist/agent-src/contexts/execution/auto-dispatch-classification.md +88 -2
- package/dist/agent-src/contexts/execution/auto-orchestration-activation.md +29 -21
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +26 -15
- package/dist/agent-src/contexts/execution/orchestration-telemetry.md +22 -1
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +17 -0
- package/dist/agent-src/contexts/execution/subagent-response-contract.md +65 -1
- package/dist/agent-src/contexts/execution/subagent-routing.md +13 -11
- package/dist/agent-src/contexts/execution/subagent-spawn-contract.md +25 -0
- package/dist/agent-src/contexts/execution/subagent-steering.md +6 -3
- package/dist/agent-src/rules/delegation-policy.md +11 -7
- package/dist/agent-src/rules/preservation-guard.md +9 -0
- package/dist/agent-src/rules/settings-ask-protocol.md +8 -7
- package/dist/agent-src/rules/token-budget-discipline.md +1 -1
- package/dist/agent-src/scripts/roadmap_gates.ts +312 -0
- package/dist/agent-src/scripts/update_roadmap_progress.ts +72 -4
- package/dist/agent-src/skills/forensics-report/SKILL.md +114 -0
- package/dist/agent-src/skills/forensics-report/evals/triggers.json +17 -0
- package/dist/agent-src/skills/premortem/SKILL.md +30 -1
- package/dist/agent-src/skills/reasoning-orchestrator/SKILL.md +2 -1
- package/dist/agent-src/skills/subagent-orchestration/SKILL.md +17 -5
- package/dist/agent-src/skills/subagent-orchestration/schemas/subagent-status.json +61 -3
- package/dist/agent-src/skills/test-case-discovery/SKILL.md +1 -1
- package/dist/agent-src/templates/AGENTS.md +4 -4
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/roadmaps.md +23 -0
- package/dist/cli/registry.js +2 -0
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli-delegate/{chunk-22GCIJ2O.js → chunk-66OVWGB5.js} +1 -1
- package/dist/cli-delegate/{chunk-7OMQCH4V.js → chunk-BTJNRUX3.js} +22 -35
- package/dist/cli-delegate/{chunk-DVRS2KVR.js → chunk-DDUNNFYX.js} +24 -17
- package/dist/cli-delegate/{chunk-PQSAQKHW.js → chunk-EQY4FLJB.js} +3 -3
- package/dist/cli-delegate/{chunk-2BE5I2MN.js → chunk-IYTGIG2Y.js} +155 -91
- package/dist/cli-delegate/{chunk-GB2FSUEE.js → chunk-LCFJIQ4J.js} +32 -0
- package/dist/cli-delegate/{chunk-VSYT7OHO.js → chunk-LVRYBKTI.js} +31 -2
- package/dist/cli-delegate/chunk-MMJL4TIX.js +18 -0
- package/dist/cli-delegate/{chunk-JXARCGVN.js → chunk-MP3JT3IU.js} +1 -1
- package/dist/cli-delegate/chunk-O3CO5BFB.js +161 -0
- package/dist/cli-delegate/{chunk-REKCMYOU.js → chunk-PFBV6XRT.js} +36 -20
- package/dist/cli-delegate/chunk-RVGBH4XK.js +142 -0
- package/dist/cli-delegate/cmd_analyze_session.js +1 -1
- package/dist/cli-delegate/cmd_brand_status.js +1 -1
- package/dist/cli-delegate/cmd_conformance.js +2803 -137
- package/dist/cli-delegate/cmd_doctor.js +10 -5
- package/dist/cli-delegate/cmd_explain.js +2 -2
- package/dist/cli-delegate/cmd_export.js +4 -4
- package/dist/cli-delegate/cmd_handoff.js +88 -8
- package/dist/cli-delegate/cmd_mcp_available.js +1 -1
- package/dist/cli-delegate/cmd_migrate.js +2 -2
- package/dist/cli-delegate/cmd_packs_active.js +2 -2
- package/dist/cli-delegate/cmd_prune.js +4 -4
- package/dist/cli-delegate/cmd_route_audit.js +3 -2
- package/dist/cli-delegate/cmd_route_explain.js +1 -1
- package/dist/cli-delegate/cmd_session_recycle.js +177 -0
- package/dist/cli-delegate/cmd_settings_get.js +2 -8
- package/dist/cli-delegate/cmd_settings_migrate.js +4 -4
- package/dist/cli-delegate/cmd_settings_set.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +4 -4
- package/dist/cli-delegate/cmd_uninstall.js +4 -4
- package/dist/cli-delegate/cmd_update.js +1 -1
- package/dist/cli-delegate/cmd_upgrade.js +1 -1
- package/dist/cli-delegate/cmd_validate.js +4 -4
- package/dist/cli-delegate/cmd_versions.js +1 -1
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +72 -23
- package/dist/discovery/discovery-manifest.json.sha256 +1 -1
- package/dist/discovery/discovery-manifest.summary.md +3 -2
- package/dist/discovery/orphan-report.md +1 -1
- package/dist/discovery/packs.json +39 -2
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +12 -4
- package/dist/hooks/dispatch.js +2278 -611
- package/dist/install/install.mjs +56 -17
- package/dist/mcp/registry-manifest.json +3 -3
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +4122 -699
- package/dist/router.json +1 -1
- package/dist/scripts/_lib/agent_settings.js +54 -0
- package/dist/scripts/_lib/agent_settings.js.map +1 -1
- package/dist/scripts/condense.js +147 -2
- package/dist/scripts/condense.js.map +1 -1
- package/dist/server/schemas/settings.js +12 -7
- package/dist/server/schemas/settings.js.map +1 -1
- package/dist/shared/settingsCarveOut.js +0 -9
- package/dist/shared/settingsCarveOut.js.map +1 -1
- package/dist/shared/settingsClasses.js +11 -9
- package/dist/shared/settingsClasses.js.map +1 -1
- package/dist/ui/assets/index-B5qGYOJo.js +26 -0
- package/dist/ui/assets/index-B5qGYOJo.js.map +1 -0
- package/dist/ui/index.html +1 -1
- package/docs/contracts/settings-classes.md +58 -12
- package/package.json +1 -1
- package/src/agent-src/scripts/roadmap_gates.ts +312 -0
- package/src/agent-src/scripts/update_roadmap_progress.ts +72 -4
- package/src/agent-src/templates/AGENTS.md +4 -4
- package/src/config/agent-settings.template.yml +88 -66
- package/src/config/discovery/packs.yml +11 -0
- package/src/config/discovery/workspaces.yml +1 -1
- package/src/config/dispatch-economy-metrics.json +61 -0
- package/src/config/evaluator-budgets.json +4 -4
- package/src/config/gate-violation-baselines.json +3 -3
- package/src/config/hook-token-budget.json +91 -0
- package/src/config/recycle-threshold-budget.json +12 -0
- package/src/config/rule-enforcement-baseline.json +86 -0
- package/src/scripts/_cli/cmd_conformance.ts +31 -44
- package/src/scripts/_cli/cmd_doctor.ts +65 -48
- package/src/scripts/_cli/cmd_session_recycle.ts +226 -0
- package/src/scripts/_dispatch.bash +23 -0
- package/src/scripts/_lib/agent_settings.ts +58 -0
- package/src/scripts/_lib/auto_dispatch.corpus.test.ts +1 -1
- package/src/scripts/_lib/auto_dispatch.ts +23 -18
- package/src/scripts/_lib/capsule_trigger.ts +124 -0
- package/src/scripts/_lib/host_capability.ts +63 -6
- package/src/scripts/_lib/judgment_ladder.ts +634 -0
- package/src/scripts/_lib/orchestration_gate.ts +8 -0
- package/src/scripts/_lib/orchestration_record.ts +78 -10
- package/src/scripts/_lib/orchestration_savings.ts +1 -1
- package/src/scripts/_lib/recycle_envelope_paths.ts +35 -0
- package/src/scripts/_lib/self_repair.ts +354 -13
- package/src/scripts/_lib/self_repair_store.ts +28 -0
- package/src/scripts/_lib/session_eol.ts +230 -0
- package/src/scripts/_lib/session_role.ts +42 -0
- package/src/scripts/_lib/subagent_capsule.ts +341 -0
- package/src/scripts/_lib/subagent_response.ts +94 -0
- package/src/scripts/_lib/subagent_spawn.ts +13 -0
- package/src/scripts/_lib/subagent_steering.ts +9 -4
- package/src/scripts/_lib/tier_budget_routing.ts +6 -1
- package/src/scripts/_lib/worker_budget.ts +37 -2
- package/src/scripts/ai_council/clients.ts +126 -55
- package/src/scripts/ai_council/config.ts +94 -6
- package/src/scripts/ai_council/handoff.ts +113 -0
- package/src/scripts/ai_council/modes.ts +19 -4
- package/src/scripts/ai_council/orchestrator.ts +105 -0
- package/src/scripts/ai_council/quorum.ts +84 -0
- package/src/scripts/ai_council/session.ts +44 -0
- package/src/scripts/ai_council/transport_resolver.ts +127 -1
- package/src/scripts/ai_team/availability.ts +110 -0
- package/src/scripts/ai_team/config.ts +34 -14
- package/src/scripts/ai_team/team_dispatch.ts +71 -29
- package/src/scripts/ask_transport.ts +221 -0
- package/src/scripts/bench_critic_protocol.ts +396 -0
- package/src/scripts/bench_hook_injection.ts +234 -0
- package/src/scripts/check_adr_frontmatter.ts +84 -40
- package/src/scripts/check_branch_freshness.ts +1 -0
- package/src/scripts/check_corpus_staleness.ts +18 -4
- package/src/scripts/check_detector_corpus.ts +219 -0
- package/src/scripts/check_gitignore_freshness.ts +42 -8
- package/src/scripts/check_hook_bundle_freshness.ts +24 -1
- package/src/scripts/check_kernel_prefix_stability.ts +1 -0
- package/src/scripts/check_standing_rule_delivery.ts +73 -32
- package/src/scripts/check_static_layer_stability.ts +154 -0
- package/src/scripts/check_suppression_hygiene.ts +48 -0
- package/src/scripts/condense.ts +162 -2
- package/src/scripts/council_cli.ts +401 -25
- package/src/scripts/dispatch_economy_report.ts +448 -0
- package/src/scripts/dispatch_r2_reviewer.ts +15 -2
- package/src/scripts/explain_run.ts +206 -4
- package/src/scripts/forensics_report.ts +333 -0
- package/src/scripts/generate_settings_reference.ts +1 -0
- package/src/scripts/handoff_context_hook.ts +112 -4
- package/src/scripts/hook_manifest.yaml +117 -10
- package/src/scripts/hooks/concern_registry.ts +8 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +106 -35
- package/src/scripts/hooks/dispatch_hook.ts +39 -2
- package/src/scripts/hooks/edit_shape_hook.ts +303 -0
- package/src/scripts/hooks/orchestration_record_hook.ts +19 -1
- package/src/scripts/hooks/reread_guard_hook.ts +314 -0
- package/src/scripts/hooks/rtk_wrap_hook.ts +133 -14
- package/src/scripts/hooks/session_eol_hook.ts +268 -0
- package/src/scripts/hooks/turn_end_gate_hook.ts +507 -0
- package/src/scripts/install-hooks.sh +1 -1
- package/src/scripts/language_mirror_hook.ts +89 -0
- package/src/scripts/lint_decision_impact.ts +14 -1
- package/src/scripts/lint_discovery_vocabulary.ts +2 -0
- package/src/scripts/lint_hook_manifest.ts +82 -0
- package/src/scripts/lint_never_silent.ts +276 -0
- package/src/scripts/lint_no_activation_gates.ts +271 -0
- package/src/scripts/lint_readme_serial_comma.ts +1 -0
- package/src/scripts/lint_rule_enforcement_declaration.ts +347 -0
- package/src/scripts/measure_turn_end_gate.ts +194 -0
- package/src/scripts/orchestration_record.ts +12 -1
- package/src/scripts/rebuild_hook_bundle.ts +178 -0
- package/src/scripts/release.ts +74 -2
- package/src/scripts/report_conformance_funnel.ts +284 -0
- package/src/scripts/report_imperative_density.ts +1 -0
- package/src/scripts/routing_doctor.ts +118 -36
- package/src/scripts/score_skill_selection.ts +19 -5
- package/src/scripts/self_repair_cli.ts +337 -59
- package/src/scripts/session_eol_report.ts +261 -0
- package/src/scripts/team_events_spike.ts +271 -0
- package/src/scripts/team_review_gate_hook.ts +31 -9
- package/src/server/schemas/settings.ts +24 -17
- package/src/shared/settingsCarveOut.ts +0 -10
- package/src/shared/settingsClasses.ts +11 -9
- package/src/templates/compact-instructions.md +40 -0
- package/dist/ui/assets/index-DmR0mbk4.js +0 -26
- package/dist/ui/assets/index-DmR0mbk4.js.map +0 -1
|
@@ -48,10 +48,22 @@ import {
|
|
|
48
48
|
type BillingClass,
|
|
49
49
|
type EnvironmentReport,
|
|
50
50
|
} from '../_lib/environment_detector.js';
|
|
51
|
+
import { resolve_mode } from './modes.js';
|
|
51
52
|
|
|
52
53
|
/** A concrete transport a member can actually run on. `auto` is not one. */
|
|
53
54
|
export type Transport = 'api' | 'manual' | 'cli';
|
|
54
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Machine-readable reason a member is `absent` from a pass
|
|
58
|
+
* (road-to-always-on-orchestration Phase 3.2). The static half — `no_binary`
|
|
59
|
+
* / `no_auth` — is decided here, at resolution time, before any provider
|
|
60
|
+
* call is attempted. The runtime half — `timeout` / `quota` — only exists
|
|
61
|
+
* once a call was actually attempted and failed; see
|
|
62
|
+
* `absentReasonFromCliFailure` below for the mapping FROM the mid-flight
|
|
63
|
+
* `CliFailureClass` a caller already classifies its errors into.
|
|
64
|
+
*/
|
|
65
|
+
export type AbsentReason = 'no_binary' | 'no_auth' | 'timeout' | 'quota';
|
|
66
|
+
|
|
55
67
|
/** The accepted values of the `mode` key, including the resolver-only `auto`. */
|
|
56
68
|
export const VALID_TRANSPORT_MODES: ReadonlySet<string> = new Set([
|
|
57
69
|
'api',
|
|
@@ -73,6 +85,16 @@ export interface ResolvedTransport {
|
|
|
73
85
|
readonly makesProviderCall: boolean;
|
|
74
86
|
/** One-line reason. Non-empty exactly when `available` is false. */
|
|
75
87
|
readonly reason: string | null;
|
|
88
|
+
/**
|
|
89
|
+
* Machine-readable classification of `reason`, for a pass artifact that
|
|
90
|
+
* needs to bucket absent members without parsing prose (Phase 3.2).
|
|
91
|
+
* Non-null for the two STATIC auto-chain failures this function can
|
|
92
|
+
* classify (`no_binary` / `no_auth`); `null` when `available` is true,
|
|
93
|
+
* and also `null` for the "unknown mode" validation-error path below —
|
|
94
|
+
* that is a config bug, not a missing-capability finding, so it does not
|
|
95
|
+
* belong in this enum.
|
|
96
|
+
*/
|
|
97
|
+
readonly absentReason: AbsentReason | null;
|
|
76
98
|
}
|
|
77
99
|
|
|
78
100
|
export interface ResolveTransportOptions {
|
|
@@ -149,6 +171,7 @@ export function resolveTransport(opts: ResolveTransportOptions): ResolvedTranspo
|
|
|
149
171
|
authSource,
|
|
150
172
|
makesProviderCall: false,
|
|
151
173
|
reason: null,
|
|
174
|
+
absentReason: null,
|
|
152
175
|
};
|
|
153
176
|
}
|
|
154
177
|
|
|
@@ -160,6 +183,7 @@ export function resolveTransport(opts: ResolveTransportOptions): ResolvedTranspo
|
|
|
160
183
|
authSource,
|
|
161
184
|
makesProviderCall: true,
|
|
162
185
|
reason: null,
|
|
186
|
+
absentReason: null,
|
|
163
187
|
};
|
|
164
188
|
}
|
|
165
189
|
|
|
@@ -185,6 +209,7 @@ export function resolveTransport(opts: ResolveTransportOptions): ResolvedTranspo
|
|
|
185
209
|
authSource,
|
|
186
210
|
makesProviderCall: true,
|
|
187
211
|
reason: null,
|
|
212
|
+
absentReason: null,
|
|
188
213
|
};
|
|
189
214
|
}
|
|
190
215
|
|
|
@@ -197,16 +222,80 @@ export function resolveTransport(opts: ResolveTransportOptions): ResolvedTranspo
|
|
|
197
222
|
authSource,
|
|
198
223
|
makesProviderCall: true,
|
|
199
224
|
reason: null,
|
|
225
|
+
absentReason: null,
|
|
200
226
|
};
|
|
201
227
|
}
|
|
202
228
|
|
|
203
|
-
return unavailable(
|
|
229
|
+
return unavailable(
|
|
230
|
+
provider,
|
|
231
|
+
billing,
|
|
232
|
+
autoUnavailableReason(provider, binary, cliAuthed),
|
|
233
|
+
classifyStaticAbsentReason(binary),
|
|
234
|
+
);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export interface ResolveMemberTransportOptions {
|
|
238
|
+
readonly provider: string;
|
|
239
|
+
/** The single read-only environment report `resolveTransport` needs. */
|
|
240
|
+
readonly report: EnvironmentReport;
|
|
241
|
+
/** e.g. `/council mode:manual`. Highest precedence layer. */
|
|
242
|
+
readonly invocationMode?: string | null;
|
|
243
|
+
/** The member's own config sub-dict (`ai_council.members.<name>`). */
|
|
244
|
+
readonly memberSettings?: ReadonlyMap<string, unknown> | Record<string, unknown> | null;
|
|
245
|
+
/** `ai_council.mode` / `defaults.mode`, already extracted by the caller. */
|
|
246
|
+
readonly globalMode?: string | null;
|
|
247
|
+
readonly binaryOverride?: string | null;
|
|
248
|
+
readonly apiKeyPresent?: boolean;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export interface ResolvedMemberTransport extends ResolvedTransport {
|
|
252
|
+
/** The mode `resolve_mode` chose BEFORE any `auto` expansion. */
|
|
253
|
+
readonly configuredMode: string;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/**
|
|
257
|
+
* The single reconciled entry point for "given this member's config, what
|
|
258
|
+
* transport does it actually run on" (road-to-always-on-orchestration
|
|
259
|
+
* Phase 3.1). Composes `modes.ts::resolve_mode` (which layer decided the
|
|
260
|
+
* mode) with `resolveTransport` (what `auto` expands to on THIS machine) so
|
|
261
|
+
* a caller never has to replicate either step by hand.
|
|
262
|
+
*
|
|
263
|
+
* This exists because the two functions previously lived at arm's length:
|
|
264
|
+
* `resolve_mode` can legitimately return the literal string `'auto'`, and
|
|
265
|
+
* nothing forced a caller to then route that value through
|
|
266
|
+
* `resolveTransport` — a hand-rolled `mode === 'api' | 'cli' | 'manual'`
|
|
267
|
+
* switch (the shape `council_cli.ts::build_members` still uses today) has
|
|
268
|
+
* no `'auto'` case and treats it as an unrecognised transport instead of
|
|
269
|
+
* expanding it. `resolveMemberTransport` is the fix: there is no longer a
|
|
270
|
+
* gap between "which mode was configured" and "which transport that becomes"
|
|
271
|
+
* for any caller that uses it instead of the two functions separately.
|
|
272
|
+
*/
|
|
273
|
+
export function resolveMemberTransport(
|
|
274
|
+
opts: ResolveMemberTransportOptions,
|
|
275
|
+
): ResolvedMemberTransport {
|
|
276
|
+
const configuredMode = resolve_mode(opts.provider, {
|
|
277
|
+
invocationMode: opts.invocationMode ?? null,
|
|
278
|
+
memberSettings: opts.memberSettings ?? null,
|
|
279
|
+
globalMode: opts.globalMode ?? null,
|
|
280
|
+
});
|
|
281
|
+
const resolved = resolveTransport({
|
|
282
|
+
provider: opts.provider,
|
|
283
|
+
mode: configuredMode,
|
|
284
|
+
report: opts.report,
|
|
285
|
+
binaryOverride: opts.binaryOverride ?? null,
|
|
286
|
+
// `exactOptionalPropertyTypes` rejects an explicit `undefined` for an
|
|
287
|
+
// optional-boolean field, so the key is omitted entirely rather than
|
|
288
|
+
// forwarded as `apiKeyPresent: undefined`.
|
|
289
|
+
...(opts.apiKeyPresent !== undefined ? { apiKeyPresent: opts.apiKeyPresent } : {}),
|
|
290
|
+
});
|
|
291
|
+
return { ...resolved, configuredMode };
|
|
204
292
|
}
|
|
205
293
|
|
|
206
294
|
function unavailable(
|
|
207
295
|
_provider: string,
|
|
208
296
|
billing: BillingClass,
|
|
209
297
|
reason: string,
|
|
298
|
+
absentReason: AbsentReason | null = null,
|
|
210
299
|
): ResolvedTransport {
|
|
211
300
|
return {
|
|
212
301
|
available: false,
|
|
@@ -217,9 +306,21 @@ function unavailable(
|
|
|
217
306
|
authSource: null,
|
|
218
307
|
makesProviderCall: false,
|
|
219
308
|
reason,
|
|
309
|
+
absentReason,
|
|
220
310
|
};
|
|
221
311
|
}
|
|
222
312
|
|
|
313
|
+
/**
|
|
314
|
+
* `no_binary` when the CLI binary itself never resolved (whether or not a
|
|
315
|
+
* credential was found — a credential with nothing to run it against is
|
|
316
|
+
* still "the binary is missing" from the caller's point of view); `no_auth`
|
|
317
|
+
* when the binary IS present but neither the cli-auth nor the key rung
|
|
318
|
+
* resolved. Mirrors `autoUnavailableReason`'s three-way branch below.
|
|
319
|
+
*/
|
|
320
|
+
function classifyStaticAbsentReason(binary: string | null): AbsentReason {
|
|
321
|
+
return binary === null ? 'no_binary' : 'no_auth';
|
|
322
|
+
}
|
|
323
|
+
|
|
223
324
|
/** The one-line reason `auto` prints when no rung resolved. */
|
|
224
325
|
function autoUnavailableReason(
|
|
225
326
|
provider: string,
|
|
@@ -298,6 +399,31 @@ export function isFallbackEligible(failure: CliFailureClass): boolean {
|
|
|
298
399
|
return FALLBACK_ELIGIBLE.has(failure);
|
|
299
400
|
}
|
|
300
401
|
|
|
402
|
+
/**
|
|
403
|
+
* Maps a mid-flight `CliFailureClass` (a call was actually attempted and
|
|
404
|
+
* failed) onto the same `AbsentReason` enum a static resolution failure
|
|
405
|
+
* uses (road-to-always-on-orchestration Phase 3.2/3.4), so a pass artifact
|
|
406
|
+
* can bucket every absent member — whether it never got a transport at all
|
|
407
|
+
* or lost one partway through — under one vocabulary. Returns `null` for a
|
|
408
|
+
* failure class outside the four-value enum (`cli_unsupported`,
|
|
409
|
+
* `server_error`, `other`); a caller falls back to the raw failure detail
|
|
410
|
+
* for those rather than mis-classifying them.
|
|
411
|
+
*/
|
|
412
|
+
export function absentReasonFromCliFailure(failure: CliFailureClass): AbsentReason | null {
|
|
413
|
+
switch (failure) {
|
|
414
|
+
case 'binary_missing':
|
|
415
|
+
return 'no_binary';
|
|
416
|
+
case 'auth_rejected':
|
|
417
|
+
return 'no_auth';
|
|
418
|
+
case 'timeout':
|
|
419
|
+
return 'timeout';
|
|
420
|
+
case 'quota_exhausted':
|
|
421
|
+
return 'quota';
|
|
422
|
+
default:
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
|
|
301
427
|
/**
|
|
302
428
|
* One-shot, per-provider fallback ledger for a single invocation.
|
|
303
429
|
*
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `/team` availability — CLI-first, not settings-gated
|
|
3
|
+
* (road-to-always-on-orchestration Phase 1, Step 1.3).
|
|
4
|
+
*
|
|
5
|
+
* `ai_team.enabled` — the former master switch — was DELETED. `/team`'s
|
|
6
|
+
* on/off state is now a FACT resolved from the machine, the same doctrine
|
|
7
|
+
* already applied to the subagent and council layers (Phase 1): the codex
|
|
8
|
+
* CLI binary must resolve on `$PATH`, AND some auth for the `openai`
|
|
9
|
+
* provider must be detectable (subscription login, API key, key file, or
|
|
10
|
+
* env key — the same transport-preference order `auto` walks via
|
|
11
|
+
* `strongestAuth`). Reuses the existing, read-only, zero-spend,
|
|
12
|
+
* per-process-cached detector (`_lib/environment_detector.ts`); this module
|
|
13
|
+
* adds NO new probing and NO new cache of its own.
|
|
14
|
+
*
|
|
15
|
+
* `ai_team.allow_delegate` is UNCHANGED by this module: a distinct,
|
|
16
|
+
* write-access authorization gate that stacks ON TOP of availability, never
|
|
17
|
+
* a replacement for it (`assert_delegate_allowed` in `team_dispatch.ts`
|
|
18
|
+
* still checks it separately, after availability).
|
|
19
|
+
*/
|
|
20
|
+
import { detectEnvironment, strongestAuth } from '../_lib/environment_detector.js';
|
|
21
|
+
import { load_agent_settings, type SettingsDict } from '../_lib/agent_settings.js';
|
|
22
|
+
import { _team_codex_binary_name, shutilWhich } from '../_cli/cmd_doctor.js';
|
|
23
|
+
|
|
24
|
+
/** Tool id `environment_detector` uses for the codex CLI (`install/toolDetection.ts`). */
|
|
25
|
+
const CODEX_HOST_ID = 'codex';
|
|
26
|
+
|
|
27
|
+
/** Provider id `environment_detector` / `PROVIDER_CLI_META` uses for the codex CLI. */
|
|
28
|
+
const CODEX_PROVIDER = 'openai';
|
|
29
|
+
|
|
30
|
+
/** The provider default `_team_codex_binary_name` falls back to with no override configured. */
|
|
31
|
+
const DEFAULT_CODEX_BINARY = 'codex';
|
|
32
|
+
|
|
33
|
+
export interface TeamAvailability {
|
|
34
|
+
readonly available: boolean;
|
|
35
|
+
/** One clear line for the user — populated only when `available` is false. */
|
|
36
|
+
readonly reason: string | null;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* codex CLI binary + auth presence — the availability half of the `/team`
|
|
41
|
+
* gate. Cached per process via `detectEnvironment()`'s own no-argument memo
|
|
42
|
+
* (`resetEnvironmentCache()` in tests); this function adds no caching of
|
|
43
|
+
* its own.
|
|
44
|
+
*
|
|
45
|
+
* m5 fix (independent-review finding) — `detectEnvironment()`'s host probe
|
|
46
|
+
* is hardcoded to the literal binary name `codex`; it has no way to be told
|
|
47
|
+
* "check this other name/path instead". So an operator who set
|
|
48
|
+
* `members.openai.binary:` in `.ai-council.yml` (honoured under an
|
|
49
|
+
* effective mode of `cli` OR `auto` — `cmd_doctor.ts::_mayRunOverCli`) was
|
|
50
|
+
* invisible to this check: `/team` could report "codex CLI not available"
|
|
51
|
+
* even with a working, custom-named/pathed binary configured and
|
|
52
|
+
* functional. `_team_codex_binary_name` (shared with `cmd_doctor.ts`'s own
|
|
53
|
+
* `council-cli` / `team` checks, so the override-resolution logic has one
|
|
54
|
+
* source) resolves the SAME name `cmd_doctor` would use; when it differs
|
|
55
|
+
* from the provider default, this probes that name directly instead of
|
|
56
|
+
* trusting the fixed-name host detector.
|
|
57
|
+
*/
|
|
58
|
+
export function checkCodexAvailability(project_root: string = process.cwd()): TeamAvailability {
|
|
59
|
+
const report = detectEnvironment();
|
|
60
|
+
const configuredBinary = _team_codex_binary_name(project_root);
|
|
61
|
+
const installed =
|
|
62
|
+
configuredBinary !== DEFAULT_CODEX_BINARY
|
|
63
|
+
? shutilWhich(configuredBinary) !== null
|
|
64
|
+
: (report.hosts.find((h) => h.id === CODEX_HOST_ID)?.installed ?? false);
|
|
65
|
+
if (!installed) {
|
|
66
|
+
return {
|
|
67
|
+
available: false,
|
|
68
|
+
reason:
|
|
69
|
+
'codex CLI not available — /team needs the codex CLI installed and ' +
|
|
70
|
+
'authenticated. Install: `npm install -g @openai/codex`, then run ' +
|
|
71
|
+
'`codex login`. Verify with `agent-config doctor --check team`.',
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (strongestAuth(report, CODEX_PROVIDER) === null) {
|
|
75
|
+
return {
|
|
76
|
+
available: false,
|
|
77
|
+
reason:
|
|
78
|
+
'codex CLI not available — no auth detected for it. Run `codex login` ' +
|
|
79
|
+
'(or set `OPENAI_API_KEY`). Verify with `agent-config doctor --check team`.',
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
return { available: true, reason: null };
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* `emergency.orchestration_halt` — the one audited incident switch over the
|
|
87
|
+
* always-on orchestration stack (subagents, council, TEAM). Reads the
|
|
88
|
+
* project settings cascade fresh (or an injected dict for tests) on every
|
|
89
|
+
* call — no cache of its own, so an armed halt takes effect on the very
|
|
90
|
+
* next read, same as every other `orchestration_halt` reader in the tree
|
|
91
|
+
* (`routing_doctor.ts`, `delegation_nudge_hook.ts`).
|
|
92
|
+
*/
|
|
93
|
+
export function isOrchestrationHalted(
|
|
94
|
+
settings?: SettingsDict | null,
|
|
95
|
+
cwd?: string | null,
|
|
96
|
+
): boolean {
|
|
97
|
+
const merged = settings ?? load_agent_settings({ cwd: cwd ?? process.cwd() });
|
|
98
|
+
const emergency = (merged as Record<string, unknown>)['emergency'];
|
|
99
|
+
if (typeof emergency !== 'object' || emergency === null || Array.isArray(emergency)) {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
return (emergency as Record<string, unknown>)['orchestration_halt'] === true;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** The one-line halt notice — `/team` prints this verbatim when halted. */
|
|
106
|
+
export const ORCHESTRATION_HALT_MESSAGE =
|
|
107
|
+
'/team is halted (`emergency.orchestration_halt: true`) — the always-on ' +
|
|
108
|
+
'orchestration stack (subagents, council, team) is paused for incident ' +
|
|
109
|
+
'response. Resume: set `orchestration_halt: false` with a non-empty ' +
|
|
110
|
+
'`orchestration_halt_justification` in `.agent-settings.yml`.';
|
|
@@ -4,23 +4,36 @@
|
|
|
4
4
|
*
|
|
5
5
|
* Mirrors the fail-closed validation posture of the council loader
|
|
6
6
|
* (`src/scripts/ai_council/config.ts`) at a fraction of its surface: the
|
|
7
|
-
* `ai_team` block has
|
|
8
|
-
* loader is deliberately lean — defaults,
|
|
9
|
-
* unknown-key rejection. Contract:
|
|
7
|
+
* `ai_team` block has a handful of flat keys and no nested sub-schemas
|
|
8
|
+
* beyond `review_gate`, so this loader is deliberately lean — defaults,
|
|
9
|
+
* per-key type checks, and hard unknown-key rejection. Contract:
|
|
10
|
+
* `docs/contracts/ai-team-config.md`.
|
|
10
11
|
*
|
|
11
12
|
* Unlike the council (ALWAYS user-global `.ai-council.yml`, ADR-104),
|
|
12
13
|
* `ai_team` lives in the PROJECT settings cascade (`.agent-settings.yml`
|
|
13
|
-
* via `load_agent_settings`) — team mode is a per-project posture
|
|
14
|
-
*
|
|
15
|
-
*
|
|
14
|
+
* via `load_agent_settings`) — team mode is a per-project posture, not a
|
|
15
|
+
* per-developer credential store; the codex CLI carries the subscription
|
|
16
|
+
* auth itself.
|
|
17
|
+
*
|
|
18
|
+
* `enabled` — the former master on/off switch — was DELETED
|
|
19
|
+
* (road-to-always-on-orchestration Phase 1, Step 1.3): `/team`'s
|
|
20
|
+
* availability is now a FACT resolved from the machine (codex CLI on
|
|
21
|
+
* `$PATH` + detectable auth — `src/scripts/ai_team/availability.ts`), not
|
|
22
|
+
* a settings flag, matching the doctrine already applied to the
|
|
23
|
+
* subagent/council layers. A leftover `enabled` key from an older install
|
|
24
|
+
* is accepted-and-ignored here (never rejected as unknown, never applied
|
|
25
|
+
* by any reader) so an existing project's `.agent-settings.yml` does not
|
|
26
|
+
* start failing `/team` outright; `load_agent_settings` still surfaces the
|
|
27
|
+
* one-line deprecation warning for it (`_lib/agent_settings.ts`).
|
|
16
28
|
*
|
|
17
29
|
* Validation contract (all enforced at load time, fail-closed):
|
|
18
30
|
*
|
|
19
|
-
* 1. `ai_team` absent → defaults
|
|
31
|
+
* 1. `ai_team` absent → defaults. Non-mapping → error.
|
|
20
32
|
* 2. Unknown keys under `ai_team` are rejected — a typo must never
|
|
21
33
|
* silently disable a gate (`allow_delegate` misspelled = delegation
|
|
22
|
-
* stays off AND the load fails loudly).
|
|
23
|
-
*
|
|
34
|
+
* stays off AND the load fails loudly). `enabled` is the one
|
|
35
|
+
* deliberate exception: accepted, untyped, ignored (see above).
|
|
36
|
+
* 3. `allow_delegate`, `suppress_setup_hint` are booleans.
|
|
24
37
|
* 4. `model` is a non-empty string. `'auto'` = pass no `--model` flag
|
|
25
38
|
* (the codex CLI default applies); any other value passes through
|
|
26
39
|
* verbatim.
|
|
@@ -47,7 +60,6 @@ export interface AiTeamReviewGateConfig {
|
|
|
47
60
|
}
|
|
48
61
|
|
|
49
62
|
export interface AiTeamConfig {
|
|
50
|
-
readonly enabled: boolean;
|
|
51
63
|
readonly model: string;
|
|
52
64
|
readonly allow_delegate: boolean;
|
|
53
65
|
readonly max_calls_per_day: number;
|
|
@@ -63,7 +75,6 @@ export const AI_TEAM_REVIEW_GATE_DEFAULTS: AiTeamReviewGateConfig = Object.freez
|
|
|
63
75
|
|
|
64
76
|
/** Shipped defaults — byte-parity with `src/config/agent-settings.template.yml`. */
|
|
65
77
|
export const AI_TEAM_DEFAULTS: AiTeamConfig = Object.freeze({
|
|
66
|
-
enabled: false,
|
|
67
78
|
model: 'auto',
|
|
68
79
|
allow_delegate: false,
|
|
69
80
|
max_calls_per_day: 50,
|
|
@@ -79,7 +90,6 @@ export const AI_TEAM_DEFAULTS: AiTeamConfig = Object.freeze({
|
|
|
79
90
|
export const AI_TEAM_MODEL_AUTO = 'auto';
|
|
80
91
|
|
|
81
92
|
const _KNOWN_KEYS: ReadonlySet<string> = new Set([
|
|
82
|
-
'enabled',
|
|
83
93
|
'model',
|
|
84
94
|
'allow_delegate',
|
|
85
95
|
'max_calls_per_day',
|
|
@@ -87,6 +97,14 @@ const _KNOWN_KEYS: ReadonlySet<string> = new Set([
|
|
|
87
97
|
'review_gate',
|
|
88
98
|
]);
|
|
89
99
|
|
|
100
|
+
/**
|
|
101
|
+
* Deleted key accepted-and-ignored for migration grace (see module docstring).
|
|
102
|
+
* Never added to `_KNOWN_KEYS` — it carries no validated type and is never
|
|
103
|
+
* read into `AiTeamConfig`; it exists in this set only so the unknown-key
|
|
104
|
+
* loop below does not fail-closed an existing project's leftover value.
|
|
105
|
+
*/
|
|
106
|
+
const _DELETED_KEYS: ReadonlySet<string> = new Set(['enabled']);
|
|
107
|
+
|
|
90
108
|
const _KNOWN_REVIEW_GATE_KEYS: ReadonlySet<string> = new Set([
|
|
91
109
|
'managed',
|
|
92
110
|
'max_consecutive_blocks',
|
|
@@ -175,6 +193,9 @@ export function build_ai_team_config(raw: unknown): AiTeamConfig {
|
|
|
175
193
|
}
|
|
176
194
|
|
|
177
195
|
for (const key of Object.keys(raw)) {
|
|
196
|
+
if (_DELETED_KEYS.has(key)) {
|
|
197
|
+
continue; // deleted, accepted-and-ignored — see module docstring.
|
|
198
|
+
}
|
|
178
199
|
if (!_KNOWN_KEYS.has(key)) {
|
|
179
200
|
throw new TeamConfigError(
|
|
180
201
|
`ai_team.${key}: unknown key; valid: ` +
|
|
@@ -183,7 +204,6 @@ export function build_ai_team_config(raw: unknown): AiTeamConfig {
|
|
|
183
204
|
}
|
|
184
205
|
}
|
|
185
206
|
|
|
186
|
-
const enabled = _requireBool(raw, 'enabled', AI_TEAM_DEFAULTS.enabled);
|
|
187
207
|
const allow_delegate = _requireBool(raw, 'allow_delegate', AI_TEAM_DEFAULTS.allow_delegate);
|
|
188
208
|
const suppress_setup_hint = _requireBool(
|
|
189
209
|
raw,
|
|
@@ -216,7 +236,7 @@ export function build_ai_team_config(raw: unknown): AiTeamConfig {
|
|
|
216
236
|
|
|
217
237
|
const review_gate = _build_review_gate('review_gate' in raw ? raw['review_gate'] : undefined);
|
|
218
238
|
|
|
219
|
-
return {
|
|
239
|
+
return { model, allow_delegate, max_calls_per_day, suppress_setup_hint, review_gate };
|
|
220
240
|
}
|
|
221
241
|
|
|
222
242
|
/**
|
|
@@ -18,11 +18,20 @@
|
|
|
18
18
|
* instead of live repo access, no background jobs, explicitly worse than the
|
|
19
19
|
* plugin — pointing Claude-Code users back to the Phase 2 plugin path.
|
|
20
20
|
*
|
|
21
|
-
* Governance reuse (Phase 3 Step 3): same `ai_team` config
|
|
22
|
-
* `
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
21
|
+
* Governance reuse (Phase 3 Step 3): same `ai_team` config, same
|
|
22
|
+
* `cli_call_budget` openai quota bucket (the gate and the recording both
|
|
23
|
+
* live in `CliClient.ask` — no new counter), same `_AUTH_FAILURE_PATTERNS`
|
|
24
|
+
* classification. `--manual` renders the bundle between `═` rules for
|
|
25
|
+
* paste-into-web usage — no call, no quota.
|
|
26
|
+
*
|
|
27
|
+
* Availability (road-to-always-on-orchestration Phase 1, Step 1.3):
|
|
28
|
+
* `ai_team.enabled` was DELETED. Every dispatch entry point below is
|
|
29
|
+
* fail-closed on TWO facts instead — `emergency.orchestration_halt` (the
|
|
30
|
+
* one audited incident switch over the always-on stack) and
|
|
31
|
+
* `checkCodexAvailability()` (codex CLI + auth presence,
|
|
32
|
+
* `./availability.js`) — never a settings flag. `ai_team.allow_delegate`
|
|
33
|
+
* is unchanged: a second, write-access opt-in that stacks ON TOP of
|
|
34
|
+
* availability in `assert_delegate_allowed`.
|
|
26
35
|
*
|
|
27
36
|
* The review system prompt is derived from the adversarial-review findings
|
|
28
37
|
* shape of an Apache-2.0 upstream — attribution in the repo-root NOTICE file.
|
|
@@ -38,6 +47,12 @@ import {
|
|
|
38
47
|
load_cli_call_counts,
|
|
39
48
|
OpenAICliClient,
|
|
40
49
|
} from '../ai_council/clients.js';
|
|
50
|
+
import {
|
|
51
|
+
checkCodexAvailability,
|
|
52
|
+
isOrchestrationHalted,
|
|
53
|
+
ORCHESTRATION_HALT_MESSAGE,
|
|
54
|
+
type TeamAvailability,
|
|
55
|
+
} from './availability.js';
|
|
41
56
|
import { AI_TEAM_MODEL_AUTO, type AiTeamConfig, load_ai_team_config } from './config.js';
|
|
42
57
|
|
|
43
58
|
// ── errors ──────────────────────────────────────────────────────────────
|
|
@@ -51,50 +66,67 @@ export class TeamDispatchError extends Error {
|
|
|
51
66
|
}
|
|
52
67
|
|
|
53
68
|
/**
|
|
54
|
-
* Raised when `
|
|
55
|
-
*
|
|
69
|
+
* Raised when `/team` is unavailable — either `emergency.orchestration_halt`
|
|
70
|
+
* is set, or the codex CLI / auth availability check
|
|
71
|
+
* (`checkCodexAvailability`) failed. `reason` is the one-line message the
|
|
72
|
+
* relevant check produced; callers print it verbatim. There is no more
|
|
73
|
+
* settings flag behind this — see the module docstring.
|
|
56
74
|
*/
|
|
57
75
|
export class TeamDisabledError extends TeamDispatchError {
|
|
58
|
-
constructor() {
|
|
59
|
-
super(
|
|
60
|
-
'team mode is disabled (`ai_team.enabled: false`, the shipped default). ' +
|
|
61
|
-
'Set `ai_team.enabled: true` in `.agent-settings.yml` to opt in — ' +
|
|
62
|
-
'see docs/contracts/ai-team-config.md.',
|
|
63
|
-
);
|
|
76
|
+
constructor(reason: string) {
|
|
77
|
+
super(reason);
|
|
64
78
|
this.name = 'TeamDisabledError';
|
|
65
79
|
}
|
|
66
80
|
}
|
|
67
81
|
|
|
68
82
|
/**
|
|
69
|
-
* Thrown by `assert_delegate_allowed` when `
|
|
83
|
+
* Thrown by `assert_delegate_allowed` when `/team` is available but the
|
|
70
84
|
* second opt-in (`ai_team.allow_delegate`) is not. `/team delegate` is the
|
|
71
85
|
* only wrapper that hands WRITE access to the second model, so it is
|
|
72
|
-
* double-gated;
|
|
86
|
+
* double-gated; availability alone is never delegate authorization.
|
|
73
87
|
*/
|
|
74
88
|
export class TeamDelegateDisabledError extends TeamDispatchError {
|
|
75
89
|
constructor() {
|
|
76
90
|
super(
|
|
77
91
|
'`/team delegate` is disabled (`ai_team.allow_delegate: false`, the shipped ' +
|
|
78
92
|
'default). It is the only team-mode wrapper that hands write access to ' +
|
|
79
|
-
'the second model, so it needs its own opt-in: set
|
|
80
|
-
'`ai_team.
|
|
81
|
-
'
|
|
93
|
+
'the second model, so it needs its own opt-in: set ' +
|
|
94
|
+
'`ai_team.allow_delegate: true` in `.agent-settings.yml` (once /team ' +
|
|
95
|
+
'itself is available — codex CLI installed and authenticated) — see ' +
|
|
96
|
+
'docs/contracts/ai-team-config.md.',
|
|
82
97
|
);
|
|
83
98
|
this.name = 'TeamDelegateDisabledError';
|
|
84
99
|
}
|
|
85
100
|
}
|
|
86
101
|
|
|
102
|
+
/** Test seams for the two availability facts (`assert_delegate_allowed`, `run_team_review`). */
|
|
103
|
+
export interface TeamAvailabilityOverrides {
|
|
104
|
+
/** Default: `checkCodexAvailability()` — the real, cached, zero-spend probe. */
|
|
105
|
+
availability?: TeamAvailability;
|
|
106
|
+
/** Default: `isOrchestrationHalted(undefined, cwd)` — reads the real settings cascade. */
|
|
107
|
+
halted?: boolean;
|
|
108
|
+
}
|
|
109
|
+
|
|
87
110
|
/**
|
|
88
111
|
* Deterministic mirror of the `/team delegate` double gate: throws
|
|
89
|
-
* `TeamDisabledError`
|
|
112
|
+
* `TeamDisabledError` when halted or `/team` is unavailable, then
|
|
90
113
|
* `TeamDelegateDisabledError` unless `ai_team.allow_delegate`. The command
|
|
91
114
|
* doc's prose gates instruct the agent; this guard is the machine-checkable
|
|
92
115
|
* contract (`--delegate-gate` CLI mode) tests pin against.
|
|
93
116
|
*/
|
|
94
|
-
export function assert_delegate_allowed(
|
|
117
|
+
export function assert_delegate_allowed(
|
|
118
|
+
config?: AiTeamConfig,
|
|
119
|
+
cwd?: string | null,
|
|
120
|
+
overrides: TeamAvailabilityOverrides = {},
|
|
121
|
+
): void {
|
|
95
122
|
const cfg = config ?? load_ai_team_config({ cwd: cwd ?? null });
|
|
96
|
-
|
|
97
|
-
|
|
123
|
+
const halted = overrides.halted ?? isOrchestrationHalted(undefined, cwd ?? null);
|
|
124
|
+
if (halted) {
|
|
125
|
+
throw new TeamDisabledError(ORCHESTRATION_HALT_MESSAGE);
|
|
126
|
+
}
|
|
127
|
+
const availability = overrides.availability ?? checkCodexAvailability(cwd ?? undefined);
|
|
128
|
+
if (!availability.available) {
|
|
129
|
+
throw new TeamDisabledError(availability.reason ?? 'codex CLI not available.');
|
|
98
130
|
}
|
|
99
131
|
if (!cfg.allow_delegate) {
|
|
100
132
|
throw new TeamDelegateDisabledError();
|
|
@@ -511,6 +543,10 @@ export interface RunTeamReviewOptions {
|
|
|
511
543
|
cli_calls_path?: string | null;
|
|
512
544
|
/** Sink for user-facing output; default `process.stdout`. */
|
|
513
545
|
out?: (line: string) => void;
|
|
546
|
+
/** Test seam: override the availability check (default: the real, cached probe). */
|
|
547
|
+
availability?: TeamAvailability;
|
|
548
|
+
/** Test seam: override the halted flag (default: read from the settings cascade). */
|
|
549
|
+
halted?: boolean;
|
|
514
550
|
}
|
|
515
551
|
|
|
516
552
|
function _quota_snapshot(config: AiTeamConfig, cli_calls_path: string | null): TeamReviewQuota {
|
|
@@ -564,16 +600,22 @@ function _envelope_from_response(
|
|
|
564
600
|
/**
|
|
565
601
|
* Run the multi-host team-review fallback.
|
|
566
602
|
*
|
|
567
|
-
* Order of operations is the contract: (1) fail-closed
|
|
568
|
-
* (2) capability-delta header at the top,
|
|
569
|
-
* (4) manual render OR one synchronous `codex exec`
|
|
570
|
-
* quota/auth machinery, (5) envelope emission.
|
|
571
|
-
*
|
|
603
|
+
* Order of operations is the contract: (1) fail-closed availability gate —
|
|
604
|
+
* halted, then codex CLI/auth — (2) capability-delta header at the top,
|
|
605
|
+
* (3) read-only bundle, (4) manual render OR one synchronous `codex exec`
|
|
606
|
+
* call through the shared quota/auth machinery, (5) envelope emission.
|
|
607
|
+
* Throws `TeamDisabledError` when halted or unavailable — never a silent
|
|
608
|
+
* no-op.
|
|
572
609
|
*/
|
|
573
610
|
export function run_team_review(opts: RunTeamReviewOptions = {}): TeamReviewRunResult {
|
|
574
611
|
const config = opts.config ?? load_ai_team_config({ cwd: opts.cwd ?? null });
|
|
575
|
-
|
|
576
|
-
|
|
612
|
+
const halted = opts.halted ?? isOrchestrationHalted(undefined, opts.cwd ?? null);
|
|
613
|
+
if (halted) {
|
|
614
|
+
throw new TeamDisabledError(ORCHESTRATION_HALT_MESSAGE);
|
|
615
|
+
}
|
|
616
|
+
const availability = opts.availability ?? checkCodexAvailability(opts.cwd ?? undefined);
|
|
617
|
+
if (!availability.available) {
|
|
618
|
+
throw new TeamDisabledError(availability.reason ?? 'codex CLI not available.');
|
|
577
619
|
}
|
|
578
620
|
const out = opts.out ?? ((line: string): void => void process.stdout.write(`${line}\n`));
|
|
579
621
|
|