open-multi-agent-kit 0.78.8 → 0.79.3

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.
Files changed (162) hide show
  1. package/AGENTS.md +131 -2
  2. package/CHANGELOG.md +32 -0
  3. package/CLAUDE.md +105 -3
  4. package/DESIGN.md +141 -117
  5. package/MATURITY.md +7 -12
  6. package/README.md +66 -347
  7. package/ROADMAP.md +33 -47
  8. package/dist/adapters/kimi/runner.js +2 -2
  9. package/dist/brand/theme.js +2 -2
  10. package/dist/cli/register-basic-commands.js +3 -2
  11. package/dist/cli/register-mcp-dag-cron-screenshot-commands.js +1 -1
  12. package/dist/cli/root.d.ts +1 -0
  13. package/dist/cli/root.js +2 -0
  14. package/dist/cli/runtime/command-bus.js +14 -1
  15. package/dist/cli/theme/terminal-capability.js +1 -1
  16. package/dist/cli/theme/theme-registry.js +90 -2
  17. package/dist/cli/ui/event.d.ts +1 -0
  18. package/dist/cli/ui/neon-grid-renderer.d.ts +8 -0
  19. package/dist/cli/ui/neon-grid-renderer.js +84 -14
  20. package/dist/cli/ui/plain-renderer.js +3 -1
  21. package/dist/cli/ui/system24-renderer.js +3 -1
  22. package/dist/cli/v2/chat-repl.d.ts +2 -1
  23. package/dist/cli/v2/chat-repl.js +58 -2
  24. package/dist/cli/v2/cli-v2-skeleton.js +7 -4
  25. package/dist/cli/v2/interactive-prompt.js +4 -3
  26. package/dist/commands/auth.js +2 -0
  27. package/dist/commands/chat/native-root-loop.d.ts +1 -0
  28. package/dist/commands/chat/native-root-loop.js +162 -13
  29. package/dist/commands/chat/runtime.js +9 -0
  30. package/dist/commands/chat/slash/commands/routing.js +31 -1
  31. package/dist/commands/chat/utils.js +3 -1
  32. package/dist/commands/cockpit/render.js +2 -1
  33. package/dist/commands/doctor/checks.js +2 -2
  34. package/dist/commands/init/core.js +2 -2
  35. package/dist/commands/init.js +2 -2
  36. package/dist/commands/mcp/config.js +38 -16
  37. package/dist/commands/mcp/doctor.js +7 -8
  38. package/dist/commands/mcp/list.js +4 -3
  39. package/dist/commands/mcp/shared.d.ts +4 -1
  40. package/dist/commands/mcp/shared.js +21 -3
  41. package/dist/commands/mcp/test.js +13 -12
  42. package/dist/commands/model.js +4 -0
  43. package/dist/commands/provider.js +5 -1
  44. package/dist/commands/version.d.ts +1 -0
  45. package/dist/commands/version.js +18 -0
  46. package/dist/contracts/command.d.ts +25 -0
  47. package/dist/contracts/command.js +1 -0
  48. package/dist/contracts/evidence-bundle.d.ts +52 -0
  49. package/dist/contracts/evidence-bundle.js +1 -0
  50. package/dist/contracts/index.d.ts +2 -0
  51. package/dist/contracts/index.js +2 -0
  52. package/dist/contracts/web-bridge.d.ts +1 -1
  53. package/dist/contracts/web-bridge.js +1 -1
  54. package/dist/evidence/bundle-validator.d.ts +7 -0
  55. package/dist/evidence/bundle-validator.js +103 -0
  56. package/dist/evidence/index.d.ts +5 -0
  57. package/dist/evidence/index.js +2 -0
  58. package/dist/evidence/proof-trust.js +2 -10
  59. package/dist/evidence/trust-calibration.d.ts +25 -0
  60. package/dist/evidence/trust-calibration.js +151 -0
  61. package/dist/mcp/omk-project-server.js +1 -1
  62. package/dist/metrics/provenance-ratio.d.ts +16 -0
  63. package/dist/metrics/provenance-ratio.js +33 -0
  64. package/dist/orchestration/dag.d.ts +17 -0
  65. package/dist/orchestration/ensemble-decision.d.ts +2 -0
  66. package/dist/orchestration/ensemble-decision.js +4 -0
  67. package/dist/orchestration/hedge-persona-weights.d.ts +25 -0
  68. package/dist/orchestration/hedge-persona-weights.js +43 -0
  69. package/dist/orchestration/parallel-ui.d.ts +1 -0
  70. package/dist/orchestration/parallel-ui.js +12 -3
  71. package/dist/orchestration/route-calibration.d.ts +27 -0
  72. package/dist/orchestration/route-calibration.js +123 -0
  73. package/dist/orchestration/routing/mcp-config.js +5 -1
  74. package/dist/orchestration/routing/types.d.ts +21 -0
  75. package/dist/orchestration/routing.d.ts +10 -0
  76. package/dist/orchestration/routing.js +89 -3
  77. package/dist/providers/model-registry.d.ts +2 -1
  78. package/dist/providers/model-registry.js +76 -4
  79. package/dist/providers/provider-runtime.js +9 -1
  80. package/dist/providers/thinking-levels.js +2 -0
  81. package/dist/providers/types.d.ts +1 -1
  82. package/dist/runtime/glm-api-runtime.d.ts +4 -0
  83. package/dist/runtime/glm-api-runtime.js +15 -0
  84. package/dist/runtime/index.d.ts +1 -0
  85. package/dist/runtime/index.js +1 -0
  86. package/dist/runtime/kimi-api-runtime.d.ts +6 -1
  87. package/dist/runtime/kimi-api-runtime.js +10 -4
  88. package/dist/runtime/recovery-artifact-store.d.ts +39 -0
  89. package/dist/runtime/recovery-artifact-store.js +111 -0
  90. package/dist/runtime/runtime-backed-task-runner.js +10 -0
  91. package/dist/runtime/runtime-bootstrap.js +17 -1
  92. package/dist/runtime/slash-commands.js +38 -5
  93. package/dist/schema/command.schema.d.ts +121 -0
  94. package/dist/schema/command.schema.js +25 -0
  95. package/dist/schema/envelope.schema.d.ts +2 -2
  96. package/dist/schema/evidence-bundle.schema.d.ts +196 -0
  97. package/dist/schema/evidence-bundle.schema.js +38 -0
  98. package/dist/schema/index.d.ts +2 -0
  99. package/dist/schema/index.js +2 -0
  100. package/dist/schema/proof-bundle.schema.d.ts +2 -2
  101. package/dist/theme/external-theme-adapters.d.ts +27 -0
  102. package/dist/theme/external-theme-adapters.js +52 -0
  103. package/dist/theme/library-effects.d.ts +12 -0
  104. package/dist/theme/library-effects.js +47 -0
  105. package/dist/theme/parallel.js +3 -2
  106. package/dist/tui/terminal-frame-renderer.d.ts +9 -1
  107. package/dist/tui/terminal-frame-renderer.js +77 -7
  108. package/dist/util/chat-cockpit.d.ts +3 -1
  109. package/dist/util/chat-cockpit.js +44 -5
  110. package/dist/util/command-envelope.d.ts +19 -0
  111. package/dist/util/command-envelope.js +31 -0
  112. package/dist/util/events-logger.d.ts +1 -1
  113. package/dist/util/events-logger.js +23 -16
  114. package/dist/util/fs/mcp-diagnose.js +2 -1
  115. package/dist/util/fs/mcp-runtime-config.js +14 -2
  116. package/dist/util/i18n.js +2 -2
  117. package/dist/util/screenshot-store.d.ts +7 -0
  118. package/dist/util/screenshot-store.js +30 -0
  119. package/dist/util/terminal-kit-bridge.d.ts +24 -0
  120. package/dist/util/terminal-kit-bridge.js +60 -0
  121. package/dist/util/terminal-owner.d.ts +1 -0
  122. package/dist/util/terminal-owner.js +31 -0
  123. package/dist/version.d.ts +3 -1
  124. package/dist/version.js +4 -0
  125. package/docs/2026-05-18-project-direction.md +1 -1
  126. package/docs/2026-06-14/critical-issues.md +19 -0
  127. package/docs/2026-06-14/improvements.md +15 -0
  128. package/docs/2026-06-14/init-checklist.md +25 -0
  129. package/docs/2026-06-14/plan.md +20 -0
  130. package/docs/2026-06-15/critical-issues.md +19 -0
  131. package/docs/2026-06-15/improvements.md +15 -0
  132. package/docs/2026-06-15/init-checklist.md +25 -0
  133. package/docs/2026-06-15/plan.md +20 -0
  134. package/docs/claims.md +85 -0
  135. package/docs/comparisons/omk-vs-claude-code.md +31 -0
  136. package/docs/comparisons/omk-vs-codex.md +33 -0
  137. package/docs/comparisons/omk-vs-mcp-agent.md +33 -0
  138. package/docs/comparisons/omk-vs-opencode.md +32 -0
  139. package/docs/decision-layer-calibration.md +20 -0
  140. package/docs/geo-eval-prompts.md +53 -0
  141. package/docs/getting-started.md +3 -31
  142. package/docs/headroom-omk-setup-guide.md +2 -2
  143. package/docs/integrations/adaptorch.md +46 -0
  144. package/docs/native-root-runtime-algorithms.md +2 -303
  145. package/docs/native-root-runtime-hardening.md +1 -1
  146. package/docs/provider-maturity.md +1 -1
  147. package/docs/use-cases/evidence-gated-coding-agents.md +52 -0
  148. package/docs/use-cases/no-kimi-mode.md +38 -0
  149. package/docs/use-cases/provider-routing-for-ai-coding.md +36 -0
  150. package/docs/use-cases/replayable-agent-runs.md +40 -0
  151. package/docs/use-cases/scoped-mcp-for-coding-agents.md +34 -0
  152. package/docs/versioning.md +9 -3
  153. package/docs/what-is-omk.md +64 -0
  154. package/llms.txt +28 -18
  155. package/package.json +7 -2
  156. package/readmeasset/.npmignore +3 -0
  157. package/readmeasset/ASSET_INDEX.md +3 -0
  158. package/readmeasset/ASSET_PROVENANCE.md +6 -0
  159. package/readmeasset/omk-adaptorch-ouroboros-supermemory.svg +95 -0
  160. package/readmeasset/omk-init-control-loop.svg +118 -0
  161. package/readmeasset/omk-parallel-subagents.svg +97 -0
  162. package/templates/web-bridge/chrome-extension/background.js +1 -1
