@event4u/agent-config 14.20.0 → 14.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.augment-plugin/marketplace.json +3 -3
- package/.augment-plugin/plugin.json +1 -1
- package/.claude-plugin/marketplace.json +1 -1
- package/CHANGELOG.md +243 -0
- package/README.md +1 -1
- package/agents/templates/.ai-council.yml.example +10 -0
- package/dist/agent-src/commands/analyze/inbox.md +1 -1
- package/dist/agent-src/commands/analyze/{reference-repo.md → repo.md} +247 -32
- package/dist/agent-src/commands/analyze/roadmap-repos.md +129 -0
- package/dist/agent-src/commands/analyze.md +12 -7
- package/dist/agent-src/commands/feature/plan.md +55 -16
- package/dist/agent-src/commands/feature/refactor.md +8 -3
- package/dist/agent-src/commands/humanize.md +48 -4
- package/dist/agent-src/contexts/execution/contract-decision-sheet.md +22 -1
- package/dist/agent-src/contexts/execution/host-capability-manifest.md +4 -1
- package/dist/agent-src/contexts/execution/roadmap-process-loop.md +35 -2
- package/dist/agent-src/contexts/execution/terminal-states.md +4 -1
- package/dist/agent-src/rules/evaluator-independence.md +4 -4
- package/dist/agent-src/rules/external-reference-deep-dive.md +2 -2
- package/dist/agent-src/skills/competitive-positioning/SKILL.md +2 -2
- package/dist/agent-src/skills/humanizer/references/anti-aiisms.md +27 -11
- package/dist/agent-src/skills/learning-to-rule-or-skill/SKILL.md +1 -1
- package/dist/agent-src/skills/markitdown/SKILL.md +1 -1
- package/dist/agent-src/skills/repomix-packer/SKILL.md +1 -1
- package/dist/agent-src/skills/technical-specification/SKILL.md +4 -0
- package/dist/agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/dist/agent-src/templates/features.md +5 -0
- package/dist/agent-src/templates/roadmaps.md +15 -0
- package/dist/cli-delegate/{chunk-KPBWFOPZ.js → chunk-2FTMENJL.js} +194 -37
- package/dist/cli-delegate/{chunk-PI2GU3DP.js → chunk-2TAQ2K4V.js} +4 -1
- package/dist/cli-delegate/{chunk-C6IPZRDA.js → chunk-643Z7FIC.js} +1 -1
- package/dist/cli-delegate/{chunk-NUMQHLS5.js → chunk-J3RFNKG4.js} +21 -2
- package/dist/cli-delegate/cmd_conformance.js +17 -3
- package/dist/cli-delegate/cmd_doctor.js +3 -3
- package/dist/cli-delegate/cmd_export.js +2 -2
- package/dist/cli-delegate/cmd_handoff.js +1 -1
- package/dist/cli-delegate/cmd_prune.js +2 -2
- package/dist/cli-delegate/cmd_settings_migrate.js +2 -2
- package/dist/cli-delegate/cmd_sync.js +2 -2
- package/dist/cli-delegate/cmd_uninstall.js +2 -2
- package/dist/cli-delegate/cmd_validate.js +2 -2
- package/dist/discovery/deprecation-report.md +1 -1
- package/dist/discovery/discovery-manifest.json +56 -25
- 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 +7 -6
- package/dist/discovery/trust-report.md +2 -2
- package/dist/discovery/workspaces.json +6 -5
- package/dist/hooks/dispatch.js +479 -241
- package/dist/install/install.mjs +4 -1
- package/dist/mcp/registry-manifest.json +2 -2
- package/dist/mcp/server.json +2 -2
- package/dist/mcp/server.mjs +313 -63
- package/dist/scripts/ai_council/content_ceiling.js +63 -0
- package/dist/scripts/ai_council/seat_policy.js +106 -0
- package/dist/scripts/ai_council/transport_resolver.js +21 -0
- package/llms.txt +14 -14
- package/package.json +1 -1
- package/src/config/gate-violation-baselines.json +3 -3
- package/src/config/release-gate-locality.yml +4 -3
- package/src/scripts/_lib/blocked_by_marker.ts +189 -0
- package/src/scripts/_lib/host_capability.ts +38 -1
- package/src/scripts/_lib/release_highlights.ts +19 -211
- package/src/scripts/_lib/release_material.ts +27 -77
- package/src/scripts/_lib/structured_ask.ts +117 -0
- package/src/scripts/_lib/tty_prompt.ts +76 -0
- package/src/scripts/ai_council/api_transport.ts +163 -0
- package/src/scripts/ai_council/clients.ts +58 -63
- package/src/scripts/ai_council/config.ts +13 -0
- package/src/scripts/ai_council/content_ceiling.ts +79 -0
- package/src/scripts/ai_council/jury_aggregate.ts +178 -0
- package/src/scripts/ai_council/seat_policy.ts +137 -0
- package/src/scripts/ai_council/status_surface.ts +79 -0
- package/src/scripts/ai_council/transport_resolver.ts +59 -1
- package/src/scripts/ai_tells_rules.ts +446 -5
- package/src/scripts/ask_block_census.ts +499 -0
- package/src/scripts/bench_humanizer_eval.ts +264 -40
- package/src/scripts/bench_prose_tells_fp.ts +198 -0
- package/src/scripts/check_branch_work_committed.ts +234 -0
- package/src/scripts/check_pack_size.ts +1 -1
- package/src/scripts/check_release_highlights.ts +0 -157
- package/src/scripts/council_cli.ts +18 -27
- package/src/scripts/detect_ai_tells.ts +142 -20
- package/src/scripts/harvest_reference_tokens.ts +698 -0
- package/src/scripts/hook_manifest.json +1 -1
- package/src/scripts/hook_manifest.yaml +31 -1
- package/src/scripts/hooks/concern_registry.ts +2 -0
- package/src/scripts/hooks/delegation_nudge_hook.ts +45 -1
- package/src/scripts/hooks/one_question_per_ask_hook.ts +191 -0
- package/src/scripts/install-hooks.sh +52 -0
- package/src/scripts/lint_roadmap_blockers.ts +51 -1
- package/src/scripts/migrate_command_suggestions.ts +1 -1
- package/src/scripts/probe_unblocked_ask.ts +135 -2
- package/src/scripts/release.ts +26 -71
- package/src/scripts/release_drill.ts +6 -10
- package/src/scripts/release_env.ts +1 -0
- package/src/scripts/release_publication.ts +34 -29
- package/src/scripts/self_review_gate.ts +409 -15
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
import {
|
|
2
|
+
classifyContentClass as _classifyContentClass,
|
|
3
|
+
DEFAULT_SEAT_CEILING as _DEFAULT_SEAT_CEILING,
|
|
4
|
+
type ContentClass,
|
|
5
|
+
} from './content_ceiling.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Seat policy — the vocabulary for refusing a route that IS reachable.
|
|
9
|
+
*
|
|
10
|
+
* road-to-admissible-council-seats 2.1. `AbsentReason` was exactly four values
|
|
11
|
+
* — `no_binary | no_auth | timeout | quota` — and every one of them means "the
|
|
12
|
+
* route is not reachable". A route that is reachable and **must not be used**
|
|
13
|
+
* had no name at all, so the resolver could not fail closed on terms, privacy,
|
|
14
|
+
* unproven cost, or a served model that is not the requested one. The absence
|
|
15
|
+
* of a word was the absence of the refusal: `resolveTransport`'s `api`/`cli`
|
|
16
|
+
* branches return `available: true` unconditionally, so anything the four
|
|
17
|
+
* reachability reasons could not express resolved OPEN.
|
|
18
|
+
*
|
|
19
|
+
* Fail closed on the unknown, which is the whole point.
|
|
20
|
+
* `classifyPolicyExclusion` maps anything it does not recognise to
|
|
21
|
+
* `policy_unknown`, and `policy_unknown` is an ABSENT reason like every other
|
|
22
|
+
* member of this union. An unrecognised policy token is the case where the
|
|
23
|
+
* config is trying to say something the code cannot understand; resolving it
|
|
24
|
+
* to "available" would make a typo an authorisation. This is the roadmap's own
|
|
25
|
+
* Risk 3 — "a new refusal vocabulary defaults open instead of closed".
|
|
26
|
+
*
|
|
27
|
+
* What this is NOT. It is not a secrets floor. Redaction (`skills/ai-council/references/
|
|
28
|
+
* cost-and-redaction.md`) still runs underneath and is not replaced. It is not
|
|
29
|
+
* an authorisation either: nothing here ever makes a seat available that was
|
|
30
|
+
* not already available — every value in the union only ever subtracts.
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/** Reachable-but-refused reasons. Every value means ABSENT. */
|
|
34
|
+
export const POLICY_EXCLUSIONS = [
|
|
35
|
+
/** The provider's terms forbid this use of the route. */
|
|
36
|
+
'policy_terms',
|
|
37
|
+
/** A privacy constraint forbids sending to this route. */
|
|
38
|
+
'policy_privacy',
|
|
39
|
+
/** The route's cost property is asserted rather than proven. */
|
|
40
|
+
'policy_unproven_cost',
|
|
41
|
+
/** The route serves a model other than the one requested. */
|
|
42
|
+
'policy_model_mismatch',
|
|
43
|
+
/** 2.2 — the payload's content class exceeds the seat's ceiling. */
|
|
44
|
+
'policy_content_ceiling',
|
|
45
|
+
/** The config named an exclusion this build does not understand. */
|
|
46
|
+
'policy_unknown',
|
|
47
|
+
] as const;
|
|
48
|
+
|
|
49
|
+
export type PolicyExclusion = (typeof POLICY_EXCLUSIONS)[number];
|
|
50
|
+
|
|
51
|
+
const _SET: ReadonlySet<string> = new Set<string>(POLICY_EXCLUSIONS);
|
|
52
|
+
|
|
53
|
+
/** True for a value this build can act on. `policy_unknown` included: it is a
|
|
54
|
+
* real, actionable verdict (refuse), not a parse failure. */
|
|
55
|
+
export function isPolicyExclusion(v: unknown): v is PolicyExclusion {
|
|
56
|
+
return typeof v === 'string' && _SET.has(v);
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Any configured value → a verdict. Unrecognised input, including `null` used
|
|
61
|
+
* where a string was meant, becomes `policy_unknown` — never `null`, because a
|
|
62
|
+
* `null` return would put the caller back in the position of deciding what an
|
|
63
|
+
* unreadable policy means, which is where the open default came from.
|
|
64
|
+
*
|
|
65
|
+
* `undefined` and `null` are the two ways of saying "no exclusion configured"
|
|
66
|
+
* and are the ONLY inputs that return `null`. Everything else is a refusal.
|
|
67
|
+
*/
|
|
68
|
+
export function classifyPolicyExclusion(raw: unknown): PolicyExclusion | null {
|
|
69
|
+
if (raw === undefined || raw === null) {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
return isPolicyExclusion(raw) ? raw : 'policy_unknown';
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/** One-line human reason for a refusal. `council:status` prints this. */
|
|
76
|
+
export function policyExclusionReason(p: PolicyExclusion, provider?: string): string {
|
|
77
|
+
const who = provider === undefined ? 'this seat' : provider;
|
|
78
|
+
switch (p) {
|
|
79
|
+
case 'policy_terms':
|
|
80
|
+
return `${who} is excluded by policy: the route's terms forbid this use`;
|
|
81
|
+
case 'policy_privacy':
|
|
82
|
+
return `${who} is excluded by policy: a privacy constraint forbids this route`;
|
|
83
|
+
case 'policy_unproven_cost':
|
|
84
|
+
return `${who} is excluded by policy: the route's cost property is asserted, not proven`;
|
|
85
|
+
case 'policy_model_mismatch':
|
|
86
|
+
return `${who} is excluded by policy: the route serves a model other than the one requested`;
|
|
87
|
+
case 'policy_content_ceiling':
|
|
88
|
+
return `${who} is excluded by policy: the payload exceeds the seat's content ceiling`;
|
|
89
|
+
case 'policy_unknown':
|
|
90
|
+
return `${who} is excluded by policy: an exclusion this build does not understand (failing closed)`;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Parsed together because they are one question — "what may this seat do, and
|
|
96
|
+
* if it may do nothing, why" — and because `config.ts` sits ~670 lines past
|
|
97
|
+
* the 1,500-line source ceiling, where every line costs one excess line.
|
|
98
|
+
*
|
|
99
|
+
* Returns an error MESSAGE rather than throwing so `config.ts` keeps raising
|
|
100
|
+
* its own `CouncilConfigError` (which its callers catch by type) without this
|
|
101
|
+
* module importing it and creating a cycle back into the loader.
|
|
102
|
+
*/
|
|
103
|
+
export interface SeatFields {
|
|
104
|
+
readonly disabled_reason: string | null;
|
|
105
|
+
readonly policy_exclusion: PolicyExclusion | null;
|
|
106
|
+
readonly content_ceiling: ContentClass;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export function parseSeatFields(
|
|
110
|
+
name: string,
|
|
111
|
+
raw: Record<string, unknown>,
|
|
112
|
+
): { fields: SeatFields } | { error: string } {
|
|
113
|
+
const dr = raw['disabled_reason'];
|
|
114
|
+
let disabled_reason: string | null = null;
|
|
115
|
+
if (dr !== undefined && dr !== null) {
|
|
116
|
+
if (typeof dr !== 'string' || dr.trim() === '') {
|
|
117
|
+
return {
|
|
118
|
+
error:
|
|
119
|
+
`members.${name}.disabled_reason must be a non-empty string when set ` +
|
|
120
|
+
`(got ${JSON.stringify(dr)}).`,
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
disabled_reason = dr.trim();
|
|
124
|
+
}
|
|
125
|
+
// Both of the next two fail CLOSED on an unreadable value: an unknown
|
|
126
|
+
// exclusion token becomes `policy_unknown` (which resolves ABSENT) and an
|
|
127
|
+
// unknown ceiling falls back to the RESTRICTIVE one. Throwing instead would
|
|
128
|
+
// be defensible, but a config typo would then take the whole council down
|
|
129
|
+
// rather than the one seat it names.
|
|
130
|
+
return {
|
|
131
|
+
fields: {
|
|
132
|
+
disabled_reason,
|
|
133
|
+
policy_exclusion: classifyPolicyExclusion(raw['policy_exclusion']),
|
|
134
|
+
content_ceiling: _classifyContentClass(raw['content_ceiling'], _DEFAULT_SEAT_CEILING),
|
|
135
|
+
},
|
|
136
|
+
};
|
|
137
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The seat-status helpers `council:status` renders from.
|
|
3
|
+
*
|
|
4
|
+
* Extracted from `council_cli.ts` (road-to-admissible-council-seats 1.2/2.1):
|
|
5
|
+
* that file sits ~2,460 lines past the 1,500-line ceiling
|
|
6
|
+
* `check_source_size_budget` ratchets, so every line added there costs one
|
|
7
|
+
* excess line. These three surfaces are what 1.2 and 2.1 grew, and they are a
|
|
8
|
+
* coherent unit — "what does this package know about a seat before any call is
|
|
9
|
+
* made" — rather than an arbitrary slice taken to satisfy a gate.
|
|
10
|
+
*
|
|
11
|
+
* Nothing here authorizes anything. `disabledSeats` reports a note the config
|
|
12
|
+
* carries; `resolvedTransportFor` only ever forwards a REFUSAL input to the
|
|
13
|
+
* resolver.
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import type { EnvironmentReport } from '../_lib/environment_detector.js';
|
|
17
|
+
import { detectEnvironment } from '../_lib/environment_detector.js';
|
|
18
|
+
import { DEFAULT_SEAT_CEILING, type ContentClass } from './content_ceiling.js';
|
|
19
|
+
import type { PolicyExclusion } from './seat_policy.js';
|
|
20
|
+
import { resolveMemberTransport } from './transport_resolver.js';
|
|
21
|
+
|
|
22
|
+
interface SeatLike {
|
|
23
|
+
readonly binary: string | null;
|
|
24
|
+
readonly api_key_ref: string | null;
|
|
25
|
+
readonly policy_exclusion?: PolicyExclusion | null;
|
|
26
|
+
readonly content_ceiling?: ContentClass;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
interface ConfigLike {
|
|
30
|
+
readonly members: ReadonlyMap<string, { readonly enabled: boolean; readonly disabled_reason: string | null }>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Resolve the concrete transport a member would use on THIS machine right now.
|
|
35
|
+
*
|
|
36
|
+
* `cmd_status` exists to answer "is the council reachable, and will it bill me".
|
|
37
|
+
* Since the transport-mode setting was removed, the second half is not a value
|
|
38
|
+
* anyone can look up in the config — it is a per-machine resolution. Printing a
|
|
39
|
+
* configured mode would be the same class of stale answer the removed setting
|
|
40
|
+
* produced. `mode` is pinned to `auto` because the loader no longer emits
|
|
41
|
+
* anything else.
|
|
42
|
+
*/
|
|
43
|
+
export function resolvedTransportFor(
|
|
44
|
+
name: string,
|
|
45
|
+
member: SeatLike,
|
|
46
|
+
report?: EnvironmentReport,
|
|
47
|
+
contentClass?: ContentClass | null,
|
|
48
|
+
): ReturnType<typeof resolveMemberTransport> {
|
|
49
|
+
return resolveMemberTransport({
|
|
50
|
+
provider: name,
|
|
51
|
+
report: report ?? detectEnvironment(),
|
|
52
|
+
invocationMode: null,
|
|
53
|
+
memberSettings: null,
|
|
54
|
+
globalMode: 'auto',
|
|
55
|
+
binaryOverride: member.binary,
|
|
56
|
+
policyExclusion: member.policy_exclusion ?? null,
|
|
57
|
+
seatCeiling: member.content_ceiling ?? DEFAULT_SEAT_CEILING,
|
|
58
|
+
contentClass: contentClass ?? null,
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Disabled seats and the reason each records. `NO_DISABLED_REASON` is the
|
|
64
|
+
* honest rendering of an unset field: an `enabled: false` with nothing beside
|
|
65
|
+
* it is a fact about the config, and hiding it is what made the seat silent.
|
|
66
|
+
*/
|
|
67
|
+
export const NO_DISABLED_REASON = 'disabled, no reason recorded';
|
|
68
|
+
|
|
69
|
+
export function disabledSeats(cfg: ConfigLike | null): Record<string, string> {
|
|
70
|
+
if (cfg === null) {
|
|
71
|
+
return {};
|
|
72
|
+
}
|
|
73
|
+
return Object.fromEntries(
|
|
74
|
+
[...cfg.members.entries()]
|
|
75
|
+
.filter(([, m]) => !m.enabled)
|
|
76
|
+
.map(([n, m]) => [n, m.disabled_reason ?? NO_DISABLED_REASON]),
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
@@ -49,6 +49,12 @@ import {
|
|
|
49
49
|
type EnvironmentReport,
|
|
50
50
|
} from '../_lib/environment_detector.js';
|
|
51
51
|
import { resolve_mode } from './modes.js';
|
|
52
|
+
import {
|
|
53
|
+
admitsContent,
|
|
54
|
+
DEFAULT_SEAT_CEILING,
|
|
55
|
+
type ContentClass,
|
|
56
|
+
} from './content_ceiling.js';
|
|
57
|
+
import { policyExclusionReason, type PolicyExclusion } from './seat_policy.js';
|
|
52
58
|
|
|
53
59
|
/** A concrete transport a member can actually run on. `auto` is not one. */
|
|
54
60
|
export type Transport = 'api' | 'manual' | 'cli';
|
|
@@ -62,7 +68,17 @@ export type Transport = 'api' | 'manual' | 'cli';
|
|
|
62
68
|
* `absentReasonFromCliFailure` below for the mapping FROM the mid-flight
|
|
63
69
|
* `CliFailureClass` a caller already classifies its errors into.
|
|
64
70
|
*/
|
|
65
|
-
|
|
71
|
+
/** The four REACHABILITY reasons. Each means "the route cannot be run". */
|
|
72
|
+
export type UnreachableReason = 'no_binary' | 'no_auth' | 'timeout' | 'quota';
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Reachability OR policy. The policy half (2.1) names the case the four
|
|
76
|
+
* original values could not: a route that IS reachable and must not be used.
|
|
77
|
+
* Both halves mean the same thing to a caller — the member is absent — which
|
|
78
|
+
* is why they share one union rather than sitting in two fields a caller could
|
|
79
|
+
* forget to check.
|
|
80
|
+
*/
|
|
81
|
+
export type AbsentReason = UnreachableReason | PolicyExclusion;
|
|
66
82
|
|
|
67
83
|
/** The accepted values of the `mode` key, including the resolver-only `auto`. */
|
|
68
84
|
export const VALID_TRANSPORT_MODES: ReadonlySet<string> = new Set([
|
|
@@ -110,6 +126,19 @@ export interface ResolveTransportOptions {
|
|
|
110
126
|
* env-key auth record exists for this provider" from the report.
|
|
111
127
|
*/
|
|
112
128
|
readonly apiKeyPresent?: boolean;
|
|
129
|
+
/**
|
|
130
|
+
* A configured policy exclusion for this seat (2.1). Non-null resolves
|
|
131
|
+
* ABSENT whatever the mode says — a reachable route that must not be used
|
|
132
|
+
* is not made usable by being reachable.
|
|
133
|
+
*/
|
|
134
|
+
readonly policyExclusion?: PolicyExclusion | null;
|
|
135
|
+
/** The seat's content ceiling (2.2). Defaults to `project-content`. */
|
|
136
|
+
readonly seatCeiling?: ContentClass | null;
|
|
137
|
+
/**
|
|
138
|
+
* The class of what this pass would send. Omitted → no content check runs,
|
|
139
|
+
* which is what keeps every existing caller's behaviour identical.
|
|
140
|
+
*/
|
|
141
|
+
readonly contentClass?: ContentClass | null;
|
|
113
142
|
}
|
|
114
143
|
|
|
115
144
|
/** Auth sources that mean "the provider CLI can authenticate". */
|
|
@@ -161,6 +190,28 @@ export function resolveTransport(opts: ResolveTransportOptions): ResolvedTranspo
|
|
|
161
190
|
const authSource = auth?.source ?? null;
|
|
162
191
|
const billing = classifyBilling(provider, authSource);
|
|
163
192
|
|
|
193
|
+
// Policy first (2.1). Before the mode dispatch, because `api` and `cli`
|
|
194
|
+
// below return `available: true` UNCONDITIONALLY — a refusal placed after
|
|
195
|
+
// them would be unreachable for exactly the seats it exists to stop.
|
|
196
|
+
const exclusion = opts.policyExclusion ?? null;
|
|
197
|
+
if (exclusion !== null) {
|
|
198
|
+
return unavailable(provider, billing, policyExclusionReason(exclusion, provider), exclusion);
|
|
199
|
+
}
|
|
200
|
+
// Content ceiling (2.2). Only runs when the caller declared what it would
|
|
201
|
+
// send; an undeclared pass is unchanged, not silently refused.
|
|
202
|
+
const contentClass = opts.contentClass ?? null;
|
|
203
|
+
if (contentClass !== null) {
|
|
204
|
+
const verdict = admitsContent(opts.seatCeiling ?? DEFAULT_SEAT_CEILING, contentClass);
|
|
205
|
+
if (!verdict.admitted) {
|
|
206
|
+
return unavailable(
|
|
207
|
+
provider,
|
|
208
|
+
billing,
|
|
209
|
+
`${provider}: ${verdict.reason ?? 'content ceiling refused the payload'}`,
|
|
210
|
+
'policy_content_ceiling',
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
164
215
|
if (mode === 'manual') {
|
|
165
216
|
return {
|
|
166
217
|
available: true,
|
|
@@ -246,6 +297,10 @@ export interface ResolveMemberTransportOptions {
|
|
|
246
297
|
readonly globalMode?: string | null;
|
|
247
298
|
readonly binaryOverride?: string | null;
|
|
248
299
|
readonly apiKeyPresent?: boolean;
|
|
300
|
+
/** Forwarded to `resolveTransport` (2.1 / 2.2). */
|
|
301
|
+
readonly policyExclusion?: PolicyExclusion | null;
|
|
302
|
+
readonly seatCeiling?: ContentClass | null;
|
|
303
|
+
readonly contentClass?: ContentClass | null;
|
|
249
304
|
}
|
|
250
305
|
|
|
251
306
|
export interface ResolvedMemberTransport extends ResolvedTransport {
|
|
@@ -287,6 +342,9 @@ export function resolveMemberTransport(
|
|
|
287
342
|
// optional-boolean field, so the key is omitted entirely rather than
|
|
288
343
|
// forwarded as `apiKeyPresent: undefined`.
|
|
289
344
|
...(opts.apiKeyPresent !== undefined ? { apiKeyPresent: opts.apiKeyPresent } : {}),
|
|
345
|
+
policyExclusion: opts.policyExclusion ?? null,
|
|
346
|
+
seatCeiling: opts.seatCeiling ?? null,
|
|
347
|
+
contentClass: opts.contentClass ?? null,
|
|
290
348
|
});
|
|
291
349
|
return { ...resolved, configuredMode };
|
|
292
350
|
}
|