@cortexkit/anthropic-auth-core 2.2.1 → 2.2.3

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.
@@ -169,8 +169,10 @@ export declare class ClaustrumConsumer<Q = QuotaObservation> {
169
169
  * a served credential (an insula reading from `cachedInsulaQuotaForRoute`).
170
170
  * It lands only while the route is still bound to that known account, so no
171
171
  * credential fetch is needed and a replaced account's reading is dropped.
172
- * The in-memory roster is reloaded from the file, without a vault call.
172
+ * Memory updates immediately; durable publication is detached and coalesced.
173
173
  */
174
174
  recordAccountQuota(routeId: string, accountIdentity: string, observation: QuotaObservation): Promise<boolean>;
175
175
  close(): void;
176
+ /** Close immediately; the last consumer also waits for the writer's bounded drain. */
177
+ dispose(): Promise<void>;
176
178
  }
@@ -1,4 +1,9 @@
1
1
  import { type ClaustrumClientOptions as ClaustrumEnrollmentClientOptions, type EnrollmentPollOutcome, type EnrollmentTokenFile, writeEnrollmentTokenFile } from '@cortexkit/claustrum-client';
2
+ /**
3
+ * The code a vault without the enrollment ceremony answers a proposal with.
4
+ * Setup reports it as "this vault cannot connect plugins" instead of waiting.
5
+ */
6
+ export declare const UNSUPPORTED_ENROLLMENT_CODE = "unknown_method";
2
7
  /**
3
8
  * Codes the vault's closed enrollment-refusal vocabulary marks permanent. The
4
9
  * client labels some module error frames transient/retry regardless, so the
@@ -9,6 +9,7 @@ export { ClaustrumConsumerError, type ClaustrumConsumerFailureKind, type Claustr
9
9
  export { assertHostSlotMatchesMode, assertNotCustodyPlaceholder, CUSTODY_PLACEHOLDER_PREFIX, classifyHostSlot, custodyPlaceholder, custodyPlaceholderKey, type HostSlotContent, isCustodyPlaceholder, isCustodyPlaceholderValue, } from './host-slot.js';
10
10
  export { acquireInsulaQuotaSource, type InsulaQuotaContext, type InsulaQuotaLookup, type InsulaQuotaMiss, type InsulaQuotaOptions, type InsulaQuotaReading, type InsulaQuotaResult, type InsulaQuotaSource, type InsulaUsageEntry, type InsulaUsageTransport, type InsulaUsageWatch, type InsulaUsageWindow, type InsulaWatchOptions, } from './insula.js';
11
11
  export { acceptAccount, type DeclinedAccount, declineAccount, isDeclined, } from './interlock.js';
12
+ export { drainVaultQuota } from './quota-writer.js';
12
13
  export { type AccountMapper, acceptVaultRoute, DEFAULT_ROUTE_PREFIX, declineVaultRoute, type MutateVaultRosterOptions, mutateVaultRoster, type ProjectionOptions, projectVaultRoster, type QuotaReceipt, readVaultRoster, recordVaultAccountQuota, recordVaultQuota, refreshVaultRoster, resolveVaultPrimary, type VaultPrimary, type VaultPrimaryBinding, type VaultPrimaryUnavailableReason, type VaultRosterFile, type VaultRosterLockContext, type VaultRosterRow, vaultRoutingRows, } from './roster.js';
13
14
  export { type ChangeNotice, ChangeNoticeReader, RESUBSCRIBE_BACKOFF_MS, SAFETY_POLL_MS, ScopedRosterWatch, type ScopedRosterWatchOptions, type ScopedWatchEnd, type ScopedWatchHandle, type ScopedWatchNoticeShape, type ScopedWatchSource, WATCH_RETRY_MS, type WatchMode, } from './watch.js';
14
15
  /**
@@ -0,0 +1,18 @@
1
+ import { type QuotaObservation } from '../quota/index.js';
2
+ import { type QuotaReceipt, type VaultRosterFile } from './roster.js';
3
+ export type VaultQuotaPublication = {
4
+ routeId: string;
5
+ accountIdentity?: string;
6
+ receipt?: QuotaReceipt;
7
+ observation: QuotaObservation;
8
+ };
9
+ type Listener = (publication: VaultQuotaPublication) => void;
10
+ /** Retain the shared writer; the last consumer's close drains it. */
11
+ export declare function retainVaultQuotaWriter(path: string, listener: Listener): () => Promise<void>;
12
+ /** Enqueue without filesystem work; each quota field retains its own receipt fence. */
13
+ export declare function enqueueVaultQuota(path: string, publication: VaultQuotaPublication): Promise<boolean>;
14
+ /** Overlay uncommitted observations when a consumer reloads the durable roster. */
15
+ export declare function pendingVaultQuota(path: string, roster: VaultRosterFile | undefined): VaultRosterFile | undefined;
16
+ /** Wait at most timeoutMs for detached publication; shutdown loss is acceptable. */
17
+ export declare function drainVaultQuota(path: string, timeoutMs?: number): Promise<void>;
18
+ export {};
@@ -2,6 +2,8 @@ import { type QuotaMap, type QuotaObservation } from '../quota/index.js';
2
2
  import type { RoutingRow } from '../routing/index.js';
