@wrongstack/cli 0.9.20 → 0.10.2
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/data/README.md +77 -0
- package/data/providers.json +1 -0
- package/dist/index.js +161 -10
- package/dist/index.js.map +1 -1
- package/package.json +13 -12
package/data/README.md
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
# Curated model-catalog overlay (`providers.json`)
|
|
2
|
+
|
|
3
|
+
`providers.json` is a **curated override layer** that WrongStack deep-merges **on top of**
|
|
4
|
+
the live `https://models.dev/api.json` catalog. models.dev stays the base/primary source; this
|
|
5
|
+
file lets us **add** providers/models it doesn't carry and **fix** fields it gets wrong (a missing
|
|
6
|
+
model, a stale context limit, etc.) without waiting for an upstream fix or a release.
|
|
7
|
+
|
|
8
|
+
At runtime the registry resolves:
|
|
9
|
+
|
|
10
|
+
```
|
|
11
|
+
merged = mergeModelsPayload(modelsDev, providers.json) // overlay wins
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
It is loaded from (first non-empty wins):
|
|
15
|
+
1. this file fetched from our GitHub raw URL (so it can refresh between releases), then
|
|
16
|
+
2. this file bundled in the installed package (offline floor).
|
|
17
|
+
|
|
18
|
+
If models.dev is completely unreachable and there's no cache, a **non-empty** overlay still drives
|
|
19
|
+
the catalog on its own. An empty `{}` overlay is a safe no-op.
|
|
20
|
+
|
|
21
|
+
## Shape
|
|
22
|
+
|
|
23
|
+
Same schema as `models.dev/api.json` — a map keyed by provider id. You only include the fields you
|
|
24
|
+
want to add or override; everything else falls through to the base. (JSON has no comments, hence
|
|
25
|
+
this README.)
|
|
26
|
+
|
|
27
|
+
```jsonc
|
|
28
|
+
{
|
|
29
|
+
// Override just one field on an existing model — here, fix a context window.
|
|
30
|
+
"deepseek": {
|
|
31
|
+
"models": {
|
|
32
|
+
"deepseek-v4-pro": { "limit": { "context": 128000 } }
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
|
|
36
|
+
// Add a provider models.dev doesn't list at all.
|
|
37
|
+
"myco": {
|
|
38
|
+
"id": "myco",
|
|
39
|
+
"name": "My Co",
|
|
40
|
+
"npm": "@ai-sdk/openai-compatible", // determines the wire family
|
|
41
|
+
"api": "https://api.myco.example/v1",
|
|
42
|
+
"env": ["MYCO_API_KEY"],
|
|
43
|
+
"models": {
|
|
44
|
+
"myco-large": {
|
|
45
|
+
"id": "myco-large",
|
|
46
|
+
"name": "MyCo Large",
|
|
47
|
+
"tool_call": true,
|
|
48
|
+
"modalities": { "input": ["text"], "output": ["text"] },
|
|
49
|
+
"limit": { "context": 200000, "output": 16000 },
|
|
50
|
+
"cost": { "input": 0.5, "output": 1.5 }
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Merge rules
|
|
58
|
+
|
|
59
|
+
- Provider in both → overlay scalar fields (`name`, `npm`, `api`, `env`, `doc`) override the base;
|
|
60
|
+
`models` are merged by id.
|
|
61
|
+
- Model in both → `{ ...baseModel, ...overlayModel }`, with the nested `limit` / `cost` /
|
|
62
|
+
`modalities` objects merged one level deeper — so `{"limit":{"context":…}}` overrides only the
|
|
63
|
+
context and keeps the base's `limit.output`.
|
|
64
|
+
- Anything only in the overlay is added.
|
|
65
|
+
|
|
66
|
+
## Editing / refreshing
|
|
67
|
+
|
|
68
|
+
Use the helper to seed and sanity-check entries against upstream:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
pnpm run sync:models -- --extract deepseek:deepseek-v4-pro # print a paste-ready overlay snippet
|
|
72
|
+
pnpm run sync:models -- --diff # what we override vs upstream + drift
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
Then edit `providers.json` and commit. Keep it **small and curated** — it is an override layer,
|
|
76
|
+
not a mirror of models.dev. Once models.dev catches up, drop the now-redundant override (`--diff`
|
|
77
|
+
flags those).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{}
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,7 @@ import { MCPRegistry } from '@wrongstack/mcp';
|
|
|
13
13
|
import { buildProviderFactoriesFromRegistry, makeProviderFromConfig, capabilitiesFor } from '@wrongstack/providers';
|
|
14
14
|
import { createDefaultContainer, routeImagesForModel, readClipboardImage } from '@wrongstack/runtime';
|
|
15
15
|
import { builtinToolsPack, rememberTool, forgetTool } from '@wrongstack/tools';
|
|
16
|
+
import { fileURLToPath } from 'url';
|
|
16
17
|
import * as readline from 'readline';
|
|
17
18
|
import * as fs10 from 'fs';
|
|
18
19
|
import { writeFileSync } from 'fs';
|
|
@@ -2353,6 +2354,7 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
|
|
|
2353
2354
|
"recover",
|
|
2354
2355
|
"no-alt-screen",
|
|
2355
2356
|
"alt-screen",
|
|
2357
|
+
"mouse",
|
|
2356
2358
|
"output-json",
|
|
2357
2359
|
"prompt",
|
|
2358
2360
|
"metrics",
|
|
@@ -9727,6 +9729,14 @@ function fmtTaskResultLine(r, color41) {
|
|
|
9727
9729
|
|
|
9728
9730
|
// src/boot.ts
|
|
9729
9731
|
init_update_check();
|
|
9732
|
+
var GITHUB_PROVIDERS_OVERLAY_URL = "https://raw.githubusercontent.com/WrongStack/WrongStack/main/packages/cli/data/providers.json";
|
|
9733
|
+
function resolveBundledOverlayFile() {
|
|
9734
|
+
try {
|
|
9735
|
+
return fileURLToPath(new URL("../data/providers.json", import.meta.url));
|
|
9736
|
+
} catch {
|
|
9737
|
+
return void 0;
|
|
9738
|
+
}
|
|
9739
|
+
}
|
|
9730
9740
|
function resolveBundledSkillsDir() {
|
|
9731
9741
|
try {
|
|
9732
9742
|
const req2 = createRequire(import.meta.url);
|
|
@@ -9742,6 +9752,10 @@ async function boot(argv) {
|
|
|
9742
9752
|
flags["resume"] = positional[1];
|
|
9743
9753
|
positional.splice(0, 2);
|
|
9744
9754
|
}
|
|
9755
|
+
if (positional.length === 0) {
|
|
9756
|
+
if (flags["help"] === true) positional.push("help");
|
|
9757
|
+
else if (flags["version"] === true) positional.push("version");
|
|
9758
|
+
}
|
|
9745
9759
|
let bootResult;
|
|
9746
9760
|
try {
|
|
9747
9761
|
bootResult = await bootConfig(flags);
|
|
@@ -9758,7 +9772,12 @@ async function boot(argv) {
|
|
|
9758
9772
|
const reader = new ReadlineInputReader({ historyFile: wpaths.historyFile });
|
|
9759
9773
|
const modelsRegistry = new DefaultModelsRegistry({
|
|
9760
9774
|
cacheFile: wpaths.modelsCache,
|
|
9761
|
-
ttlSeconds: 24 * 3600
|
|
9775
|
+
ttlSeconds: 24 * 3600,
|
|
9776
|
+
// Curated overlay merged on top of models.dev: fetched from GitHub raw for
|
|
9777
|
+
// freshness, with the bundled file as the offline floor.
|
|
9778
|
+
overlayUrl: GITHUB_PROVIDERS_OVERLAY_URL,
|
|
9779
|
+
overlayFile: resolveBundledOverlayFile(),
|
|
9780
|
+
overlayCacheFile: wpaths.modelsOverlayCache
|
|
9762
9781
|
});
|
|
9763
9782
|
let updateInfo;
|
|
9764
9783
|
if (!flags["no-check"] && !process.env["WRONGSTACK_NO_CHECK"]) {
|
|
@@ -10911,9 +10930,15 @@ async function execute(deps) {
|
|
|
10911
10930
|
// Default OFF so the terminal's native scrollback works for chat
|
|
10912
10931
|
// history out of the box (mouse wheel / Shift+PgUp). Users who hit
|
|
10913
10932
|
// resize/overlay-leak artifacts can opt back into alt-screen with
|
|
10914
|
-
// `--alt-screen
|
|
10915
|
-
// when both are passed.
|
|
10933
|
+
// `--alt-screen`. `--no-alt-screen` still wins when both are passed.
|
|
10916
10934
|
altScreen: flags["alt-screen"] === true && flags["no-alt-screen"] !== true,
|
|
10935
|
+
// Full mouse mode: clickable pickers + in-app wheel scroll. Opt-in
|
|
10936
|
+
// via --mouse. The TUI combines altScreen || mouse to determine
|
|
10937
|
+
// whether to use the managed viewport (ScrollableHistory + in-app
|
|
10938
|
+
// scroll); --mouse alone enables that without requiring --alt-screen.
|
|
10939
|
+
// Mouse mode takes ownership of the terminal's mouse, suspending
|
|
10940
|
+
// native scroll/copy until `/mouse off`.
|
|
10941
|
+
mouse: flags.mouse === true,
|
|
10917
10942
|
director,
|
|
10918
10943
|
fleetRoster,
|
|
10919
10944
|
onAfterExit: () => {
|
|
@@ -11208,7 +11233,11 @@ var MultiAgentHost = class {
|
|
|
11208
11233
|
makeSubagentFactory(config) {
|
|
11209
11234
|
return async (subCfg) => {
|
|
11210
11235
|
const events = new EventBus();
|
|
11211
|
-
const provider = await this.buildSubagentProvider(
|
|
11236
|
+
const provider = await this.buildSubagentProvider(
|
|
11237
|
+
config,
|
|
11238
|
+
subCfg.provider,
|
|
11239
|
+
subCfg.model ?? config.model
|
|
11240
|
+
);
|
|
11212
11241
|
const subCwd = subCfg.cwd ?? this.deps.cwd;
|
|
11213
11242
|
const baseSystem = await this.deps.systemPromptBuilder.build({
|
|
11214
11243
|
cwd: subCwd,
|
|
@@ -11346,17 +11375,28 @@ var MultiAgentHost = class {
|
|
|
11346
11375
|
* not configured (so a typo doesn't crash the whole run — we just
|
|
11347
11376
|
* use the leader and the calling code can decide to error later).
|
|
11348
11377
|
*/
|
|
11349
|
-
async buildSubagentProvider(config, overrideId) {
|
|
11378
|
+
async buildSubagentProvider(config, overrideId, model) {
|
|
11350
11379
|
const providerId = overrideId && config.providers?.[overrideId] ? overrideId : config.provider;
|
|
11351
11380
|
const newCfg = config.providers?.[providerId] ?? {
|
|
11352
11381
|
type: providerId,
|
|
11353
11382
|
apiKey: config.apiKey,
|
|
11354
11383
|
baseUrl: config.baseUrl
|
|
11355
11384
|
};
|
|
11356
|
-
|
|
11385
|
+
const provider = makeProviderFromConfig(providerId, {
|
|
11357
11386
|
...newCfg,
|
|
11358
11387
|
type: providerId
|
|
11359
11388
|
});
|
|
11389
|
+
if (this.deps.modelsRegistry) {
|
|
11390
|
+
const resolvedModel = model ?? config.model;
|
|
11391
|
+
const caps = await capabilitiesFor(
|
|
11392
|
+
this.deps.modelsRegistry,
|
|
11393
|
+
providerId,
|
|
11394
|
+
resolvedModel
|
|
11395
|
+
).catch(() => void 0);
|
|
11396
|
+
const mc = caps?.maxContext ?? config.context?.effectiveMaxContext ?? provider.capabilities.maxContext;
|
|
11397
|
+
if (mc && mc > 0) provider.capabilities.maxContext = mc;
|
|
11398
|
+
}
|
|
11399
|
+
return provider;
|
|
11360
11400
|
}
|
|
11361
11401
|
async spawnACP(subagentId, task, config) {
|
|
11362
11402
|
const taskId = randomUUID();
|
|
@@ -12138,6 +12178,13 @@ function renderProgress3(ratio, width) {
|
|
|
12138
12178
|
const capped = Math.min(width, filled);
|
|
12139
12179
|
return FILLED2.repeat(capped) + EMPTY2.repeat(width - capped);
|
|
12140
12180
|
}
|
|
12181
|
+
var createSessionEventBridge = (_writer, level) => ({
|
|
12182
|
+
append: async (_e) => {
|
|
12183
|
+
},
|
|
12184
|
+
level: level ?? "standard",
|
|
12185
|
+
allows: () => true
|
|
12186
|
+
});
|
|
12187
|
+
var resolveAuditLevel = (cfg) => cfg?.session?.auditLevel ?? "standard";
|
|
12141
12188
|
function setupPipelines(params) {
|
|
12142
12189
|
const { events, logger } = params;
|
|
12143
12190
|
const pipelines = createDefaultPipelines();
|
|
@@ -12164,11 +12211,13 @@ function setupPipelines(params) {
|
|
|
12164
12211
|
return pipelines;
|
|
12165
12212
|
}
|
|
12166
12213
|
async function setupCompaction(params) {
|
|
12167
|
-
const { compactor, events, modelsRegistry, context, config, provider, pipelines } = params;
|
|
12214
|
+
const { compactor, events, modelsRegistry, context, config, provider, pipelines, fullConfig, sessionWriter, sessionBridge: providedBridge } = params;
|
|
12168
12215
|
const resolvedCaps = await capabilitiesFor(modelsRegistry, provider.id, context.model).catch(() => void 0);
|
|
12169
12216
|
const effectiveMaxContext = config.context.effectiveMaxContext ?? resolvedCaps?.maxContext ?? provider.capabilities.maxContext;
|
|
12170
12217
|
let autoCompactor;
|
|
12171
12218
|
if (config.context.autoCompact !== false) {
|
|
12219
|
+
const auditLevel = resolveAuditLevel(fullConfig ?? config);
|
|
12220
|
+
const sessionBridge = providedBridge ?? createSessionEventBridge(sessionWriter, auditLevel);
|
|
12172
12221
|
autoCompactor = new AutoCompactionMiddleware(
|
|
12173
12222
|
compactor,
|
|
12174
12223
|
effectiveMaxContext,
|
|
@@ -12180,7 +12229,12 @@ async function setupCompaction(params) {
|
|
|
12180
12229
|
soft: config.context.softThreshold,
|
|
12181
12230
|
hard: config.context.hardThreshold
|
|
12182
12231
|
},
|
|
12183
|
-
{
|
|
12232
|
+
{
|
|
12233
|
+
aggressiveOn: "soft",
|
|
12234
|
+
failureMode: "throw_on_hard",
|
|
12235
|
+
events,
|
|
12236
|
+
sessionBridge
|
|
12237
|
+
}
|
|
12184
12238
|
);
|
|
12185
12239
|
pipelines.contextWindow.use({ name: "AutoCompaction", handler: autoCompactor.handler() });
|
|
12186
12240
|
}
|
|
@@ -12553,6 +12607,21 @@ async function setupSession(params) {
|
|
|
12553
12607
|
}
|
|
12554
12608
|
|
|
12555
12609
|
// src/index.ts
|
|
12610
|
+
var createSessionEventBridge2 = (_writer, level, _opts) => ({
|
|
12611
|
+
append: async (_e) => {
|
|
12612
|
+
},
|
|
12613
|
+
level: level ?? "standard",
|
|
12614
|
+
allows: () => true
|
|
12615
|
+
});
|
|
12616
|
+
var resolveAuditLevel2 = (cfg) => cfg?.session?.auditLevel ?? "standard";
|
|
12617
|
+
var resolveSessionLoggingConfig = (cfg) => ({
|
|
12618
|
+
auditLevel: resolveAuditLevel2(cfg),
|
|
12619
|
+
sampling: {
|
|
12620
|
+
toolProgress: {
|
|
12621
|
+
sampleRate: cfg?.session?.sampling?.toolProgress?.sampleRate ?? 8
|
|
12622
|
+
}
|
|
12623
|
+
}
|
|
12624
|
+
});
|
|
12556
12625
|
function resolveBundledSkillsDir2() {
|
|
12557
12626
|
try {
|
|
12558
12627
|
const req2 = createRequire(import.meta.url);
|
|
@@ -12782,18 +12851,99 @@ async function main(argv) {
|
|
|
12782
12851
|
const planPath = sessResult.planPath;
|
|
12783
12852
|
const detachTodosCheckpoint = sessResult.detachTodosCheckpoint;
|
|
12784
12853
|
const priorFleetState = sessResult.priorFleetState;
|
|
12854
|
+
const sessionConfig = resolveSessionLoggingConfig(config);
|
|
12855
|
+
const sessionBridge = createSessionEventBridge2(
|
|
12856
|
+
session,
|
|
12857
|
+
sessionConfig.auditLevel);
|
|
12785
12858
|
const stats = new SessionStats(events, tokenCounter);
|
|
12786
12859
|
const errorRing = [];
|
|
12787
12860
|
events.on("error", (e) => {
|
|
12788
12861
|
const err = e.err;
|
|
12789
12862
|
const code = err && typeof err === "object" && "code" in err && typeof err.code === "string" ? err.code : "UNKNOWN";
|
|
12790
12863
|
const message = e.err instanceof Error ? e.err.message : String(e.err);
|
|
12791
|
-
|
|
12864
|
+
const ts = (/* @__PURE__ */ new Date()).toISOString();
|
|
12865
|
+
errorRing.push({ ts, phase: e.phase, code, message });
|
|
12792
12866
|
if (errorRing.length > 5) errorRing.shift();
|
|
12867
|
+
sessionBridge.append({
|
|
12868
|
+
type: "error",
|
|
12869
|
+
ts,
|
|
12870
|
+
message,
|
|
12871
|
+
phase: e.phase
|
|
12872
|
+
}).catch(() => {
|
|
12873
|
+
});
|
|
12874
|
+
});
|
|
12875
|
+
events.on("tool.started", (e) => {
|
|
12876
|
+
sessionBridge.append({
|
|
12877
|
+
type: "tool_call_start",
|
|
12878
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12879
|
+
name: e.name,
|
|
12880
|
+
id: e.id,
|
|
12881
|
+
input: e.input
|
|
12882
|
+
}).catch(() => {
|
|
12883
|
+
});
|
|
12884
|
+
});
|
|
12885
|
+
events.on("tool.executed", (e) => {
|
|
12886
|
+
sessionBridge.append({
|
|
12887
|
+
type: "tool_call_end",
|
|
12888
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12889
|
+
name: e.name,
|
|
12890
|
+
id: e.id ?? "",
|
|
12891
|
+
durationMs: e.durationMs,
|
|
12892
|
+
outputSize: e.outputBytes ?? 0,
|
|
12893
|
+
ok: e.ok,
|
|
12894
|
+
outputBytes: e.outputBytes,
|
|
12895
|
+
outputTokens: e.outputTokens,
|
|
12896
|
+
outputLines: e.outputLines
|
|
12897
|
+
}).catch(() => {
|
|
12898
|
+
});
|
|
12899
|
+
});
|
|
12900
|
+
events.on("tool.progress", (e) => {
|
|
12901
|
+
sessionBridge.append({
|
|
12902
|
+
type: "tool_progress",
|
|
12903
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12904
|
+
name: e.name,
|
|
12905
|
+
id: e.id,
|
|
12906
|
+
event: { type: e.event.type, text: e.event.text, data: e.event.data }
|
|
12907
|
+
}).catch(() => {
|
|
12908
|
+
});
|
|
12909
|
+
});
|
|
12910
|
+
events.on("provider.retry", (e) => {
|
|
12911
|
+
sessionBridge.append({
|
|
12912
|
+
type: "provider_retry",
|
|
12913
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12914
|
+
providerId: e.providerId,
|
|
12915
|
+
attempt: e.attempt,
|
|
12916
|
+
delayMs: e.delayMs,
|
|
12917
|
+
status: e.status,
|
|
12918
|
+
description: e.description
|
|
12919
|
+
}).catch(() => {
|
|
12920
|
+
});
|
|
12921
|
+
});
|
|
12922
|
+
events.on("provider.error", (e) => {
|
|
12923
|
+
sessionBridge.append({
|
|
12924
|
+
type: "provider_error",
|
|
12925
|
+
ts: (/* @__PURE__ */ new Date()).toISOString(),
|
|
12926
|
+
providerId: e.providerId,
|
|
12927
|
+
status: e.status,
|
|
12928
|
+
description: e.description,
|
|
12929
|
+
retryable: e.retryable
|
|
12930
|
+
}).catch(() => {
|
|
12931
|
+
});
|
|
12793
12932
|
});
|
|
12794
12933
|
const pipelines = setupPipelines({ events, logger });
|
|
12795
12934
|
const compactor = container.resolve(TOKENS.Compactor);
|
|
12796
|
-
const { effectiveMaxContext, autoCompactor } = await setupCompaction({
|
|
12935
|
+
const { effectiveMaxContext, autoCompactor } = await setupCompaction({
|
|
12936
|
+
compactor,
|
|
12937
|
+
events,
|
|
12938
|
+
modelsRegistry,
|
|
12939
|
+
context,
|
|
12940
|
+
config,
|
|
12941
|
+
provider,
|
|
12942
|
+
pipelines,
|
|
12943
|
+
fullConfig: config,
|
|
12944
|
+
sessionBridge
|
|
12945
|
+
// share the same bridge for consistent audit logging (compaction + errors + future)
|
|
12946
|
+
});
|
|
12797
12947
|
const refreshMaxContext = async (providerId, modelId) => {
|
|
12798
12948
|
if (!autoCompactor) return;
|
|
12799
12949
|
const cap = await capabilitiesFor(modelsRegistry, providerId, modelId).catch(() => void 0);
|
|
@@ -12890,6 +13040,7 @@ async function main(argv) {
|
|
|
12890
13040
|
toolRegistry,
|
|
12891
13041
|
providerRegistry,
|
|
12892
13042
|
configStore,
|
|
13043
|
+
modelsRegistry,
|
|
12893
13044
|
events,
|
|
12894
13045
|
systemPromptBuilder: promptBuilder,
|
|
12895
13046
|
session,
|