@@ -30,6 +30,7 @@ export function registerBasicCommands(program) {
30
30
  .command("version")
31
31
  .description("Print OMK version, runtime, contract, and schema status")
32
32
  .option("--json", "Output version report as a JSON envelope")
33
+ .option("--command-envelope", "With --json, emit the automation-facing omk.command.v1 envelope")
33
34
  .action(async (options) => {
34
35
  const { versionCommand } = await import("../commands/version.js");
35
36
  await versionCommand(options);
@@ -70,13 +71,13 @@ export function registerBasicCommands(program) {
70
71
  }
71
72
  if (actionMode === "omk") {
72
73
  if (!options.yes) {
73
- console.log("Upgrade omk via: npm i -g @omk/cli");
74
+ console.log("Upgrade omk via: npm i -g open-multi-agent-kit");
74
75
  console.log("Press Enter to continue or Ctrl+C to cancel...");
75
76
  const rl = (await import("readline")).createInterface({ input: process.stdin, output: process.stdout });
76
77
  await new Promise((resolve) => rl.question("", () => { rl.close(); resolve(); }));
77
78
  }
78
79
  const { runShell } = await import("../util/shell.js");
79
- const result = await runShell("npm", ["i", "-g", "@omk/cli"], { stdio: "inherit", timeout: 120_000 });
80
+ const result = await runShell("npm", ["i", "-g", "open-multi-agent-kit"], { stdio: "inherit", timeout: 120_000 });
80
81
  process.exit(result.failed ? (result.exitCode ?? 1) : 0);
81
82
  }
82
83
  if (isKimiAdapterAction) {
@@ -105,7 +105,7 @@ export function registerMcpDagCronScreenshotCommands(program) {
105
105
  });
106
106
  mcp
107
107
  .command("add <server>")
108
- .description("Copy an MCP server from global ~/.kimi/mcp.json into project .kimi/mcp.json")
108
+ .description("Copy an MCP server from trusted global config into project .kimi/mcp.json")
109
109
  .action(async (server) => {
110
110
  const { mcpAddCommand } = await import("../commands/mcp.js");
111
111
  await mcpAddCommand(server);
@@ -1,5 +1,6 @@
1
1
  import type { Command } from "commander";
2
2
  export declare const OMK_ROOT_ENTRY_SURFACE = "omk-root-orchestrator";
3
+ export declare const OMK_ROOT_ENTRY_DISPLAY_NAME = "omk";
3
4
  interface RootChatLaunchArgsOptions {
4
5
  readonly cliPath: string;
5
6
  readonly runId?: string;
package/dist/cli/root.js CHANGED
@@ -4,6 +4,7 @@ import { buildCustomHelp } from "../util/help-text.js";
4
4
  const OMK_ENTRY_BRAND = "neon-grid";
5
5
  const OMK_ENTRY_UI = "neon-grid";
6
6
  export const OMK_ROOT_ENTRY_SURFACE = "omk-root-orchestrator";
7
+ export const OMK_ROOT_ENTRY_DISPLAY_NAME = "omk";
7
8
  export function buildRootChatLaunchArgs(options) {
8
9
  const chatArgs = [
9
10
  options.cliPath,
@@ -92,6 +93,7 @@ export function configureRootProgram(program, OMK_VERSION, OMK_VERSION_FOOTER) {
92
93
  const childEnv = {
93
94
  ...process.env,
94
95
  OMK_ENTRY_SURFACE: OMK_ROOT_ENTRY_SURFACE,
96
+ OMK_ENTRY_DISPLAY_NAME: OMK_ROOT_ENTRY_DISPLAY_NAME,
95
97
  };
96
98
  const result = spawnSync(process.execPath, chatArgs, { stdio: "inherit", env: childEnv });
97
99
  if (result.status && result.status !== 0) {
@@ -120,7 +120,20 @@ registerSlashCommand({
120
120
  const model = envelope.config.env.OMK_PROVIDER_MODEL;
121
121
  const levels = thinkingLevelsFor(provider, model);
122
122
  const requested = args[0]?.toLowerCase();
123
- const level = !requested || requested === "next" || requested === "tab"
123
+ if (!requested) {
124
+ return {
125
+ handled: true,
126
+ output: [
127
+ "OMK Thinking Control · choose level",
128
+ `Target: ${provider}/${model ?? "auto"}`,
129
+ levels.map((level) => `${level === envelope.config.env.OMK_THINKING ? "●" : "○"} ${level}`).join(" "),
130
+ `Choose directly: ${levels.map((level) => `/think ${level}`).join(" | ")}`,
131
+ "Cycle only when explicit: /think next",
132
+ ].join("\n"),
133
+ exitCode: 0,
134
+ };
135
+ }
136
+ const level = requested === "next" || requested === "tab"
124
137
  ? nextThinkingLevel(envelope.config.env.OMK_THINKING, provider, model)
125
138
  : normalizeThinkingLevel(requested);
126
139
  if (!level || !levels.includes(level)) {
@@ -100,5 +100,5 @@ export function defaultThemeForCapability(cap) {
100
100
  if (cap.isCi) {
101
101
  return { name: "minimal", mode: "auto" };
102
102
  }
103
- return { name: "omk", mode: "dark" };
103
+ return { name: "omk-control-grid-dark", mode: "dark" };
104
104
  }
@@ -3,6 +3,7 @@
3
3
  * Maps semantic tokens to ANSI colors. Reuses src/theme/colors.ts exports.
4
4
  */
5
5
  import { style } from "../../theme/colors.js";
6
+ import { renderChalkAnimationKeyframe, renderInkGradientFallback, renderTerminalKitCapabilityBadge } from "../../theme/library-effects.js";
6
7
  import { compileTheme } from "./render-table.js";
7
8
  import { readFileSync } from "node:fs";
8
9
  import { join, dirname } from "node:path";
@@ -34,9 +35,9 @@ const omkPalette = {
34
35
  }
35
36
  },
36
37
  };
37
- // --- night-city (README control image / Night City Ops Console) ---
38
+ // --- omk-control-grid-dark (README omk_tui.png / Night City Ops Console) ---
38
39
  const nightCityPalette = {
39
- name: "night-city",
40
+ name: "omk-control-grid-dark",
40
41
  mode: "dark",
41
42
  supportsColor: true,
42
43
  render(semantic, text) {
@@ -142,6 +143,85 @@ const neonCircuitPalette = {
142
143
  }
143
144
  },
144
145
  };
146
+ // --- library-enhanced palettes (external theme adapters) ---
147
+ const chalkRainbowPalette = {
148
+ name: "chalk-rainbow",
149
+ mode: "dark",
150
+ supportsColor: true,
151
+ render(token, text) {
152
+ switch (token) {
153
+ case "success": return renderChalkAnimationKeyframe(text, "karaoke");
154
+ case "warning": return renderChalkAnimationKeyframe(text, "pulse");
155
+ case "error": return style.hotPink(text);
156
+ case "info": return renderChalkAnimationKeyframe(text, "radar");
157
+ case "agent": return renderChalkAnimationKeyframe(text, "rainbow");
158
+ case "task": return renderChalkAnimationKeyframe(text, "neon");
159
+ case "tool": return style.cyanBold(text);
160
+ case "header": return renderChalkAnimationKeyframe(text, "rainbow");
161
+ case "subheader": return renderChalkAnimationKeyframe(text, "neon");
162
+ case "dim": return style.dim + text + style.reset;
163
+ case "bold": return style.bold + text + style.reset;
164
+ case "reset": return style.reset + text;
165
+ case "separator": return renderChalkAnimationKeyframe(text, "radar");
166
+ case "bullet": return renderChalkAnimationKeyframe("▸ " + text, "rainbow");
167
+ case "labelKey": return style.gray(text);
168
+ case "labelValue": return renderChalkAnimationKeyframe(text, "karaoke");
169
+ default: return text;
170
+ }
171
+ },
172
+ };
173
+ const inkGradientPalette = {
174
+ name: "ink-gradient",
175
+ mode: "dark",
176
+ supportsColor: true,
177
+ render(token, text) {
178
+ switch (token) {
179
+ case "success": return renderInkGradientFallback(text, "summer");
180
+ case "warning": return renderInkGradientFallback(text, "morning");
181
+ case "error": return renderInkGradientFallback(text, "passion");
182
+ case "info": return renderInkGradientFallback(text, "cristal");
183
+ case "agent": return renderInkGradientFallback(text, "atlas");
184
+ case "task": return renderInkGradientFallback(text, "fruit");
185
+ case "tool": return renderInkGradientFallback(text, "teen");
186
+ case "header": return renderInkGradientFallback(text, "pastel");
187
+ case "subheader": return renderInkGradientFallback(text, "instagram");
188
+ case "dim": return style.dim + text + style.reset;
189
+ case "bold": return style.bold + text + style.reset;
190
+ case "reset": return style.reset + text;
191
+ case "separator": return renderInkGradientFallback(text, "mind");
192
+ case "bullet": return renderInkGradientFallback("◆ " + text, "pastel");
193
+ case "labelKey": return style.gray(text);
194
+ case "labelValue": return renderInkGradientFallback(text, "retro");
195
+ default: return text;
196
+ }
197
+ },
198
+ };
199
+ const terminalKitPalette = {
200
+ name: "terminal-kit",
201
+ mode: "auto",
202
+ supportsColor: true,
203
+ render(token, text) {
204
+ switch (token) {
205
+ case "success": return style.greenBold(text);
206
+ case "warning": return style.amberBold(text);
207
+ case "error": return style.metricsRedBold(text);
208
+ case "info": return renderTerminalKitCapabilityBadge(text);
209
+ case "agent": return style.cyanBold(text);
210
+ case "task": return style.silver(text);
211
+ case "tool": return style.mintBold(text);
212
+ case "header": return renderTerminalKitCapabilityBadge(style.whiteBold(text));
213
+ case "subheader": return style.blueBold(text);
214
+ case "dim": return style.dim + text + style.reset;
215
+ case "bold": return style.bold + text + style.reset;
216
+ case "reset": return style.reset + text;
217
+ case "separator": return style.slate(text);
218
+ case "bullet": return style.cyan("▣ " + text);
219
+ case "labelKey": return style.gray(text);
220
+ case "labelValue": return style.cream(text);
221
+ default: return text;
222
+ }
223
+ },
224
+ };
145
225
  // --- rust-forge (oxidized forge console) ---
146
226
  // Compiled from themes/rust-forge.theme.json (omk.theme.v1).
147
227
  const rustForgeDoc = JSON.parse(readFileSync(join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "themes", "rust-forge.theme.json"), "utf8"));
@@ -279,6 +359,8 @@ const lightPalette = {
279
359
  };
280
360
  const registry = new Map([
281
361
  ["omk", omkPalette],
362
+ ["omk-control-grid-dark", nightCityPalette],
363
+ ["omk-control-grid", nightCityPalette],
282
364
  ["night-city", nightCityPalette],
283
365
  ["night-city-ops", nightCityPalette],
284
366
  ["omk-control", nightCityPalette],
@@ -297,6 +379,12 @@ const registry = new Map([
297
379
  ["forge", rustForgePalette],
298
380
  ["rust-native", rustForgePalette],
299
381
  ["neon-circuit", neonCircuitPalette],
382
+ ["chalk-rainbow", chalkRainbowPalette],
383
+ ["chalk-animation", chalkRainbowPalette],
384
+ ["ink-gradient", inkGradientPalette],
385
+ ["ink-pastel", inkGradientPalette],
386
+ ["terminal-kit", terminalKitPalette],
387
+ ["terminal", terminalKitPalette],
300
388
  ["minimal", minimalPalette],
301
389
  ["mono", monoPalette],
302
390
  ["dark", darkPalette],
@@ -7,6 +7,7 @@ export type CliUiEvent = {
7
7
  root?: string;
8
8
  cwd?: string;
9
9
  rootSource?: string;
10
+ brandLabel?: string;
10
11
  } | {
11
12
  type: "input:submitted";
12
13
  text: string;
@@ -6,6 +6,7 @@ export declare class NeonGridRenderer implements CliRenderer {
6
6
  private readonly err;
7
7
  private readonly noColor;
8
8
  private readonly motion;
9
+ private headerTimer;
9
10
  private started;
10
11
  constructor(streams?: System24RendererStreams);
11
12
  start(): void;
@@ -13,5 +14,12 @@ export declare class NeonGridRenderer implements CliRenderer {
13
14
  setThinkingSummary(summary: string | undefined): void;
14
15
  stop(): void;
15
16
  private neonGridHeaderRows;
17
+ private canAnimateHeader;
18
+ private shouldRenderScanline;
19
+ private currentFrame;
20
+ private startHeaderAnimation;
21
+ private stopHeaderAnimation;
22
+ private repaintNeonGridHeader;
16
23
  private renderNeonGridHeader;
24
+ private neonGridHeaderLines;
17
25
  }
@@ -3,8 +3,11 @@ import { System24Renderer } from "./system24-renderer.js";
3
3
  import { NEON_GRID_THEME, resolveTuiMotion, shouldUseAnsiColor } from "../../brand/theme.js";
4
4
  import { BRAND_HEX } from "../../brand/palette.js";
5
5
  import { renderOmkSparkleText } from "../../ui/omk-sigil.js";
6
+ import { renderChalkAnimationKeyframe, renderInkGradientFallback, renderTerminalKitCapabilityBadge } from "../../theme/library-effects.js";
6
7
  const ESC = "\x1b[";
7
8
  const RST = `${ESC}0m`;
9
+ const SAVE_CURSOR = "\x1b7";
10
+ const RESTORE_CURSOR = "\x1b8";
8
11
  function stripAnsi(value) {
9
12
  return value.replace(/\x1b\[[0-9;]*m/g, "");
10
13
  }
@@ -17,6 +20,12 @@ function line(stream, text, noColor) {
17
20
  function truncate(value, max) {
18
21
  return value.length > max ? `${value.slice(0, Math.max(0, max - 1))}…` : value;
19
22
  }
23
+ function normalizeEntryBrandLabel(value) {
24
+ const stripped = stripAnsi(value ?? "")
25
+ .replace(/[\x00-\x1F\x7F]/g, "")
26
+ .trim();
27
+ return stripped.length > 0 ? truncate(stripped, 20) : undefined;
28
+ }
20
29
  function center(text, width) {
21
30
  const pad = Math.max(0, Math.floor((width - visibleLength(text)) / 2));
22
31
  return `${" ".repeat(pad)}${text}`;
@@ -41,6 +50,7 @@ export class NeonGridRenderer {
41
50
  err;
42
51
  noColor;
43
52
  motion;
53
+ headerTimer = null;
44
54
  started = false;
45
55
  constructor(streams = {}) {
46
56
  this.err = streams.stderr ?? process.stderr;
@@ -60,6 +70,9 @@ export class NeonGridRenderer {
60
70
  if (event.type === "session:start") {
61
71
  this.base.setStickyHeaderPrefixRows(this.neonGridHeaderRows());
62
72
  this.renderNeonGridHeader(event);
73
+ this.base.emit(event);
74
+ this.startHeaderAnimation(event);
75
+ return;
63
76
  }
64
77
  this.base.emit(event);
65
78
  }
@@ -67,34 +80,91 @@ export class NeonGridRenderer {
67
80
  this.base.setThinkingSummary(summary);
68
81
  }
69
82
  stop() {
83
+ this.stopHeaderAnimation();
70
84
  this.base.stop();
71
85
  }
72
86
  neonGridHeaderRows() {
73
- const shouldRenderScanline = this.motion !== "off" && this.started && this.err.isTTY !== false;
74
- return (shouldRenderScanline ? 1 : 0) + 5;
87
+ return this.neonGridHeaderLines({
88
+ type: "session:start",
89
+ runId: "",
90
+ provider: "auto",
91
+ model: "auto",
92
+ }).length;
93
+ }
94
+ canAnimateHeader() {
95
+ const requested = process.env.OMK_EXPERIMENTAL_HEADER_REPAINT;
96
+ const enabled = /^(1|true|yes|on)$/i.test(requested ?? "");
97
+ return enabled && this.motion !== "off" && this.started && this.err.isTTY === true && !this.noColor;
98
+ }
99
+ shouldRenderScanline() {
100
+ return this.motion !== "off" && this.started && this.err.isTTY !== false;
101
+ }
102
+ currentFrame() {
103
+ return Math.floor(Date.now() / 80);
104
+ }
105
+ startHeaderAnimation(event) {
106
+ this.stopHeaderAnimation();
107
+ if (!this.canAnimateHeader())
108
+ return;
109
+ this.headerTimer = setInterval(() => this.repaintNeonGridHeader(event), 120);
110
+ this.headerTimer.unref?.();
111
+ }
112
+ stopHeaderAnimation() {
113
+ if (!this.headerTimer)
114
+ return;
115
+ clearInterval(this.headerTimer);
116
+ this.headerTimer = null;
117
+ }
118
+ repaintNeonGridHeader(event) {
119
+ if (!this.canAnimateHeader())
120
+ return;
121
+ const maxRows = Math.max(0, (this.err.rows ?? process.stderr.rows ?? Number.POSITIVE_INFINITY) - 1);
122
+ const rows = this.neonGridHeaderLines(event, this.currentFrame()).slice(0, maxRows);
123
+ const repaint = rows
124
+ .map((row, index) => `${ESC}${index + 1};1H${ESC}2K${row}`)
125
+ .join("");
126
+ this.err.write(SAVE_CURSOR + repaint + RESTORE_CURSOR);
75
127
  }
76
128
  renderNeonGridHeader(event) {
129
+ for (const row of this.neonGridHeaderLines(event, this.currentFrame())) {
130
+ line(this.err, row, this.noColor);
131
+ }
132
+ }
133
+ neonGridHeaderLines(event, frame = 0) {
77
134
  const width = Math.min(76, Math.max(40, this.err.columns ?? process.stderr.columns ?? 80) - 2);
78
135
  const dim = NEON_GRID_THEME.colors.muted;
79
136
  const run = event.runId ? `run#${event.runId.slice(0, 7)}` : "run#pending";
80
137
  const root = event.root ? truncate(event.root, Math.max(12, width - 16)) : "root:unknown";
81
- const shouldRenderScanline = this.motion !== "off" && this.started && this.err.isTTY !== false;
82
- const titleLine = center("◢█ OMK//CONTROL █◣", width);
138
+ const entryBrand = normalizeEntryBrandLabel(event.brandLabel);
139
+ const titleLine = center(entryBrand ? `◢█ ${entryBrand} █◣` : "◢█ OMK//CONTROL █◣", width);
140
+ const controlLine = center(entryBrand ? `${entryBrand}://CONTROL` : "OMK://CONTROL", width);
141
+ const coreLine = center("CYBERPUNK OPS CORE", width);
142
+ const gridLine = center("MATRIX RAIN // NEON GRID ONLINE", width);
143
+ const variantLine = center("NIGHT-CITY-MATRIX-V3", width);
83
144
  const mottoLine = center(truncate(NEON_GRID_THEME.motto, width), width);
145
+ const stateLine = truncate(`state: ● ready · route: route · evidence · loop · control`, Math.max(12, width - 18));
84
146
  const routeLine = truncate(`${NEON_GRID_THEME.symbols.signal} ROUTE ${run} · provider:${event.provider} · model:${event.model ?? "auto"}`, width);
85
- const statusLine = truncate(`▣ NEON metrics live · VERIFY armed · SCOPE MCP/skills/hooks · LOOP controlled`, width);
86
147
  const rootLine = truncate(`${NEON_GRID_THEME.symbols.pending} PENDING root ${root}`, width);
87
- if (shouldRenderScanline) {
88
- line(this.err, `${dim}${signalScanline(event.runId ?? "omk-control", width)}${RST}`, this.noColor);
148
+ const rows = [];
149
+ if (this.shouldRenderScanline()) {
150
+ const seed = `${event.runId ?? (entryBrand ? `${entryBrand}-control` : "omk-control")}:${frame}`;
151
+ rows.push(`${dim}${signalScanline(seed, width)}${RST}`);
89
152
  }
90
- line(this.err, renderOmkSparkleText(titleLine, {
91
- frame: Math.floor(Date.now() / 80),
153
+ rows.push(renderOmkSparkleText(titleLine, {
154
+ frame,
92
155
  noColor: this.noColor,
93
156
  colors: [BRAND_HEX.cyan, BRAND_HEX.sparkleWhite, BRAND_HEX.sparkleGold, BRAND_HEX.magenta, BRAND_HEX.mint],
94
- }), this.noColor);
95
- line(this.err, gradientLine(mottoLine, [BRAND_HEX.mint, BRAND_HEX.cyan, BRAND_HEX.purple], this.noColor), this.noColor);
96
- line(this.err, `${dim}${routeLine}${RST}`, this.noColor);
97
- line(this.err, gradientLine(statusLine, [BRAND_HEX.mint, BRAND_HEX.cyan, BRAND_HEX.amber], this.noColor), this.noColor);
98
- line(this.err, `${dim}${rootLine}${RST}`, this.noColor);
157
+ }));
158
+ rows.push(renderChalkAnimationKeyframe(controlLine, "rainbow", frame, { noColor: this.noColor }));
159
+ rows.push(renderChalkAnimationKeyframe(coreLine, "karaoke", frame, { noColor: this.noColor }));
160
+ rows.push(renderInkGradientFallback(gridLine, "pastel", { noColor: this.noColor }));
161
+ rows.push(`${dim}${variantLine}${RST}`);
162
+ rows.push(renderInkGradientFallback(mottoLine, "cristal", { noColor: this.noColor }));
163
+ rows.push(this.noColor
164
+ ? gradientLine(stateLine, [BRAND_HEX.mint, BRAND_HEX.cyan], this.noColor)
165
+ : renderTerminalKitCapabilityBadge(gradientLine(stateLine, [BRAND_HEX.mint, BRAND_HEX.cyan], this.noColor)));
166
+ rows.push(`${dim}${routeLine}${RST}`);
167
+ rows.push(`${dim}${rootLine}${RST}`);
168
+ return rows;
99
169
  }
100
170
  }
@@ -9,7 +9,9 @@ function termRows(stream) {
9
9
  return Number.isFinite(rows) ? Math.max(10, rows) : 24;
10
10
  }
11
11
  function shouldUseTerminalControls(stream) {
12
- return stream.isTTY === true && process.env.TERM !== "dumb";
12
+ const requested = process.env.OMK_TUI_ALT_SCREEN ?? process.env.OMK_TUI_TERMINAL_CONTROLS;
13
+ const enabled = /^(1|true|yes|on|full|alt-screen)$/i.test(requested ?? "");
14
+ return enabled && stream.isTTY === true && process.env.TERM !== "dumb";
13
15
  }
14
16
  function countRows(chunk) {
15
17
  return (chunk.match(/\n/g) ?? []).length;
@@ -49,7 +49,9 @@ function termRows(stream) {
49
49
  return Number.isFinite(rows) ? Math.max(10, rows) : 24;
50
50
  }
51
51
  function shouldUseTerminalControls(stream) {
52
- return stream.isTTY === true && process.env.TERM !== "dumb";
52
+ const requested = process.env.OMK_TUI_ALT_SCREEN ?? process.env.OMK_TUI_TERMINAL_CONTROLS;
53
+ const enabled = /^(1|true|yes|on|full|alt-screen)$/i.test(requested ?? "");
54
+ return enabled && stream.isTTY === true && process.env.TERM !== "dumb";
53
55
  }
54
56
  function countRows(chunk) {
55
57
  return (chunk.match(/\n/g) ?? []).length;
@@ -21,10 +21,11 @@ export interface ChatReplState {
21
21
  thinking?: string;
22
22
  modelVariant?: string;
23
23
  activeProviderTab?: string;
24
+ thinkingPickerOpen?: boolean;
24
25
  }
25
26
  export declare function createChatReplState(options?: ChatReplOptions): ChatReplState;
26
27
  export declare function createChatReplCommandBus(options?: ChatReplOptions, state?: ChatReplState): import("../../runtime/command-bus.js").CommandBus;
27
- export declare function applyChatReplSlashResultToState(state: ChatReplState, result: CommandBusResult): void;
28
+ export declare function applyChatReplSlashResultToState(state: ChatReplState, result: CommandBusResult, rawText?: string): void;
28
29
  export declare function prepareChatReplModelPickerForShow(args: {
29
30
  input: string;
30
31
  state: ChatReplState;
@@ -14,6 +14,7 @@ import { createOutputRouter } from "../../runtime/output-router.js";
14
14
  import { normalizeProviderId, readProviderRegistry } from "../../providers/model-registry.js";
15
15
  import { renderProviderModelTable } from "../../providers/model-table.js";
16
16
  import { ALL_PROVIDER_TAB, buildProviderTabs, createModelPickerState, debugModelTabs, handleModelPickerKey, initializeModelPickerState, providerTabIdForProvider, } from "../../providers/model-tabs.js";
17
+ import { formatThinkingModelVariant, nextThinkingLevel, thinkingLevelsFor } from "../../providers/thinking-levels.js";
17
18
  export function createChatReplState(options = {}) {
18
19
  return {
19
20
  provider: options.provider,
@@ -25,7 +26,7 @@ export function createChatReplCommandBus(options = {}, state = createChatReplSta
25
26
  registerSlashCommands(bus, state);
26
27
  return bus;
27
28
  }
28
- export function applyChatReplSlashResultToState(state, result) {
29
+ export function applyChatReplSlashResultToState(state, result, rawText) {
29
30
  if (!result.handled || !result.output)
30
31
  return;
31
32
  let payload;
@@ -39,6 +40,8 @@ export function applyChatReplSlashResultToState(state, result) {
39
40
  return;
40
41
  const data = payload;
41
42
  const routeChanged = typeof data.provider === "string" || typeof data.model === "string";
43
+ const modelSetRequested = rawText ? /^\/(?:model|m)\s+\S/.test(rawText.trim()) : false;
44
+ const thinkRequested = rawText ? /^\/(?:think|thinking)(?:\s|$)/.test(rawText.trim()) : false;
42
45
  if (typeof data.provider === "string")
43
46
  state.provider = data.provider;
44
47
  if (typeof data.model === "string")
@@ -51,6 +54,15 @@ export function applyChatReplSlashResultToState(state, result) {
51
54
  else if (routeChanged) {
52
55
  delete state.modelVariant;
53
56
  }
57
+ if (modelSetRequested && routeChanged && typeof data.thinking !== "string") {
58
+ state.thinkingPickerOpen = true;
59
+ }
60
+ else if (thinkRequested || typeof data.thinking === "string") {
61
+ state.thinkingPickerOpen = true;
62
+ }
63
+ else if (rawText && rawText.trimStart().startsWith("/")) {
64
+ state.thinkingPickerOpen = false;
65
+ }
54
66
  }
55
67
  function explicitProviderTabFromModelLine(line, providerIds) {
56
68
  const trimmed = line.trim();
@@ -97,8 +109,31 @@ export function prepareChatReplModelPickerForShow(args) {
97
109
  });
98
110
  args.modelPickerState.query = "";
99
111
  args.state.activeProviderTab = args.modelPickerState.activeProviderTab;
112
+ args.state.thinkingPickerOpen = false;
100
113
  return true;
101
114
  }
115
+ function isThinkingPickerInput(input, state) {
116
+ return state.thinkingPickerOpen === true && input.trim().length === 0;
117
+ }
118
+ function renderChatReplThinkingPicker(state) {
119
+ const levels = thinkingLevelsFor(state.provider, state.model);
120
+ const current = state.thinking;
121
+ const levelsText = levels
122
+ .map((level) => level === current ? `● ${level}` : `○ ${level}`)
123
+ .join(" ");
124
+ const active = current
125
+ ? `${current} (${formatThinkingModelVariant(state.model, current)})`
126
+ : "not selected";
127
+ const firstLevel = levels[0] ?? "medium";
128
+ return [
129
+ "\n OMK Thinking Control · choose level",
130
+ ` Target: ${state.provider ?? "auto"}/${state.model ?? "auto"}`,
131
+ ` ${levelsText}`,
132
+ " Press Tab on an empty prompt to cycle, or run /think <level>.",
133
+ ` Shortcut: /model ${state.provider ?? "auto"}/${state.model ?? "auto"}:${firstLevel}`,
134
+ ` Active: ${active}\n`,
135
+ ].join("\n");
136
+ }
102
137
  /**
103
138
  * Start an interactive chat REPL that routes all input through the full pipeline.
104
139
  */
@@ -117,6 +152,21 @@ export async function startChatRepl(options) {
117
152
  prompt: options.json ? "omk> " : `${"\x1b["}36momk>${"\x1b["}0m `,
118
153
  completer: (line) => {
119
154
  const trimmed = line.trim();
155
+ if (/^\/(?:think|thinking)\s*$/.test(trimmed) && replState.thinkingPickerOpen !== true) {
156
+ replState.thinkingPickerOpen = true;
157
+ delete replState.activeProviderTab;
158
+ console.log(renderChatReplThinkingPicker(replState));
159
+ return [[], line];
160
+ }
161
+ if (isThinkingPickerInput(line, replState)) {
162
+ const next = nextThinkingLevel(replState.thinking, replState.provider, replState.model);
163
+ replState.thinking = next;
164
+ replState.modelVariant = formatThinkingModelVariant(replState.model, next);
165
+ replState.thinkingPickerOpen = true;
166
+ delete replState.activeProviderTab;
167
+ console.log(renderChatReplThinkingPicker(replState));
168
+ return [[], line];
169
+ }
120
170
  if (/^\/(?:model|m)(?:\s|$)/.test(trimmed) || (trimmed.length === 0 && replState.activeProviderTab)) {
121
171
  const previousActiveProviderTab = modelPickerState.activeProviderTab;
122
172
  const explicitProviderTab = explicitProviderTabFromModelLine(line, modelProviderIds);
@@ -133,6 +183,7 @@ export async function startChatRepl(options) {
133
183
  }
134
184
  modelPickerState.query = line;
135
185
  replState.activeProviderTab = modelPickerState.activeProviderTab;
186
+ replState.thinkingPickerOpen = false;
136
187
  const tabs = buildProviderTabs(modelProviderIds);
137
188
  debugModelTabs({
138
189
  providerIds: modelProviderIds,
@@ -214,7 +265,8 @@ export async function startChatRepl(options) {
214
265
  rawText: input,
215
266
  });
216
267
  if (busResult.handled) {
217
- applyChatReplSlashResultToState(replState, busResult);
268
+ const thinkingPickerWasOpen = replState.thinkingPickerOpen === true;
269
+ applyChatReplSlashResultToState(replState, busResult, input);
218
270
  if (preparedModelPicker) {
219
271
  replState.activeProviderTab = modelPickerState.activeProviderTab;
220
272
  }
@@ -225,9 +277,13 @@ export async function startChatRepl(options) {
225
277
  router.route(ev);
226
278
  }
227
279
  router.flush();
280
+ if (replState.thinkingPickerOpen && !thinkingPickerWasOpen && /^\/(?:model|m)\s+\S/.test(input)) {
281
+ console.log(renderChatReplThinkingPicker(replState));
282
+ }
228
283
  rl.prompt();
229
284
  return;
230
285
  }
286
+ replState.thinkingPickerOpen = false;
231
287
  // For regular messages: classify intent → build sidecar → render
232
288
  const intent = classifyIntent(input);
233
289
  const capabilityPlan = selectCapabilities({
@@ -390,7 +390,8 @@ export class ThemeCommand extends OmkCommand {
390
390
  const compiled = compileTheme(doc, tier);
391
391
  this.context.stdout.write(`${doc.displayName ?? doc.name} — tier ${tier}\n\n`);
392
392
  this.context.stdout.write(renderStatusFrame(compiled) + "\n");
393
- const current = process.env.OMK_THEME ?? "omk";
393
+ const { resolveTheme } = await import("../theme/theme-resolver.js");
394
+ const current = resolveTheme({ cwd: this.cwd, flagTheme: this.theme || undefined }).name;
394
395
  this.context.stdout.write(`\nCurrent theme: ${current}. Use \`omk theme set ${target}\` to switch.\n`);
395
396
  return 0;
396
397
  }
@@ -404,7 +405,8 @@ export class ThemeCommand extends OmkCommand {
404
405
  return 2;
405
406
  }
406
407
  this.context.stdout.write(renderThemePreview(palette) + "\n");
407
- const current = process.env.OMK_THEME ?? "omk";
408
+ const { resolveTheme } = await import("../theme/theme-resolver.js");
409
+ const current = resolveTheme({ cwd: this.cwd, flagTheme: this.theme || undefined }).name;
408
410
  this.context.stdout.write(`\nCurrent theme: ${current}. Use \`omk theme set ${target}\` to switch.\n`);
409
411
  return 0;
410
412
  }
@@ -421,8 +423,9 @@ export class ThemeCommand extends OmkCommand {
421
423
  }
422
424
  // omk theme — show current + all with swatches
423
425
  const { renderAllThemePreviews, getBuiltinTheme } = await import("../theme/theme-registry.js");
424
- const current = process.env.OMK_THEME ?? "omk";
425
- const currentPalette = getBuiltinTheme(current) ?? getBuiltinTheme("omk");
426
+ const { resolveTheme } = await import("../theme/theme-resolver.js");
427
+ const current = resolveTheme({ cwd: this.cwd, flagTheme: this.theme || undefined }).name;
428
+ const currentPalette = getBuiltinTheme(current) ?? getBuiltinTheme("omk-control-grid-dark");
426
429
  this.context.stdout.write((currentPalette?.render("header", `Current theme: ${current}`) ?? `Current theme: ${current}`) + "\n\n");
427
430
  this.context.stdout.write(renderAllThemePreviews() + "\n");
428
431
  return 0;
@@ -47,8 +47,9 @@ export async function promptProviderSelection(currentProvider, currentModel) {
47
47
  */
48
48
  export async function promptThemeSelection(currentTheme) {
49
49
  const themes = [
50
- { value: "omk", label: "OMK", hint: "Default colorful theme" },
51
- { value: "night-city", label: "Night City", hint: "README control image palette" },
50
+ { value: "omk-control-grid-dark", label: "OMK//CONTROL", hint: "Original README omk_tui.png palette" },
51
+ { value: "night-city", label: "Night City", hint: "Alias for OMK//CONTROL" },
52
+ { value: "omk", label: "OMK", hint: "Legacy colorful theme" },
52
53
  { value: "green-rain", label: "Green Rain", hint: "Phosphor signal console" },
53
54
  { value: "rust-forge", label: "Rust Forge", hint: "Oxidized forge control console" },
54
55
  { value: "neon-circuit", label: "Neon Circuit", hint: "High-energy neon terminal" },
@@ -61,7 +62,7 @@ export async function promptThemeSelection(currentTheme) {
61
62
  const theme = await clack.select({
62
63
  message: "Select theme:",
63
64
  options: themes,
64
- initialValue: currentTheme ?? "omk",
65
+ initialValue: currentTheme ?? "omk-control-grid-dark",
65
66
  });
66
67
  if (clack.isCancel(theme)) {
67
68
  clack.cancel("Cancelled.");
@@ -145,6 +145,8 @@ function providerLabel(provider) {
145
145
  return "OpenRouter";
146
146
  if (provider === "qwen")
147
147
  return "Qwen/DashScope";
148
+ if (provider === "glm")
149
+ return "Zhipu GLM";
148
150
  return provider;
149
151
  }
150
152
  function setupActions(entry) {
@@ -34,6 +34,7 @@ export interface NativeRootSessionState {
34
34
  view?: TuiView;
35
35
  animation?: OmkTuiMotion;
36
36
  modelPickerOpen?: boolean;
37
+ thinkingPickerOpen?: boolean;
37
38
  activeProviderTab?: string;
38
39
  updatedAt?: string;
39
40
  }