@askalf/dario 6.19.2 → 6.20.1
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/README.md +4 -3
- package/dist/admin-api.d.ts +1 -1
- package/dist/admin-api.js +40 -5
- package/dist/analytics.d.ts +16 -1
- package/dist/analytics.js +25 -2
- package/dist/anthropic-responses-translate.d.ts +2 -0
- package/dist/anthropic-responses-translate.js +1 -1
- package/dist/cc-template.d.ts +9 -8
- package/dist/cc-template.js +39 -25
- package/dist/cli.js +44 -10
- package/dist/codex-backend.d.ts +13 -0
- package/dist/codex-backend.js +22 -1
- package/dist/doctor-core.d.ts +1 -1
- package/dist/doctor-core.js +2 -2
- package/dist/effort.d.ts +18 -0
- package/dist/effort.js +38 -0
- package/dist/keys.d.ts +20 -0
- package/dist/keys.js +27 -1
- package/dist/ledger.d.ts +17 -1
- package/dist/ledger.js +43 -9
- package/dist/live-fingerprint.js +2 -2
- package/dist/proxy.js +46 -12
- package/docs/admin-api.md +2 -1
- package/docs/configuration.md +10 -3
- package/docs/drift-monitor.md +4 -4
- package/docs/keys.md +36 -1
- package/docs/pool-keys-and-analytics.md +3 -0
- package/docs/system-prompt.md +2 -0
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -10,13 +10,14 @@
|
|
|
10
10
|
<a href="https://www.npmjs.com/package/@askalf/dario"><img src="https://img.shields.io/npm/v/@askalf/dario?color=6f42c1&label=npm&logo=npm" alt="npm version"></a>
|
|
11
11
|
<a href="https://www.npmjs.com/package/@askalf/dario"><img src="https://img.shields.io/npm/dm/@askalf/dario?color=6f42c1" alt="Downloads"></a>
|
|
12
12
|
<a href="https://github.com/askalf/dario/actions/workflows/ci.yml"><img src="https://github.com/askalf/dario/actions/workflows/ci.yml/badge.svg?branch=master" alt="CI"></a>
|
|
13
|
+
<a href="https://pitlane.askalf.org/"><img src="https://img.shields.io/endpoint?url=https%3A%2F%2Fpitlane.askalf.org%2Fbadge.json" alt="CI on pitlane"></a>
|
|
13
14
|
<a href="https://scorecard.dev/viewer/?uri=github.com/askalf/dario"><img src="https://api.scorecard.dev/projects/github.com/askalf/dario/badge" alt="OpenSSF Scorecard"></a>
|
|
14
15
|
<a href="https://github.com/askalf/dario/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/@askalf/dario?color=6f42c1" alt="License"></a>
|
|
15
16
|
</p>
|
|
16
17
|
|
|
17
18
|
<p><strong>One local endpoint. Every AI tool you own. Your Claude seats first, your ChatGPT plan and a metered key as the rungs you chain behind them, and a ledger of who paid for what.</strong></p>
|
|
18
19
|
|
|
19
|
-
<sub><code>npm i -g @askalf/dario</code> or <code>brew install askalf/tap/dario</code> · <strong>0</strong> runtime deps · <a href="https://www.npmjs.com/package/@askalf/dario">SLSA-attested</a> every release · nothing phones home · ~
|
|
20
|
+
<sub><code>npm i -g @askalf/dario</code> or <code>brew install askalf/tap/dario</code> · <strong>0</strong> runtime deps · <a href="https://www.npmjs.com/package/@askalf/dario">SLSA-attested</a> every release · nothing phones home · ~44k lines you can read in a weekend · independent, unofficial, third-party (<a href="DISCLAIMER.md">DISCLAIMER.md</a>)</sub>
|
|
20
21
|
|
|
21
22
|
<sub><a href="#start-in-60-seconds">Start</a> · <a href="#why-people-pick-it">Why dario</a> · <a href="#what-it-does-with-a-request">Routing</a> · <a href="#will-my-account-get-suspended">Risk</a> · <a href="#reference">Reference</a> · <a href="#trust--transparency">Trust</a> · <a href="docs/returning.md">Coming back after a while?</a></sub>
|
|
22
23
|
|
|
@@ -113,11 +114,11 @@ dario runs entirely on your machine, authenticates as you with your own Claude l
|
|
|
113
114
|
|
|
114
115
|
| Signal | Status |
|
|
115
116
|
|---|---|
|
|
116
|
-
| Source | **~
|
|
117
|
+
| Source | **~44k** lines of TypeScript across **88** files, auditable in a weekend. One credential path since v5: the pool. |
|
|
117
118
|
| Dependencies | **0 runtime.** Verify: `npm ls --production` |
|
|
118
119
|
| Provenance | Every release [SLSA-attested](https://www.npmjs.com/package/@askalf/dario) via GitHub Actions + Sigstore, published with OIDC trusted publishing: no long-lived npm token exists to leak |
|
|
119
120
|
| Scanning | [CodeQL](https://github.com/askalf/dario/actions/workflows/codeql.yml) on every push to master, every PR and weekly · [ClusterFuzzLite](./.github/workflows/cflite.yml) fuzzes the SSE translator, the rejection parsers and the cch stamper weekly · the [OpenSSF Scorecard](https://scorecard.dev/viewer/?uri=github.com/askalf/dario) badge above and the [Best Practices](https://www.bestpractices.dev/projects/13638) entry are live |
|
|
120
|
-
| Tests | **
|
|
121
|
+
| Tests | **288 test files** run in parallel by `npm test` on Node 18, 20 and 22; the live e2e / compat / stealth suites have their own entry points. Green on every release |
|
|
121
122
|
| Credentials | Your own subscription tokens, never logged, redacted from errors, `0600` on disk in `0700` dirs |
|
|
122
123
|
| Network | Binds `127.0.0.1` by default; upstream only to configured backends over HTTPS; hardcoded SSRF allow-list; refuses a non-loopback bind without `DARIO_API_KEY`; refuses cross-origin and DNS-rebound browser requests |
|
|
123
124
|
| Telemetry | **None.** No analytics, no tracking, nothing phones home |
|
package/dist/admin-api.d.ts
CHANGED
|
@@ -168,7 +168,7 @@ export interface AdminCodexAccountRecord extends CodexSeatState {
|
|
|
168
168
|
usage: CodexUsageView | null;
|
|
169
169
|
}
|
|
170
170
|
export interface AdminAuditEvent {
|
|
171
|
-
action: 'login_start' | 'login_complete' | 'account_remove' | 'auth_reject' | 'rate_limited' | 'key_create' | 'key_revoke' | 'key_rotate' | 'key_budget';
|
|
171
|
+
action: 'login_start' | 'login_complete' | 'account_remove' | 'auth_reject' | 'rate_limited' | 'key_create' | 'key_revoke' | 'key_rotate' | 'key_budget' | 'key_effort';
|
|
172
172
|
/** Which engine's credentials the event touched; absent means Claude, the only engine before codex joined (dario#1009). */
|
|
173
173
|
engine?: 'codex';
|
|
174
174
|
ok: boolean;
|
package/dist/admin-api.js
CHANGED
|
@@ -5,7 +5,7 @@ import { startAddCodexAccount, completeAddCodexAccount, removeCodexAccount, load
|
|
|
5
5
|
import { codexUsageView } from './codex-usage.js';
|
|
6
6
|
import { parseManualPaste } from './oauth.js';
|
|
7
7
|
import { grantAge } from './refresh-grant.js';
|
|
8
|
-
import { createKey, revokeKey, rotateKey, parseExpiry, publicKey, setKeyBudget, normalizeBudget, KEY_NAME_RE } from './keys.js';
|
|
8
|
+
import { createKey, revokeKey, rotateKey, parseExpiry, publicKey, setKeyBudget, setKeyEffort, isKeyEffort, normalizeBudget, KEY_NAME_RE, KEY_EFFORT_VALUES } from './keys.js';
|
|
9
9
|
/**
|
|
10
10
|
* `{ budget_usd_per_day, budget_tokens_per_day }` (numbers, or numeric strings)
|
|
11
11
|
* → a KeyBudget; undefined when neither is present; throws with a 400-worthy
|
|
@@ -267,8 +267,10 @@ export async function handleAdminRequest(req, res, urlPath, deps) {
|
|
|
267
267
|
const isKeyRotate = keyTarget !== null && keyTarget.endsWith('/rotate');
|
|
268
268
|
// POST /admin/keys/<name>/budget — set or clear a key's daily caps (dario#1318 follow-up).
|
|
269
269
|
const isKeyBudget = keyTarget !== null && keyTarget.endsWith('/budget');
|
|
270
|
-
|
|
271
|
-
const
|
|
270
|
+
// POST /admin/keys/<name>/effort: set or clear a key's default effort.
|
|
271
|
+
const isKeyEffortRoute = keyTarget !== null && keyTarget.endsWith('/effort');
|
|
272
|
+
const keyName = keyTarget === null ? null : isKeyRotate ? keyTarget.slice(0, -'/rotate'.length) : isKeyBudget ? keyTarget.slice(0, -'/budget'.length) : isKeyEffortRoute ? keyTarget.slice(0, -'/effort'.length) : keyTarget;
|
|
273
|
+
const isKeyRevoke = keyTarget !== null && !isKeyRotate && !isKeyBudget && !isKeyEffortRoute && method === 'DELETE';
|
|
272
274
|
const known = urlPath === '/admin/login/start' ||
|
|
273
275
|
urlPath === '/admin/login/start-needed' ||
|
|
274
276
|
urlPath === '/admin/login/complete' ||
|
|
@@ -281,6 +283,7 @@ export async function handleAdminRequest(req, res, urlPath, deps) {
|
|
|
281
283
|
isAccountDelete ||
|
|
282
284
|
isKeyRotate ||
|
|
283
285
|
isKeyBudget ||
|
|
286
|
+
isKeyEffortRoute ||
|
|
284
287
|
isKeyRevoke;
|
|
285
288
|
if (!known)
|
|
286
289
|
return false;
|
|
@@ -313,7 +316,7 @@ export async function handleAdminRequest(req, res, urlPath, deps) {
|
|
|
313
316
|
// accounts' credentials for the price of one throttle token.
|
|
314
317
|
const isMutation = urlPath === '/admin/login/start' || isAccountDelete
|
|
315
318
|
|| urlPath === '/admin/codex/login/start' || isCodexAccountDelete
|
|
316
|
-
|| (urlPath === '/admin/keys' && method === 'POST') || isKeyRotate || isKeyBudget || isKeyRevoke;
|
|
319
|
+
|| (urlPath === '/admin/keys' && method === 'POST') || isKeyRotate || isKeyBudget || isKeyEffortRoute || isKeyRevoke;
|
|
317
320
|
if (isMutation) {
|
|
318
321
|
const wait = deps.rateLimit?.('mutation') ?? 0;
|
|
319
322
|
if (wait > 0) {
|
|
@@ -642,8 +645,16 @@ export async function handleAdminRequest(req, res, urlPath, deps) {
|
|
|
642
645
|
send(res, 400, { error: err.message });
|
|
643
646
|
return true;
|
|
644
647
|
}
|
|
648
|
+
let effort;
|
|
649
|
+
if (body.effort !== undefined && body.effort !== null) {
|
|
650
|
+
if (!isKeyEffort(body.effort)) {
|
|
651
|
+
send(res, 400, { error: `invalid "effort": one of ${KEY_EFFORT_VALUES.join(', ')}` });
|
|
652
|
+
return true;
|
|
653
|
+
}
|
|
654
|
+
effort = body.effort;
|
|
655
|
+
}
|
|
645
656
|
try {
|
|
646
|
-
const made = store.mutate((file) => createKey(file, name, { seat, models, expiresAt, budget, now }));
|
|
657
|
+
const made = store.mutate((file) => createKey(file, name, { seat, models, expiresAt, budget, effort, now }));
|
|
647
658
|
deps.audit?.({ action: 'key_create', ok: true, status: 201, key: made.record.name, remote, detail: seat ? `seat=${seat}` : undefined });
|
|
648
659
|
send(res, 201, { key: publicKey(made.record, now), secret: made.secret, note: 'the secret is shown once and is not stored' });
|
|
649
660
|
}
|
|
@@ -702,6 +713,30 @@ export async function handleAdminRequest(req, res, urlPath, deps) {
|
|
|
702
713
|
send(res, 200, { key: publicKey(updated, now) });
|
|
703
714
|
return true;
|
|
704
715
|
}
|
|
716
|
+
// POST /admin/keys/<name>/effort { effort: "low" | ... | "max" | null }; null or absent clears.
|
|
717
|
+
if (isKeyEffortRoute) {
|
|
718
|
+
if (method !== 'POST') {
|
|
719
|
+
send(res, 405, { error: 'Method not allowed (use POST)' });
|
|
720
|
+
return true;
|
|
721
|
+
}
|
|
722
|
+
const body = await readJsonBody(req);
|
|
723
|
+
let effort = null;
|
|
724
|
+
if (body.effort !== undefined && body.effort !== null) {
|
|
725
|
+
if (!isKeyEffort(body.effort)) {
|
|
726
|
+
send(res, 400, { error: `invalid "effort": one of ${KEY_EFFORT_VALUES.join(', ')}` });
|
|
727
|
+
return true;
|
|
728
|
+
}
|
|
729
|
+
effort = body.effort;
|
|
730
|
+
}
|
|
731
|
+
const updated = store.mutate((file) => setKeyEffort(file, keyName, effort));
|
|
732
|
+
deps.audit?.({ action: 'key_effort', ok: updated !== null, status: updated ? 200 : 404, key: keyName, remote, detail: effort ?? 'cleared' });
|
|
733
|
+
if (!updated) {
|
|
734
|
+
send(res, 404, { error: `no key named "${keyName}"` });
|
|
735
|
+
return true;
|
|
736
|
+
}
|
|
737
|
+
send(res, 200, { key: publicKey(updated, now) });
|
|
738
|
+
return true;
|
|
739
|
+
}
|
|
705
740
|
// DELETE /admin/keys/<name> — revoked, kept for the list.
|
|
706
741
|
if (isKeyRevoke) {
|
|
707
742
|
const revoked = store.mutate((file) => revokeKey(file, keyName));
|
package/dist/analytics.d.ts
CHANGED
|
@@ -18,6 +18,12 @@
|
|
|
18
18
|
*/
|
|
19
19
|
import { EventEmitter } from 'node:events';
|
|
20
20
|
import { type RequestTiming, type TimingStats } from './timing.js';
|
|
21
|
+
/**
|
|
22
|
+
* The billed thinking count inside a usage block, or undefined when the block
|
|
23
|
+
* carries none: Anthropic's `output_tokens_details.thinking_tokens`. The
|
|
24
|
+
* number is a share of `output_tokens`, never an addition to it.
|
|
25
|
+
*/
|
|
26
|
+
export declare function thinkingTokensFromUsage(usage: unknown): number | undefined;
|
|
21
27
|
export interface RequestRecord {
|
|
22
28
|
timestamp: number;
|
|
23
29
|
/**
|
|
@@ -341,7 +347,16 @@ export declare class Analytics extends EventEmitter {
|
|
|
341
347
|
* empty list.
|
|
342
348
|
*/
|
|
343
349
|
recent(n?: number): RequestRecord[];
|
|
344
|
-
/**
|
|
350
|
+
/**
|
|
351
|
+
* Parse usage from a non-streaming Anthropic response body.
|
|
352
|
+
*
|
|
353
|
+
* Thinking tokens come from `usage.output_tokens_details.thinking_tokens`
|
|
354
|
+
* when the response carries it. The older estimate, a quarter of the
|
|
355
|
+
* characters in the `thinking` blocks, stays as the fallback for responses
|
|
356
|
+
* without the field, but it reads ZERO on every current model, because
|
|
357
|
+
* they return thinking blocks with empty text unless the caller asked for a
|
|
358
|
+
* summary. The usage field counts what was billed.
|
|
359
|
+
*/
|
|
345
360
|
static parseUsage(body: Record<string, unknown>): {
|
|
346
361
|
inputTokens: number;
|
|
347
362
|
outputTokens: number;
|
package/dist/analytics.js
CHANGED
|
@@ -19,6 +19,20 @@
|
|
|
19
19
|
import { EventEmitter } from 'node:events';
|
|
20
20
|
import { createHash } from 'node:crypto';
|
|
21
21
|
import { timingStats } from './timing.js';
|
|
22
|
+
/**
|
|
23
|
+
* The billed thinking count inside a usage block, or undefined when the block
|
|
24
|
+
* carries none: Anthropic's `output_tokens_details.thinking_tokens`. The
|
|
25
|
+
* number is a share of `output_tokens`, never an addition to it.
|
|
26
|
+
*/
|
|
27
|
+
export function thinkingTokensFromUsage(usage) {
|
|
28
|
+
if (!usage || typeof usage !== 'object')
|
|
29
|
+
return undefined;
|
|
30
|
+
const details = usage.output_tokens_details;
|
|
31
|
+
if (!details || typeof details !== 'object')
|
|
32
|
+
return undefined;
|
|
33
|
+
const n = details.thinking_tokens;
|
|
34
|
+
return typeof n === 'number' && Number.isFinite(n) && n >= 0 ? Math.round(n) : undefined;
|
|
35
|
+
}
|
|
22
36
|
export function continuationStats(records) {
|
|
23
37
|
const out = { attempted: 0, finished: 0, unfinished: 0, failed: 0, noTarget: 0 };
|
|
24
38
|
for (const r of records) {
|
|
@@ -440,14 +454,23 @@ export class Analytics extends EventEmitter {
|
|
|
440
454
|
recent(n = 100) {
|
|
441
455
|
return this.records.slice(-n);
|
|
442
456
|
}
|
|
443
|
-
/**
|
|
457
|
+
/**
|
|
458
|
+
* Parse usage from a non-streaming Anthropic response body.
|
|
459
|
+
*
|
|
460
|
+
* Thinking tokens come from `usage.output_tokens_details.thinking_tokens`
|
|
461
|
+
* when the response carries it. The older estimate, a quarter of the
|
|
462
|
+
* characters in the `thinking` blocks, stays as the fallback for responses
|
|
463
|
+
* without the field, but it reads ZERO on every current model, because
|
|
464
|
+
* they return thinking blocks with empty text unless the caller asked for a
|
|
465
|
+
* summary. The usage field counts what was billed.
|
|
466
|
+
*/
|
|
444
467
|
static parseUsage(body) {
|
|
445
468
|
const u = body.usage;
|
|
446
469
|
const content = body.content;
|
|
447
470
|
const thinkingChars = content
|
|
448
471
|
?.filter(b => b.type === 'thinking')
|
|
449
472
|
.reduce((s, b) => s + (b.thinking?.length ?? 0), 0) ?? 0;
|
|
450
|
-
const thinkingTokens = Math.round(thinkingChars / 4);
|
|
473
|
+
const thinkingTokens = thinkingTokensFromUsage(u) ?? Math.round(thinkingChars / 4);
|
|
451
474
|
return {
|
|
452
475
|
inputTokens: u?.input_tokens ?? 0,
|
|
453
476
|
outputTokens: u?.output_tokens ?? 0,
|
|
@@ -387,6 +387,8 @@ export interface ResponsesResponse {
|
|
|
387
387
|
} | null;
|
|
388
388
|
usage?: ResponsesUsage | null;
|
|
389
389
|
}
|
|
390
|
+
/** thinking → reasoning effort, at the same cut points as the chat path. */
|
|
391
|
+
export declare function thinkingToReasoningEffort(thinking: AnthropicThinkingConfig | undefined): 'low' | 'medium' | 'high' | undefined;
|
|
390
392
|
/**
|
|
391
393
|
* Extra max_output_tokens reserved for reasoning tokens, by effort, so the
|
|
392
394
|
* client's intended visible-output budget survives on a reasoning model
|
|
@@ -111,7 +111,7 @@ export function anthropicUsageFromResponses(u) {
|
|
|
111
111
|
// Thresholds are IMPORTED so the two translators stay in lock-step.
|
|
112
112
|
// ─────────────────────────────────────────────────────────────────────
|
|
113
113
|
/** thinking → reasoning effort, at the same cut points as the chat path. */
|
|
114
|
-
function thinkingToReasoningEffort(thinking) {
|
|
114
|
+
export function thinkingToReasoningEffort(thinking) {
|
|
115
115
|
if (!thinking || thinking.type !== 'enabled')
|
|
116
116
|
return undefined;
|
|
117
117
|
const budget = thinking.budget_tokens;
|
package/dist/cc-template.d.ts
CHANGED
|
@@ -156,12 +156,13 @@ export declare const CC_AGENT_IDENTITY: string;
|
|
|
156
156
|
* - undefined / 'verbatim' — CC's prompt unchanged (default; existing
|
|
157
157
|
* setups don't regress).
|
|
158
158
|
* - 'partial' — strip purely behavioral constraints, leaving every
|
|
159
|
-
* refusal reminder and tool description intact.
|
|
160
|
-
* prompt
|
|
161
|
-
*
|
|
162
|
-
*
|
|
163
|
-
* Tone-and-style
|
|
164
|
-
*
|
|
159
|
+
* refusal reminder and tool description intact. The strip runs on
|
|
160
|
+
* the prompt CC sends for `model`: a one-line comment rule (compact
|
|
161
|
+
* prompts, the fable variant) is swapped for a positive "be thorough"
|
|
162
|
+
* instruction; a verbose prompt (the sonnet-5 variant) also loses its
|
|
163
|
+
* Tone-and-style and Text-output sections and the scope, verbosity
|
|
164
|
+
* and comment bullets in Doing tasks. A prompt with none of these is
|
|
165
|
+
* sent unchanged. Recovers the output capability the
|
|
165
166
|
* constraint-removal research test measured.
|
|
166
167
|
* - 'aggressive' — partial + remove the prompt-level RLHF reminder (the
|
|
167
168
|
* IMPORTANT: line re-stating refusal categories) and the caution
|
|
@@ -177,7 +178,7 @@ export declare const CC_AGENT_IDENTITY: string;
|
|
|
177
178
|
* Precedence framing inserted between CC's persona prompt and the client's
|
|
178
179
|
* own system text in the merged block-3 system prompt.
|
|
179
180
|
*
|
|
180
|
-
* Why this exists (observed 2026-06-12,
|
|
181
|
+
* Why this exists (observed 2026-06-12, a downstream planner regression): a bare
|
|
181
182
|
* `\n\n` append silently stopped working on claude-sonnet-4-6 — the model
|
|
182
183
|
* followed the CC persona and treated the appended client instructions as
|
|
183
184
|
* ignorable boilerplate, deterministically (0/6 on a trivial "reply with
|
|
@@ -330,7 +331,7 @@ export declare function detectTextToolClient(systemText: string): string | null;
|
|
|
330
331
|
* own native tools (CC_NATIVE_NAMES), so it can never present a fully-foreign
|
|
331
332
|
* surface; only a genuinely non-CC tool set can. This catches the small
|
|
332
333
|
* in-house clients the 3-tool rule below
|
|
333
|
-
* misses, e.g.
|
|
334
|
+
* misses, e.g. in-house inspection agents dispatched with just their capability
|
|
334
335
|
* floor ([memory_store, db_query]). Before this, those 2-tool surfaces fell
|
|
335
336
|
* under a len<3 guard and got round-robined onto CC fallback slots, which
|
|
336
337
|
* silently corrupts every call (the model upstream never sees the real tool).
|
package/dist/cc-template.js
CHANGED
|
@@ -209,12 +209,13 @@ export const CC_AGENT_IDENTITY = TEMPLATE.agent_identity;
|
|
|
209
209
|
* - undefined / 'verbatim' — CC's prompt unchanged (default; existing
|
|
210
210
|
* setups don't regress).
|
|
211
211
|
* - 'partial' — strip purely behavioral constraints, leaving every
|
|
212
|
-
* refusal reminder and tool description intact.
|
|
213
|
-
* prompt
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
* Tone-and-style
|
|
217
|
-
*
|
|
212
|
+
* refusal reminder and tool description intact. The strip runs on
|
|
213
|
+
* the prompt CC sends for `model`: a one-line comment rule (compact
|
|
214
|
+
* prompts, the fable variant) is swapped for a positive "be thorough"
|
|
215
|
+
* instruction; a verbose prompt (the sonnet-5 variant) also loses its
|
|
216
|
+
* Tone-and-style and Text-output sections and the scope, verbosity
|
|
217
|
+
* and comment bullets in Doing tasks. A prompt with none of these is
|
|
218
|
+
* sent unchanged. Recovers the output capability the
|
|
218
219
|
* constraint-removal research test measured.
|
|
219
220
|
* - 'aggressive' — partial + remove the prompt-level RLHF reminder (the
|
|
220
221
|
* IMPORTANT: line re-stating refusal categories) and the caution
|
|
@@ -230,7 +231,7 @@ export const CC_AGENT_IDENTITY = TEMPLATE.agent_identity;
|
|
|
230
231
|
* Precedence framing inserted between CC's persona prompt and the client's
|
|
231
232
|
* own system text in the merged block-3 system prompt.
|
|
232
233
|
*
|
|
233
|
-
* Why this exists (observed 2026-06-12,
|
|
234
|
+
* Why this exists (observed 2026-06-12, a downstream planner regression): a bare
|
|
234
235
|
* `\n\n` append silently stopped working on claude-sonnet-4-6 — the model
|
|
235
236
|
* followed the CC persona and treated the appended client instructions as
|
|
236
237
|
* ignorable boilerplate, deterministically (0/6 on a trivial "reply with
|
|
@@ -281,35 +282,48 @@ export function resolveSystemPrompt(arg, model) {
|
|
|
281
282
|
* Port of scripts/research/test-constraint-removal.mjs:stripConstraints. Pure over
|
|
282
283
|
* its input; returns the input unchanged if no target matches (so a CC
|
|
283
284
|
* bump that renames sections degrades to verbatim rather than producing
|
|
284
|
-
* an unpredictable strip).
|
|
285
|
-
*
|
|
286
|
-
*
|
|
285
|
+
* an unpredictable strip). CC sends a different prompt per model family
|
|
286
|
+
* and each words its constraints differently: the verbose shape (headed
|
|
287
|
+
* sections and Doing-tasks bullets, e.g. the sonnet-5 variant), the
|
|
288
|
+
* compact shape's comment-density line, and the fable variant's comment
|
|
289
|
+
* paragraph. Patterns for a shape not in play are no-ops, so a prompt
|
|
290
|
+
* carrying none of them (the 2.1.316 base and the opus-5 variant) comes
|
|
291
|
+
* back unchanged. test/system-prompt-modes.mjs lists which bundled prompt
|
|
292
|
+
* carries which constraint, so a rebake that rewords one fails there.
|
|
287
293
|
*/
|
|
288
294
|
function stripBehavioralConstraints(input, level) {
|
|
289
295
|
let s = input;
|
|
290
|
-
//
|
|
291
|
-
|
|
292
|
-
|
|
296
|
+
// Verbose shape: whole style sections, then the Doing-tasks bullets. A
|
|
297
|
+
// section runs to the next top-level heading or the end of the prompt.
|
|
298
|
+
// The end test is (?![\s\S]), not $: under the m flag $ matches at every
|
|
299
|
+
// line end, which cut the match at the heading and left the body in.
|
|
300
|
+
s = s.replace(/^# Tone and style[\s\S]*?(?:\n(?=# )|(?![\s\S]))/m, '');
|
|
301
|
+
s = s.replace(/^# Text output[\s\S]*?(?:\n(?=# )|(?![\s\S]))/m, '');
|
|
302
|
+
// Each pattern takes one bullet plus any wrapped or nested lines under
|
|
303
|
+
// it, and stops at the next top-level bullet, heading or blank line. CC
|
|
304
|
+
// writes each bullet on one line and the next bullet is often a neutral
|
|
305
|
+
// one ("Prefer editing existing files"), so a fixed line count would
|
|
306
|
+
// delete it.
|
|
293
307
|
const doingTasksConstraints = [
|
|
294
|
-
/^ - Don't add features, refactor, or introduce abstractions[^\n]*\n[^\n]
|
|
295
|
-
/^ - Don't add error handling, fallbacks, or validation[^\n]*\n[^\n]
|
|
296
|
-
/^ - Default to writing no comments\.[^\n]*\n[^\n]
|
|
297
|
-
/^ - Don't explain WHAT the code does[^\n]*\n[^\n]
|
|
298
|
-
/^ - For exploratory questions[^\n]*\n[^\n]
|
|
299
|
-
/^ - Avoid backwards-compatibility hacks[^\n]*\n[^\n]
|
|
308
|
+
/^ - Don't add features, refactor, or introduce abstractions[^\n]*\n(?:(?! - |#)[^\n]+\n)*/m,
|
|
309
|
+
/^ - Don't add error handling, fallbacks, or validation[^\n]*\n(?:(?! - |#)[^\n]+\n)*/m,
|
|
310
|
+
/^ - Default to writing no comments\.[^\n]*\n(?:(?! - |#)[^\n]+\n)*/m,
|
|
311
|
+
/^ - Don't explain WHAT the code does[^\n]*\n(?:(?! - |#)[^\n]+\n)*/m,
|
|
312
|
+
/^ - For exploratory questions[^\n]*\n(?:(?! - |#)[^\n]+\n)*/m,
|
|
313
|
+
/^ - Avoid backwards-compatibility hacks[^\n]*\n(?:(?! - |#)[^\n]+\n)*/m,
|
|
300
314
|
];
|
|
301
315
|
for (const re of doingTasksConstraints) {
|
|
302
316
|
s = s.replace(re, '');
|
|
303
317
|
}
|
|
304
318
|
s = s.replace(/^# Doing tasks\n/m, '# Doing tasks\n\nBe thorough. Show your reasoning. Provide the context and explanations the user is likely to find useful. Use as many tokens as the task warrants.\n\n');
|
|
305
|
-
//
|
|
306
|
-
//
|
|
307
|
-
//
|
|
308
|
-
s = s.replace(/^Write code that reads like the surrounding code
|
|
319
|
+
// Compact shape and the fable variant: the comment constraint is one
|
|
320
|
+
// unheaded line. Swap it for the same positive instruction the
|
|
321
|
+
// Doing-tasks rewrite inserts.
|
|
322
|
+
s = s.replace(/^(?:Write code that reads like the surrounding code:|Only write a code comment to state a constraint)[^\n]*\n/gm, 'Be thorough. Show your reasoning. Provide the context and explanations the user is likely to find useful. Use as many tokens as the task warrants.\n');
|
|
309
323
|
if (level === 'aggressive') {
|
|
310
324
|
s = s.replace(/^IMPORTANT: Assist with authorized security testing[^\n]*\n/m, '');
|
|
311
325
|
s = s.replace(/^IMPORTANT: You must NEVER generate or guess URLs[^\n]*\n/m, '');
|
|
312
|
-
s = s.replace(
|
|
326
|
+
s = s.replace(/^# Executing actions with care[\s\S]*?(?:\n(?=# )|(?![\s\S]))/m, '');
|
|
313
327
|
// Compact prompt: the caution guidance is a single unheaded paragraph.
|
|
314
328
|
s = s.replace(/^For actions that are hard to reverse or outward-facing,[^\n]*\n/m, '');
|
|
315
329
|
}
|
|
@@ -712,7 +726,7 @@ export function detectTextToolClient(systemText) {
|
|
|
712
726
|
* own native tools (CC_NATIVE_NAMES), so it can never present a fully-foreign
|
|
713
727
|
* surface; only a genuinely non-CC tool set can. This catches the small
|
|
714
728
|
* in-house clients the 3-tool rule below
|
|
715
|
-
* misses, e.g.
|
|
729
|
+
* misses, e.g. in-house inspection agents dispatched with just their capability
|
|
716
730
|
* floor ([memory_store, db_query]). Before this, those 2-tool surfaces fell
|
|
717
731
|
* under a len<3 guard and got round-robined onto CC fallback slots, which
|
|
718
732
|
* silently corrupts every call (the model upstream never sees the real tool).
|
package/dist/cli.js
CHANGED
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
import { unlink, writeFile } from 'node:fs/promises';
|
|
20
20
|
import { formatLedgerSummary, formatLedgerConsumers, formatUsd, renderLedgerCard, readLedgerFile, resolveLedgerPath, summarizeLedger } from './ledger.js';
|
|
21
21
|
import { renderSpendDonuts } from './donuts.js';
|
|
22
|
-
import { KeyStore, createKey, revokeKey, rotateKey, deleteKey, parseExpiry, publicKey, resolveKeysPath, KEY_NAME_RE, parseKeySocketSpec, setKeyBudget, parseUsdBudget, parseTokenBudget, formatBudget } from './keys.js';
|
|
22
|
+
import { KeyStore, createKey, revokeKey, rotateKey, deleteKey, parseExpiry, publicKey, resolveKeysPath, KEY_NAME_RE, parseKeySocketSpec, setKeyBudget, setKeyEffort, isKeyEffort, KEY_EFFORT_VALUES, parseUsdBudget, parseTokenBudget, formatBudget } from './keys.js';
|
|
23
23
|
/**
|
|
24
24
|
* `--budget=$5/day` / `--budget-tokens=2M/day` → a KeyBudget, or undefined when
|
|
25
25
|
* neither flag is present. A flag that does not parse exits 1 with the accepted
|
|
@@ -875,9 +875,9 @@ async function keys() {
|
|
|
875
875
|
return;
|
|
876
876
|
}
|
|
877
877
|
const w = Math.max(4, ...list.map((k) => k.name.length));
|
|
878
|
-
console.log(` ${'NAME'.padEnd(w)} ${'STATUS'.padEnd(7)} ${'SEAT'.padEnd(12)} ${'LAST USED'.padEnd(10)} ${'EXPIRES'.padEnd(10)} ${'BUDGET'.padEnd(20)} MODELS`);
|
|
878
|
+
console.log(` ${'NAME'.padEnd(w)} ${'STATUS'.padEnd(7)} ${'SEAT'.padEnd(12)} ${'EFFORT'.padEnd(6)} ${'LAST USED'.padEnd(10)} ${'EXPIRES'.padEnd(10)} ${'BUDGET'.padEnd(20)} MODELS`);
|
|
879
879
|
for (const k of list) {
|
|
880
|
-
console.log(` ${k.name.padEnd(w)} ${k.status.padEnd(7)} ${(k.seat ?? '-').padEnd(12)} ${fmtAgo(k.last_used).padEnd(10)} ${fmtDay(k.expires).padEnd(10)} ${formatBudget(k.budget ? { usdPerDay: k.budget.usd_per_day ?? undefined, tokensPerDay: k.budget.tokens_per_day ?? undefined } : null).padEnd(20)} ${k.models.length ? k.models.join(', ') : 'any'}`);
|
|
880
|
+
console.log(` ${k.name.padEnd(w)} ${k.status.padEnd(7)} ${(k.seat ?? '-').padEnd(12)} ${(k.effort ?? '-').padEnd(6)} ${fmtAgo(k.last_used).padEnd(10)} ${fmtDay(k.expires).padEnd(10)} ${formatBudget(k.budget ? { usdPerDay: k.budget.usd_per_day ?? undefined, tokensPerDay: k.budget.tokens_per_day ?? undefined } : null).padEnd(20)} ${k.models.length ? k.models.join(', ') : 'any'}`);
|
|
881
881
|
}
|
|
882
882
|
console.log('');
|
|
883
883
|
console.log(` ${list.length} key${list.length === 1 ? '' : 's'} in ${path}. Spend per key: dario usage --by-key`);
|
|
@@ -888,7 +888,7 @@ async function keys() {
|
|
|
888
888
|
const name = args[2];
|
|
889
889
|
if (!name || name.startsWith('--')) {
|
|
890
890
|
console.error('');
|
|
891
|
-
console.error(' Usage: dario keys create <name> [--seat=<alias>] [--models=a,b,prefix*] [--expires=30d|12h|2w|<ISO date>] [--budget=$5/day] [--budget-tokens=2M/day]');
|
|
891
|
+
console.error(' Usage: dario keys create <name> [--seat=<alias>] [--models=a,b,prefix*] [--expires=30d|12h|2w|<ISO date>] [--budget=$5/day] [--budget-tokens=2M/day] [--effort=low|medium|high|xhigh|max]');
|
|
892
892
|
console.error('');
|
|
893
893
|
process.exit(1);
|
|
894
894
|
}
|
|
@@ -907,8 +907,14 @@ async function keys() {
|
|
|
907
907
|
expiresAt = parsed;
|
|
908
908
|
}
|
|
909
909
|
const budget = readBudgetFlags(args);
|
|
910
|
+
const effortArg = args.find((a) => a.startsWith('--effort='));
|
|
911
|
+
const effort = effortArg ? effortArg.slice('--effort='.length).trim().toLowerCase() : undefined;
|
|
912
|
+
if (effort !== undefined && !isKeyEffort(effort)) {
|
|
913
|
+
console.error(`[dario] --effort: "${effort}" is not one of ${KEY_EFFORT_VALUES.join(', ')}.`);
|
|
914
|
+
process.exit(1);
|
|
915
|
+
}
|
|
910
916
|
try {
|
|
911
|
-
const made = store.mutate((file) => createKey(file, name, { seat, models, expiresAt, budget, now }));
|
|
917
|
+
const made = store.mutate((file) => createKey(file, name, { seat, models, expiresAt, budget, effort, now }));
|
|
912
918
|
printSecret('created', publicKey(made.record, now), made.secret);
|
|
913
919
|
}
|
|
914
920
|
catch (err) {
|
|
@@ -917,6 +923,34 @@ async function keys() {
|
|
|
917
923
|
}
|
|
918
924
|
return;
|
|
919
925
|
}
|
|
926
|
+
// dario keys effort <name> low|medium|high|xhigh|max | --clear
|
|
927
|
+
if (sub === 'effort') {
|
|
928
|
+
const name = args[2];
|
|
929
|
+
const raw = args.includes('--clear') ? null : (args[3] ?? '').trim().toLowerCase();
|
|
930
|
+
if (!name || !KEY_NAME_RE.test(name) || (raw !== null && !isKeyEffort(raw))) {
|
|
931
|
+
console.error('');
|
|
932
|
+
console.error(` Usage: dario keys effort <name> ${KEY_EFFORT_VALUES.join('|')} | --clear`);
|
|
933
|
+
console.error('');
|
|
934
|
+
console.error(' The effort this key\'s requests run at when they name none themselves (no model-name');
|
|
935
|
+
console.error(' suffix, no effort field in the body). --effort / DARIO_EFFORT still wins over it.');
|
|
936
|
+
console.error('');
|
|
937
|
+
process.exit(1);
|
|
938
|
+
}
|
|
939
|
+
const level = raw !== null && isKeyEffort(raw) ? raw : null;
|
|
940
|
+
try {
|
|
941
|
+
const updated = store.mutate((file) => setKeyEffort(file, name, level));
|
|
942
|
+
if (!updated) {
|
|
943
|
+
console.error(`[dario] No key named "${name}".`);
|
|
944
|
+
process.exit(1);
|
|
945
|
+
}
|
|
946
|
+
console.log(level ? `[dario] Key "${name}" default effort: ${level}` : `[dario] Key "${name}" default effort cleared.`);
|
|
947
|
+
}
|
|
948
|
+
catch (err) {
|
|
949
|
+
console.error(`[dario] ${err instanceof Error ? err.message : String(err)}`);
|
|
950
|
+
process.exit(1);
|
|
951
|
+
}
|
|
952
|
+
return;
|
|
953
|
+
}
|
|
920
954
|
// dario keys budget <name> --budget=$5/day --budget-tokens=2M/day | --clear
|
|
921
955
|
if (sub === 'budget') {
|
|
922
956
|
const name = args[2];
|
|
@@ -985,7 +1019,7 @@ async function keys() {
|
|
|
985
1019
|
return;
|
|
986
1020
|
}
|
|
987
1021
|
console.error(`[dario] Unknown keys subcommand: ${sub}`);
|
|
988
|
-
console.error('Usage: dario keys [list|create <name> [--seat=..] [--models=..] [--expires=..] [--budget=..] [--budget-tokens=..]|budget <name> ..|revoke <name>|rotate <name>|remove <name>] [--json] [--keys-path=<file>]');
|
|
1022
|
+
console.error('Usage: dario keys [list|create <name> [--seat=..] [--models=..] [--expires=..] [--budget=..] [--budget-tokens=..] [--effort=..]|budget <name> ..|effort <name> <level>|--clear|revoke <name>|rotate <name>|remove <name>] [--json] [--keys-path=<file>]');
|
|
989
1023
|
process.exit(1);
|
|
990
1024
|
}
|
|
991
1025
|
/**
|
|
@@ -2025,8 +2059,8 @@ async function help() {
|
|
|
2025
2059
|
miss. Needs \`dario proxy\` running; costs
|
|
2026
2060
|
at most a few tiny subscription requests.
|
|
2027
2061
|
dario doctor --json Emit the check report as structured JSON
|
|
2028
|
-
for machine consumption (
|
|
2029
|
-
|
|
2062
|
+
for machine consumption (status bots,
|
|
2063
|
+
CI scripts, etc.) instead of the
|
|
2030
2064
|
human-readable table.
|
|
2031
2065
|
dario doctor --auth-check
|
|
2032
2066
|
One-shot listener on an ephemeral loopback
|
|
@@ -2759,8 +2793,8 @@ async function doctor() {
|
|
|
2759
2793
|
}
|
|
2760
2794
|
const checks = await runChecks({ probe, usage, obedience });
|
|
2761
2795
|
if (asJson) {
|
|
2762
|
-
// JSON mode is meant for machine consumption (
|
|
2763
|
-
//
|
|
2796
|
+
// JSON mode is meant for machine consumption (status bots, health
|
|
2797
|
+
// checks, CI scripts): no decorative header, no
|
|
2764
2798
|
// trailing prose, stable shape. Exit code is also surfaced in the
|
|
2765
2799
|
// JSON envelope for callers that can't read process exit codes.
|
|
2766
2800
|
process.stdout.write(formatChecksJson(checks) + '\n');
|
package/dist/codex-backend.d.ts
CHANGED
|
@@ -50,6 +50,9 @@ export interface CodexForwardOutcome {
|
|
|
50
50
|
outputTokens: number;
|
|
51
51
|
cacheReadTokens: number;
|
|
52
52
|
cacheCreateTokens: number;
|
|
53
|
+
/** Reasoning tokens counted inside outputTokens (Responses
|
|
54
|
+
* `output_tokens_details.reasoning_tokens`); 0 when the backend sent none. */
|
|
55
|
+
reasoningTokens: number;
|
|
53
56
|
stream: boolean;
|
|
54
57
|
model: string;
|
|
55
58
|
alias: string;
|
|
@@ -202,7 +205,17 @@ export interface CodexTokenUsage {
|
|
|
202
205
|
output: number;
|
|
203
206
|
cacheRead: number;
|
|
204
207
|
cacheCreate: number;
|
|
208
|
+
/** The part of `output` that was reasoning; see reasoningTokensFromResponses. */
|
|
209
|
+
reasoning: number;
|
|
205
210
|
}
|
|
211
|
+
/**
|
|
212
|
+
* The reasoning share of a Responses usage block: `output_tokens_details.
|
|
213
|
+
* reasoning_tokens`, which OpenAI counts inside `output_tokens` the way
|
|
214
|
+
* Anthropic counts thinking inside its output. Rounded and capped at the
|
|
215
|
+
* output it is part of, 0 when absent or malformed, so an analytics row never
|
|
216
|
+
* carries more reasoning than output.
|
|
217
|
+
*/
|
|
218
|
+
export declare function reasoningTokensFromResponses(u: unknown, output: number): number;
|
|
206
219
|
/**
|
|
207
220
|
* Terminal Responses usage -> analytics accounting. Delegates the netting to
|
|
208
221
|
* anthropicUsageFromResponses so the analytics row and the Anthropic-shape
|
package/dist/codex-backend.js
CHANGED
|
@@ -535,6 +535,24 @@ export function chatCompletionsUsage(u) {
|
|
|
535
535
|
}
|
|
536
536
|
return out;
|
|
537
537
|
}
|
|
538
|
+
/**
|
|
539
|
+
* The reasoning share of a Responses usage block: `output_tokens_details.
|
|
540
|
+
* reasoning_tokens`, which OpenAI counts inside `output_tokens` the way
|
|
541
|
+
* Anthropic counts thinking inside its output. Rounded and capped at the
|
|
542
|
+
* output it is part of, 0 when absent or malformed, so an analytics row never
|
|
543
|
+
* carries more reasoning than output.
|
|
544
|
+
*/
|
|
545
|
+
export function reasoningTokensFromResponses(u, output) {
|
|
546
|
+
if (!u || typeof u !== 'object')
|
|
547
|
+
return 0;
|
|
548
|
+
const d = u.output_tokens_details;
|
|
549
|
+
if (!d || typeof d !== 'object')
|
|
550
|
+
return 0;
|
|
551
|
+
const n = d.reasoning_tokens;
|
|
552
|
+
if (typeof n !== 'number' || !Number.isFinite(n) || n <= 0)
|
|
553
|
+
return 0;
|
|
554
|
+
return Math.min(Math.round(n), Math.max(0, Math.floor(output)));
|
|
555
|
+
}
|
|
538
556
|
/**
|
|
539
557
|
* Terminal Responses usage -> analytics accounting. Delegates the netting to
|
|
540
558
|
* anthropicUsageFromResponses so the analytics row and the Anthropic-shape
|
|
@@ -550,6 +568,7 @@ export function splitResponsesUsage(u) {
|
|
|
550
568
|
output: a.output_tokens,
|
|
551
569
|
cacheRead: a.cache_read_input_tokens ?? 0,
|
|
552
570
|
cacheCreate: a.cache_creation_input_tokens ?? 0,
|
|
571
|
+
reasoning: reasoningTokensFromResponses(u, a.output_tokens),
|
|
553
572
|
};
|
|
554
573
|
}
|
|
555
574
|
/**
|
|
@@ -947,7 +966,8 @@ timing) {
|
|
|
947
966
|
reported = true;
|
|
948
967
|
try {
|
|
949
968
|
onDone({ status, latencyMs: Date.now() - startedAt, upstreamTtfbMs: ttfbMs(), upstreamMs: upstreamMsNow(), inputTokens: usage?.input ?? 0, outputTokens: usage?.output ?? 0,
|
|
950
|
-
cacheReadTokens: usage?.cacheRead ?? 0, cacheCreateTokens: usage?.cacheCreate ?? 0,
|
|
969
|
+
cacheReadTokens: usage?.cacheRead ?? 0, cacheCreateTokens: usage?.cacheCreate ?? 0, reasoningTokens: usage?.reasoning ?? 0,
|
|
970
|
+
stream: true, model, alias: creds.alias, cache });
|
|
951
971
|
}
|
|
952
972
|
catch { /* never break a served request */ }
|
|
953
973
|
};
|
|
@@ -1205,6 +1225,7 @@ timing) {
|
|
|
1205
1225
|
status, latencyMs: Date.now() - startedAt, upstreamTtfbMs: ttfbMs(), upstreamMs: upstreamMsNow(),
|
|
1206
1226
|
inputTokens: usage?.input ?? 0, outputTokens: usage?.output ?? 0,
|
|
1207
1227
|
cacheReadTokens: usage?.cacheRead ?? 0, cacheCreateTokens: usage?.cacheCreate ?? 0,
|
|
1228
|
+
reasoningTokens: usage?.reasoning ?? 0,
|
|
1208
1229
|
stream, model, alias: creds.alias, cache,
|
|
1209
1230
|
});
|
|
1210
1231
|
}
|
package/dist/doctor-core.d.ts
CHANGED
|
@@ -108,7 +108,7 @@ export declare function formatChecks(checks: Check[]): string;
|
|
|
108
108
|
export declare function exitCodeFor(checks: Check[]): number;
|
|
109
109
|
/**
|
|
110
110
|
* Serialize a check report as structured JSON. Lets other tools
|
|
111
|
-
* (
|
|
111
|
+
* (chat-bot /status commands, health probes, CI scripts) consume
|
|
112
112
|
* dario's health programmatically instead of scraping the formatted
|
|
113
113
|
* text. Emitted by `dario doctor --json`.
|
|
114
114
|
*/
|
package/dist/doctor-core.js
CHANGED
|
@@ -227,7 +227,7 @@ export function exitCodeFor(checks) {
|
|
|
227
227
|
}
|
|
228
228
|
/**
|
|
229
229
|
* Serialize a check report as structured JSON. Lets other tools
|
|
230
|
-
* (
|
|
230
|
+
* (chat-bot /status commands, health probes, CI scripts) consume
|
|
231
231
|
* dario's health programmatically instead of scraping the formatted
|
|
232
232
|
* text. Emitted by `dario doctor --json`.
|
|
233
233
|
*/
|
|
@@ -1019,7 +1019,7 @@ export async function runChecks(opts = {}) {
|
|
|
1019
1019
|
}
|
|
1020
1020
|
}
|
|
1021
1021
|
// ---- Client-system obedience probe (opt-in, --obedience).
|
|
1022
|
-
// dario#509 / the 2026-06-12
|
|
1022
|
+
// dario#509 / the 2026-06-12 downstream planner outage: Anthropic's serving
|
|
1023
1023
|
// side changed how claude-sonnet-4-6 weighed client system text merged
|
|
1024
1024
|
// after the CC persona in block 3 — wire bytes identical, every existing
|
|
1025
1025
|
// check green — and the only symptom was a downstream consumer failing to
|
package/dist/effort.d.ts
CHANGED
|
@@ -32,6 +32,24 @@ export declare const VALID_EFFORT_VALUES: ReadonlyArray<EffortValue>;
|
|
|
32
32
|
* and the request's own thinking budget decides, as before.
|
|
33
33
|
*/
|
|
34
34
|
export declare function effortForCodex(effort: EffortValue | undefined): 'low' | 'medium' | 'high' | 'xhigh' | 'max' | undefined;
|
|
35
|
+
/**
|
|
36
|
+
* Did the request name an effort of its own? Three places a client can say it:
|
|
37
|
+
* `output_config.effort` (Messages shape), `reasoning_effort` (chat completions),
|
|
38
|
+
* `reasoning.effort` (Responses). A named key's default effort fills in only when
|
|
39
|
+
* this is false, so a client that chose is never overruled by a default. The
|
|
40
|
+
* model-name suffix is checked separately by the caller (it is parsed off the
|
|
41
|
+
* model before this runs).
|
|
42
|
+
*/
|
|
43
|
+
export declare function requestNamesEffort(body: Record<string, unknown> | null | undefined): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* A Responses body for the codex passthrough with `reasoning.effort` filled in.
|
|
46
|
+
* Returned as is when there is no effort to add or when the client already
|
|
47
|
+
* named one (a non-empty `reasoning.effort`); otherwise a copy carries the
|
|
48
|
+
* level and keeps every other `reasoning` field, since Codex CLI sends
|
|
49
|
+
* `summary` there. The passthrough forwards the client's body as written, so
|
|
50
|
+
* this is the only place a suffix or a key default can reach that route.
|
|
51
|
+
*/
|
|
52
|
+
export declare function withResponsesEffort(body: Record<string, unknown>, effort: 'low' | 'medium' | 'high' | 'xhigh' | 'max' | undefined): Record<string, unknown>;
|
|
35
53
|
export declare function parseEffortSuffix(model: string): {
|
|
36
54
|
model: string;
|
|
37
55
|
effort?: EffortValue;
|