@mmmbuto/nexuscrew 0.9.24 → 0.9.25

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/CHANGELOG.md CHANGED
@@ -2,6 +2,82 @@
2
2
 
3
3
  All notable changes to NexusCrew are tracked here.
4
4
 
5
+ ## 0.9.25 - 2026-09-13
6
+
7
+ - **Compatibility note**: codex-vl >= 0.154.0-vl.3 inside a bound Fleet cell
8
+ requires NexusCrew >= 0.9.25 for the `nc_*` tools; NexusCrew 0.9.24 only
9
+ reads the legacy identity variables, which vl.3 removes from MCP children on
10
+ verified bindings. Standalone (legacy) cells keep working with both.
11
+
12
+ - **MCP identity reads the verified binding metadata first (fail-closed).**
13
+ `nexuscrew mcp` now evaluates the `NEXUSCREW_VERIFIED_*` metadata delivered
14
+ by codex-vl >= 0.154.0-vl.3 launchers before any legacy source: version,
15
+ owner instance, cell, channel and the authority introspection must all be
16
+ coherent (source `verified-env`, verified identity in `nc_identity`), and any
17
+ incoherence fails closed with `NEXUSCREW_MCP_IDENTITY_VERIFIED_ENV_INVALID`
18
+ instead of falling back to the legacy variables. Without the metadata the
19
+ legacy path is unchanged.
20
+ - **`CODEX_APP_SERVER_IDENTITY_REQUIRED=1` is exported only in authority
21
+ mode.** The fleet launcher now exports the flag for codex-vl cells only when
22
+ the fleet identity mode is `authority` and the authority is constructible;
23
+ in legacy mode it exports an explicit `0` and names the reason in a
24
+ structured log line. Explicit user overrides (`engine.env`, `cfg.env`) still win, with a
25
+ warning when they force `1` outside authority mode.
26
+ - **Standalone cells (`CODEX_APP_SERVER_IDENTITY_REQUIRED=0`) no longer
27
+ receive the identity channel.** The launcher created fd 3:4 and
28
+ `NEXUSCREW_IDENTITY_FD` even when it had resolved the flag to `0`, so the
29
+ child started an identity handshake against a legacy lease server that
30
+ answered `revoked` and the cell died at startup. The channel decision now
31
+ comes from the same resolution that produces the flag.
32
+ - **A configured but unavailable fleet identity authority now refuses
33
+ protected cell launches instead of silently falling back to standalone.**
34
+ The launch fails with `IDENTITY_AUTHORITY_UNAVAILABLE` naming the reason
35
+ (never a value), and `nexuscrew doctor` reports the same state.
36
+ - **Upgrade NexusCrew before codex-vl**: `codex-vl >= 0.154.0-vl.3` with
37
+ `NexusCrew <= 0.9.24` fails closed in Fleet cells (the old launcher forces
38
+ identity without an authority).
39
+ - **`nexuscrew identity provision`.** New command that generates the two
40
+ distinct authority credentials (32 random bytes each, files `0600` inside a
41
+ `0700` directory under `~/.nexuscrew/identity-authority/`), never overwrites
42
+ existing credentials without `--force`, activates
43
+ `fleet.identity.mode = "authority"` in the config (or prints the exact keys
44
+ with `--no-write-config`). `loadConfig` reads the credential files
45
+ automatically and `nexuscrew doctor` shows the identity mode and authority
46
+ constructibility (no values).
47
+ - **MCP identity remediation rewritten.** `nc_identity` and `doctor` no longer
48
+ recommend allowlisting `TMUX`/`TMUX_PANE`/`NEXUSCREW_MCP_SESSION` in the MCP
49
+ server `env_vars`: on a shared verified spawn that allowlist fails the launch
50
+ (the names are reserved). The two valid paths are a verified session
51
+ (codex-vl >= 0.154.0-vl.3 + NexusCrew >= 0.9.25 with provisioned authority)
52
+ or a legacy session inheriting the three variables.
53
+
54
+ - **Ollama Cloud catalog refreshed against the official measured sample of
55
+ 2026-09-12.** Bare-name `gemma4` and `qwen3.5` enter the catalog (verified
56
+ alive with a 200 on `/v1/chat/completions`; official measured sample:
57
+ 22 live ids / 8 dead variants), with context and capabilities taken from
58
+ the channel cards (`gemma4` 128K text+image, `qwen3.5` 256K text+image).
59
+ The 404 variants (`gemma4:12b`/`:e4b`, `qwen3.5:27b`/`:35b`/`:122b`, bare
60
+ `mistral-large-3`, `nemotron-3-super:120b`, `nemotron-3-nano:4b`) and
61
+ community ids stay out on purpose. `glm-5.3` and `deepseek-v4.1-flash`
62
+ were already listed and are not duplicated.
63
+ - **Fixture-based liveness guard for the Ollama Cloud models.** New test
64
+ (`tests/fleet-ollama-liveness.test.js`): every catalog id must belong to
65
+ the official measured sample; when upstream retires an id, the test goes
66
+ red naming it as "to be verified". No network calls in tests; the real
67
+ periodic check remains a separate item.
68
+ - **OpenCode Go catalog refreshed signal-only (401 CreditsError on
69
+ 2026-09-12).** The Go plan could not generate (credits exhausted, listing
70
+ OK): the additions are wire signals, not end-to-end measurements.
71
+ `glm-5.3`, `glm-5.3-flash`, `longcat-2.0`, `deepseek-v4.1-flash` and
72
+ `hy4-preview` enter all three wires; `qwen3.8-flash` enters Messages/Chat
73
+ ONLY (Responses rejected, measured). New limits declared by models.dev
74
+ (1M context; `hy4-preview` 1024000), marked "declared, not measured".
75
+ Retired `glm-5`, `kimi-k2.5`, `minimax-m2.5` and `qwen3.5-plus` are
76
+ removed from the lists, LIMITS and the catalog, with a test guard against
77
+ re-introduction. `hy3` unchanged (diverging source on its output limit:
78
+ to be re-checked), default `deepseek-v4-flash` unchanged, VISION
79
+ unchanged (only the PNG-measured vision-exp id).
80
+
5
81
  ## 0.9.24 — 2026-09-12
