jeo-code 0.6.26 → 0.6.27

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
@@ -6,6 +6,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6
6
 
7
7
  The README mirrors the latest 5 entries — regenerate with `bun run changelog:sync`.
8
8
 
9
+ ## [0.6.27] - 2026-06-19
10
+ _Ponytail pass on the reasoning-tier mapper, plus a real-tmux verification of `jeo --tmux`._
11
+
12
+ ### Changed
13
+ - **`thinkingToReasoningEffort` collapsed to its essential mapping (ponytail/YAGNI pass).** The four redundant pass-through branches (`minimal`/`low`/`medium`/`high` each returning themselves) are now a single `level === "xhigh" ? "high" : level` — behavior-identical (every level still maps to a genuine reasoning effort; only an unset level stays off), 8 fewer lines, fully covered by the existing `model-manager`/`round-b` contract tests. Reasoning continues to activate at EVERY thinking level (gajae parity).
14
+
15
+ ### Fixed
16
+ - **`/agents <role> provider <name>` now accepts every registered provider and always shows a model list (jeo team role config).** Three compounding bugs surfaced via a real `jeo --tmux` session pinning a role to `groq`: (1) `isProviderName` was an unsound type guard hardcoding only 5 names (`anthropic|openai|gemini|antigravity|ollama`), so `/agents <role> provider groq` (and every other OpenAI-compat provider — deepseek, openrouter, mistral, …) was rejected as invalid usage; it now validates against the canonical `PROVIDER_NAMES` registry. (2) Live discovery only returns ids for a logged-in, reachable provider, and the catalog backfill applied only to OAuth-source providers — so an unconfigured API-key provider showed an EMPTY model list and silently pinned a bare default. (3) The 24 OpenAI-compat providers carry no capability-catalog rows, so even the catalog fallback was empty for them. The new `providerPickEntries` helper now climbs live ids → static catalog → the provider's known default model, so the list is never empty, and the source is labeled (`Live`/`Catalog … log in to list live models`). Verified end-to-end in a real tmux session (`#1 groq/llama-3.3-70b-versatile` listed and pinned). Covered by `test/provider-pick-entries.test.ts` and a new `isProviderName` regression test in `test/launch-flags.test.ts`.
17
+
18
+ ### Verified
19
+ - **`jeo --tmux` session profile confirmed against the real `tmux` binary.** The gjc-parity profile (`mouse on`, `@jeo-profile`/`@jeo-branch`/`@jeo-project` markers, `set-clipboard on`, copy-mode `mode-style`) was exercised on an isolated `-L` socket using the exact `=name:` target syntax the launch code emits — every option set and read back correctly. `test/tmux.test.ts` passes 12/0 alongside the full 1645/0 suite.
20
+
9
21
  ## [0.6.26] - 2026-06-19
10
22
  _The forge emblem is redrawn again as the mascot crayfish, foregrounding its signature pincer claws (집게)._
11
23
 
package/README.ja.md CHANGED
@@ -204,11 +204,11 @@ CI は `.github/workflows/npm-publish.yml` で公開します — GitHub リリ
204
204
  ## 変更履歴 (Changelog)
205
205
 
206
206
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
207
+ - **[0.6.27]** (2026-06-19) — Ponytail pass on the reasoning-tier mapper, plus a real-tmux verification of `jeo --tmux`.
207
208
  - **[0.6.26]** (2026-06-19) — The forge emblem is redrawn again as the mascot crayfish, foregrounding its signature pincer claws (집게).
208
209
  - **[0.6.25]** (2026-06-19) — Reasoning works at every thinking level (gajae parity), and the forge emblem is redrawn as the neon-lens coding wizard.
209
210
  - **[0.6.24]** (2026-06-19) — `/provider` opens an interactive onboarding selector (OAuth vs API-compatible), and OpenAI-compatible backends gain per-vendor native-reasoning formats.
210
211
  - **[0.6.23]** (2026-06-19) — Live reasoning/thinking streams in the TUI across every provider, three new OpenAI-compatible backends (LM Studio, xAI, Kimi) join the auth/discovery/catalog surface, and Gemini gains native function-calling.