3
3
  import type { ClaustrumScopedAttempt, ClaustrumScopedCustody, SkippedVaultRecord, VaultCredential, VaultCredentialType, VaultInventory } from './custody.js';
4
4
  import { type DeclinedAccount } from './interlock.js';
5
+ import { type VaultQuotaPublication } from './quota-writer.js';
6
+ export { drainVaultQuota } from './quota-writer.js';
5
7
  /**
6
8
  * One vault account as the pool sees it. It carries no bearer material: the
7
9
  * access token is fetched from the vault for every send. Several vault
@@ -91,6 +93,7 @@ export interface VaultRosterLockContext {
91
93
  assertOwned: () => Promise<void>;
92
94
  }
93
95
  export interface MutateVaultRosterOptions {
96
+ signal?: AbortSignal;
94
97
  /**
95
98
  * Runs when the new roster is fully staged on disk, immediately before the
96
99
  * rename that publishes it, so a caller can confirm a lease of its own at
@@ -127,7 +130,10 @@ export type QuotaReceipt = Pick<ClaustrumScopedAttempt, 'credentialId' | 'accoun
127
130
  * write is fenced on the receipt the reading was taken with: it lands only
128
131
  * while the row still holds that receipt's credential (as representative or
129
132
  * alias) and account, so a slow read for a replaced account can never land on
130
- * its successor. Returns whether the observation was kept.
133
+ * its successor. Resolves false when the fence refuses the reading against the
134
+ * roster as it stands now (on disk, plus readings this process has not
135
+ * written yet), and true once the reading is queued for the next write. The
136
+ * write applies the fence again, so a rebind between the two still refuses.
131
137
  */
