@askalf/dario 3.32.2 → 3.34.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/README.md +119 -710
- package/dist/cc-template.d.ts +30 -0
- package/dist/cc-template.js +88 -2
- package/dist/cli.d.ts +18 -0
- package/dist/cli.js +78 -2
- package/dist/doctor.js +27 -1
- package/dist/oauth.d.ts +9 -0
- package/dist/oauth.js +43 -10
- package/dist/proxy.d.ts +19 -0
- package/dist/proxy.js +1 -0
- package/package.json +1 -1
package/dist/cc-template.d.ts
CHANGED
|
@@ -24,6 +24,35 @@ export declare const CC_TOOL_DEFINITIONS: {
|
|
|
24
24
|
export declare const CC_SYSTEM_PROMPT: string;
|
|
25
25
|
/** CC's agent identity string. */
|
|
26
26
|
export declare const CC_AGENT_IDENTITY: string;
|
|
27
|
+
/**
|
|
28
|
+
* Resolve the system prompt for outbound CC-shaped requests.
|
|
29
|
+
*
|
|
30
|
+
* Empirically validated against Anthropic's billing classifier in
|
|
31
|
+
* docs/research/system-prompt.md (and reproducible from
|
|
32
|
+
* scripts/test-system-prompt-mods.mjs + scripts/test-constraint-removal.mjs):
|
|
33
|
+
* system prompt content, length, and block count are not classifier
|
|
34
|
+
* inputs — every variant tested routed to `five_hour` (subscription).
|
|
35
|
+
*
|
|
36
|
+
* Modes:
|
|
37
|
+
* - undefined / 'verbatim' — CC's prompt unchanged (default; existing
|
|
38
|
+
* setups don't regress).
|
|
39
|
+
* - 'partial' — strip purely behavioral constraints (Tone-and-style +
|
|
40
|
+
* Text-output sections, scope-discipline / verbosity / commenting
|
|
41
|
+
* bullets in Doing-tasks). Recovers most of the 1.2-2.8x output
|
|
42
|
+
* capability seen in the constraint-removal test while leaving
|
|
43
|
+
* every IMPORTANT: refusal reminder and every tool description
|
|
44
|
+
* intact.
|
|
45
|
+
* - 'aggressive' — partial + remove prompt-level RLHF reminders (the
|
|
46
|
+
* IMPORTANT: lines that re-state refusal categories) and the
|
|
47
|
+
* Executing-actions-with-care overcaution language. Adds <3%
|
|
48
|
+
* practical difference vs partial because alignment is RLHF-trained,
|
|
49
|
+
* not prompt-trained — RLHF refusals on harmful content survive
|
|
50
|
+
* prompt removal.
|
|
51
|
+
* - any other string — used as the literal system prompt text. The
|
|
52
|
+
* CLI resolves file paths to file contents up-front so this layer
|
|
53
|
+
* stays filesystem-pure.
|
|
54
|
+
*/
|
|
55
|
+
export declare function resolveSystemPrompt(arg: string | undefined): string;
|
|
27
56
|
/**
|
|
28
57
|
* Apply the live template's captured header_order to an outbound header
|
|
29
58
|
* record. Returns a HeadersInit in one of two forms:
|
|
@@ -246,6 +275,7 @@ export declare function buildCCRequest(clientBody: Record<string, unknown>, bill
|
|
|
246
275
|
noAutoDetect?: boolean;
|
|
247
276
|
effort?: EffortValue;
|
|
248
277
|
maxTokens?: number | 'client';
|
|
278
|
+
systemPrompt?: string;
|
|
249
279
|
}): {
|
|
250
280
|
body: Record<string, unknown>;
|
|
251
281
|
toolMap: Map<string, ToolMapping>;
|
package/dist/cc-template.js
CHANGED
|
@@ -43,6 +43,72 @@ export const CC_TOOL_DEFINITIONS = filterToolsForPlatform(TEMPLATE.tools, proces
|
|
|
43
43
|
export const CC_SYSTEM_PROMPT = TEMPLATE.system_prompt;
|
|
44
44
|
/** CC's agent identity string. */
|
|
45
45
|
export const CC_AGENT_IDENTITY = TEMPLATE.agent_identity;
|
|
46
|
+
/**
|
|
47
|
+
* Resolve the system prompt for outbound CC-shaped requests.
|
|
48
|
+
*
|
|
49
|
+
* Empirically validated against Anthropic's billing classifier in
|
|
50
|
+
* docs/research/system-prompt.md (and reproducible from
|
|
51
|
+
* scripts/test-system-prompt-mods.mjs + scripts/test-constraint-removal.mjs):
|
|
52
|
+
* system prompt content, length, and block count are not classifier
|
|
53
|
+
* inputs — every variant tested routed to `five_hour` (subscription).
|
|
54
|
+
*
|
|
55
|
+
* Modes:
|
|
56
|
+
* - undefined / 'verbatim' — CC's prompt unchanged (default; existing
|
|
57
|
+
* setups don't regress).
|
|
58
|
+
* - 'partial' — strip purely behavioral constraints (Tone-and-style +
|
|
59
|
+
* Text-output sections, scope-discipline / verbosity / commenting
|
|
60
|
+
* bullets in Doing-tasks). Recovers most of the 1.2-2.8x output
|
|
61
|
+
* capability seen in the constraint-removal test while leaving
|
|
62
|
+
* every IMPORTANT: refusal reminder and every tool description
|
|
63
|
+
* intact.
|
|
64
|
+
* - 'aggressive' — partial + remove prompt-level RLHF reminders (the
|
|
65
|
+
* IMPORTANT: lines that re-state refusal categories) and the
|
|
66
|
+
* Executing-actions-with-care overcaution language. Adds <3%
|
|
67
|
+
* practical difference vs partial because alignment is RLHF-trained,
|
|
68
|
+
* not prompt-trained — RLHF refusals on harmful content survive
|
|
69
|
+
* prompt removal.
|
|
70
|
+
* - any other string — used as the literal system prompt text. The
|
|
71
|
+
* CLI resolves file paths to file contents up-front so this layer
|
|
72
|
+
* stays filesystem-pure.
|
|
73
|
+
*/
|
|
74
|
+
export function resolveSystemPrompt(arg) {
|
|
75
|
+
if (!arg || arg === 'verbatim')
|
|
76
|
+
return CC_SYSTEM_PROMPT;
|
|
77
|
+
if (arg === 'partial')
|
|
78
|
+
return stripBehavioralConstraints(CC_SYSTEM_PROMPT, 'partial');
|
|
79
|
+
if (arg === 'aggressive')
|
|
80
|
+
return stripBehavioralConstraints(CC_SYSTEM_PROMPT, 'aggressive');
|
|
81
|
+
return arg;
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Port of scripts/test-constraint-removal.mjs:stripConstraints. Pure over
|
|
85
|
+
* its input; returns the input unchanged if section headers don't match
|
|
86
|
+
* (so a future CC bump that renames sections degrades to verbatim rather
|
|
87
|
+
* than producing an unpredictable strip).
|
|
88
|
+
*/
|
|
89
|
+
function stripBehavioralConstraints(input, level) {
|
|
90
|
+
let s = input;
|
|
91
|
+
s = s.replace(/# Tone and style[\s\S]*?(?=\n# |\n$|$)/m, '');
|
|
92
|
+
s = s.replace(/# Text output[^\n]*\n[\s\S]*?(?=\n# |\n$|$)/m, '');
|
|
93
|
+
const doingTasksConstraints = [
|
|
94
|
+
/^ - Don't add features, refactor, or introduce abstractions[^\n]*\n[^\n]*\n[^\n]*\n[^\n]*\n[^\n]*\n/m,
|
|
95
|
+
/^ - Don't add error handling, fallbacks, or validation[^\n]*\n[^\n]*\n/m,
|
|
96
|
+
/^ - Default to writing no comments\.[^\n]*\n[^\n]*\n[^\n]*\n[^\n]*\n/m,
|
|
97
|
+
/^ - Don't explain WHAT the code does[^\n]*\n[^\n]*\n/m,
|
|
98
|
+
/^ - For exploratory questions[^\n]*\n[^\n]*\n/m,
|
|
99
|
+
/^ - Avoid backwards-compatibility hacks[^\n]*\n[^\n]*\n/m,
|
|
100
|
+
];
|
|
101
|
+
for (const re of doingTasksConstraints) {
|
|
102
|
+
s = s.replace(re, '');
|
|
103
|
+
}
|
|
104
|
+
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');
|
|
105
|
+
if (level === 'aggressive') {
|
|
106
|
+
s = s.replace(/^IMPORTANT: Assist with authorized security testing[^\n]*\n/m, '');
|
|
107
|
+
s = s.replace(/^IMPORTANT: You must NEVER generate or guess URLs[^\n]*\n/m, '');
|
|
108
|
+
s = s.replace(/# Executing actions with care[\s\S]*?(?=\n# |\n$|$)/m, '');
|
|
109
|
+
}
|
|
110
|
+
return s;
|
|
111
|
+
}
|
|
46
112
|
/**
|
|
47
113
|
* Apply the live template's captured header_order to an outbound header
|
|
48
114
|
* record. Returns a HeadersInit in one of two forms:
|
|
@@ -255,6 +321,21 @@ export function detectTextToolClient(systemText) {
|
|
|
255
321
|
// preserve-tools is the only correct routing.
|
|
256
322
|
if (/\bYou are Arnie\b/.test(systemText))
|
|
257
323
|
return 'arnie';
|
|
324
|
+
// hands (askalf) — cross-platform computer-use agent built on the
|
|
325
|
+
// Anthropic SDK with computer-use beta tools (computer_20251124,
|
|
326
|
+
// bash_20250124, text_editor_20250728). Identity line is stable
|
|
327
|
+
// across CLI mode ("You are a computer control agent with FULL
|
|
328
|
+
// access to this <os> machine ...") and SDK mode ("You are a
|
|
329
|
+
// computer control agent on <os> ..."). Tool name `bash` overlaps
|
|
330
|
+
// with TOOL_MAP, but the wire shape is Anthropic's beta computer-
|
|
331
|
+
// use tool (`type: 'bash_20250124'`, no `command`/`description`
|
|
332
|
+
// schema) — default round-robin remap would corrupt those calls
|
|
333
|
+
// and lose the `computer` / `text_editor` tools entirely (neither
|
|
334
|
+
// is in TOOL_MAP, structural fallback won't catch them at the
|
|
335
|
+
// 80% threshold either). Identity match → auto preserve-tools,
|
|
336
|
+
// like arnie.
|
|
337
|
+
if (/\bYou are a computer control agent\b/.test(systemText))
|
|
338
|
+
return 'hands';
|
|
258
339
|
// Protocol-signature fallback — unique to the Cline family and its
|
|
259
340
|
// forks; survives a forked system prompt that edited the identity
|
|
260
341
|
// string out but kept the tool protocol intact.
|
|
@@ -1074,9 +1155,14 @@ export function buildCCRequest(clientBody, billingTag, cacheControl, identity, o
|
|
|
1074
1155
|
// [0] billing tag (no cache)
|
|
1075
1156
|
// [1] agent identity (1h cache)
|
|
1076
1157
|
// [2] CC's full 25KB system prompt + client's custom prompt appended (1h cache)
|
|
1158
|
+
// resolveSystemPrompt is the seam for --system-prompt=verbatim|partial|
|
|
1159
|
+
// aggressive|<file>. Default (undefined) returns CC_SYSTEM_PROMPT
|
|
1160
|
+
// unchanged. See docs/research/system-prompt.md for the empirical
|
|
1161
|
+
// validation that this slot is unfingerprinted by the billing classifier.
|
|
1162
|
+
const baseSystemPrompt = resolveSystemPrompt(opts.systemPrompt);
|
|
1077
1163
|
const fullSystemPrompt = systemText
|
|
1078
|
-
? `${
|
|
1079
|
-
:
|
|
1164
|
+
? `${baseSystemPrompt}\n\n${systemText}`
|
|
1165
|
+
: baseSystemPrompt;
|
|
1080
1166
|
const ccRequest = {
|
|
1081
1167
|
model,
|
|
1082
1168
|
messages,
|
package/dist/cli.d.ts
CHANGED
|
@@ -10,6 +10,24 @@
|
|
|
10
10
|
* dario logout — Remove saved credentials
|
|
11
11
|
*/
|
|
12
12
|
import { type EffortValue } from './cc-template.js';
|
|
13
|
+
/**
|
|
14
|
+
* Parse `--system-prompt=<verbatim|partial|aggressive|filepath>` (or the
|
|
15
|
+
* `DARIO_SYSTEM_PROMPT` env-var fallback) into the value passed through
|
|
16
|
+
* to startProxy. The CLI flag wins over the env var when both are set —
|
|
17
|
+
* convention every other dario flag uses.
|
|
18
|
+
*
|
|
19
|
+
* Returns:
|
|
20
|
+
* - undefined for missing / 'verbatim' (proxy default — CC unchanged)
|
|
21
|
+
* - 'partial' / 'aggressive' as keyword strings (stripping happens
|
|
22
|
+
* in cc-template.ts:resolveSystemPrompt at request build time)
|
|
23
|
+
* - the literal text contents of the file at <filepath> for the
|
|
24
|
+
* custom-prompt escape hatch. Fails fast (process.exit(1)) on a
|
|
25
|
+
* value that is neither a recognized keyword nor a readable file.
|
|
26
|
+
*
|
|
27
|
+
* Exported for the test suite — same shape as resolveEffortFlag /
|
|
28
|
+
* resolveMaxTokensFlag.
|
|
29
|
+
*/
|
|
30
|
+
export declare function resolveSystemPromptFlag(args: string[], envVar: string | undefined): string | undefined;
|
|
13
31
|
/**
|
|
14
32
|
* Parse `--passthrough-betas=<csv>` (or the env-var fallback) into a
|
|
15
33
|
* deduped, trimmed list. The CLI flag wins over the env var when both
|
package/dist/cli.js
CHANGED
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// just want `parsePositiveIntEnv`) doesn't trigger a Bun relaunch or any
|
|
18
18
|
// other startup side effect.
|
|
19
19
|
import { unlink } from 'node:fs/promises';
|
|
20
|
-
import { realpathSync } from 'node:fs';
|
|
20
|
+
import { realpathSync, readFileSync } from 'node:fs';
|
|
21
21
|
import { join } from 'node:path';
|
|
22
22
|
import { homedir } from 'node:os';
|
|
23
23
|
import { pathToFileURL } from 'node:url';
|
|
@@ -284,6 +284,20 @@ async function proxy() {
|
|
|
284
284
|
// Falls back to DARIO_LOG_FILE; off by default. Path is opened with
|
|
285
285
|
// append mode so multiple proxy restarts share a rolling history.
|
|
286
286
|
const logFile = parseLogFileFlag(args) ?? process.env['DARIO_LOG_FILE'] ?? undefined;
|
|
287
|
+
// --system-prompt=<verbatim|partial|aggressive|filepath> — system-prompt
|
|
288
|
+
// mode for outbound CC-shaped requests (v3.34.0). The classifier is
|
|
289
|
+
// empirically not reading this slot (docs/research/system-prompt.md),
|
|
290
|
+
// so users can strip CC's behavioral constraints — Tone-and-style,
|
|
291
|
+
// Text-output, scope/verbosity bullets — and recover 1.2-2.8x output
|
|
292
|
+
// capability without flipping subscription billing. Default 'verbatim'
|
|
293
|
+
// preserves existing setups.
|
|
294
|
+
//
|
|
295
|
+
// The CLI resolves the value here so the runtime path stays
|
|
296
|
+
// filesystem-pure: 'verbatim'/'partial'/'aggressive' pass through as
|
|
297
|
+
// keywords; anything else is treated as a file path and read at
|
|
298
|
+
// startup. A bad path fails fast rather than silently degrading to
|
|
299
|
+
// verbatim — same fail-loud philosophy as --strict-tls / --strict-template.
|
|
300
|
+
const systemPrompt = resolveSystemPromptFlag(args, process.env['DARIO_SYSTEM_PROMPT']);
|
|
287
301
|
// --passthrough-betas=name1,name2 — operator-pinned beta allow-list.
|
|
288
302
|
// Names listed here are always forwarded to Anthropic regardless of
|
|
289
303
|
// CC's captured set or the client's own beta header; bypasses the
|
|
@@ -309,7 +323,47 @@ async function proxy() {
|
|
|
309
323
|
console.error(`[dario] Override (not recommended): pass --unsafe-no-auth if you have out-of-band network controls and accept the risk.`);
|
|
310
324
|
process.exit(1);
|
|
311
325
|
}
|
|
312
|
-
await startProxy({ port, host, verbose, verboseBodies, model, passthrough, preserveTools, hybridTools, mergeTools, noAutoDetect, strictTls, pacingMinMs, pacingJitterMs, drainOnClose, sessionIdleRotateMs, sessionRotateJitterMs, sessionMaxAgeMs, sessionPerClient, preserveOrchestrationTags, noLiveCapture, strictTemplate, maxConcurrent, maxQueued, queueTimeoutMs, effort, maxTokens, logFile, passthroughBetas });
|
|
326
|
+
await startProxy({ port, host, verbose, verboseBodies, model, passthrough, preserveTools, hybridTools, mergeTools, noAutoDetect, strictTls, pacingMinMs, pacingJitterMs, drainOnClose, sessionIdleRotateMs, sessionRotateJitterMs, sessionMaxAgeMs, sessionPerClient, preserveOrchestrationTags, noLiveCapture, strictTemplate, maxConcurrent, maxQueued, queueTimeoutMs, effort, maxTokens, logFile, passthroughBetas, systemPrompt });
|
|
327
|
+
}
|
|
328
|
+
/**
|
|
329
|
+
* Parse `--system-prompt=<verbatim|partial|aggressive|filepath>` (or the
|
|
330
|
+
* `DARIO_SYSTEM_PROMPT` env-var fallback) into the value passed through
|
|
331
|
+
* to startProxy. The CLI flag wins over the env var when both are set —
|
|
332
|
+
* convention every other dario flag uses.
|
|
333
|
+
*
|
|
334
|
+
* Returns:
|
|
335
|
+
* - undefined for missing / 'verbatim' (proxy default — CC unchanged)
|
|
336
|
+
* - 'partial' / 'aggressive' as keyword strings (stripping happens
|
|
337
|
+
* in cc-template.ts:resolveSystemPrompt at request build time)
|
|
338
|
+
* - the literal text contents of the file at <filepath> for the
|
|
339
|
+
* custom-prompt escape hatch. Fails fast (process.exit(1)) on a
|
|
340
|
+
* value that is neither a recognized keyword nor a readable file.
|
|
341
|
+
*
|
|
342
|
+
* Exported for the test suite — same shape as resolveEffortFlag /
|
|
343
|
+
* resolveMaxTokensFlag.
|
|
344
|
+
*/
|
|
345
|
+
export function resolveSystemPromptFlag(args, envVar) {
|
|
346
|
+
const eqArg = args.find((a) => a.startsWith('--system-prompt='));
|
|
347
|
+
const raw = eqArg !== undefined ? eqArg.split('=').slice(1).join('=') : envVar;
|
|
348
|
+
if (raw === undefined || raw === '' || raw === 'verbatim')
|
|
349
|
+
return undefined;
|
|
350
|
+
if (raw === 'partial' || raw === 'aggressive')
|
|
351
|
+
return raw;
|
|
352
|
+
// File-path mode. Read the file at startup so the runtime path stays
|
|
353
|
+
// pure. Fail loud on missing/unreadable paths.
|
|
354
|
+
try {
|
|
355
|
+
const text = readFileSync(raw, 'utf-8');
|
|
356
|
+
if (text.length === 0) {
|
|
357
|
+
console.error(`[dario] --system-prompt=${raw}: file is empty. Refusing to start.`);
|
|
358
|
+
console.error(`[dario] Use --system-prompt=verbatim to disable, or write a non-empty file.`);
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
return text;
|
|
362
|
+
}
|
|
363
|
+
catch (err) {
|
|
364
|
+
console.error(`[dario] --system-prompt=${raw}: not 'verbatim'/'partial'/'aggressive' and not a readable file (${err.message}). Refusing to start.`);
|
|
365
|
+
process.exit(1);
|
|
366
|
+
}
|
|
313
367
|
}
|
|
314
368
|
/**
|
|
315
369
|
* Parse `--passthrough-betas=<csv>` (or the env-var fallback) into a
|
|
@@ -914,6 +968,28 @@ async function help() {
|
|
|
914
968
|
on your account but isn't in the captured
|
|
915
969
|
template. Env: DARIO_PASSTHROUGH_BETAS.
|
|
916
970
|
|
|
971
|
+
--system-prompt=<MODE> System-prompt mode for outbound CC-shaped
|
|
972
|
+
requests (v3.34.0). One of:
|
|
973
|
+
verbatim — CC unchanged (default)
|
|
974
|
+
partial — strip behavioral constraints
|
|
975
|
+
(Tone-and-style, Text-output,
|
|
976
|
+
verbosity / comment / scope
|
|
977
|
+
bullets in Doing-tasks).
|
|
978
|
+
Recovers ~1.2-2.8x output on
|
|
979
|
+
open-ended work.
|
|
980
|
+
aggressive — partial + remove prompt-level
|
|
981
|
+
RLHF restatements + Executing-
|
|
982
|
+
actions-with-care section.
|
|
983
|
+
Adds <3% over partial; RLHF
|
|
984
|
+
refusals on harmful content
|
|
985
|
+
unaffected (alignment is in
|
|
986
|
+
the weights, not the prompt).
|
|
987
|
+
<filepath> — replace the slot entirely
|
|
988
|
+
with the file's contents.
|
|
989
|
+
Empirically validated as unfingerprinted by
|
|
990
|
+
the billing classifier — see docs/research/
|
|
991
|
+
system-prompt.md. Env: DARIO_SYSTEM_PROMPT.
|
|
992
|
+
|
|
917
993
|
Quick start:
|
|
918
994
|
dario login # auto-detects Claude Code credentials
|
|
919
995
|
dario proxy --model=opus # or: dario proxy --passthrough
|
package/dist/doctor.js
CHANGED
|
@@ -16,7 +16,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
16
16
|
import { homedir, platform, arch, release } from 'node:os';
|
|
17
17
|
import { execFileSync } from 'node:child_process';
|
|
18
18
|
import { createServer } from 'node:http';
|
|
19
|
-
import { CC_TEMPLATE, } from './cc-template.js';
|
|
19
|
+
import { CC_TEMPLATE, resolveSystemPrompt, } from './cc-template.js';
|
|
20
20
|
import { describeTemplate, detectDrift, checkCCCompat, findInstalledCC, SUPPORTED_CC_RANGE, CURRENT_SCHEMA_VERSION, compareVersions, } from './live-fingerprint.js';
|
|
21
21
|
import { detectCCOAuthConfig } from './cc-oauth-detect.js';
|
|
22
22
|
import { runAuthorizeProbe } from './cc-authorize-probe.js';
|
|
@@ -242,6 +242,32 @@ export async function runChecks(opts = {}) {
|
|
|
242
242
|
}
|
|
243
243
|
}
|
|
244
244
|
catch { /* don't let overhead reporting break the doctor */ }
|
|
245
|
+
// ---- System-prompt mode (v3.34.0)
|
|
246
|
+
// Surfaces the configured `--system-prompt` mode + the resulting char
|
|
247
|
+
// count delta vs CC verbatim. Read-only — does not run a request.
|
|
248
|
+
// Env-only path here so doctor can be invoked without a live proxy.
|
|
249
|
+
try {
|
|
250
|
+
const rawMode = process.env['DARIO_SYSTEM_PROMPT'];
|
|
251
|
+
if (rawMode && rawMode !== 'verbatim') {
|
|
252
|
+
const cc = CC_TEMPLATE.system_prompt ?? '';
|
|
253
|
+
let resolved;
|
|
254
|
+
if (rawMode === 'partial' || rawMode === 'aggressive') {
|
|
255
|
+
resolved = resolveSystemPrompt(rawMode);
|
|
256
|
+
}
|
|
257
|
+
else {
|
|
258
|
+
// file-path mode — doctor doesn't read files (might leak path),
|
|
259
|
+
// just report that custom mode is active.
|
|
260
|
+
resolved = '';
|
|
261
|
+
}
|
|
262
|
+
const isCustom = rawMode !== 'partial' && rawMode !== 'aggressive';
|
|
263
|
+
const detail = isCustom
|
|
264
|
+
? `DARIO_SYSTEM_PROMPT=${rawMode} (custom file). Runtime path replaces system[2].text with file contents.`
|
|
265
|
+
: `DARIO_SYSTEM_PROMPT=${rawMode}. Strips ${(cc.length - resolved.length).toLocaleString()} chars from CC's ${cc.length.toLocaleString()}-char prompt. ` +
|
|
266
|
+
`See docs/research/system-prompt.md for the empirical validation that this slot is unfingerprinted by the billing classifier.`;
|
|
267
|
+
checks.push({ status: 'info', label: 'System-prompt mode', detail });
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
catch { /* never let prompt-mode reporting break the doctor */ }
|
|
245
271
|
// ---- Template drift
|
|
246
272
|
try {
|
|
247
273
|
const drift = detectDrift(CC_TEMPLATE);
|
package/dist/oauth.d.ts
CHANGED
|
@@ -14,6 +14,15 @@ export interface CredentialsFile {
|
|
|
14
14
|
claudeAiOauth: OAuthTokens;
|
|
15
15
|
}
|
|
16
16
|
export declare function loadCredentials(): Promise<CredentialsFile | null>;
|
|
17
|
+
/**
|
|
18
|
+
* Pick the freshest of a set of `CredentialsFile` candidates by
|
|
19
|
+
* `expiresAt` (unix-ms timestamp; missing/zero sorts last). Stable on
|
|
20
|
+
* ties — the first-pushed candidate wins when expiresAt is equal,
|
|
21
|
+
* which means the canonical call order
|
|
22
|
+
* `[darioFile, ccFile, keychain]` keeps the dario-file source as the
|
|
23
|
+
* tiebreaker preference. Exported for direct testing.
|
|
24
|
+
*/
|
|
25
|
+
export declare function pickFreshestCredentials(candidates: CredentialsFile[]): CredentialsFile | null;
|
|
17
26
|
/**
|
|
18
27
|
* Automatic OAuth flow using a local callback server (same as Claude Code).
|
|
19
28
|
* Opens browser, captures the authorization code automatically.
|
package/dist/oauth.js
CHANGED
|
@@ -159,27 +159,60 @@ export async function loadCredentials() {
|
|
|
159
159
|
if (credentialsCache && Date.now() - credentialsCacheTime < CACHE_TTL_MS) {
|
|
160
160
|
return credentialsCache;
|
|
161
161
|
}
|
|
162
|
-
//
|
|
162
|
+
// Read every available source (dario file, CC file, OS keychain) and
|
|
163
|
+
// pick the freshest. Previously this returned the first source that
|
|
164
|
+
// had both tokens regardless of expiry — which means a stale
|
|
165
|
+
// ~/.dario/credentials.json (left over from a prior `dario login`
|
|
166
|
+
// whose refresh_token has since been invalidated by Anthropic) would
|
|
167
|
+
// shadow CC's still-fresh ~/.claude/.credentials.json forever, with
|
|
168
|
+
// no automatic recovery. Picking the freshest makes auto-detection
|
|
169
|
+
// work the way it did before any `dario login` had ever run, while
|
|
170
|
+
// still preferring dario's own file when both sources are equivalent
|
|
171
|
+
// (dario file wins ties on expiresAt by being checked first).
|
|
172
|
+
const candidates = [];
|
|
163
173
|
for (const path of [getDarioCredentialsPath(), getClaudeCodeCredentialsPath()]) {
|
|
164
174
|
try {
|
|
165
175
|
const raw = await readFile(path, 'utf-8');
|
|
166
176
|
const parsed = JSON.parse(raw);
|
|
167
177
|
if (parsed?.claudeAiOauth?.accessToken && parsed?.claudeAiOauth?.refreshToken) {
|
|
168
|
-
|
|
169
|
-
credentialsCacheTime = Date.now();
|
|
170
|
-
return credentialsCache;
|
|
178
|
+
candidates.push(parsed);
|
|
171
179
|
}
|
|
172
180
|
}
|
|
173
181
|
catch { /* try next */ }
|
|
174
182
|
}
|
|
175
|
-
//
|
|
183
|
+
// OS keychain (modern CC stores credentials here, not on disk).
|
|
176
184
|
const keychainCreds = await loadKeychainCredentials();
|
|
177
|
-
if (keychainCreds) {
|
|
178
|
-
|
|
179
|
-
credentialsCacheTime = Date.now();
|
|
180
|
-
return credentialsCache;
|
|
185
|
+
if (keychainCreds?.claudeAiOauth?.accessToken && keychainCreds?.claudeAiOauth?.refreshToken) {
|
|
186
|
+
candidates.push(keychainCreds);
|
|
181
187
|
}
|
|
182
|
-
|
|
188
|
+
const best = pickFreshestCredentials(candidates);
|
|
189
|
+
if (!best)
|
|
190
|
+
return null;
|
|
191
|
+
credentialsCache = best;
|
|
192
|
+
credentialsCacheTime = Date.now();
|
|
193
|
+
return credentialsCache;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Pick the freshest of a set of `CredentialsFile` candidates by
|
|
197
|
+
* `expiresAt` (unix-ms timestamp; missing/zero sorts last). Stable on
|
|
198
|
+
* ties — the first-pushed candidate wins when expiresAt is equal,
|
|
199
|
+
* which means the canonical call order
|
|
200
|
+
* `[darioFile, ccFile, keychain]` keeps the dario-file source as the
|
|
201
|
+
* tiebreaker preference. Exported for direct testing.
|
|
202
|
+
*/
|
|
203
|
+
export function pickFreshestCredentials(candidates) {
|
|
204
|
+
if (candidates.length === 0)
|
|
205
|
+
return null;
|
|
206
|
+
let best = candidates[0];
|
|
207
|
+
let bestExp = best.claudeAiOauth.expiresAt ?? 0;
|
|
208
|
+
for (let i = 1; i < candidates.length; i++) {
|
|
209
|
+
const exp = candidates[i].claudeAiOauth.expiresAt ?? 0;
|
|
210
|
+
if (exp > bestExp) {
|
|
211
|
+
best = candidates[i];
|
|
212
|
+
bestExp = exp;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
return best;
|
|
183
216
|
}
|
|
184
217
|
async function saveCredentials(creds) {
|
|
185
218
|
const path = getDarioCredentialsPath();
|
package/dist/proxy.d.ts
CHANGED
|
@@ -124,6 +124,25 @@ interface ProxyOptions {
|
|
|
124
124
|
* pinned flags has been rejected and is therefore being dropped.
|
|
125
125
|
*/
|
|
126
126
|
passthroughBetas?: string[];
|
|
127
|
+
/**
|
|
128
|
+
* System-prompt mode for the Claude backend. Empirically validated as
|
|
129
|
+
* unfingerprinted by the billing classifier in docs/research/system-prompt.md.
|
|
130
|
+
*
|
|
131
|
+
* - undefined / 'verbatim' — CC's prompt unchanged (default).
|
|
132
|
+
* - 'partial' — strip behavioral constraints (Tone-and-style, Text-output,
|
|
133
|
+
* scope/verbosity/comment bullets in Doing-tasks). Recovers ~1.2-2.8x
|
|
134
|
+
* output capability on open-ended work; leaves IMPORTANT: refusal
|
|
135
|
+
* reminders and tool descriptions intact.
|
|
136
|
+
* - 'aggressive' — partial + remove prompt-level RLHF restatements and
|
|
137
|
+
* the Executing-actions-with-care section. Adds <3% over partial; RLHF
|
|
138
|
+
* refusals on harmful content are unaffected (alignment is in the weights).
|
|
139
|
+
* - any other string — used as the literal system prompt text. The CLI
|
|
140
|
+
* resolves --system-prompt=<file path> to the file's contents up front
|
|
141
|
+
* so the runtime path stays filesystem-pure.
|
|
142
|
+
*
|
|
143
|
+
* Sourced from `--system-prompt=<value>` or DARIO_SYSTEM_PROMPT.
|
|
144
|
+
*/
|
|
145
|
+
systemPrompt?: string;
|
|
127
146
|
}
|
|
128
147
|
/**
|
|
129
148
|
* One JSON-ND record per completed request. Field set kept narrow to
|
package/dist/proxy.js
CHANGED
|
@@ -1105,6 +1105,7 @@ export async function startProxy(opts = {}) {
|
|
|
1105
1105
|
noAutoDetect: opts.noAutoDetect ?? false,
|
|
1106
1106
|
effort: opts.effort,
|
|
1107
1107
|
maxTokens: opts.maxTokens,
|
|
1108
|
+
systemPrompt: opts.systemPrompt,
|
|
1108
1109
|
});
|
|
1109
1110
|
detectedClientForLog = detectedClient;
|
|
1110
1111
|
preserveToolsEffective = Boolean(opts.preserveTools)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@askalf/dario",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.34.0",
|
|
4
4
|
"description": "A local LLM router. One endpoint, every provider — Claude subscriptions, OpenAI, OpenRouter, Groq, local LiteLLM, any OpenAI-compat endpoint — your tools don't need to change.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|