211
- - **[0.6.22]** (2026-06-18) — Extended-thinking activation is now consistent across providers: a `low` session thinking level enables reasoning everywhere.
212
212
 
213
213
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
214
214
  <!-- CHANGELOG:END -->
package/README.ko.md CHANGED
@@ -204,11 +204,11 @@ CI는 `.github/workflows/npm-publish.yml`로 배포합니다 — GitHub 릴리
204
204
  ## 변경 이력 (Changelog)
205
205
 
206
206
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
207
+ - **[0.6.27]** (2026-06-19) — Ponytail pass on the reasoning-tier mapper, plus a real-tmux verification of `jeo --tmux`.
207
208
  - **[0.6.26]** (2026-06-19) — The forge emblem is redrawn again as the mascot crayfish, foregrounding its signature pincer claws (집게).
208
209
  - **[0.6.25]** (2026-06-19) — Reasoning works at every thinking level (gajae parity), and the forge emblem is redrawn as the neon-lens coding wizard.
209
210
  - **[0.6.24]** (2026-06-19) — `/provider` opens an interactive onboarding selector (OAuth vs API-compatible), and OpenAI-compatible backends gain per-vendor native-reasoning formats.
210
211
  - **[0.6.23]** (2026-06-19) — Live reasoning/thinking streams in the TUI across every provider, three new OpenAI-compatible backends (LM Studio, xAI, Kimi) join the auth/discovery/catalog surface, and Gemini gains native function-calling.
211
- - **[0.6.22]** (2026-06-18) — Extended-thinking activation is now consistent across providers: a `low` session thinking level enables reasoning everywhere.
212
212
 
213
213
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
214
214
  <!-- CHANGELOG:END -->
package/README.md CHANGED
@@ -204,11 +204,11 @@ Required npm token permissions (repository secret `NPM_TOKEN`):
204
204
  ## Changelog
205
205
 
206
206
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
207
+ - **[0.6.27]** (2026-06-19) — Ponytail pass on the reasoning-tier mapper, plus a real-tmux verification of `jeo --tmux`.
207
208
  - **[0.6.26]** (2026-06-19) — The forge emblem is redrawn again as the mascot crayfish, foregrounding its signature pincer claws (집게).
208
209
  - **[0.6.25]** (2026-06-19) — Reasoning works at every thinking level (gajae parity), and the forge emblem is redrawn as the neon-lens coding wizard.
209
210
  - **[0.6.24]** (2026-06-19) — `/provider` opens an interactive onboarding selector (OAuth vs API-compatible), and OpenAI-compatible backends gain per-vendor native-reasoning formats.
210
211
  - **[0.6.23]** (2026-06-19) — Live reasoning/thinking streams in the TUI across every provider, three new OpenAI-compatible backends (LM Studio, xAI, Kimi) join the auth/discovery/catalog surface, and Gemini gains native function-calling.
211
- - **[0.6.22]** (2026-06-18) — Extended-thinking activation is now consistent across providers: a `low` session thinking level enables reasoning everywhere.
212
212
 
213
213
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
214
214
  <!-- CHANGELOG:END -->
package/README.zh.md CHANGED
@@ -204,11 +204,11 @@ CI 通过 `.github/workflows/npm-publish.yml` 发布 — GitHub 发布 release
204
204
  ## 更新日志 (Changelog)
205
205
 
206
206
  <!-- CHANGELOG:START (auto-generated from CHANGELOG.md — run `bun run changelog:sync`) -->
207
+ - **[0.6.27]** (2026-06-19) — Ponytail pass on the reasoning-tier mapper, plus a real-tmux verification of `jeo --tmux`.
207
208
  - **[0.6.26]** (2026-06-19) — The forge emblem is redrawn again as the mascot crayfish, foregrounding its signature pincer claws (집게).
208
209
  - **[0.6.25]** (2026-06-19) — Reasoning works at every thinking level (gajae parity), and the forge emblem is redrawn as the neon-lens coding wizard.
