acuvo-code 0.2.0 → 0.2.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/lib/model.mjs +49 -4
- package/lib/turn.mjs +37 -3
- package/package.json +1 -1
package/lib/model.mjs
CHANGED
|
@@ -91,6 +91,26 @@ export const DEEPSEEK_DIRECT_MODELS = Object.freeze({
|
|
|
91
91
|
* @returns {{ url: string, apiKey: string, model: string } | null}
|
|
92
92
|
*/
|
|
93
93
|
export function directDeepSeek(model, env = process.env) {
|
|
94
|
+
/**
|
|
95
|
+
* ── ⭐⭐⭐ OFF UNLESS EXPLICITLY ASKED FOR (Roman, 2026-08-22) ──────────────
|
|
96
|
+
*
|
|
97
|
+
* *"no direct deepseek api, we can just use the rest of it for testing."*
|
|
98
|
+
*
|
|
99
|
+
* ⚠️ A KEY BEING PRESENT IS NOT A REQUEST TO USE IT. Before this line, merely
|
|
100
|
+
* exporting `DEEPSEEK_API_KEY` silently re-routed every build onto the direct
|
|
101
|
+
* endpoint — which is 3.7x dearer on OUTPUT ($0.66/M vs OpenRouter's $0.18/M)
|
|
102
|
+
* and doubles for 7 hours a day under DeepSeek's peak billing (01:00-04:00 and
|
|
103
|
+
* 06:00-10:00 UTC = 11am-2pm / 4pm-8pm AEST). Measured over 95M tokens that is
|
|
104
|
+
* 62.3% margin against 85.6%.
|
|
105
|
+
*
|
|
106
|
+
* ⭐ Direct's cache READ is genuinely cheaper ($0.007/M vs $0.0154/M) and that
|
|
107
|
+
* is why it once led. It cannot pay for the cache MISSES (2.9x dearer) or the
|
|
108
|
+
* output (3.7x dearer), and output is ~60% of the bill.
|
|
109
|
+
*
|
|
110
|
+
* Mirrors `deepSeekDirectEnabled()` in `console/lib/llm.ts` — the builder and
|
|
111
|
+
* the CLI must not disagree about which vendor serves a build.
|
|
112
|
+
*/
|
|
113
|
+
if (String(env?.ACUVO_DEEPSEEK_DIRECT ?? '') !== '1') return null;
|
|
94
114
|
const key = String(env?.DEEPSEEK_API_KEY ?? '').trim();
|
|
95
115
|
if (!key) return null;
|
|
96
116
|
const mapped = DEEPSEEK_DIRECT_MODELS[String(model ?? '')];
|
|
@@ -325,17 +345,42 @@ export function readModelConfig(env = process.env) {
|
|
|
325
345
|
* is stuck: it needs no key, runs offline, and every line it prints names the
|
|
326
346
|
* variable that fixes it.
|
|
327
347
|
*/
|
|
348
|
+
/**
|
|
349
|
+
* ── ⭐⭐⭐ THE GATEWAY SHIPPED, SO THIS MESSAGE CHANGED WITH IT (2026-08-22) ──
|
|
350
|
+
*
|
|
351
|
+
* The note above promised exactly that: *"When the gateway ships, this message
|
|
352
|
+
* changes with it."* It shipped — `acuvo --login` lands an Acuvo key, and the
|
|
353
|
+
* metered path recorded its first real usage row today after never once having
|
|
354
|
+
* worked.
|
|
355
|
+
*
|
|
356
|
+
* ⚠️⚠️ AND UNTIL THIS EDIT THE FRONT DOOR SOLD THE COMPETITION. The first thing
|
|
357
|
+
* a brand-new user saw was "create your own OpenRouter key" — BYOK, which Roman
|
|
358
|
+
* has ruled out twice, printed as step 1 of onboarding on a package anyone can
|
|
359
|
+
* now `npm i -g`. `--help` did list `--login`; the message people actually hit
|
|
360
|
+
* did not. Every stranger who installed this brought their own key, so we
|
|
361
|
+
* metered nothing and earned nothing.
|
|
362
|
+
*
|
|
363
|
+
* ⭐ BOTH PATHS STAY, ORDER REVERSED. BYOK is not removed — it is honest, it
|
|
364
|
+
* works, and hiding it would make the tool look locked. It is simply no longer
|
|
365
|
+
* the default answer to "how do I start".
|
|
366
|
+
*
|
|
367
|
+
* ⚠️ IT STILL PROMISES NOTHING THAT DOES NOT EXIST. No pricing, no "sign up
|
|
368
|
+
* free", no plan names — self-serve signup has never been walked end to end
|
|
369
|
+
* (every tenant today is operated · unmetered). It names the two commands that
|
|
370
|
+
* are real and stops there.
|
|
371
|
+
*/
|
|
328
372
|
export const MISSING_KEY_MESSAGE = [
|
|
329
373
|
'Acuvo Code — a terminal coding agent that tells you the price before it runs,',
|
|
330
374
|
'stops at the number you set, and can re-check every claim it ever made.',
|
|
331
375
|
'',
|
|
332
|
-
'It needs a
|
|
376
|
+
'It needs a key. Two ways — then run the same command again:',
|
|
377
|
+
'',
|
|
378
|
+
' A) Your Acuvo account, billed to your Acuvo credits:',
|
|
379
|
+
' acuvo --login (paste the key from Settings → API keys)',
|
|
333
380
|
'',
|
|
334
|
-
'
|
|
335
|
-
' 2. Set it:',
|
|
381
|
+
' B) Your own key, billed to you — https://openrouter.ai/keys',
|
|
336
382
|
' export OPENROUTER_API_KEY=sk-or-v1-... (bash / zsh)',
|
|
337
383
|
' $env:OPENROUTER_API_KEY = "sk-or-v1-..." (PowerShell)',
|
|
338
|
-
' 3. Run the same command again.',
|
|
339
384
|
'',
|
|
340
385
|
'A typical task costs $0.001-$0.003. The ceiling is $0.02 a run unless you',
|
|
341
386
|
'raise it, so a mistake costs two cents to find.',
|
package/lib/turn.mjs
CHANGED
|
@@ -49,7 +49,7 @@ import { budgetedAsker } from './ask-user.mjs';
|
|
|
49
49
|
import { normalizeRelativePath } from './workspace.mjs';
|
|
50
50
|
import { stringifyForModel } from './model-json.mjs';
|
|
51
51
|
import { callModel, DEFAULT_MAX_TOKENS, DEFAULT_TIMEOUT_MS, pinOutcome, providerOrderFor } from './model.mjs';
|
|
52
|
-
import { randomUUID } from 'node:crypto';
|
|
52
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
53
53
|
/**
|
|
54
54
|
* The pure prefix instrument. See the prefix-stability block in the round loop.
|
|
55
55
|
* It was written, tested, and imported by nothing until 2026-08-20 —
|
|
@@ -71,8 +71,42 @@ import { detectDrift, driftNudge, reanchorDecision, reconcile, formatReconciliat
|
|
|
71
71
|
* key OpenRouter groups by — and a resumed session passes its own id anyway,
|
|
72
72
|
* which is exactly where the two need to agree and do.
|
|
73
73
|
*/
|
|
74
|
-
|
|
75
|
-
|
|
74
|
+
/**
|
|
75
|
+
* ── 💰⭐⭐⭐ DERIVED FROM THE WORKSPACE, NOT RANDOM (2026-08-22) ──────────────
|
|
76
|
+
*
|
|
77
|
+
* This returned `acuvo-${randomUUID()}` and the note below already recorded the
|
|
78
|
+
* damage without naming it as a bug: *"each cold process rolled the dice
|
|
79
|
+
* afresh"*, measured as a **65 / 98 / 31 / 98** cache alternation across runs.
|
|
80
|
+
*
|
|
81
|
+
* ⭐ THAT ALTERNATION *IS* THE MISSING CACHE. `session_id` is what OpenRouter
|
|
82
|
+
* groups by when choosing an upstream, and `deepseek-v4-flash-0731` has 28 of
|
|
83
|
+
* them. A fresh id every process means a fresh upstream every process — and a
|
|
84
|
+
* cold prompt cache each time, because a cache lives on ONE provider. Only
|
|
85
|
+
* resumed sessions escaped it, and the common case (`acuvo "do the thing"` in a
|
|
86
|
+
* project, over and over) never resumes.
|
|
87
|
+
*
|
|
88
|
+
* ⭐ THE WORKSPACE IS THE RIGHT ANCHOR. The same project returns to the same
|
|
89
|
+
* upstream every run, so its prefix stays warm across processes, across days,
|
|
90
|
+
* and across several terminals open on the same repo — which now SHARE a cache
|
|
91
|
+
* instead of each warming their own.
|
|
92
|
+
*
|
|
93
|
+
* ⚠️ IT IS A PREFERENCE, NOT A PIN. OpenRouter still falls back when that
|
|
94
|
+
* upstream is unhealthy, so this trades no availability for the cache.
|
|
95
|
+
*
|
|
96
|
+
* ⚠️ AND IT MUST NOT BE THE PATH ITSELF. The key goes over the wire to a third
|
|
97
|
+
* party; `C:/Projects/clients/<name>` would leak a customer list into request
|
|
98
|
+
* metadata. Hashed, so it is stable and says nothing.
|
|
99
|
+
*/
|
|
100
|
+
export function defaultStickyKey() {
|
|
101
|
+
let root;
|
|
102
|
+
try {
|
|
103
|
+
root = process.cwd();
|
|
104
|
+
} catch {
|
|
105
|
+
// A deleted cwd throws here. Falling back to a random id is correct — an
|
|
106
|
+
// unidentifiable workspace should not collide with a real one's cache.
|
|
107
|
+
return `acuvo-${randomUUID()}`;
|
|
108
|
+
}
|
|
109
|
+
return `acuvo-${createHash('sha256').update(root).digest('hex').slice(0, 32)}`;
|
|
76
110
|
}
|
|
77
111
|
/**
|
|
78
112
|
* ⭐ THE DEFAULT IS THE CHAIN, NOT THE SINGLE CALL. `callModel` is still
|