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.
- package/AGENTS.md +131 -2
- package/CHANGELOG.md +32 -0
- package/CLAUDE.md +105 -3
- package/DESIGN.md +141 -117
- package/MATURITY.md +7 -12
- package/README.md +66 -347
- package/ROADMAP.md +33 -47
- package/dist/adapters/kimi/runner.js +2 -2
- package/dist/brand/theme.js +2 -2
- package/dist/cli/register-basic-commands.js +3 -2
- package/dist/cli/register-mcp-dag-cron-screenshot-commands.js +1 -1
- package/dist/cli/root.d.ts +1 -0
- package/dist/cli/root.js +2 -0
- package/dist/cli/runtime/command-bus.js +14 -1
- package/dist/cli/theme/terminal-capability.js +1 -1
- package/dist/cli/theme/theme-registry.js +90 -2
- package/dist/cli/ui/event.d.ts +1 -0
- package/dist/cli/ui/neon-grid-renderer.d.ts +8 -0
- package/dist/cli/ui/neon-grid-renderer.js +84 -14
- package/dist/cli/ui/plain-renderer.js +3 -1
- package/dist/cli/ui/system24-renderer.js +3 -1
- package/dist/cli/v2/chat-repl.d.ts +2 -1
- package/dist/cli/v2/chat-repl.js +58 -2
- package/dist/cli/v2/cli-v2-skeleton.js +7 -4
- package/dist/cli/v2/interactive-prompt.js +4 -3
- package/dist/commands/auth.js +2 -0
- package/dist/commands/chat/native-root-loop.d.ts +1 -0
- package/dist/commands/chat/native-root-loop.js +162 -13
- package/dist/commands/chat/runtime.js +9 -0
- package/dist/commands/chat/slash/commands/routing.js +31 -1
- package/dist/commands/chat/utils.js +3 -1
- package/dist/commands/cockpit/render.js +2 -1
- package/dist/commands/doctor/checks.js +2 -2
- package/dist/commands/init/core.js +2 -2
- package/dist/commands/init.js +2 -2
- package/dist/commands/mcp/config.js +38 -16
- package/dist/commands/mcp/doctor.js +7 -8
- package/dist/commands/mcp/list.js +4 -3
- package/dist/commands/mcp/shared.d.ts +4 -1
- package/dist/commands/mcp/shared.js +21 -3
- package/dist/commands/mcp/test.js +13 -12
- package/dist/commands/model.js +4 -0
- package/dist/commands/provider.js +5 -1
- package/dist/commands/version.d.ts +1 -0
- package/dist/commands/version.js +18 -0
- package/dist/contracts/command.d.ts +25 -0
- package/dist/contracts/command.js +1 -0
- package/dist/contracts/evidence-bundle.d.ts +52 -0
- package/dist/contracts/evidence-bundle.js +1 -0
- package/dist/contracts/index.d.ts +2 -0
- package/dist/contracts/index.js +2 -0
- package/dist/contracts/web-bridge.d.ts +1 -1
- package/dist/contracts/web-bridge.js +1 -1
- package/dist/evidence/bundle-validator.d.ts +7 -0
- package/dist/evidence/bundle-validator.js +103 -0
- package/dist/evidence/index.d.ts +5 -0
- package/dist/evidence/index.js +2 -0
- package/dist/evidence/proof-trust.js +2 -10
- package/dist/evidence/trust-calibration.d.ts +25 -0
- package/dist/evidence/trust-calibration.js +151 -0
- package/dist/mcp/omk-project-server.js +1 -1
- package/dist/metrics/provenance-ratio.d.ts +16 -0
- package/dist/metrics/provenance-ratio.js +33 -0
- package/dist/orchestration/dag.d.ts +17 -0
- package/dist/orchestration/ensemble-decision.d.ts +2 -0
- package/dist/orchestration/ensemble-decision.js +4 -0
- package/dist/orchestration/hedge-persona-weights.d.ts +25 -0
- package/dist/orchestration/hedge-persona-weights.js +43 -0
- package/dist/orchestration/parallel-ui.d.ts +1 -0
- package/dist/orchestration/parallel-ui.js +12 -3
- package/dist/orchestration/route-calibration.d.ts +27 -0
- package/dist/orchestration/route-calibration.js +123 -0
- package/dist/orchestration/routing/mcp-config.js +5 -1
- package/dist/orchestration/routing/types.d.ts +21 -0
- package/dist/orchestration/routing.d.ts +10 -0
- package/dist/orchestration/routing.js +89 -3
- package/dist/providers/model-registry.d.ts +2 -1
- package/dist/providers/model-registry.js +76 -4
- package/dist/providers/provider-runtime.js +9 -1
- package/dist/providers/thinking-levels.js +2 -0
- package/dist/providers/types.d.ts +1 -1
- package/dist/runtime/glm-api-runtime.d.ts +4 -0
- package/dist/runtime/glm-api-runtime.js +15 -0
- package/dist/runtime/index.d.ts +1 -0
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/kimi-api-runtime.d.ts +6 -1
- package/dist/runtime/kimi-api-runtime.js +10 -4
- package/dist/runtime/recovery-artifact-store.d.ts +39 -0
- package/dist/runtime/recovery-artifact-store.js +111 -0
- package/dist/runtime/runtime-backed-task-runner.js +10 -0
- package/dist/runtime/runtime-bootstrap.js +17 -1
- package/dist/runtime/slash-commands.js +38 -5
- package/dist/schema/command.schema.d.ts +121 -0
- package/dist/schema/command.schema.js +25 -0
- package/dist/schema/envelope.schema.d.ts +2 -2
- package/dist/schema/evidence-bundle.schema.d.ts +196 -0
- package/dist/schema/evidence-bundle.schema.js +38 -0
- package/dist/schema/index.d.ts +2 -0
- package/dist/schema/index.js +2 -0
- package/dist/schema/proof-bundle.schema.d.ts +2 -2
- package/dist/theme/external-theme-adapters.d.ts +27 -0
- package/dist/theme/external-theme-adapters.js +52 -0
- package/dist/theme/library-effects.d.ts +12 -0
- package/dist/theme/library-effects.js +47 -0
- package/dist/theme/parallel.js +3 -2
- package/dist/tui/terminal-frame-renderer.d.ts +9 -1
- package/dist/tui/terminal-frame-renderer.js +77 -7
- package/dist/util/chat-cockpit.d.ts +3 -1
- package/dist/util/chat-cockpit.js +44 -5
- package/dist/util/command-envelope.d.ts +19 -0
- package/dist/util/command-envelope.js +31 -0
- package/dist/util/events-logger.d.ts +1 -1
- package/dist/util/events-logger.js +23 -16
- package/dist/util/fs/mcp-diagnose.js +2 -1
- package/dist/util/fs/mcp-runtime-config.js +14 -2
- package/dist/util/i18n.js +2 -2
- package/dist/util/screenshot-store.d.ts +7 -0
- package/dist/util/screenshot-store.js +30 -0
- package/dist/util/terminal-kit-bridge.d.ts +24 -0
- package/dist/util/terminal-kit-bridge.js +60 -0
- package/dist/util/terminal-owner.d.ts +1 -0
- package/dist/util/terminal-owner.js +31 -0
- package/dist/version.d.ts +3 -1
- package/dist/version.js +4 -0
- package/docs/2026-05-18-project-direction.md +1 -1
- package/docs/2026-06-14/critical-issues.md +19 -0
- package/docs/2026-06-14/improvements.md +15 -0
- package/docs/2026-06-14/init-checklist.md +25 -0
- package/docs/2026-06-14/plan.md +20 -0
- package/docs/2026-06-15/critical-issues.md +19 -0
- package/docs/2026-06-15/improvements.md +15 -0
- package/docs/2026-06-15/init-checklist.md +25 -0
- package/docs/2026-06-15/plan.md +20 -0
- package/docs/claims.md +85 -0
- package/docs/comparisons/omk-vs-claude-code.md +31 -0
- package/docs/comparisons/omk-vs-codex.md +33 -0
- package/docs/comparisons/omk-vs-mcp-agent.md +33 -0
- package/docs/comparisons/omk-vs-opencode.md +32 -0
- package/docs/decision-layer-calibration.md +20 -0
- package/docs/geo-eval-prompts.md +53 -0
- package/docs/getting-started.md +3 -31
- package/docs/headroom-omk-setup-guide.md +2 -2
- package/docs/integrations/adaptorch.md +46 -0
- package/docs/native-root-runtime-algorithms.md +2 -303
- package/docs/native-root-runtime-hardening.md +1 -1
- package/docs/provider-maturity.md +1 -1
- package/docs/use-cases/evidence-gated-coding-agents.md +52 -0
- package/docs/use-cases/no-kimi-mode.md +38 -0
- package/docs/use-cases/provider-routing-for-ai-coding.md +36 -0
- package/docs/use-cases/replayable-agent-runs.md +40 -0
- package/docs/use-cases/scoped-mcp-for-coding-agents.md +34 -0
- package/docs/versioning.md +9 -3
- package/docs/what-is-omk.md +64 -0
- package/llms.txt +28 -18
- package/package.json +7 -2
- package/readmeasset/.npmignore +3 -0
- package/readmeasset/ASSET_INDEX.md +3 -0
- package/readmeasset/ASSET_PROVENANCE.md +6 -0
- package/readmeasset/omk-adaptorch-ouroboros-supermemory.svg +95 -0
- package/readmeasset/omk-init-control-loop.svg +118 -0
- package/readmeasset/omk-parallel-subagents.svg +97 -0
- package/templates/web-bridge/chrome-extension/background.js +1 -1
|
@@ -25,15 +25,18 @@ import { buildNativeChatSlashCommands } from "./slash/commands/index.js";
|
|
|
25
25
|
import { parseSlashInput } from "./slash/parser.js";
|
|
26
26
|
import { createSlashCommandRegistry } from "./slash/registry.js";
|
|
27
27
|
import { emitSlashResult, okSlashResult, printSlashResult, } from "./slash/result.js";
|
|
28
|
-
async function runSlashHandler(handler, parsed, ctx) {
|
|
28
|
+
async function runSlashHandler(handler, parsed, ctx, options = {}) {
|
|
29
29
|
const result = await handler.handler(ctx, parsed.args);
|
|
30
30
|
const normalized = result ?? okSlashResult();
|
|
31
31
|
if (normalized.statePatch)
|
|
32
32
|
Object.assign(ctx.state, normalized.statePatch);
|
|
33
|
+
const visibleResult = options.suppressOutput
|
|
34
|
+
? { ...normalized, text: undefined, json: undefined, events: [] }
|
|
35
|
+
: normalized;
|
|
33
36
|
if (ctx.renderer)
|
|
34
|
-
emitSlashResult(
|
|
37
|
+
emitSlashResult(visibleResult, ctx.renderer);
|
|
35
38
|
else
|
|
36
|
-
printSlashResult(
|
|
39
|
+
printSlashResult(visibleResult);
|
|
37
40
|
return normalized;
|
|
38
41
|
}
|
|
39
42
|
function isModelSlashCommand(parsed) {
|
|
@@ -76,7 +79,112 @@ function explicitProviderTabFromModelLine(line, providerIds) {
|
|
|
76
79
|
function isModelPickerLine(line, state) {
|
|
77
80
|
const trimmed = line.trim();
|
|
78
81
|
return /^\/(?:model|m)(?:\s|$)/.test(trimmed)
|
|
79
|
-
|| (state.modelPickerOpen === true && trimmed.length === 0);
|
|
82
|
+
|| (state.modelPickerOpen === true && state.thinkingPickerOpen !== true && trimmed.length === 0);
|
|
83
|
+
}
|
|
84
|
+
function isThinkingPickerLine(line, state) {
|
|
85
|
+
const trimmed = line.trim();
|
|
86
|
+
return (state.thinkingPickerOpen === true && trimmed.length === 0)
|
|
87
|
+
|| (state.modelPickerOpen !== true && trimmed.length === 0);
|
|
88
|
+
}
|
|
89
|
+
function isThinkingSlashCommand(parsed) {
|
|
90
|
+
return parsed.command === "/think" || parsed.command === "/thinking" || parsed.command === ":think";
|
|
91
|
+
}
|
|
92
|
+
function isThinkingShowSlash(parsed) {
|
|
93
|
+
return isThinkingSlashCommand(parsed)
|
|
94
|
+
&& parsed.args.positional.length === 0
|
|
95
|
+
&& parsed.args.flags.json !== true;
|
|
96
|
+
}
|
|
97
|
+
function isThinkingFollowupSlash(parsed) {
|
|
98
|
+
return parsed.command === "/model"
|
|
99
|
+
|| parsed.command === "/m"
|
|
100
|
+
|| parsed.command === "/use"
|
|
101
|
+
|| parsed.command === ":use";
|
|
102
|
+
}
|
|
103
|
+
function shouldOpenThinkingPickerAfterSlash(parsed, result) {
|
|
104
|
+
if (!result.ok || !isThinkingFollowupSlash(parsed))
|
|
105
|
+
return false;
|
|
106
|
+
if (parsed.args.positional.length === 0)
|
|
107
|
+
return false;
|
|
108
|
+
if (result.statePatch?.thinking)
|
|
109
|
+
return false;
|
|
110
|
+
return Boolean(result.statePatch?.model || result.statePatch?.provider || result.statePatch?.bootstrap);
|
|
111
|
+
}
|
|
112
|
+
function renderThinkingPickerText(state) {
|
|
113
|
+
const levels = thinkingLevelsFor(state.provider, state.model);
|
|
114
|
+
const current = state.thinking;
|
|
115
|
+
const levelLine = levels
|
|
116
|
+
.map((level) => {
|
|
117
|
+
const label = `${level === current ? "●" : "○"} ${level}`;
|
|
118
|
+
if (level === current)
|
|
119
|
+
return style.mintBold(label);
|
|
120
|
+
if (level === "max" || level === "xhigh")
|
|
121
|
+
return style.cyanBold(label);
|
|
122
|
+
return style.gray(label);
|
|
123
|
+
})
|
|
124
|
+
.join(style.gray(" "));
|
|
125
|
+
const active = current
|
|
126
|
+
? `${current} (${formatThinkingModelVariant(state.model, current)})`
|
|
127
|
+
: "not selected";
|
|
128
|
+
const firstLevel = levels[0] ?? "medium";
|
|
129
|
+
return [
|
|
130
|
+
style.phosphorBold("\n OMK Thinking Control · choose level"),
|
|
131
|
+
style.phosphorDim(` Target: ${state.provider ?? "auto"}/${state.model ?? "auto"}`),
|
|
132
|
+
` ${levelLine}`,
|
|
133
|
+
style.phosphorDim(" Press Tab on an empty prompt to cycle, or run /think <level>."),
|
|
134
|
+
style.phosphorDim(` Shortcut: /model ${state.provider ?? "auto"}/${state.model ?? "auto"}:${firstLevel}`),
|
|
135
|
+
style.phosphorDim(` Active: ${active}\n`),
|
|
136
|
+
].join("\n");
|
|
137
|
+
}
|
|
138
|
+
function canUseInteractiveThinkingSelector(input, renderer) {
|
|
139
|
+
if (renderer)
|
|
140
|
+
return false;
|
|
141
|
+
if (isDisabledEnvValue(input.env.OMK_INTERACTIVE_THINKING_SELECT))
|
|
142
|
+
return false;
|
|
143
|
+
return process.stdin.isTTY === true && process.stderr.isTTY === true;
|
|
144
|
+
}
|
|
145
|
+
function applyThinkingLevelToNativeState(input, state, level) {
|
|
146
|
+
state.thinking = level;
|
|
147
|
+
state.thinkingPickerOpen = true;
|
|
148
|
+
state.modelPickerOpen = false;
|
|
149
|
+
state.updatedAt = new Date().toISOString();
|
|
150
|
+
input.env.OMK_THINKING = level;
|
|
151
|
+
input.env.OMK_MODEL_VARIANT = formatThinkingModelVariant(state.model, level);
|
|
152
|
+
}
|
|
153
|
+
async function promptThinkingSelection(input, state, renderer) {
|
|
154
|
+
if (!canUseInteractiveThinkingSelector(input, renderer))
|
|
155
|
+
return false;
|
|
156
|
+
const levels = thinkingLevelsFor(state.provider, state.model);
|
|
157
|
+
if (levels.length === 0)
|
|
158
|
+
return false;
|
|
159
|
+
try {
|
|
160
|
+
const { select } = await import("@inquirer/prompts");
|
|
161
|
+
const selected = await select({
|
|
162
|
+
message: `Select thinking level for ${state.provider ?? "auto"}/${state.model ?? "auto"}`,
|
|
163
|
+
choices: levels.map((level) => ({
|
|
164
|
+
value: level,
|
|
165
|
+
name: `${level === state.thinking ? "● " : ""}${level}`,
|
|
166
|
+
description: formatThinkingModelVariant(state.model, level),
|
|
167
|
+
})),
|
|
168
|
+
pageSize: Math.min(levels.length, 8),
|
|
169
|
+
loop: true,
|
|
170
|
+
default: state.thinking ?? input.env.OMK_THINKING ?? levels[0],
|
|
171
|
+
}, {
|
|
172
|
+
input: process.stdin,
|
|
173
|
+
output: process.stderr,
|
|
174
|
+
clearPromptOnDone: true,
|
|
175
|
+
});
|
|
176
|
+
applyThinkingLevelToNativeState(input, state, selected);
|
|
177
|
+
process.stderr.write(style.phosphorDim(`\n Thinking variant: ${selected}\n Active: ${input.env.OMK_MODEL_VARIANT}\n`));
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
catch (err) {
|
|
181
|
+
const name = err instanceof Error ? err.name : "Error";
|
|
182
|
+
if (name === "ExitPromptError") {
|
|
183
|
+
process.stderr.write(style.phosphorDim("\n Thinking selection cancelled.\n"));
|
|
184
|
+
return true;
|
|
185
|
+
}
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
80
188
|
}
|
|
81
189
|
function countModelPickerRows(registry, activeProviderTab) {
|
|
82
190
|
return registry
|
|
@@ -690,6 +798,7 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
690
798
|
root: input.root,
|
|
691
799
|
cwd: input.activeCwd,
|
|
692
800
|
rootSource: input.rootSource,
|
|
801
|
+
brandLabel: input.env.OMK_ENTRY_DISPLAY_NAME,
|
|
693
802
|
});
|
|
694
803
|
// opencode-style: banner handled by PlainModernRenderer session:start event
|
|
695
804
|
const modelProviderRegistry = await readProviderRegistry({ env: input.env }).catch(() => []);
|
|
@@ -719,6 +828,13 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
719
828
|
else
|
|
720
829
|
process.stdout.write(`${text}\n`);
|
|
721
830
|
};
|
|
831
|
+
const renderThinkingPicker = () => {
|
|
832
|
+
const text = renderThinkingPickerText(state);
|
|
833
|
+
if (renderer)
|
|
834
|
+
renderer.emit({ type: "control:output", text });
|
|
835
|
+
else
|
|
836
|
+
process.stdout.write(`${text}\n`);
|
|
837
|
+
};
|
|
722
838
|
// Defensive stdin re-validation before the chat readline takes ownership.
|
|
723
839
|
// The first-run GitHub-star / update prompts (@inquirer/prompts) can take
|
|
724
840
|
// over raw mode and leave the shared interactive stdin paused; a paused TTY
|
|
@@ -742,6 +858,7 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
742
858
|
explicitProviderTab,
|
|
743
859
|
});
|
|
744
860
|
state.modelPickerOpen = true;
|
|
861
|
+
state.thinkingPickerOpen = false;
|
|
745
862
|
state.activeProviderTab = modelPickerState.activeProviderTab;
|
|
746
863
|
modelPickerState.query = line;
|
|
747
864
|
renderModelPicker(explicitProviderTab ? "/model explicit" : "/model");
|
|
@@ -752,24 +869,28 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
752
869
|
providerIds: modelProviderIds,
|
|
753
870
|
})) {
|
|
754
871
|
state.modelPickerOpen = true;
|
|
872
|
+
state.thinkingPickerOpen = false;
|
|
755
873
|
state.activeProviderTab = modelPickerState.activeProviderTab;
|
|
756
874
|
modelPickerState.query = line;
|
|
757
875
|
renderModelPicker("\t", previousActiveProviderTab);
|
|
758
876
|
}
|
|
759
877
|
return [[], line];
|
|
760
878
|
}
|
|
761
|
-
if (line.trim()
|
|
879
|
+
if (/^\/(?:think|thinking)\s*$/.test(line.trim()) && state.thinkingPickerOpen !== true) {
|
|
880
|
+
state.thinkingPickerOpen = true;
|
|
881
|
+
state.modelPickerOpen = false;
|
|
882
|
+
renderThinkingPicker();
|
|
883
|
+
return [[], line];
|
|
884
|
+
}
|
|
885
|
+
if (isThinkingPickerLine(line, state)) {
|
|
762
886
|
const next = nextThinkingLevel(state.thinking ?? input.env.OMK_THINKING, state.provider, state.model);
|
|
763
887
|
state.thinking = next;
|
|
888
|
+
state.thinkingPickerOpen = true;
|
|
889
|
+
state.modelPickerOpen = false;
|
|
764
890
|
state.updatedAt = new Date().toISOString();
|
|
765
891
|
input.env.OMK_THINKING = next;
|
|
766
892
|
input.env.OMK_MODEL_VARIANT = formatThinkingModelVariant(state.model, next);
|
|
767
|
-
|
|
768
|
-
const message = `\n Thinking: ${next} (${levels}) · ${input.env.OMK_MODEL_VARIANT}\n`;
|
|
769
|
-
if (renderer)
|
|
770
|
-
renderer.emit({ type: "control:output", text: style.phosphorDim(message) });
|
|
771
|
-
else
|
|
772
|
-
process.stdout.write(style.phosphorDim(message));
|
|
893
|
+
renderThinkingPicker();
|
|
773
894
|
}
|
|
774
895
|
return [[], line];
|
|
775
896
|
},
|
|
@@ -882,6 +1003,7 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
882
1003
|
});
|
|
883
1004
|
modelPickerState.query = "";
|
|
884
1005
|
state.modelPickerOpen = true;
|
|
1006
|
+
state.thinkingPickerOpen = false;
|
|
885
1007
|
state.activeProviderTab = modelPickerState.activeProviderTab;
|
|
886
1008
|
const tabs = buildProviderTabs(modelProviderIds);
|
|
887
1009
|
debugModelTabs({
|
|
@@ -899,8 +1021,12 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
899
1021
|
state.activeProviderTab = ALL_PROVIDER_TAB;
|
|
900
1022
|
}
|
|
901
1023
|
try {
|
|
1024
|
+
let slashResult;
|
|
1025
|
+
const thinkingShow = isThinkingShowSlash(parsedSlash);
|
|
1026
|
+
const interactiveThinkingSelector = canUseInteractiveThinkingSelector(input, renderer);
|
|
902
1027
|
await terminalOwner.withChildProcess(rl, async () => {
|
|
903
|
-
const result = await runSlashHandler(handler, parsedSlash, slashContext);
|
|
1028
|
+
const result = await runSlashHandler(handler, parsedSlash, slashContext, { suppressOutput: thinkingShow && interactiveThinkingSelector });
|
|
1029
|
+
slashResult = result;
|
|
904
1030
|
if (parsedSlash.command === "/paste" && result.ok) {
|
|
905
1031
|
const pasteLine = result.text?.trim();
|
|
906
1032
|
if (pasteLine) {
|
|
@@ -912,8 +1038,28 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
912
1038
|
if (result.exit)
|
|
913
1039
|
running = false;
|
|
914
1040
|
});
|
|
915
|
-
|
|
1041
|
+
const shouldSelectThinking = slashResult
|
|
1042
|
+
? (thinkingShow && interactiveThinkingSelector) || shouldOpenThinkingPickerAfterSlash(parsedSlash, slashResult)
|
|
1043
|
+
: false;
|
|
1044
|
+
if (shouldSelectThinking) {
|
|
1045
|
+
state.modelPickerOpen = false;
|
|
1046
|
+
state.thinkingPickerOpen = true;
|
|
1047
|
+
state.activeProviderTab = ALL_PROVIDER_TAB;
|
|
1048
|
+
const selected = await terminalOwner.withRawSelector(rl, () => promptThinkingSelection(input, state, renderer));
|
|
1049
|
+
if (!selected)
|
|
1050
|
+
renderThinkingPicker();
|
|
1051
|
+
}
|
|
1052
|
+
else if (isThinkingSlashCommand(parsedSlash)) {
|
|
1053
|
+
state.modelPickerOpen = false;
|
|
1054
|
+
state.thinkingPickerOpen = true;
|
|
1055
|
+
}
|
|
1056
|
+
else if (!modelShow && isThinkingFollowupSlash(parsedSlash)) {
|
|
1057
|
+
state.modelPickerOpen = false;
|
|
1058
|
+
state.thinkingPickerOpen = false;
|
|
1059
|
+
}
|
|
1060
|
+
else if (!modelShow && !isModelSlashCommand(parsedSlash)) {
|
|
916
1061
|
state.modelPickerOpen = false;
|
|
1062
|
+
state.thinkingPickerOpen = false;
|
|
917
1063
|
}
|
|
918
1064
|
}
|
|
919
1065
|
catch (err) {
|
|
@@ -930,6 +1076,8 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
930
1076
|
}
|
|
931
1077
|
continue;
|
|
932
1078
|
}
|
|
1079
|
+
state.modelPickerOpen = false;
|
|
1080
|
+
state.thinkingPickerOpen = false;
|
|
933
1081
|
const message = `Unknown command: ${parsedSlash.command}. Type /help for commands.`;
|
|
934
1082
|
if (renderer) {
|
|
935
1083
|
renderer.emit({ type: "turn:error", message });
|
|
@@ -940,6 +1088,7 @@ export async function runNativeOmkRootLoop(input) {
|
|
|
940
1088
|
continue;
|
|
941
1089
|
}
|
|
942
1090
|
state.modelPickerOpen = false;
|
|
1091
|
+
state.thinkingPickerOpen = false;
|
|
943
1092
|
if (shouldRunNativeParallelTurn(state.approvalPolicy)) {
|
|
944
1093
|
const turnStartedAt = Date.now();
|
|
945
1094
|
try {
|
|
@@ -48,6 +48,8 @@ function buildBaseChatRuntimeEnv(root, sessionId) {
|
|
|
48
48
|
const omkNames = [
|
|
49
49
|
"OMK_AUTHORITY_PROVIDER",
|
|
50
50
|
"OMK_CHAT_NO_BANNER",
|
|
51
|
+
"OMK_ENTRY_DISPLAY_NAME",
|
|
52
|
+
"OMK_ENTRY_SURFACE",
|
|
51
53
|
"OMK_DEBUG",
|
|
52
54
|
"OMK_DEFAULT_PROVIDER",
|
|
53
55
|
"OMK_MCP_PREFLIGHT",
|
|
@@ -92,6 +94,13 @@ function attachSelectedProviderEnv(env, provider, source = process.env) {
|
|
|
92
94
|
add("KIMI_MODEL");
|
|
93
95
|
add("KIMI_BASE_URL");
|
|
94
96
|
}
|
|
97
|
+
if (provider === "glm" || provider === "auto") {
|
|
98
|
+
add("BIGMODEL_API_KEY");
|
|
99
|
+
add("GLM_API_KEY");
|
|
100
|
+
add("GLM_MODEL");
|
|
101
|
+
add("GLM_BASE_URL");
|
|
102
|
+
add("BIGMODEL_BASE_URL");
|
|
103
|
+
}
|
|
95
104
|
return next;
|
|
96
105
|
}
|
|
97
106
|
function formatRuntimeBootstrapFailure(bootstrap) {
|
|
@@ -152,7 +152,13 @@ export function buildRoutingSlashCommands() {
|
|
|
152
152
|
].join("\n"),
|
|
153
153
|
});
|
|
154
154
|
}
|
|
155
|
-
|
|
155
|
+
if (!requested) {
|
|
156
|
+
return okSlashResult({
|
|
157
|
+
statePatch: { thinkingPickerOpen: true, updatedAt: new Date().toISOString() },
|
|
158
|
+
text: renderThinkingLevelList(ctx, levels),
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
const level = requested === "next" || requested === "tab"
|
|
156
162
|
? nextThinkingLevel(ctx.state.thinking ?? ctx.env.OMK_THINKING, ctx.state.provider, ctx.state.model)
|
|
157
163
|
: normalizeThinkingLevel(requested);
|
|
158
164
|
if (!level || !levels.includes(level)) {
|
|
@@ -202,6 +208,30 @@ export function buildRoutingSlashCommands() {
|
|
|
202
208
|
},
|
|
203
209
|
];
|
|
204
210
|
}
|
|
211
|
+
function renderThinkingLevelList(ctx, levels) {
|
|
212
|
+
const current = ctx.state.thinking ?? ctx.env.OMK_THINKING;
|
|
213
|
+
const levelLine = levels
|
|
214
|
+
.map((level) => {
|
|
215
|
+
const label = `${level === current ? "●" : "○"} ${level}`;
|
|
216
|
+
if (level === current)
|
|
217
|
+
return style.mintBold(label);
|
|
218
|
+
if (level === "max" || level === "xhigh")
|
|
219
|
+
return style.cyanBold(label);
|
|
220
|
+
return style.gray(label);
|
|
221
|
+
})
|
|
222
|
+
.join(style.gray(" "));
|
|
223
|
+
const active = current
|
|
224
|
+
? `${current} (${formatThinkingModelVariant(ctx.state.model, current)})`
|
|
225
|
+
: "not selected";
|
|
226
|
+
return [
|
|
227
|
+
style.phosphorBold("\n OMK Thinking Control · choose level"),
|
|
228
|
+
style.phosphorDim(` Target: ${ctx.state.provider ?? "auto"}/${ctx.state.model ?? "auto"}`),
|
|
229
|
+
` ${levelLine}`,
|
|
230
|
+
style.phosphorDim(` Choose directly: /think ${levels.join(" | /think ")}`),
|
|
231
|
+
style.phosphorDim(" Cycle only when explicit: /think next"),
|
|
232
|
+
style.phosphorDim(` Active: ${active}\n`),
|
|
233
|
+
].join("\n");
|
|
234
|
+
}
|
|
205
235
|
function modelRefFromParsed(parsed, raw) {
|
|
206
236
|
return `${parsed.provider ? `${parsed.provider}/` : ""}${parsed.model ?? raw}`;
|
|
207
237
|
}
|
|
@@ -55,10 +55,12 @@ export function resolveLayout(requested) {
|
|
|
55
55
|
export function defaultChatUiForBrand(brand) {
|
|
56
56
|
if (brand === "green-rain" || brand === "neon-grid" || brand === "rust-forge")
|
|
57
57
|
return brand;
|
|
58
|
+
if (brand === undefined || brand === "omk")
|
|
59
|
+
return "neon-grid";
|
|
58
60
|
return undefined;
|
|
59
61
|
}
|
|
60
62
|
export function resolveChatUi(requested, env = process.env) {
|
|
61
|
-
const raw = requested ?? env.OMK_UI ?? env.OMK_CHAT_UI ?? "
|
|
63
|
+
const raw = requested ?? env.OMK_UI ?? env.OMK_CHAT_UI ?? "neon-grid";
|
|
62
64
|
const normalized = raw.trim().toLowerCase();
|
|
63
65
|
if (normalized === "plain-modern" || normalized === "modern" || normalized === "agent-console")
|
|
64
66
|
return "plain-modern";
|
|
@@ -325,7 +325,8 @@ export async function renderCockpit(options = {}) {
|
|
|
325
325
|
let latestRunName = options.runId ?? null;
|
|
326
326
|
let stateContent = null;
|
|
327
327
|
let sessionMeta = null;
|
|
328
|
-
|
|
328
|
+
const shouldLoadRunState = !quick || Boolean(latestRunName);
|
|
329
|
+
if (shouldLoadRunState && await pathExists(runsDir)) {
|
|
329
330
|
if (!latestRunName) {
|
|
330
331
|
const entries = await readdir(runsDir, { withFileTypes: true });
|
|
331
332
|
const dirs = entries.filter((e) => e.isDirectory());
|
|
@@ -179,7 +179,7 @@ export async function runtimeChecks(resources) {
|
|
|
179
179
|
});
|
|
180
180
|
const currentVersion = getOmkVersionSync();
|
|
181
181
|
try {
|
|
182
|
-
const latest = execSync("npm view
|
|
182
|
+
const latest = execSync("npm view open-multi-agent-kit version", {
|
|
183
183
|
encoding: "utf-8",
|
|
184
184
|
stdio: ["pipe", "pipe", "ignore"],
|
|
185
185
|
timeout: 5000,
|
|
@@ -188,7 +188,7 @@ export async function runtimeChecks(resources) {
|
|
|
188
188
|
results.push({
|
|
189
189
|
name: "OMK Version",
|
|
190
190
|
status: "warn",
|
|
191
|
-
message: `${currentVersion} → ${latest} available. Run: npm i -g
|
|
191
|
+
message: `${currentVersion} → ${latest} available. Run: npm i -g open-multi-agent-kit`,
|
|
192
192
|
});
|
|
193
193
|
}
|
|
194
194
|
else {
|
|
@@ -286,9 +286,9 @@ export async function initCommand(options) {
|
|
|
286
286
|
console.log("");
|
|
287
287
|
console.log(style.orange("⚠️ omk is not in PATH."));
|
|
288
288
|
console.log(style.gray(" Run one of the following:"));
|
|
289
|
-
console.log(style.gray(" 1) npm install -g
|
|
289
|
+
console.log(style.gray(" 1) npm install -g open-multi-agent-kit"));
|
|
290
290
|
console.log(style.gray(" 2) npm link (for development)"));
|
|
291
|
-
console.log(style.gray(" 3) alias omk='npx -p
|
|
291
|
+
console.log(style.gray(" 3) alias omk='npx -p open-multi-agent-kit omk'"));
|
|
292
292
|
}
|
|
293
293
|
else {
|
|
294
294
|
await maybeInstallShellCompletion(root);
|
package/dist/commands/init.js
CHANGED
|
@@ -2366,9 +2366,9 @@ export async function initCommand(options) {
|
|
|
2366
2366
|
console.log("");
|
|
2367
2367
|
console.log(style.orange("⚠️ omk is not in PATH."));
|
|
2368
2368
|
console.log(style.gray(" Run one of the following:"));
|
|
2369
|
-
console.log(style.gray(" 1) npm install -g
|
|
2369
|
+
console.log(style.gray(" 1) npm install -g open-multi-agent-kit"));
|
|
2370
2370
|
console.log(style.gray(" 2) npm link (for development)"));
|
|
2371
|
-
console.log(style.gray(" 3) alias omk='npx -p
|
|
2371
|
+
console.log(style.gray(" 3) alias omk='npx -p open-multi-agent-kit omk'"));
|
|
2372
2372
|
}
|
|
2373
2373
|
else {
|
|
2374
2374
|
await maybeInstallShellCompletion(root);
|
|
@@ -3,14 +3,19 @@ import { join } from "path";
|
|
|
3
3
|
import { getProjectRoot, pathExists, getUserHome, STALE_PACKAGE_NAMES, } from "../../util/fs.js";
|
|
4
4
|
import { style, header, status, label } from "../../util/theme.js";
|
|
5
5
|
import { maskSensitiveText } from "../../util/secret-mask.js";
|
|
6
|
-
import { formatArgsForDisplay, isHttpUrl, isSecretEnvName, loadConfig, RAILWAY_REMOTE_MCP_URL, sanitizeMcpServerForProject, sanitizeMcpUrlForDisplay, } from "./shared.js";
|
|
6
|
+
import { formatArgsForDisplay, isHttpUrl, isSecretEnvName, loadConfig, normalizeMcpServerName, RAILWAY_REMOTE_MCP_URL, sanitizeMcpServerForProject, sanitizeMcpUrlForDisplay, } from "./shared.js";
|
|
7
7
|
export async function mcpRemoveCommand(serverName, options = {}) {
|
|
8
|
+
const normalizedServerName = normalizeMcpServerName(serverName);
|
|
8
9
|
const root = getProjectRoot();
|
|
9
10
|
const localPath = join(root, ".kimi", "mcp.json");
|
|
10
11
|
const omkPath = join(root, ".omk", "mcp.json");
|
|
11
12
|
const globalPath = join(getUserHome(), ".kimi", "mcp.json");
|
|
12
13
|
const sources = options.global
|
|
13
|
-
? [
|
|
14
|
+
? [
|
|
15
|
+
{ path: join(getUserHome(), ".omk", "agent", "mcp.json"), label: "global-agent" },
|
|
16
|
+
{ path: globalPath, label: "global" },
|
|
17
|
+
{ path: join(getUserHome(), ".omk", "mcp.json"), label: "global-omk" },
|
|
18
|
+
]
|
|
14
19
|
: [
|
|
15
20
|
{ path: localPath, label: "project-local" },
|
|
16
21
|
{ path: omkPath, label: "omk-project" },
|
|
@@ -18,50 +23,67 @@ export async function mcpRemoveCommand(serverName, options = {}) {
|
|
|
18
23
|
let removed = false;
|
|
19
24
|
for (const src of sources) {
|
|
20
25
|
const cfg = await loadConfig(src.path);
|
|
21
|
-
if (!cfg.parsed || !cfg.config.mcpServers || !(
|
|
26
|
+
if (!cfg.parsed || !cfg.config.mcpServers || !(normalizedServerName in cfg.config.mcpServers)) {
|
|
22
27
|
continue;
|
|
23
28
|
}
|
|
24
29
|
const next = { ...cfg.config.mcpServers };
|
|
25
|
-
delete next[
|
|
30
|
+
delete next[normalizedServerName];
|
|
26
31
|
await writeFile(src.path, JSON.stringify({ mcpServers: next }, null, 2) + "\n", "utf-8");
|
|
27
|
-
console.log(status.ok(`Removed "${
|
|
32
|
+
console.log(status.ok(`Removed "${normalizedServerName}" from ${src.label} MCP config: ${src.path}`));
|
|
28
33
|
removed = true;
|
|
29
34
|
break;
|
|
30
35
|
}
|
|
31
36
|
if (!removed) {
|
|
32
|
-
console.log(status.error(`Server "${
|
|
37
|
+
console.log(status.error(`Server "${normalizedServerName}" not found in ${options.global ? "global" : "project-local"} MCP configs.`));
|
|
33
38
|
console.log(style.gray(`Checked: ${sources.map((s) => s.path).join(", ")}`));
|
|
34
39
|
if (!options.global) {
|
|
35
|
-
console.log(style.gray(`To remove from global config, run \`omk mcp remove ${
|
|
40
|
+
console.log(style.gray(`To remove from global config, run \`omk mcp remove ${normalizedServerName} --global\`, or edit ~/.kimi/mcp.json manually.`));
|
|
36
41
|
}
|
|
37
42
|
else {
|
|
38
|
-
console.log(style.gray(`To remove from project-local configs, run \`omk mcp remove ${
|
|
43
|
+
console.log(style.gray(`To remove from project-local configs, run \`omk mcp remove ${normalizedServerName}\`.`));
|
|
39
44
|
}
|
|
40
45
|
process.exit(1);
|
|
41
46
|
}
|
|
42
47
|
}
|
|
43
48
|
export async function mcpAddCommand(serverName) {
|
|
49
|
+
const normalizedServerName = normalizeMcpServerName(serverName);
|
|
44
50
|
const root = getProjectRoot();
|
|
45
|
-
const
|
|
51
|
+
const home = getUserHome();
|
|
52
|
+
const globalPaths = [
|
|
53
|
+
join(home, ".omk", "agent", "mcp.json"),
|
|
54
|
+
join(home, ".kimi", "mcp.json"),
|
|
55
|
+
join(home, ".omk", "mcp.json"),
|
|
56
|
+
];
|
|
46
57
|
const projectMcpPath = join(root, ".kimi", "mcp.json");
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
58
|
+
let selected;
|
|
59
|
+
let selectedPath = "";
|
|
60
|
+
for (const globalPath of globalPaths) {
|
|
61
|
+
const globalSource = await loadConfig(globalPath);
|
|
62
|
+
if (globalSource.parsed && globalSource.config.mcpServers && normalizedServerName in globalSource.config.mcpServers) {
|
|
63
|
+
selected = globalSource.config.mcpServers[normalizedServerName];
|
|
64
|
+
selectedPath = globalPath;
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
if (!selected) {
|
|
69
|
+
console.log(status.error(`Server "${normalizedServerName}" not found in trusted global MCP configs.`));
|
|
70
|
+
console.log(style.gray(`Checked: ${globalPaths.join(", ")}`));
|
|
50
71
|
process.exit(1);
|
|
51
72
|
}
|
|
52
73
|
const projectMcpSource = await loadConfig(projectMcpPath);
|
|
53
74
|
const projectMcpServers = projectMcpSource.parsed && projectMcpSource.config.mcpServers
|
|
54
75
|
? { ...projectMcpSource.config.mcpServers }
|
|
55
76
|
: {};
|
|
56
|
-
if (
|
|
57
|
-
console.log(status.error(`Server "${
|
|
77
|
+
if (normalizedServerName in projectMcpServers) {
|
|
78
|
+
console.log(status.error(`Server "${normalizedServerName}" already exists in ${projectMcpPath}`));
|
|
58
79
|
process.exit(1);
|
|
59
80
|
}
|
|
60
|
-
projectMcpServers[
|
|
81
|
+
projectMcpServers[normalizedServerName] = sanitizeMcpServerForProject(selected);
|
|
61
82
|
await mkdir(join(root, ".kimi"), { recursive: true });
|
|
62
83
|
await writeFile(projectMcpPath, JSON.stringify({ mcpServers: projectMcpServers }, null, 2) + "\n", "utf-8");
|
|
63
84
|
console.log(header("MCP Add"));
|
|
64
|
-
console.log(status.ok(`Added "${
|
|
85
|
+
console.log(status.ok(`Added "${normalizedServerName}" to ${projectMcpPath}`));
|
|
86
|
+
console.log(label("Source", selectedPath));
|
|
65
87
|
}
|
|
66
88
|
export async function mcpInstallCommand(name, command, args, options = {}) {
|
|
67
89
|
const root = getProjectRoot();
|
|
@@ -4,7 +4,7 @@ import { collectMcpConfigs, diagnoseRuntimeMcpServer, pathExists, getUserHome, p
|
|
|
4
4
|
import { getOmkResourceSettings } from "../../util/resource-profile.js";
|
|
5
5
|
import { style, header, status, label } from "../../util/theme.js";
|
|
6
6
|
import { maskSensitiveText } from "../../util/secret-mask.js";
|
|
7
|
-
import { basenameOfCommand, collectServers, isHttpUrl, isRailwayMcpServer, isSupabaseMcpServer, RAILWAY_REMOTE_MCP_URL, resolveAllConfigs, sanitizeMcpUrlForDisplay, selectEffectiveServer, serverTargetText, validateRemoteMcpUrl, } from "./shared.js";
|
|
7
|
+
import { basenameOfCommand, collectServers, isHttpUrl, isRailwayMcpServer, isSupabaseMcpServer, normalizeMcpEnv, RAILWAY_REMOTE_MCP_URL, resolveAllConfigs, sanitizeMcpUrlForDisplay, selectEffectiveServer, serverTargetText, validateRemoteMcpUrl, } from "./shared.js";
|
|
8
8
|
import { repairMcpDoctorIssues } from "./doctor-fix.js";
|
|
9
9
|
export async function mcpDoctorCommand(options = {}) {
|
|
10
10
|
const fixes = options.fix
|
|
@@ -149,13 +149,12 @@ export async function buildMcpDoctorReport() {
|
|
|
149
149
|
: { severity: "error", kind: "arg-path-not-found", message: `arg path not found: ${displayArg}` });
|
|
150
150
|
}
|
|
151
151
|
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
}
|
|
152
|
+
const env = normalizeMcpEnv(server.env);
|
|
153
|
+
for (const [key, value] of Object.entries(env)) {
|
|
154
|
+
if (value.startsWith("${") && value.endsWith("}")) {
|
|
155
|
+
const envName = value.slice(2, -1);
|
|
156
|
+
if (!process.env[envName]) {
|
|
157
|
+
checks.push({ severity: "warn", kind: "env-reference-undefined", message: `env reference undefined: ${key} → ${envName}` });
|
|
159
158
|
}
|
|
160
159
|
}
|
|
161
160
|
}
|
|
@@ -2,7 +2,7 @@ import { collectMcpConfigs } from "../../util/fs.js";
|
|
|
2
2
|
import { getOmkResourceSettings } from "../../util/resource-profile.js";
|
|
3
3
|
import { style, header, bullet } from "../../util/theme.js";
|
|
4
4
|
import { maskSensitiveText } from "../../util/secret-mask.js";
|
|
5
|
-
import { collectServers, formatArgsForDisplay, resolveAllConfigs, sanitizeMcpUrlForDisplay, selectEffectiveServer, } from "./shared.js";
|
|
5
|
+
import { collectServers, formatArgsForDisplay, formatMcpEnvKeys, resolveAllConfigs, sanitizeMcpUrlForDisplay, selectEffectiveServer, } from "./shared.js";
|
|
6
6
|
export async function mcpListCommand() {
|
|
7
7
|
const sources = await resolveAllConfigs();
|
|
8
8
|
const servers = collectServers(sources);
|
|
@@ -41,8 +41,9 @@ export async function mcpListCommand() {
|
|
|
41
41
|
if (server.args && server.args.length > 0) {
|
|
42
42
|
console.log(` ${style.gray("args:")} ${formatArgsForDisplay(server.args)}`);
|
|
43
43
|
}
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
const envKeys = formatMcpEnvKeys(server.env);
|
|
45
|
+
if (envKeys.length > 0) {
|
|
46
|
+
console.log(` ${style.gray("env:")} ${envKeys.join(", ")}`);
|
|
46
47
|
}
|
|
47
48
|
console.log(` ${style.gray("from:")} ${info.sources.join(", ")}`);
|
|
48
49
|
if (activeSources.length > 0) {
|
|
@@ -2,7 +2,7 @@ export interface McpServerConfig {
|
|
|
2
2
|
url?: string;
|
|
3
3
|
command?: string;
|
|
4
4
|
args?: string[];
|
|
5
|
-
env?: Record<string, string
|
|
5
|
+
env?: Record<string, string> | string[];
|
|
6
6
|
headers?: Record<string, string>;
|
|
7
7
|
http_headers?: Record<string, string>;
|
|
8
8
|
startup_timeout_sec?: number;
|
|
@@ -25,6 +25,7 @@ export interface CollectedMcpServer {
|
|
|
25
25
|
}
|
|
26
26
|
export declare const RAILWAY_REMOTE_MCP_URL = "https://mcp.railway.com";
|
|
27
27
|
export declare function loadConfig(filePath: string): Promise<ConfigSource>;
|
|
28
|
+
export declare function normalizeMcpServerName(name: string): string;
|
|
28
29
|
export declare function resolveAllConfigs(): Promise<ConfigSource[]>;
|
|
29
30
|
export declare function collectServers(sources: ConfigSource[]): Map<string, CollectedMcpServer>;
|
|
30
31
|
export declare function selectEffectiveServer(info: CollectedMcpServer, activePathOrder: string[]): McpServerConfig;
|
|
@@ -41,6 +42,8 @@ export declare function serverTargetText(server: McpServerConfig): string;
|
|
|
41
42
|
export declare function isRailwayMcpServer(name: string, server: McpServerConfig): boolean;
|
|
42
43
|
export declare function isSupabaseMcpServer(name: string, server: McpServerConfig): boolean;
|
|
43
44
|
export declare function formatArgsForDisplay(args: string[]): string;
|
|
45
|
+
export declare function normalizeMcpEnv(env: McpServerConfig["env"]): Record<string, string>;
|
|
46
|
+
export declare function formatMcpEnvKeys(env: McpServerConfig["env"]): string[];
|
|
44
47
|
export declare function sanitizeMcpServerForProject(server: McpServerConfig): McpServerConfig;
|
|
45
48
|
export declare function sanitizeMcpArgsForProject(args: string[]): string[];
|
|
46
49
|
export declare function isHttpUrl(value: string): boolean;
|
|
@@ -17,13 +17,18 @@ export async function loadConfig(filePath) {
|
|
|
17
17
|
return { path: filePath, config: {}, exists: true, parsed: false, error: `Invalid JSON: ${message}` };
|
|
18
18
|
}
|
|
19
19
|
}
|
|
20
|
+
export function normalizeMcpServerName(name) {
|
|
21
|
+
return name.trim().toLowerCase() === "adptorch" ? "adaptorch" : name;
|
|
22
|
+
}
|
|
20
23
|
export async function resolveAllConfigs() {
|
|
21
24
|
const root = getProjectRoot();
|
|
25
|
+
const home = getUserHome();
|
|
22
26
|
const paths = [
|
|
23
27
|
join(root, ".omk", "mcp.json"),
|
|
24
28
|
join(root, ".kimi", "mcp.json"),
|
|
25
|
-
join(
|
|
26
|
-
join(
|
|
29
|
+
join(home, ".omk", "agent", "mcp.json"),
|
|
30
|
+
join(home, ".kimi", "mcp.json"),
|
|
31
|
+
join(home, ".omk", "mcp.json"),
|
|
27
32
|
];
|
|
28
33
|
const results = [];
|
|
29
34
|
for (const p of paths) {
|
|
@@ -116,6 +121,19 @@ export function isSupabaseMcpServer(name, server) {
|
|
|
116
121
|
export function formatArgsForDisplay(args) {
|
|
117
122
|
return sanitizeMcpArgsForProject(args).join(" ");
|
|
118
123
|
}
|
|
124
|
+
export function normalizeMcpEnv(env) {
|
|
125
|
+
if (!env)
|
|
126
|
+
return {};
|
|
127
|
+
if (Array.isArray(env)) {
|
|
128
|
+
return Object.fromEntries(env
|
|
129
|
+
.filter((key) => typeof key === "string" && /^[A-Za-z_][A-Za-z0-9_]*$/.test(key))
|
|
130
|
+
.map((key) => [key, `\${${key}}`]));
|
|
131
|
+
}
|
|
132
|
+
return env;
|
|
133
|
+
}
|
|
134
|
+
export function formatMcpEnvKeys(env) {
|
|
135
|
+
return Object.keys(normalizeMcpEnv(env));
|
|
136
|
+
}
|
|
119
137
|
export function sanitizeMcpServerForProject(server) {
|
|
120
138
|
const cleaned = JSON.parse(JSON.stringify(server));
|
|
121
139
|
if (typeof cleaned.url === "string") {
|
|
@@ -129,7 +147,7 @@ export function sanitizeMcpServerForProject(server) {
|
|
|
129
147
|
}
|
|
130
148
|
if (cleaned.env && typeof cleaned.env === "object") {
|
|
131
149
|
const env = {};
|
|
132
|
-
for (const [key, value] of Object.entries(cleaned.env)) {
|
|
150
|
+
for (const [key, value] of Object.entries(normalizeMcpEnv(cleaned.env))) {
|
|
133
151
|
env[key] = isSecretEnvName(key) ? `\${${key}}` : value;
|
|
134
152
|
}
|
|
135
153
|
cleaned.env = env;
|