209
210
  - **[0.6.24]** (2026-06-19) — `/provider` opens an interactive onboarding selector (OAuth vs API-compatible), and OpenAI-compatible backends gain per-vendor native-reasoning formats.
210
211
  - **[0.6.23]** (2026-06-19) — Live reasoning/thinking streams in the TUI across every provider, three new OpenAI-compatible backends (LM Studio, xAI, Kimi) join the auth/discovery/catalog surface, and Gemini gains native function-calling.
211
- - **[0.6.22]** (2026-06-18) — Extended-thinking activation is now consistent across providers: a `low` session thinking level enables reasoning everywhere.
212
212
 
213
213
  See [CHANGELOG.md](CHANGELOG.md) for the full history.
214
214
  <!-- CHANGELOG:END -->
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jeo-code",
3
- "version": "0.6.26",
3
+ "version": "0.6.27",
4
4
  "description": "Clean, highly optimized AI coding agent using spec-first loop",
5
5
  "type": "module",
6
6
  "main": "src/cli.ts",
@@ -100,18 +100,15 @@ export function thinkingMaxTokens(level?: "minimal" | "low" | "medium" | "high"
100
100
  return 16000;
101
101
  }
102
102
 
103
- /** Map the thinking level to an OpenAI reasoning-effort tier. `minimal` is preserved as a
104
- * genuine (lightest) reasoning effort NOT collapsed to `low` so reasoning works at EVERY
105
- * thinking level (gajae parity: Minimal is a real effort). Only an unset level returns undefined
106
- * (reasoning off). `xhigh` maps to `high`, the deepest tier the provider APIs accept. */
103
+ /** Map the thinking level to an OpenAI reasoning-effort tier. minimal/low/medium/high pass
104
+ * through unchanged and xhigh folds to high (the deepest tier the provider APIs accept), so
105
+ * reasoning works at EVERY thinking level (gajae parity: minimal is a real effort). Only an
106
+ * unset level returns undefined (reasoning off the explicit /fast path). */
107
107
  export function thinkingToReasoningEffort(
108
108
  level?: "minimal" | "low" | "medium" | "high" | "xhigh",
109
109
  ): "minimal" | "low" | "medium" | "high" | undefined {
110
110
  if (!level) return undefined;
111
- if (level === "minimal") return "minimal";
112
- if (level === "low") return "low";
113
- if (level === "high" || level === "xhigh") return "high";
114
- return "medium";
111
+ return level === "xhigh" ? "high" : level;
115
112
  }
116
113
 