132
138
  export declare function recordVaultQuota(path: string, input: QuotaReceipt & {
133
139
  routeId: string;
@@ -139,19 +145,28 @@ export declare function recordVaultQuota(path: string, input: QuotaReceipt & {
139
145
  * lands only while the row is still bound to that exact known account, so a
140
146
  * reading taken before a replacement never reaches the successor. Unlike
141
147
  * `recordVaultQuota` it never binds an account: a row without a known account
142
- * is refused. Returns whether the observation was kept.
148
+ * is refused. Resolves false when the row is not bound to that account now,
149
+ * and true once the reading is queued, as `recordVaultQuota` does.
143
150
  */
144
151
  export declare function recordVaultAccountQuota(path: string, input: {
145
152
  routeId: string;
146
153
  accountIdentity: string;
147
154
  observation: QuotaObservation;
148
155
  }): Promise<boolean>;
156
+ /** Apply the same identity fence to memory overlays and durable quota batches. */
157
+ export declare function applyVaultQuota(current: VaultRosterFile | undefined, input: VaultQuotaPublication): {
158
+ roster: VaultRosterFile | undefined;
159
+ kept: boolean;
160
+ };
161
+ export declare function forgetPendingVaultDiscovery(path: string): void;
149
162
  /**
150
163
  * Discover and commit the roster. The lease covers the list call and the
151
164
  * commit, not just the write: an opaque view cannot tell a delayed old reply
152
165
  * from a newer one, so a run that lost its lease is refused at commit. A
153
166
  * failed list, or custody switching off mid-run, never writes anything; a
154
167
  * peer holding the lease means its last committed roster is served instead.
168
+ * A write-lock timeout keeps the discovered inventory for the next poll,
169
+ * unless a peer has superseded its base view in the meantime.
155
170
  */
156
171
  export declare function refreshVaultRoster(options: {
157
172
  path: string;
@@ -159,6 +174,7 @@ export declare function refreshVaultRoster(options: {
159
174
  isActive?: () => boolean | Promise<boolean>;
160
175
  signal?: AbortSignal;
161
176
  projection?: ProjectionOptions | (() => ProjectionOptions);
177
+ onInventory?: (inventory: VaultInventory) => void;
162
178
  }): Promise<VaultRosterFile | undefined>;
163
179
  /**
164
180
  * A host-owned main account as last verified from the vault: the plugin's
@@ -5,6 +5,8 @@ type Backend = typeof import('@cortexkit/private-files');
5
5
  type Kind = 'file' | 'directory';
6
6
  /** Tests inject a backend here; production always imports the private-files package. */
7
7
  export declare function setPrivateFilesBackendForTest(api?: Backend): void;
8
+ /** The fixed `reason` of a refusal because the Windows native addon did not load. */
9
+ export declare const PRIVATE_FILES_UNAVAILABLE_REASON = "native-addon-unavailable";
8
10
  export declare class PrivateFileError extends Error {
9
11
  readonly path: string;
10
12
  readonly stage: string;
@@ -28,6 +28,7 @@ export interface LockOptions {
28
28
  ttlMs: number;
29
29
  timeoutMs: number;
30
30
  renew?: boolean;
31
+ signal?: AbortSignal;
31
32
  onStep?: Parameters<typeof acquireRefreshFileLock>[0]['onStep'];
32
33
  onRenewalTimer?: Parameters<typeof acquireRefreshFileLock>[0]['onRenewalTimer'];
33
34
  }
@@ -1,3 +1,4 @@
1
+ import { type QuotaCredits } from './map.js';
1
2
  import type { ProjectedLimit, ProjectedQuota } from './projection.js';
2
3
  /** A reading older than this is marked with its age. */
3
4
  export declare const QUOTA_STALE_AFTER_MS: number;
@@ -31,6 +32,13 @@ export interface QuotaWindowPart {
31
32
  kind: 'window' | 'budget';
32
33
  /** The display name of a quota window, or `credits` for the budget. */
33
34
  name: string;
35
+ /**
36
+ * The label the narrow line gives this item: an account-wide window keeps
37
+ * its name (`5h`), a model-scoped window reads as its model's short name
38
+ * (`Fa` for Fable, extended until names in the same reading differ), and
39
+ * the budget reads `cr`. For layouts too narrow for the full name.
40
+ */
41
+ short: string;
34
42
  /** The shared figure wording, including `credits` for budget figures. */
35
43
  figure: string;
36
44
  /** The unrounded remaining percentage, clamped to 0–100, when known. */
@@ -84,3 +92,14 @@ export declare function quotaTextParts(quota: ProjectedQuota | undefined, option
84
92
  * provider answered without a limit for any window.
85
93
  */
86
94
  export declare function formatQuota(quota: ProjectedQuota | undefined, options: QuotaTextOptions): string;
95
+ /**
96
+ * Prepaid credits as one short value, worded as the official Codex client's
97
+ * status view words them: `Unlimited` for unlimited credits, `<n> credits`
98
+ * for a finite positive balance (rounded to a whole number), `Available` when
99
+ * the account has credits but no usable balance was reported, and undefined
100
+ * when the account has no credits (or `credits` is undefined), so a caller
101
+ * shows no credits row at all. Unlimited wins even when `hasCredits` is false.
102
+ * No other formatter adds this text; a caller that wants a credits row asks
103
+ * for it.
104
+ */
105
+ export declare function quotaCreditsText(credits: Pick<QuotaCredits, 'hasCredits' | 'unlimited' | 'balance'> | undefined): string | undefined;
@@ -1,9 +1,9 @@
1
1
  export type { QuotaCodec } from './codec.js';
2
2
  export { quotaCodec } from './codec.js';
3
3
  export type { QuotaParts, QuotaTextForm, QuotaTextOptions, QuotaWindowPart, } from './format.js';
4
- export { formatQuota, isQuotaStale, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaTextParts, quotaWindowName, quotaWindowParts, } from './format.js';
5
- export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaCounter, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
6
- export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaMap, } from './map.js';
4
+ export { formatQuota, isQuotaStale, NO_LIMITS_REPORTED, NO_QUOTA_READING, QUOTA_STALE_AFTER_MS, quotaCreditsText, quotaTextParts, quotaWindowName, quotaWindowParts, } from './format.js';
5
+ export type { CreditBudgetCleared, CreditBudgetEntry, CreditBudgetReading, QuotaAbsentEntry, QuotaCounter, QuotaCredits, QuotaEntry, QuotaMap, QuotaReadingEntry, QuotaRetiredEntry, } from './map.js';
6
+ export { ALL_SCOPE, DEFAULT_REQUIRED_LABELS, decodeQuotaMap, emptyQuotaMap, isQuotaCredits, isQuotaMap, } from './map.js';
7
7
  export type { ObservedBudget, ObservedPair, ObservedReading, QuotaObservation, } from './merge.js';
8
8
  export { isQuotaObservation, mergeQuotaObservation, QuotaCodecError, } from './merge.js';
9
9
  export type { ExhaustionReset, ProjectedBudget, ProjectedLimit, ProjectedQuota, QuotaGroup, } from './projection.js';
@@ -48,12 +48,30 @@ export interface QuotaCounter {
48
48
  value: number;
49
49
  checkedAt: number;
50
50
  }
51
+ /**
52
+ * Prepaid credits a provider account holds beside its rate-limit windows (for
53
+ * example a ChatGPT workspace's credits, which keep requests working after a
54
+ * window is used up). This is a state, not a count or the spend budget: the
55
+ * account has credits or not, they may be unlimited, and the provider may
56
+ * report a balance. It carries its own observation time.
57
+ */
58
+ export interface QuotaCredits {
59
+ hasCredits: boolean;
60
+ unlimited: boolean;
61
+ /** The balance the provider reported, when it reported one. */
62
+ balance?: number;
63
+ /** The provider's verdict that the overage allowance is used up. */
64
+ overageLimitReached?: boolean;
65
+ checkedAt: number;
66
+ }
51
67
  export interface QuotaMap {
52
68
  limits: QuotaEntry[];
53
69
  /** Absent means no observation has reported on the budget yet. */
54
70
  budget?: CreditBudgetEntry;
55
71
  /** Provider-named counts, each with its own observation time. */
56
72
  counters?: Record<string, QuotaCounter>;
73
+ /** Absent means no observation has reported on prepaid credits yet. */
74
+ credits?: QuotaCredits;
57
75
  }
58
76
  export declare function emptyQuotaMap(): QuotaMap;
59
77
  /** The time an entry speaks for: its reading, retirement or absence time. */
@@ -61,9 +79,14 @@ export declare function entryTime(entry: QuotaEntry): number;
61
79
  export declare function limitKey(scope: string, label: string): string;
62
80
  export declare function isCreditBudgetEntry(value: unknown): value is CreditBudgetEntry;
63
81
  export declare function isQuotaCounters(value: unknown): value is Record<string, QuotaCounter>;
82
+ /** True when `value` is a well-formed prepaid-credits record. */
83
+ export declare function isQuotaCredits(value: unknown): value is QuotaCredits;
64
84
  /** Keep usable counters without letting one malformed count hide a quota map. */
65
85
  export declare function readQuotaCounters(value: unknown): Record<string, QuotaCounter> | undefined;
66
- /** Decode persisted quota, dropping malformed counters but not limit evidence. */
86
+ /**
87
+ * Decode persisted quota, dropping malformed counters or a malformed credits
88
+ * record but not limit or budget evidence.
89
+ */
67
90
  export declare function decodeQuotaMap(value: unknown): QuotaMap | undefined;
68
91
  /**
69
92
  * True when `value` is a well-formed quota map. Unrecognised top-level keys
@@ -1,4 +1,4 @@
1
- import { type QuotaCounter, type QuotaMap } from './map.js';
1
+ import { type QuotaCounter, type QuotaCredits, type QuotaMap } from './map.js';
2
2
  export interface ObservedReading {
3
3
  /** Defaults to `all`. */
4
4
  scope?: string;
@@ -34,6 +34,11 @@ export interface QuotaObservation {
34
34
  budget?: ObservedBudget;
35
35
  /** Each named count uses its own time, independently of limits and budget. */
36
36
  counters?: Record<string, QuotaCounter>;
37
+ /**
38
+ * Prepaid credits, judged by their own time independently of limits, budget
39
+ * and counters. Absent means the observation says nothing about credits.
40
+ */
41
+ credits?: QuotaCredits;
37
42
  }
38
43
  /** Thrown by `mergeQuotaObservation` when either argument is malformed. */
39
44
  export declare class QuotaCodecError extends TypeError {
@@ -1,4 +1,4 @@
1
- import { type QuotaCounter, type QuotaMap } from './map.js';
1
+ import { type QuotaCounter, type QuotaCredits, type QuotaMap } from './map.js';
2
2
  export interface ProjectedLimit {
3
3
  scope: string;
4
4
  label: string;
@@ -40,6 +40,8 @@ export interface ProjectedQuota {
40
40
  /** Present only when the stored budget is a reading, not cleared. */
41
41
  budget?: ProjectedBudget;
42
42
  counters?: Record<string, QuotaCounter>;
43
+ /** The stored prepaid credits, unchanged in every scope. */
44
+ credits?: QuotaCredits;
43
45
  /**
44
46
  * The named groups the windows belong to, in the order the plugin lists
45
47
  * them. Set only by a plugin whose windows all belong to such groups; the
@@ -0,0 +1,34 @@
1
+ import { type MenuTerminal } from '../auth-menu/terminal.js';
2
+ import type { SetupHost, SetupIO, SetupPlugin } from './types.js';
3
+ /** Fixed text: no version, host or account detail, so it is safe to print anywhere. */
4
+ export declare const UNSUPPORTED_VAULT_MESSAGE = "This Claustrum vault does not support connecting plugins yet, so setup cannot connect to it. Run setup without --claustrum to use local accounts.";
5
+ /**
6
+ * The longest setup waits, in total, for the vault to create the enrollment
7
+ * request and for an operator to approve it. It matches the wait setup always
8
+ * had (300 one-second polls), but is now measured on the clock: a vault that
9
+ * answers each request slowly can no longer stretch it.
10
+ */
11
+ export declare const ENROLLMENT_WAIT_MS = 300000;
12
+ /** Why a host's enrollment ended without a token. */
13
+ export type HostNotEnrolledReason = 'connection-failed' | 'unsupported-vault' | 'request-not-created' | 'approval-pending' | 'refused' | 'invalid-request' | 'incomplete';
14
+ /**
15
+ * Thrown when the enrollment ended without writing the host's token: the vault
16
+ * could not be reached, it cannot enroll plugins, it refused the request, it
17
+ * never created one, approval never came before setup stopped waiting, or
18
+ * the enrollment failed in some other way (`incomplete`).
19
+ * Setup can then report the host as not connected without reading its files.
20
+ * The message is fixed text built only from vetted values, never from what
21
+ * the vault or client threw. `lastErrorCode` is the last enrollment error code
22
+ * setup saw, from the vault or from the client's transport (for example
23
+ * `transport_error`), for diagnostics; it carries no secret.
24
+ */
25
+ export declare class HostNotEnrolledError extends Error {
26
+ readonly reason: HostNotEnrolledReason;
27
+ readonly lastErrorCode?: string;
28
+ constructor(message: string, details: {
29
+ reason: HostNotEnrolledReason;
30
+ lastErrorCode?: string;
31
+ });
32
+ }
33
+ /** Enroll an independently revocable host; plugin callbacks choose its credential store and account permissions. */
34
+ export declare function connectSetupHost(plugin: SetupPlugin, host: SetupHost, io: SetupIO, terminal: MenuTerminal, fence: () => Promise<void>): Promise<void>;
@@ -1,6 +1,7 @@
1
1
  export { AMBIGUOUS_OPENCODE_MESSAGE, runSetup } from './command.js';
2
2
  export { SetupCommandError, setupEnrollment } from './command-error.js';
3
3
  export { detectHosts, detectOpenCodeGeneration, findExecutableOnPath, openCodeDesktopMarkers, } from './detect.js';
4
+ export { ENROLLMENT_WAIT_MS, HostNotEnrolledError, type HostNotEnrolledReason, UNSUPPORTED_VAULT_MESSAGE, } from './enroll.js';
4
5
  export { assertHostsStopped, createProcessFence, listSetupProcesses, ProcessFenceViolationError, ProcessInspectionError, type ProcessInspectionStep, } from './process-fence.js';
5
6
  export { type PlannedWrite, planOpenCodeRegistration, registerOpenCode, registerPi, updatePluginConfig, } from './register.js';
6
7
  export type { CommandResult, CommandRunner, HostDetection, OpenCodeGeneration, ProcessFence, SetupCallbackContext, SetupDetection, SetupEnrollmentOutcome, SetupEnvironment, SetupFileSystem, SetupHost, SetupHostEnrollment, SetupIO, SetupPlugin, SetupResult, } from './types.js';
@@ -33,11 +33,25 @@ export declare function listSetupProcesses(runner?: CommandRunner, options?: {
33
33
  onStep?: (details: ProcessInspectionStep) => void;
34
34
  /** False skips the Windows PowerShell query for Node and Bun command lines. */
35
35
  inspectRuntimes?: boolean;
36
+ /**
37
+ * The process to leave out, the running setup itself. Defaults to
38
+ * `process.pid`; tests pass a value no listed process can have, because in
39
+ * a container this process can hold any small PID a fixture uses.
40
+ */
41
+ selfPid?: number;
42
+ /**
43
+ * Windows only: answer with one CIM query for the images a fence cares
44
+ * about (host programs, Node and Bun), returning image name, PID and
45
+ * command line together, instead of `tasklist` followed by a second CIM
46
+ * query. The result lists only those processes.
47
+ */
48
+ hostImagesOnly?: boolean;
36
49
  }): Promise<Array<{
37
50
  pid: number;
38
51
  command: string;
39
52
  }>>;
40
53
  export declare function createProcessFence(runner?: CommandRunner, options?: {
41
54
  platform?: string;
55
+ selfPid?: number;
42
56
  }): ProcessFence;
43
57
  export declare function assertHostsStopped(fence: ProcessFence, env?: SetupEnvironment): Promise<void>;
@@ -125,6 +125,11 @@ export interface SetupIO {
125
125
  handshakeTimeoutMs: number;
126
126
  }) => Promise<ClaustrumEnrollmentConnection>;
127
127
  wait?: (ms: number) => Promise<void>;
128
+ /**
129
+ * Monotonic milliseconds that bound the enrollment wait. Defaults to
130
+ * `performance.now`; tests pair it with `wait` to run a fake clock.
131
+ */
132
+ now?: () => number;
128
133
  }
129
134
  export interface HostDetection {
130
135
  installed: boolean;
@@ -1,9 +1,10 @@
1
+ import { PER_TURN_CONTROL_BETA } from './model-capabilities.ts';
1
2
  /**
2
- * Per-turn control beta that Claude Code sends on Opus 5.5, Sonnet 5.5,
3
- * Fable 5.1 and Haiku 5.5. It enables effort changes carried by empty
3
+ * Per-turn control beta that Claude Code sends on the models whose
4
+ * capability rows list it. It enables effort changes carried by empty
4
5
  * `role: "system"` messages on the eligible models below.
5
6
  */
6
- export declare const PER_TURN_CONTROL_BETA = "per-turn-control-2026-07-01";
7
+ export { PER_TURN_CONTROL_BETA };
7
8
  export type AdaptiveEffort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';
8
9
  export type MidConversationEffortTransition = {
9
10
  /** Number of assistant messages preceding the user boundary for this effort. */
@@ -12,30 +13,33 @@ export type MidConversationEffortTransition = {
12
13
  };
13
14
  export declare function normalizeAdaptiveEffort(value: unknown): AdaptiveEffort | undefined;
14
15
  /**
15
- * Models that require adaptive thinking for per-message effort: Opus 5.5
16
- * and Sonnet 5.5, including dated and `[1m]` context-window ids. Header
17
- * eligibility is separate; `sendsPerTurnControlBeta` follows Claude Code.
16
+ * Models that require adaptive thinking for per-message effort (capability
17
+ * `effortMarkers: "with-adaptive-thinking"`: Opus 5.5 and Sonnet 5.5),
18
+ * including dated and `[1m]` context-window ids. Header eligibility is
19
+ * separate; `sendsPerTurnControlBeta` follows Claude Code.
18
20
  */
19
21
  export declare function usesPerTurnControl(model: unknown): boolean;
20
22
  /**
21
- * Models whose OAuth requests carry `PER_TURN_CONTROL_BETA`: Opus 5.5 and
22
- * Sonnet 5.5, plus Fable 5.1 and Haiku 5.5, which Claude Code 2.1.302 also
23
- * sends it on. Sending the beta does not by itself make a model eligible for
24
- * effort markers; `supportsPerMessageEffort` decides that.
23
+ * Models whose OAuth requests carry `PER_TURN_CONTROL_BETA`: those whose
24
+ * Claude Code main beta list includes it (Opus 5.5, Sonnet 5.5, Fable 5.1 and
25
+ * Haiku 5.5 in Claude Code 2.1.302). Sending the beta does not by itself make
26
+ * a model eligible for effort markers; `supportsPerMessageEffort` decides that.
25
27
  */
26
28
  export declare function sendsPerTurnControlBeta(model: unknown): boolean;
27
29
  /**
28
30
  * Whether a request may move effort changes into empty system messages.
29
31
  *
30
- * Fable 5.1 is always eligible, whatever its thinking field says. Opus 5.5
31
- * and Sonnet 5.5 qualify only with adaptive thinking: Anthropic rejects a
32
- * differing per-message effort with HTTP 400 on Sonnet 5.5 `between_tools`,
33
- * and asks callers to use adaptive thinking to vary effort.
32
+ * Models with `effortMarkers: "always"` (Fable 5.1) are always eligible,
33
+ * whatever the thinking field says. Models with `"with-adaptive-thinking"`
34
+ * (Opus 5.5, Sonnet 5.5) qualify only with adaptive thinking: Anthropic
35
+ * rejects a differing per-message effort with HTTP 400 on Sonnet 5.5
36
+ * `between_tools`, and asks callers to use adaptive thinking to vary effort.
34
37
  */
35
38
  export declare function supportsPerMessageEffort(body: Record<string, unknown>): boolean;
36
39
  /**
37
40
  * The effort Anthropic applies when a request sets no `output_config.effort`:
38
- * `medium` on Opus 5.5 and Haiku 5.5, `high` on the other models.
41
+ * the capability row's default (`medium` on Opus 5.5 and Haiku 5.5), and
42
+ * `high` on models without a row.
39
43
  * A history entry without a recognized effort level uses this value.
40
44
  */
41
45
  export declare function defaultAdaptiveEffort(model: unknown): AdaptiveEffort;
@@ -0,0 +1,136 @@
1
+ import type { AdaptiveEffort } from './mid-conversation-output-config.ts';
2
+ /**
3
+ * One row per Claude model that this plugin treats differently from an
4
+ * unknown model. Every per-model decision in Core (family checks, effort
5
+ * handling, Claude Code betas, turn-end notes, thinking-binding controls) and
6
+ * the per-model facts OpenCode and Pi read (catalogue entries, fallback
7
+ * eligibility) come from this table, so adding a model is one new row.
8
+ *
9
+ * Ids stay exact. A request model resolves to a row only when it equals the
10
+ * row's id (after removing OpenCode's `[1m]` context-window qualifier) or
11
+ * extends it as the row's `idSuffixes` allows; when several rows match, the
12
+ * longest id wins. A model that matches no row gets the defaults described on
13
+ * each field.
14
+ */
15
+ /** Per-turn control beta Claude Code sends on the models whose rows list it. */
16
+ export declare const PER_TURN_CONTROL_BETA = "per-turn-control-2026-07-01";
17
+ /** Beta that allows turn-scoped system messages (`clear_at`). */
18
+ export declare const TURN_END_NOTE_BETA = "mid-conversation-system-clear-at-2026-08-21";
19
+ export declare const CLAUDE_CODE_FULL_AGENT_BETAS: readonly ["claude-code-20250219", 'oauth-2025-04-20', 'interleaved-thinking-2025-05-14', 'thinking-token-count-2026-05-13', 'context-management-2025-06-27', "prompt-caching-scope-2026-01-05", 'mid-conversation-tool-changes-2026-07-01', 'advanced-tool-use-2025-11-20', "mid-conversation-system-clear-at-2026-08-21", 'effort-2025-11-24', 'thinking-binding-controls-2026-08-01', 'thinking-display-updates-2026-08-18', 'extended-cache-ttl-2025-04-11', 'cache-diagnosis-2026-04-07'];
20
+ /**
21
+ * Claude Code's `anthropic-beta` list for each request class, before the
22
+ * body-dependent additions (turn-end note, structured outputs, fast mode and
23
+ * caller-supplied betas). `main` is a conversation turn; `title` is any other
24
+ * auxiliary request; `quota` is a one-token auxiliary probe.
25
+ */
26
+ export type ClaudeCodeBetaLists = {
27
+ main: readonly string[];
28
+ title: readonly string[];
29
+ quota: readonly string[];
30
+ };
31
+ /** The lists for a model without its own row. */
32
+ export declare const CLAUDE_CODE_DEFAULT_BETAS: ClaudeCodeBetaLists;
33
+ export type ClaudeModelFamily = 'fable' | 'mythos' | 'opus' | 'sonnet' | 'haiku';
34
+ /** Per-million-token USD prices. */
35
+ export type ClaudeModelPricing = {
36
+ input: number;
37
+ output: number;
38
+ cacheRead: number;
39
+ cacheWrite5m: number;
40
+ cacheWrite1h: number;
41
+ };
42
+ /**
43
+ * Catalogue facts for a model the plugin registers itself in OpenCode and Pi,
44
+ * rather than taking from the host's own model list.
45
+ */
46
+ export type ClaudeModelCatalogue = {
47
+ releaseDate: string;
48
+ contextWindow: number;
49
+ maxOutputTokens: number;
50
+ pricing: ClaudeModelPricing;
51
+ /** Higher prices for every token of a request above `inputTokensAbove` input tokens. */
52
+ longContext?: {
53
+ inputTokensAbove: number;
54
+ pricing: ClaudeModelPricing;
55
+ };
56
+ };
57
+ /** Source-model keys for content-filter recovery to Opus 4.8 in OpenCode. */
58
+ export type ClaudeRefusalRecoveryFamily = 'fable-5' | 'fable-5-1' | 'opus-5' | 'opus-5-5';
59
+ export type ClaudeModelCapabilities = {
60
+ /** Exact Anthropic model id. */
61
+ id: string;
62
+ /** Display name in host model pickers. */
63
+ name: string;
64
+ family: ClaudeModelFamily;
65
+ /**
66
+ * Longer ids that also resolve to this row: `any` accepts the id followed by
67
+ * `-` and anything (a date, `-fast`); `dated` accepts only `-YYYYMMDD`.
68
+ */
69
+ idSuffixes: 'any' | 'dated';
70
+ /** Shown as limited availability. */
71
+ limited?: true;
72
+ /** Absent when the model comes from the host's own catalogue. */
73
+ catalogue?: ClaudeModelCatalogue;
74
+ /**
75
+ * Thinking settings the API accepts. OpenCode's transform.ts and Pi's
76
+ * convert.ts still normalize thinking with per-family code; a test checks
77
+ * that OpenCode's result agrees with this list.
78
+ */
79
+ thinkingModes: readonly ('adaptive' | 'disabled' | 'between_tools')[];
80
+ /** The API rejects `tool_choice` of type `any` or `tool`, so it is removed. */
81
+ rejectsForcedToolChoice: boolean;
82
+ efforts: {
83
+ /**
84
+ * Efforts offered as host model variants with adaptive thinking. Absent:
85
+ * the host's own variants are left alone. Unknown models: absent.
86
+ */
87
+ variants?: readonly AdaptiveEffort[];
88
+ /** Anthropic's effort when a request sets none. Unknown models: `high`. */
89
+ default: AdaptiveEffort;
90
+ };
91
+ /**
92
+ * When effort changes may be sent as empty `role: "system"` messages:
93
+ * `always` (and top-level effort is then pinned on every request),
94
+ * `with-adaptive-thinking` (only when the request's thinking is adaptive),
95
+ * or `none`. Unknown models: `none`.
96
+ */
97
+ effortMarkers: 'none' | 'with-adaptive-thinking' | 'always';
98
+ /** Turn-end notes may be inserted. Unknown models: no. */
99
+ turnEndNote: boolean;
100
+ /** `thinking.block_binding` may be set. Unknown models: no. */
101
+ thinkingBindingControls: boolean;
102
+ /** Unknown models: `CLAUDE_CODE_DEFAULT_BETAS`. */
103
+ claudeCodeBetas: ClaudeCodeBetaLists;
104
+ /** OpenCode may ask Anthropic for its server-side safety fallback. */
105
+ serverSideFallback: boolean;
106
+ /** OpenCode recovers from a content-filter refusal on Opus 4.8 under this key. */
107
+ refusalRecoveryFamily?: ClaudeRefusalRecoveryFamily;
108
+ /**
109
+ * Model-scoped weekly quota this model draws from. Quota matching in
110
+ * accounts.ts and native-quota-codec.ts still matches the substrings
111
+ * `fable`/`mythos`; a test checks that they agree with this field.
112
+ */
113
+ scopedQuota?: 'fable' | 'mythos';
114
+ /**
115
+ * Fast mode (`speed: "fast"`) is offered. `isFastModeSupportedModel` also
116
+ * keeps its older prefix rule for ids outside this table.
117
+ */
118
+ fastMode: boolean;
119
+ };
120
+ export declare const CLAUDE_MODEL_CAPABILITIES: readonly ClaudeModelCapabilities[];
121
+ /** Remove OpenCode's context-window qualifier before model-family checks. */
122
+ export declare function normalizeAnthropicModelId(model: string): string;
123
+ /**
124
+ * The row for a request model, or `undefined` for a model this plugin does
125
+ * not special-case. See the table's description for how ids match.
126
+ */
127
+ export declare function resolveClaudeModelCapabilities(model: unknown): ClaudeModelCapabilities | undefined;
128
+ /** The row for an exact model id; throws when the table has no such row. */
129
+ export declare function getClaudeModelCapabilities(id: string): ClaudeModelCapabilities;
130
+ /** Catalogue facts for an exact model id; throws when the row has none. */
131
+ export declare function getClaudeModelCatalogue(id: string): ClaudeModelCatalogue;
132
+ /**
133
+ * Whether effort is pinned in top-level `output_config` on every request,
134
+ * even one without an effort change.
135
+ */
136
+ export declare function pinsEffortOnEveryRequest(model: unknown): boolean;