6
82
 
7
83
  - **Verifica dell'identità legata alla challenge, con claims completi.** La
@@ -1 +1 @@
1
- {"version":"0.9.24"}
1
+ {"version": "0.9.25"}
@@ -86,7 +86,7 @@ Lifecycle semantics:
86
86
 
87
87
  const CLI_VALUE_FLAGS = new Set([
88
88
  'label', 'ssh', 'ssh-port', 'autostart', 'visibility', 'selected',
89
- 'name', 'local-name', 'local-label', 'identity-file', 'port',
89
+ 'name', 'local-name', 'local-label', 'identity-file', 'port', 'dir',
90
90
  ]);
91
91
 
92
92
  // valueFlags (Set|array opzionale): flag che consumano il token successivo nella
@@ -1508,6 +1508,38 @@ function dispatch(argv, opts = {}) {
1508
1508
  serve({ ...opts, pidfile: flags.pidfile, serverStart: opts.serverStart });
1509
1509
  return { code: 0, keepAlive: true }; // server.listen tiene il processo vivo; non exit
1510
1510
  }
1511
+ if (cmd === 'identity') {
1512
+ const sub = rest[1];
1513
+ if (sub !== 'provision') {
1514
+ log('usage: nexuscrew identity provision [--force] [--no-write-config] [--dir <path>]');
1515
+ return { code: sub === undefined ? 0 : 1 };
1516
+ }
1517
+ const provision = require('../fleet/identity-provision.js').provisionIdentityAuthority;
1518
+ const home = opts.home || require('node:os').homedir();
1519
+ const result = provision({
1520
+ home,
1521
+ dir: flags.dir,
1522
+ force: flags.force === true,
1523
+ writeConfig: flags['no-write-config'] !== true,
1524
+ });
1525
+ if (!result.ok) {
1526
+ log(`identity provision: ${result.message}`);
1527
+ log(` existing credential files:`);
1528
+ for (const filePath of result.existing) log(` ${filePath}`);
1529
+ return { code: 1 };
1530
+ }
1531
+ log(`identity provision: credentials written under ${result.dir} (0600, dir 0700)`);
1532
+ log('identity provision: fleet.identity.mode = "authority"');
1533
+ if (result.configUpdated) {
1534
+ log(`identity provision: config updated (${result.mode})`);
1535
+ } else {
1536
+ log('identity provision: config NOT written (--no-write-config). Apply manually:');
1537
+ log(' fleet.identity.mode = "authority"');
1538
+ log(` keep both credential files in place under ${result.dir}`);
1539
+ }
1540
+ log('identity provision: restart the NexusCrew service to activate authority mode');
1541
+ return { code: 0 };
1542
+ }
1511
1543
  if (cmd === 'doctor') {
1512
1544
  if (flags.peers === true) return dispatchNodes(['nodes', 'doctor'], flags, opts);
1513
1545
  const r = doctor({ ...opts, log });
package/lib/cli/doctor.js CHANGED
@@ -462,6 +462,24 @@ function checkMcpIdentity(env) {
462
462
  const e = env || process.env;
463
463
  const hasTmux = !!(typeof e.TMUX === 'string' && e.TMUX);
464
464
  const hasSession = !!(typeof e.NEXUSCREW_MCP_SESSION === 'string' && e.NEXUSCREW_MCP_SESSION.trim());
465
+ const verifiedNames = [
466
+ 'NEXUSCREW_VERIFIED_ENV_VERSION',
467
+ 'NEXUSCREW_VERIFIED_OWNER_INSTANCE_ID',
468
+ 'NEXUSCREW_VERIFIED_CELL_ID',
469
+ 'NEXUSCREW_VERIFIED_INCARNATION_ID',
470
+ 'NEXUSCREW_VERIFIED_BINDING_ID',
471
+ 'NEXUSCREW_VERIFIED_ORIGIN',
472
+ 'NEXUSCREW_VERIFIED_THREAD_ID',
473
+ ];
474
+ const verifiedPresent = verifiedNames.filter((name) => typeof e[name] === 'string' && e[name]);
475
+ if (verifiedPresent.length) {
476
+ return {
477
+ name: 'MCP identity env',
478
+ ok: true,
479
+ detail: `metadati verified osservabili (${verifiedPresent.length}/7 nomi NEXUSCREW_VERIFIED_*): `
480
+ + 'identita attribuita solo dopo introspezione authority riuscita',
481
+ };
482
+ }
465
483
  if (hasTmux || hasSession) {
466
484
  const src = [];
467
485
  if (hasTmux) src.push('TMUX');
@@ -469,16 +487,80 @@ function checkMcpIdentity(env) {
469
487
  return {
470
488
  name: 'MCP identity env',
471
489
  ok: true,
472
- detail: `identita osservabile (${src.join('+')})`,
490
+ detail: `identita legacy osservabile (${src.join('+')})`,
473
491
  };
474
492
  }
475
493
  return {
476
494
  name: 'MCP identity env',
477
495
  ok: true, // mai FAIL: PWA-only senza MCP configurato non deve rompere il doctor
478
496
  warn: true,
479
- detail: 'nessuna identita MCP osservabile (TMUX/NEXUSCREW_MCP_SESSION assenti nel processo doctor); '
480
- + 'se integri l\'MCP stdio allowlista i nomi (codex-vl mcp add --env-var) — PWA-only: ignorabile',
497
+ detail: 'nessuna identita MCP osservabile (nessun NEXUSCREW_VERIFIED_* e nessuna variabile legacy '
498
+ + 'nel processo doctor). Percorsi validi: sessione verified (codex-vl >= 0.154.0-vl.3 + NexusCrew '
499
+ + '>= 0.9.25 con authority mode provisionato) oppure sessione legacy con le tre variabili ereditate '
500
+ + 'dal client MCP. NON allowlistare i nomi legacy in env_vars del server MCP: con vl.3 lo spawn '
501
+ + 'verified fallisce — PWA-only: ignorabile',
502
+ };
503
+ }
504
+
505
+ // Stato dell'authority mode (registro flotta). Mostra mode e costruibilita' dell'
506
+ // authority (file presenti, permessi) SENZA mai esporre valori.
507
+ function checkIdentityAuthorityMode({ home, configPath, env } = {}) {
508
+ const e = env || process.env;
509
+ const fs = require('node:fs');
510
+ const path = require('node:path');
511
+ const cfgPath = configPath
512
+ || process.env.NEXUSCREW_CONFIG_FILE
513
+ || path.join(home || os.homedir(), '.nexuscrew', 'config.json');
514
+ let cfg = {};
515
+ try { cfg = JSON.parse(fs.readFileSync(cfgPath, 'utf8')) || {}; } catch (_) { cfg = {}; }
516
+ const mode = cfg.fleetIdentityMode
517
+ || (cfg.fleet && cfg.fleet.identity && cfg.fleet.identity.mode)
518
+ || 'legacy';
519
+ const dir = cfg.identityAuthorityDir || path.join(home || os.homedir(), '.nexuscrew', 'identity-authority');
520
+ const files = ['daemon.credential', 'launcher.credential'];
521
+ const present = [];
522
+ const values = {};
523
+ const problems = [];
524
+ for (const name of files) {
525
+ const filePath = path.join(dir, name);
526
+ try {
527
+ const st = fs.lstatSync(filePath);
528
+ if (!st.isFile()) { problems.push(`${name}: non regolare`); continue; }
529
+ if ((st.mode & 0o777) !== 0o600) { problems.push(`${name}: permessi non 0600`); continue; }
530
+ const value = fs.readFileSync(filePath, 'utf8').trim();
531
+ if (!value) { problems.push(`${name}: vuoto`); continue; }
532
+ present.push(name);
533
+ values[name] = value;
534
+ } catch (_) { problems.push(`${name}: assente`); }
535
+ }
536
+ // The two credentials must be DISTINCT (constant-time comparison):
537
+ // identical means the authority is not constructible, fail-closed.
538
+ let distinctLine = 'credentials distinct: n/a (una o entrambe assenti)';
539
+ if (present.length === 2) {
540
+ const distinct = Buffer.from(values['daemon.credential'])
541
+ .compare(Buffer.from(values['launcher.credential'])) !== 0;
542
+ distinctLine = `credentials distinct: ${distinct ? 'yes' : 'no'}`;
543
+ if (!distinct) problems.push('credenziali daemon e launcher identiche');
544
+ }
545
+ const constructible = present.length === 2 && problems.length === 0;
546
+ const ok = mode !== 'authority' || constructible;
547
+ const out = {
548
+ name: 'Fleet identity authority mode',
549
+ ok,
550
+ detail: `mode=${mode}; credenziali: ${present.length === 2 ? 'entrambe presenti (0600)' : present.join('+') || 'nessuna'} `
551
+ + `(${dir}); ${distinctLine}${problems.length ? '; problemi: ' + problems.join(', ') : ''}`,
481
552
  };
553
+ if (mode !== 'authority') {
554
+ out.warn = true;
555
+ out.detail += ' — il launcher espone CODEX_APP_SERVER_IDENTITY_REQUIRED=0: provisiona con '
556
+ + '`nexuscrew identity provision` per attivare l\'authority mode';
557
+ } else if (!constructible) {
558
+ out.detail += ' — authority: configured but unavailable: '
559
+ + (problems.length ? problems.join(', ') : 'credentials not constructible')
560
+ + ' — le celle protette (codex-vl) non partono [IDENTITY_AUTHORITY_UNAVAILABLE]';
561
+ }
562
+ void e;
563
+ return out;
482
564
  }
483
565
 
484
566
  function checkFleetDefinitions(home, fleetDefsPath, enabled = true) {
@@ -578,6 +660,7 @@ function doctor(opts = {}) {
578
660
  checkAutossh(existsImpl),
579
661
  checkSshPermitlisten(opts.sshVersion),
580
662
  checkMcpIdentity(opts.env),
663
+ checkIdentityAuthorityMode({ home: opts.home, env: opts.env }),
581
664
  checkTmuxServerCwd(platform, execImpl, { tmuxBin: opts.tmuxBin, procCwdImpl: opts.procCwdImpl }),
582
665
  checkTmuxServerTermuxPreload(opts.env, execImpl, { platform, home, tmuxBin: opts.tmuxBin }),
583
666
  ];
@@ -601,6 +684,6 @@ module.exports = {
601
684
  checkFleetServiceWorkingDirectory,
602
685
  checkTmuxSurvival, checkUserLinger,
603
686
  checkSshClient, checkAutossh, checkSshPermitlisten,
604
- checkMcpIdentity, checkTermuxExec,
687
+ checkMcpIdentity, checkIdentityAuthorityMode, checkTermuxExec,
605
688
  checkTmuxServerCwd, checkTmuxServerTermuxPreload,
606
689
  };
package/lib/config.js CHANGED
@@ -135,8 +135,58 @@ function envOverrides() {
135
135
  }
136
136
 
137
137
  // Precedence: baseDefaults < config.json < env < opts. [B2]
138
+ // Le credenziali authority vivono in due file 0600 nella dir
139
+ // dell'authority (default ~/.nexuscrew/identity-authority). Se esistono vengono
140
+ // caricate qui, senza MAI loggare i valori: builtin costruisce l'authority
141
+ // solo quando ENTRAMBE sono presenti e distinte.
142
+ function readIdentityAuthorityCredentials(cfg = {}) {
143
+ const fs = require('node:fs');
144
+ const crypto = require('node:crypto');
145
+ const dir = cfg.identityAuthorityDir
146
+ || path.join(cfg.home || os.homedir(), '.nexuscrew', 'identity-authority');
147
+ // Enforced AT LOAD. Identical credentials or wrong permissions
148
+ // (files != 0600, dir != 0700) => the credentials are NOT loaded: the
149
+ // authority is not constructed and the protected launch is refused instead.
150
+ // The reason travels in identityAuthorityFault, and never carries a value.
151
+ const faults = [];
152
+ let dirMode = null;
153
+ try { dirMode = fs.lstatSync(dir).mode & 0o777; } catch (_) { faults.push('authority dir missing'); }
154
+ if (dirMode !== null && dirMode !== 0o700) faults.push('authority dir mode is not 0700');
155
+ const readOne = (name) => {
156
+ try {
157
+ const st = fs.lstatSync(path.join(dir, name));
158
+ if (!st.isFile()) { faults.push(`${name}: not a regular file`); return undefined; }
159
+ if ((st.mode & 0o777) !== 0o600) { faults.push(`${name}: mode is not 0600`); return undefined; }
160
+ const value = fs.readFileSync(path.join(dir, name), 'utf8').trim();
161
+ if (!value) { faults.push(`${name}: empty`); return undefined; }
162
+ return value;
163
+ } catch (_) { faults.push(`${name}: missing`); return undefined; }
164
+ };
165
+ const daemon = readOne('daemon.credential');
166
+ const launcher = readOne('launcher.credential');
167
+ if (daemon && launcher) {
168
+ let distinct;
169
+ try {
170
+ distinct = !crypto.timingSafeEqual(Buffer.from(daemon), Buffer.from(launcher));
171
+ } catch (_) { distinct = false; }
172
+ if (!distinct) faults.push('daemon and launcher credentials are identical');
173
+ }
174
+ const out = {};
175
+ if (faults.length) {
176
+ out.identityAuthorityFault = faults.join('; ');
177
+ return out;
178
+ }
179
+ if (daemon) out.identityDaemonCredential = daemon;
180
+ if (launcher) out.identityLauncherCredential = launcher;
181
+ return out;
182
+ }
183
+
138
184
  function loadConfig(opts = {}) {
139
- return { ...baseDefaults(), ...readConfigJson(), ...envOverrides(), ...opts };
185
+ const merged = { ...baseDefaults(), ...readConfigJson(), ...envOverrides(), ...opts };
186
+ // I file di credenziale sono fallback: non sovrascrivono mai valori espliciti
187
+ // di config.json/env/opts (precedence invariata).
188
+ const creds = readIdentityAuthorityCredentials(merged);
189
+ return { ...merged, ...creds };
140
190
  }
141
191
 
142
192
  // Retrocompat: defaults() = baseDefaults + env (NO config.json — per test isolati
@@ -700,11 +700,23 @@ async function createBuiltinFleet(cfg = {}) {
700
700
  // leaseManager e launchBroker. L'authority resta server-side: nessuna
701
701
  // credenziale o chiave HMAC attraversa il payload consegnato alla cella.
702
702
  const identityMode = cfg.fleetIdentityMode || cfg.fleet?.identity?.mode || 'legacy';
703
+ // Identical credentials => the authority is NOT constructed (consistent with
704
+ // identity-authority, which would reject at runtime anyway), and the reason
705
+ // goes to the log.
706
+ const identityCredentialsDistinct = (typeof cfg.identityDaemonCredential === 'string'
707
+ && typeof cfg.identityLauncherCredential === 'string')
708
+ ? Buffer.from(cfg.identityDaemonCredential).compare(Buffer.from(cfg.identityLauncherCredential)) !== 0
709
+ : !!(cfg.identityDaemonCredential && cfg.identityLauncherCredential);
710
+ if (identityMode === 'authority' && !identityCredentialsDistinct && cfg.log) {
711
+ cfg.log('fleet identity: authority mode requested but daemon and launcher credentials '
712
+ + 'are identical or missing; identity enforcement stays off (REQUIRED=0)');
713
+ }
703
714
  const identityOwnerInstanceId = typeof cfg.identityOwnerInstanceId === 'function'
704
715
  ? cfg.identityOwnerInstanceId()
705
716
  : (cfg.identityOwnerInstanceId || null);
706
717
  const identityAuthority = cfg.identityAuthority || (
707
- cfg.identityDaemonCredential && cfg.identityLauncherCredential
718
+ identityCredentialsDistinct
719
+ && cfg.identityDaemonCredential && cfg.identityLauncherCredential
708
720
  ? createIdentityAuthority({
709
721
  dir: cfg.identityAuthorityDir || path.join(home, '.nexuscrew', 'identity-authority'),
710
722
  daemonCredential: cfg.identityDaemonCredential,
@@ -279,9 +279,102 @@
279
279
  "supports_search_tool": false
280
280
  },
281
281
  {
282
- "slug": "glm-5",
283
- "display_name": "glm-5",
284
- "description": "OpenCode Go: glm-5",
282
+ "slug": "deepseek-v4-flash-vision-exp",
283
+ "display_name": "deepseek-v4-flash-vision-exp",
284
+ "description": "OpenCode Go: deepseek-v4-flash-vision-exp (vision)",
285
+ "default_reasoning_level": "high",
286
+ "supported_reasoning_levels": [
287
+ {
288
+ "effort": "low",
289
+ "description": "Fast responses with lighter reasoning"
290
+ },
291
+ {
292
+ "effort": "high",
293
+ "description": "Greater reasoning depth for complex problems"
294
+ },
295
+ {
296
+ "effort": "max",
297
+ "description": "Maximum reasoning depth"
298
+ }
299
+ ],
300
+ "shell_type": "default",
301
+ "visibility": "list",
302
+ "supported_in_api": true,
303
+ "priority": 57,
304
+ "availability_nux": null,
305
+ "upgrade": null,
306
+ "base_instructions": "",
307
+ "supports_reasoning_summaries": true,
308
+ "default_reasoning_summary": "none",
309
+ "support_verbosity": false,
310
+ "default_verbosity": null,
311
+ "apply_patch_tool_type": null,
312
+ "web_search_tool_type": "text",
313
+ "truncation_policy": {
314
+ "mode": "tokens",
315
+ "limit": 1000000
316
+ },
317
+ "supports_parallel_tool_calls": false,
318
+ "supports_image_detail_original": true,
319
+ "context_window": 1000000,
320
+ "effective_context_window_percent": 95,
321
+ "experimental_supported_tools": [],
322
+ "input_modalities": [
323
+ "text",
324
+ "image"
325
+ ],
326
+ "supports_search_tool": false
327
+ },
328
+ {
329
+ "slug": "deepseek-v4.1-flash",
330
+ "display_name": "deepseek-v4.1-flash",
331
+ "description": "OpenCode Go: deepseek-v4.1-flash",
332
+ "default_reasoning_level": "high",
333
+ "supported_reasoning_levels": [
334
+ {
335
+ "effort": "low",
336
+ "description": "Fast responses with lighter reasoning"
337
+ },
338
+ {
339
+ "effort": "high",
340
+ "description": "Greater reasoning depth for complex problems"
341
+ },
342
+ {
343
+ "effort": "max",
344
+ "description": "Maximum reasoning depth"
345
+ }
346
+ ],
347
+ "shell_type": "default",
348
+ "visibility": "list",
349
+ "supported_in_api": true,
350
+ "priority": 58,
351
+ "availability_nux": null,
352
+ "upgrade": null,
353
+ "base_instructions": "",
354
+ "supports_reasoning_summaries": true,
355
+ "default_reasoning_summary": "none",
356
+ "support_verbosity": false,
357
+ "default_verbosity": null,
358
+ "apply_patch_tool_type": null,
359
+ "web_search_tool_type": "text",
360
+ "truncation_policy": {
361
+ "mode": "tokens",
362
+ "limit": 1000000
363
+ },
364
+ "supports_parallel_tool_calls": false,
365
+ "supports_image_detail_original": false,
366
+ "context_window": 1000000,
367
+ "effective_context_window_percent": 95,
368
+ "experimental_supported_tools": [],
369
+ "input_modalities": [
370
+ "text"
371
+ ],
372
+ "supports_search_tool": false
373
+ },
374
+ {
375
+ "slug": "glm-5.3",
376
+ "display_name": "glm-5.3",
377
+ "description": "OpenCode Go: glm-5.3",
285
378
  "default_reasoning_level": "medium",
286
379
  "supported_reasoning_levels": [
287
380
  {
@@ -300,7 +393,7 @@
300
393
  "shell_type": "default",
301
394
  "visibility": "list",
302
395
  "supported_in_api": true,
303
- "priority": 55,
396
+ "priority": 59,
304
397
  "availability_nux": null,
305
398
  "upgrade": null,
306
399
  "base_instructions": "",
@@ -312,11 +405,11 @@
312
405
  "web_search_tool_type": "text",
313
406
  "truncation_policy": {
314
407
  "mode": "tokens",
315
- "limit": 202752
408
+ "limit": 1000000
316
409
  },
317
410
  "supports_parallel_tool_calls": false,
318
411
  "supports_image_detail_original": false,
319
- "context_window": 202752,
412
+ "context_window": 1000000,
320
413
  "effective_context_window_percent": 95,
321
414
  "experimental_supported_tools": [],
322
415
  "input_modalities": [
@@ -325,28 +418,74 @@
325
418
  "supports_search_tool": false
326
419
  },
327
420
  {
328
- "slug": "deepseek-v4-flash-vision-exp",
329
- "display_name": "deepseek-v4-flash-vision-exp",
330
- "description": "OpenCode Go: deepseek-v4-flash-vision-exp (vision)",
331
- "default_reasoning_level": "high",
421
+ "slug": "glm-5.3-flash",
422
+ "display_name": "glm-5.3-flash",
423
+ "description": "OpenCode Go: glm-5.3-flash",
424
+ "default_reasoning_level": "medium",
332
425
  "supported_reasoning_levels": [
333
426
  {
334
427
  "effort": "low",
335
428
  "description": "Fast responses with lighter reasoning"
336
429
  },
430
+ {
431
+ "effort": "medium",
432
+ "description": "Balanced reasoning depth"
433
+ },
337
434
  {
338
435
  "effort": "high",
339
436
  "description": "Greater reasoning depth for complex problems"
437
+ }
438
+ ],
439
+ "shell_type": "default",
440
+ "visibility": "list",
441
+ "supported_in_api": true,
442
+ "priority": 60,
443
+ "availability_nux": null,
444
+ "upgrade": null,
445
+ "base_instructions": "",
446
+ "supports_reasoning_summaries": true,
447
+ "default_reasoning_summary": "none",
448
+ "support_verbosity": false,
449
+ "default_verbosity": null,
450
+ "apply_patch_tool_type": null,
451
+ "web_search_tool_type": "text",
452
+ "truncation_policy": {
453
+ "mode": "tokens",
454
+ "limit": 1000000
455
+ },
456
+ "supports_parallel_tool_calls": false,
457
+ "supports_image_detail_original": false,
458
+ "context_window": 1000000,
459
+ "effective_context_window_percent": 95,
460
+ "experimental_supported_tools": [],
461
+ "input_modalities": [
462
+ "text"
463
+ ],
464
+ "supports_search_tool": false
465
+ },
466
+ {
467
+ "slug": "longcat-2.0",
468
+ "display_name": "longcat-2.0",
469
+ "description": "OpenCode Go: longcat-2.0",
470
+ "default_reasoning_level": "medium",
471
+ "supported_reasoning_levels": [
472
+ {
473
+ "effort": "low",
474
+ "description": "Fast responses with lighter reasoning"
340
475
  },
341
476
  {
342
- "effort": "max",
343
- "description": "Maximum reasoning depth"
477
+ "effort": "medium",
478
+ "description": "Balanced reasoning depth"
479
+ },
480
+ {
481
+ "effort": "high",
482
+ "description": "Greater reasoning depth for complex problems"
344
483
  }
345
484
  ],
346
485
  "shell_type": "default",
347
486
  "visibility": "list",
348
487
  "supported_in_api": true,
349
- "priority": 57,
488
+ "priority": 61,
350
489
  "availability_nux": null,
351
490
  "upgrade": null,
352
491
  "base_instructions": "",
@@ -361,13 +500,58 @@
361
500
  "limit": 1000000
362
501
  },
363
502
  "supports_parallel_tool_calls": false,
364
- "supports_image_detail_original": true,
503
+ "supports_image_detail_original": false,
365
504
  "context_window": 1000000,
366
505
  "effective_context_window_percent": 95,
367
506
  "experimental_supported_tools": [],
368
507
  "input_modalities": [
369
- "text",
370
- "image"
508
+ "text"
509
+ ],
510
+ "supports_search_tool": false
511
+ },
512
+ {
513
+ "slug": "hy4-preview",
514
+ "display_name": "hy4-preview",
515
+ "description": "OpenCode Go: hy4-preview",
516
+ "default_reasoning_level": "medium",
517
+ "supported_reasoning_levels": [
518
+ {
519
+ "effort": "low",
520
+ "description": "Fast responses with lighter reasoning"
521
+ },
522
+ {
523
+ "effort": "medium",
524
+ "description": "Balanced reasoning depth"
525
+ },
526
+ {
527
+ "effort": "high",
528
+ "description": "Greater reasoning depth for complex problems"
529
+ }
530
+ ],
531
+ "shell_type": "default",
532
+ "visibility": "list",
533
+ "supported_in_api": true,
534
+ "priority": 62,
535
+ "availability_nux": null,
536
+ "upgrade": null,
537
+ "base_instructions": "",
538
+ "supports_reasoning_summaries": true,
539
+ "default_reasoning_summary": "none",
540
+ "support_verbosity": false,
541
+ "default_verbosity": null,
542
+ "apply_patch_tool_type": null,
543
+ "web_search_tool_type": "text",
544
+ "truncation_policy": {
545
+ "mode": "tokens",
546
+ "limit": 1024000
547
+ },
548
+ "supports_parallel_tool_calls": false,
549
+ "supports_image_detail_original": false,
550
+ "context_window": 1024000,
551
+ "effective_context_window_percent": 95,
552
+ "experimental_supported_tools": [],
553
+ "input_modalities": [
554
+ "text"
371
555
  ],
372
556
  "supports_search_tool": false
373
557
  }