117
114
  /** Describe a model id: alias expansion + the provider it routes to. For `/model` + diagnostics.
@@ -1,6 +1,6 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
- import { type ProviderName, type ModelRole, type ThinkLevel, catalogMetadata } from "../../ai";
3
+ import { type ProviderName, type ModelRole, type ThinkLevel, catalogMetadata, PROVIDER_NAMES } from "../../ai";
4
4
 
5
5
  export interface LaunchFlags {
6
6
  list: boolean;
@@ -39,7 +39,10 @@ function takeValue(args: string[], index: number, inlinePrefix: string): { value
39
39
  }
40
40
 
41
41
  export function isProviderName(input: string | undefined): input is ProviderName {
42
- return input === "anthropic" || input === "openai" || input === "gemini" || input === "antigravity" || input === "ollama";
42
+ // Validate against the canonical registry, not a hand-maintained subset the
43
+ // old 5-name list silently rejected every OpenAI-compat provider (groq,
44
+ // deepseek, openrouter, …) at `/agents <role> provider <name>`.
45
+ return input !== undefined && (PROVIDER_NAMES as readonly string[]).includes(input);
43
46
  }
44
47
 
45
48
  export function isThinkingLevel(input: string | undefined): input is ThinkLevel {
@@ -36,7 +36,7 @@ import { callLlm, type Message } from "../agent/loop";
36
36
  import { friendlyProviderError } from "../util/provider-error";
37
37
  import { readGlobalConfig, saveConfigPatch } from "../agent/state";
38
38
  import { rememberModelPatch, recentModelsForDisplay } from "../agent/model-recency";
39
- import { describeModel, describeAllProviders, thinkingMaxTokens, thinkingToReasoningEffort, discoverModels, flattenModels, resolveSelection, catalogMetadata, resolveRoleModel, CODEX_MODELS, qualifyModelId } from "../ai";
39
+ import { describeModel, describeAllProviders, thinkingMaxTokens, thinkingToReasoningEffort, discoverModels, flattenModels, resolveSelection, catalogMetadata, catalogByProvider, resolveRoleModel, CODEX_MODELS, qualifyModelId } from "../ai";
40
40
  import type { ProviderModelsResult, PickEntry, ProviderName, ModelRole, ThinkLevel } from "../ai";
41
41
  import { readGoalState, writeGoalState, clearGoalState, verifyGoal } from "../agent/goal-verifier";
42
42
 
@@ -232,6 +232,29 @@ function providerDefaultModel(p: ProviderName): string {
232
232
  return openaiCompatDef(p)?.defaultModel ?? STATIC_PROVIDER_DEFAULT[p] ?? "";
233
233
  }
234
234
 
235
+ /**
236
+ * Pick-list entries for ONE provider, with static fallbacks so the list is never
237
+ * empty.
238
+ *
239
+ * Live discovery yields ids only for a logged-in, reachable provider, and
240
+ * `catalogOr` backfills the static catalog ONLY for OAuth sources — so an
241
+ * API-key/keyless provider that isn't configured yet had an empty list and was
242
+ * silently pinned to a bare default. Prefer live ids; else the provider's
243
+ * capability catalog; else its single known default model (all 24 OpenAI-compat
244
+ * providers carry one) so the user always sees at least one pickable id.
245
+ */
246
+ export function providerPickEntries(live: ProviderModelsResult[], want: ProviderName): PickEntry[] {
247
+ const fromLive = flattenModels(live.filter(r => r.provider === want));
248
+ if (fromLive.length) return fromLive;
249
+ const catalog = catalogByProvider(want);
250
+ if (catalog.length) {
251
+ return catalog.map((m, i) => ({ index: i + 1, provider: want, model: qualifyModelId(m.providerModel, want) }));
252
+ }
253
+ const fallback = providerDefaultModel(want);
254
+ return fallback ? [{ index: 1, provider: want, model: qualifyModelId(fallback, want) }] : [];
255
+ }
256
+
257
+
235
258
 
236
259
  export function formatResumeHint(sessionId: string): string {
237
260
  return `Resume with: jeo launch --resume ${sessionId}`;
@@ -3462,7 +3485,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
3462
3485
  if (modelArg?.toLowerCase() === "provider") {
3463
3486
  const want = (tokens[2] ?? "").toLowerCase();
3464
3487
  if (!isProviderName(want)) {
3465
- console.log(`Usage: /agents ${role.id} provider <anthropic|openai|gemini|antigravity|ollama> [model|#N]`);
3488
+ console.log(`Usage: /agents ${role.id} provider <name> [model|#N] — e.g. anthropic, openai, gemini, groq, deepseek, openrouter (any configured provider)`);
3466
3489
  continue;
3467
3490
  }
3468
3491
  const st = (await describeAllProviders()).find(s => s.name === want);
@@ -3475,7 +3498,8 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
3475
3498
  }
3476
3499
  }
3477
3500
  const live = await getLiveModels();
3478
- const forProvider = flattenModels(live.filter(r => r.provider === want));
3501
+ const forProvider = providerPickEntries(live, want);
3502
+ const liveForProvider = live.some(r => r.ok && r.provider === want && r.models.length > 0);
3479
3503
  const explicit = tokens[3];
3480
3504
  let chosenModel: string;
3481
3505
  if (explicit && forProvider.length) {
@@ -3494,7 +3518,7 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
3494
3518
  } else if (explicit) {
3495
3519
  chosenModel = qualifyModelId(explicit, want);
3496
3520
  } else if (forProvider.length) {
3497
- // No model given → the provider's first live model, provider-qualified.
3521
+ // No model given → the provider's first known model, provider-qualified.
3498
3522
  chosenModel = qualifyModelId(forProvider[0]!.model, want);
3499
3523
  } else {
3500
3524
  chosenModel = providerDefaultModel(want);
@@ -3503,7 +3527,9 @@ export async function runLaunchCommand(args: string[]): Promise<void> {
3503
3527
  console.log(`${role.title} pinned to ${want} via model ${chosenModel} — saved to ~/.jeo/config.json`);
3504
3528
  if (forProvider.length) {
3505
3529
  lastPickIndex = forProvider;
3506
- console.log(`Live ${want} models refine with /agents ${role.id} #N:`);
3530
+ const sourceNote = liveForProvider ? "Live" : "Catalog";
3531
+ const tail = liveForProvider ? "" : " (log in to list live models)";
3532
+ console.log(`${sourceNote} ${want} models — refine with /agents ${role.id} #N:${tail}`);
3507
3533
  for (const line of formatPickListWithCapabilities(lastPickIndex, { current: chosenModel, cap: 12 })) console.log(line);
3508
3534
  }
3509
3535
  continue;
@@ -412,52 +412,48 @@ export async function animateFrames(stage: AsciiStage, opts: AnimateFramesOption
412
412
  }
413
413
  return total;
414
414
  }
415
- /** The compact jeo forge mark: a clean, wordless pictograph of the mascot — the
416
- * neon crayfish (가재) from assets/character.png, read for its signature feature:
417
- * the two raised pincer CLAWS (집게). Read top→bottom: the open pincer jaws
418
- * (◣◣ / ◢◢) reaching up, the claw arms (◆══╲ ╱══◆) angling into the body where
419
- * the blue knuckles meet, the head with its glowing eye/terminal cluster
420
- * (◉◉◉), then the rounded carapace/tail (╲▔▔╱). The mark is purely symbolic —
421
- * NO embedded lettering (the brand wordmark lives in the welcome header, not
422
- * under the emblem); the JEO identity is carried by the crayfish-claw silhouette
423
- * alone. Width-1 glyphs only (box drawing + geometrics) so padding/centering
424
- * math stays exact. Frame 0 is the static symbol. */
415
+ /** The compact jeo forge mark: a horizontal lobster/crayfish (가재) emblem composed
416
+ * of SIMPLE, DISCONNECTED shapes (no connecting strokes) that together read as the
417
+ * mascot lying on its side, left→right: the raised pincer CLAWS (집게) as the splayed
418
+ * corner wedges (◤◣ left, ◥◢ right), the body carrying the JEO wordmark (J E O), and
419
+ * a DNA double-helix woven above and below as a row of crossing nodes (╳ = base-pair
420
+ * crossings). gjc-forge aesthetic: clean negative space, geometric symmetry, the
421
+ * blue→violet→pink flow gradient applied by renderForgeMark doing the neon glow. The
422
+ * lobster identity is carried by the disconnected silhouette; the JEO typography is
423
+ * the deliberate lettermark at the core. Width-1 glyphs only (box drawing + geometrics)
424
+ * so padding/centering math stays exact. Frame 0 is the static symbol. */
425
425
  export const FORGE_MARK_ART: string[] = [
426
- " ◣◣ ◢◢ ",
427
- " ◆══╲ ╱══◆ ",
428
- " ╲◉◉◉╱ ",
429
- " ╲▔▔╱ "
426
+ " ╳ ╳ ╳ ╳ ◥",
427
+ "❮ J E O ▮",
428
+ "◣ ╳ ╳ ╳ ╳ ◢"
430
429
  ];
431
430
 
432
431
  export const FORGE_MARK_ART_ASCII: string[] = [
433
- " // \\\\ ",
434
- " o==\\ /==o ",
435
- " \\ooo/ ",
436
- " \\__/ "
432
+ "/ x x x x \\",
433
+ "< J E O |",
434
+ "\\ x x x x /"
437
435
  ];
438
436
 
439
- /** Claw-snap blink frames for the compact crayfish forge mark: the arms, head and
440
- * carapace stay fixed while the two pincer jaws SNAP shut (◣◣/◢◢ open ◢◣/◢◣
441
- * closed), so the crayfish "clicks" its claws. Frame 0 === FORGE_MARK_ART, so a
442
- * frameless render is byte-identical to the static symbol. All lines share the
443
- * same width and width-1 glyphs. */
437
+ /** Claw-snap blink frames for the compact lobster forge mark: the helix nodes, the
438
+ * JEO body and the inner claw/tail glyphs stay fixed while the four splayed pincer
439
+ * CORNERS snap (◤◣ / ◥◢ open → ◢◥ / ◣◤ closed), so the lobster "clicks" its claws.
440
+ * Frame 0 === FORGE_MARK_ART, so a frameless render is byte-identical to the static
441
+ * symbol. All lines share the same width and width-1 glyphs. */
444
442
  export const FORGE_MARK_FRAMES: string[][] = [
445
443
  FORGE_MARK_ART,
446
444
  [
447
- " ◢◣ ◢◣ ",
448
- " ◆══╲ ╱══◆ ",
449
- " ╲◉◉◉╱ ",
450
- " ╲▔▔╱ "
445
+ "◢ ╳ ╳ ╳ ╳ ◣",
446
+ "❮ J E O ▮",
447
+ "◥ ╳ ╳ ╳ ╳ ◤"
451
448
  ]
452
449
  ];
453
450
 
454
451
  export const FORGE_MARK_FRAMES_ASCII: string[][] = [
455
452
  FORGE_MARK_ART_ASCII,
456
453
  [
457
- " >< >< ",
458
- " o==\\ /==o ",
459
- " \\ooo/ ",
460
- " \\__/ "
454
+ "\\ x x x x /",
455
+ "< J E O |",
456
+ "/ x x x x \\"
461
457
  ]
462
458
  ];
463
459
 
@@ -466,24 +462,25 @@ export function forgeMarkFrameCount(): number {
466
462
  return FORGE_MARK_FRAMES.length;
467
463
  }
468
464
 
469
- /** Grand hero variant for the welcome forge box (gjc-style spacious banner): the
470
- * same mascot crayfish rendered large and wordless — the two raised pincer claws
471
- * (◣◣ / ◢◢, the 집게 feature) on wide arms (◆══╲ ╱══◆), the head with its glowing
472
- * eye/terminal cluster (◉ ◉), and the broad rounded carapace/tail (╲▔▔▔▔▔╱).
473
- * Purely symbolic NO embedded lettering or caption. Width-1 glyphs only so
465
+ /** Grand hero variant for the welcome forge box (gjc-style spacious banner): the same
466
+ * horizontal lobster emblem rendered large — the splayed pincer claws as corner wedges
467
+ * (◤◣ left, ◥◢ right), the JEO wordmark spaced across the body (J E O), and the DNA
468
+ * double-helix woven above and below as a wider row of crossing nodes (). gjc-forge
469
+ * aesthetic: generous negative space + geometric symmetry, with renderForgeMark's
470
+ * blue→violet→pink flow gradient supplying the neon glow. The JEO typography is the
471
+ * deliberate lettermark; the lobster reads from the disconnected silhouette. Width 29
472
+ * (matches the welcome compact↔grand threshold) and width-1 glyphs only so
474
473
  * padding/centering math stays exact. */
475
474
  export const FORGE_MARK_ART_GRAND: string[] = [
476
- " ◣◣ ◢◢ ",
477
- " ◆══╲ ╱══◆ ",
478
- " ╲ ◉ ◉ ╱ ",
479
- " ╲▔▔▔▔▔▔▔▔▔╱ "
475
+ " ╳ ╳ ╳ ╳ ╳ ╳ ◥",
476
+ "❮ J E O ▮",
477
+ " ╳ ╳ ╳ ╳ ◢"
480
478
  ];
481
479
 
482
480
  export const FORGE_MARK_ART_GRAND_ASCII: string[] = [
483
- " // \\\\ ",
484
- " o==\\ /==o ",
485
- " \\ o o o / ",
486
- " \\_________/ "
481
+ "/ x x x x x x \\",
482
+ "< J E O |",
483
+ "\\ x x x x x x /"
487
484
  ];
488
485
 
489
486