@epoch-agent/runtime 0.2.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +60 -34
- package/dist/index.d.ts +750 -56
- package/dist/index.js +849 -74
- package/package.json +9 -9
package/dist/index.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { loadCommandDefinitions, SLASH_COMMANDS_DIAG_MODULE, createToolScope, expandCommand, RecentWorkspaces, clearModelCache, discoverModels, ScheduleStore, isWithinWindow, acquireScheduleLock, triggerPeriodMs, ScheduleRecorder, pruneRecordings, suggestRuleFromApproval, needsAllowlist, isNonInteractive, loadPlugins, assertLevelAllowed, discoverProject, ProviderRouter, ProviderError, PROVIDER_NO_CREDENTIAL, SessionManager, GoalService, MemoryManager, MemoryReviewer, resolveSkillDirs, SkillSystem, SkillLearner, HookManager, loadHookSources, ApprovalCache, PermissionManager, resolvePolicyDirs, resolvePoliciesDir, loadPolicyFiles, checkShadowing, compileRuleLists, ContextCompressor, compressionOptions, TrackerDB, getConfigOrigins, mapOperationType, affectedPaths, workspaceDisplayPath, persistParts, persistToolResults, ToolRegistry, createTodoTool, createGoalTool, createMemoryTool, createCodeExecTool, createRunCodeTool, createSkillViewTool, createToolSearchTool, createToolRevealState, createAskQuestionTool, resolveWorkspace, unloadedInstructionFiles, resolveProjectRoot, loadConfig, CodeSandbox, CheckpointManager, createDelegateTool, findRole, resolveInWorkspace, listWorkspaceFiles, loadRoleDefinitions, mergeRoles, AGENT_ROLE_DIAG_MODULE, builtinRoles, unknownRoleTools, BLOCKED_TOOLS, blockedRoleTools, unknownRoleSkills, BudgetStore, mergeHeadlessPolicy, describeHeadlessPolicy, importSkills, previewSkillImport, removeSessionArtifacts, gateForTool, managedSwitchNotes, originOf, EpochConfigSchema, defaultConfig, SettingsFileSchema, isWritableSettingKey, writeSettingValue, SETTING_WRITE_APPLY, resolveSettings, SETTING_WRITE_LAYERS, readProviderKeyEnv, validateSchedule, createAllowAllGate, setTrustGate, TrustManager, createTrustGate, createDenyAllGate, getImportGate, setImportGate, createAllowAllImportGate, ImportTrustStore, createDenyAllImportGate, createImportGate, getCapability, restoreParts, createSessionSearchTools, PlanModeState, createPlanModeTools, toolTableTokens, explainSetting, settingWriteTargets, uninstallPlugin, readMarketplaces, searchMarketplaces, resolveEntrySource, resolveSource, readPluginRecords, createRoleFile, AgentLoop, resolveArtifactRetention, roleScopedProvider, createRoleScope, revealScopedProvider, withGoalPrompt, withGoalCompression, ScheduleRegistrar, resolveScheduleBackend, updateMarketplace,
|
|
1
|
+
import { loadCommandDefinitions, SLASH_COMMANDS_DIAG_MODULE, createToolScope, expandCommand, RecentWorkspaces, clearModelCache, discoverModels, substitutePluginRoot, ScheduleStore, isWithinWindow, acquireScheduleLock, triggerPeriodMs, ScheduleRecorder, pruneRecordings, suggestRuleFromApproval, needsAllowlist, isNonInteractive, loadPlugins, assertLevelAllowed, discoverProject, ProviderRouter, ProviderError, PROVIDER_NO_CREDENTIAL, SessionManager, GoalService, MemoryManager, MemoryReviewer, resolveSkillDirs, SkillSystem, SkillLearner, HookManager, loadHookSources, ApprovalCache, PermissionManager, resolvePolicyDirs, resolvePoliciesDir, loadPolicyFiles, checkShadowing, compileRuleLists, ContextCompressor, compressionOptions, TrackerDB, getConfigOrigins, resolveComplianceDirs, resolveComplianceDir, loadLexiconFiles, ComplianceEngine, DEFAULT_COMPLIANCE_SETTINGS, mapOperationType, affectedPaths, workspaceDisplayPath, persistParts, persistToolResults, ToolRegistry, createTodoTool, createGoalTool, createMemoryTool, createCodeExecTool, createRunCodeTool, createSkillViewTool, createSkillWriteTools, createToolSearchTool, createToolRevealState, createAskQuestionTool, resolveWorkspace, unloadedInstructionFiles, resolveProjectRoot, loadConfig, CodeSandbox, CheckpointManager, createDelegateTool, findRole, persistedUsage, systemOpenArgv, resolveInWorkspace, listWorkspaceFiles, loadRoleDefinitions, mergeRoles, AGENT_ROLE_DIAG_MODULE, builtinRoles, unknownRoleTools, BLOCKED_TOOLS, blockedRoleTools, unknownRoleSkills, BudgetStore, mergeHeadlessPolicy, describeHeadlessPolicy, importSkills, previewSkillImport, removeSessionArtifacts, gateForTool, managedSwitchNotes, originOf, EpochConfigSchema, defaultConfig, SettingsFileSchema, isWritableSettingKey, writeSettingValue, SETTING_WRITE_APPLY, resolveSettings, SETTING_WRITE_LAYERS, readProviderKeyEnv, validateSchedule, createAllowAllGate, setTrustGate, TrustManager, createTrustGate, createDenyAllGate, getImportGate, setImportGate, createAllowAllImportGate, ImportTrustStore, createDenyAllImportGate, createImportGate, getCapability, restoreParts, createSessionSearchTools, PlanModeState, createPlanModeTools, toolTableTokens, explainSetting, settingWriteTargets, settingValueKind, settingValueChoices, uninstallPlugin, removeMarketplace, addMarketplace, readMarketplaces, searchMarketplaces, resolveEntrySource, resolveSource, readPluginRecords, createRoleFile, AgentLoop, resolveArtifactRetention, roleScopedProvider, createRoleScope, revealScopedProvider, withGoalPrompt, withGoalCompression, ScheduleRegistrar, resolveScheduleBackend, updateMarketplace, SETTINGS_LAYER_ORDER, resolveMarketplaceRef, BudgetGuard, installPlugin, currentEpochVersion } from '@epoch-agent/core';
|
|
2
2
|
export { DEFAULT_GOAL_MAX_ROUNDS, MAX_GOAL_MAX_ROUNDS, MAX_OBJECTIVE_CHARS, MAX_REASON_CHARS, SANDBOX_COVERS, SANDBOX_EXCLUDES, SCHEDULE_DEFAULTS, SCHEDULE_DEFAULT_ALLOWLIST, nextRunAt, readRecording, skillIndexDescription } from '@epoch-agent/core';
|
|
3
|
-
import { projectCommandsDir, commandsDir, t, issueDetails, resolveHomeDir, configPath, readKey, setScalar, setSectionField, mcpConfigPath, workspacesPath, dbPath, automationLogsDir, pluginsStatePath, safeInit, memoriesDir, skillsDir, claudeUserSettingsPath, hooksPath, artifactsDir, resolveRipgrep, withTimeout, createProcessTable, marketplacesPath, pluginsDir, agentsDir, budgetStatePath, createSecretStore, setSecretStore, prefetchProviderSecrets, setSecretValues, migrateEnvSecrets,
|
|
3
|
+
import { projectCommandsDir, commandsDir, t, issueDetails, resolveHomeDir, configPath, readKey, setScalar, setSectionField, mcpConfigPath, workspacesPath, writeProviderSecret, envPath, maskApiKey, dbPath, automationLogsDir, pluginsStatePath, safeInit, memoriesDir, skillsDir, claudeUserSettingsPath, hooksPath, artifactsDir, resolveRipgrep, withTimeout, createProcessTable, marketplacesPath, pluginsDir, agentsDir, budgetStatePath, createSecretStore, setSecretStore, prefetchProviderSecrets, setSecretValues, migrateEnvSecrets, setDataKey, parseDataKey, generateDataKey, i18nDiagnostics, automationWorkDir, trustPath, trustedImportsPath, setShell, probeShell } from '@epoch-agent/infra';
|
|
4
4
|
export { t } from '@epoch-agent/infra';
|
|
5
|
-
import { isAbsolute, dirname, delimiter, resolve, join } from 'path';
|
|
5
|
+
import { isAbsolute, dirname, delimiter, resolve, join, relative, sep } from 'path';
|
|
6
6
|
import { AGENT_ROLE_NAME_PATTERN, isValidAgentRoleName, apiKeyEnvVar, getProviderInfo, PROVIDER_INFOS, parseModelRef, contentToText, assistantTurnText, systemNote, ToolExposure, isMentionableFilePath, DiagnosticSink, METRIC, API_KEY_ENV_VARS, MCP_DATA_KEY_NAME, isPermissionLevel, NOOP_TELEMETRY, DEFAULT_AGENT_ROLE } from '@epoch-agent/protocol';
|
|
7
|
-
import { existsSync, mkdirSync, writeFileSync, readFileSync, statSync, renameSync, rmSync, appendFileSync } from 'fs';
|
|
7
|
+
import { existsSync, mkdirSync, writeFileSync, readFileSync, statSync, renameSync, rmSync, realpathSync, openSync, readSync, closeSync, appendFileSync } from 'fs';
|
|
8
8
|
import { randomUUID, createHash } from 'crypto';
|
|
9
9
|
import { parseMcpConfig, McpRegistry, McpAuthStore, mcpAuthStatus, loginToMcpServer, logoutFromMcpServer } from '@epoch-agent/plugin-mcp';
|
|
10
10
|
import { filePlugin } from '@epoch-agent/plugin-file';
|
|
@@ -58,7 +58,7 @@ function wireCommands(opts) {
|
|
|
58
58
|
"no-command-loaded"
|
|
59
59
|
);
|
|
60
60
|
}
|
|
61
|
-
const available = new Set(toolProvider.list().map((
|
|
61
|
+
const available = new Set(toolProvider.list().map((t25) => t25.name));
|
|
62
62
|
for (const cmd of summary.commands) {
|
|
63
63
|
const unknown = (cmd.allowedTools ?? []).filter((name) => !available.has(name));
|
|
64
64
|
if (unknown.length > 0) {
|
|
@@ -589,6 +589,223 @@ var init_host_preset = __esm({
|
|
|
589
589
|
HOST_PRESET_MODULE = "HostPreset";
|
|
590
590
|
}
|
|
591
591
|
});
|
|
592
|
+
function fail(message, start, code, suggestion) {
|
|
593
|
+
return {
|
|
594
|
+
success: false,
|
|
595
|
+
output: "",
|
|
596
|
+
error: { code, message, ...suggestion ? { suggestion } : {} },
|
|
597
|
+
duration: Date.now() - start
|
|
598
|
+
};
|
|
599
|
+
}
|
|
600
|
+
function done(output, start) {
|
|
601
|
+
return { success: true, output, duration: Date.now() - start };
|
|
602
|
+
}
|
|
603
|
+
function str(args, key) {
|
|
604
|
+
const raw = args[key];
|
|
605
|
+
return typeof raw === "string" ? raw : "";
|
|
606
|
+
}
|
|
607
|
+
function toAddInput(args) {
|
|
608
|
+
const name = str(args, "name").trim();
|
|
609
|
+
if (!name) return "name must not be empty";
|
|
610
|
+
const transport = str(args, "transport").trim();
|
|
611
|
+
if (transport !== "stdio" && transport !== "sse" && transport !== "http") {
|
|
612
|
+
return `transport must be one of "stdio" / "sse" / "http", got ${JSON.stringify(transport)}`;
|
|
613
|
+
}
|
|
614
|
+
const rawArgs = args["args"];
|
|
615
|
+
if (rawArgs !== void 0 && !isStringArray(rawArgs)) {
|
|
616
|
+
return "args must be an array of strings";
|
|
617
|
+
}
|
|
618
|
+
const rawEnv = args["env"];
|
|
619
|
+
if (rawEnv !== void 0 && !isStringRecord(rawEnv)) {
|
|
620
|
+
return "env must be an object whose values are all strings";
|
|
621
|
+
}
|
|
622
|
+
const command = str(args, "command").trim();
|
|
623
|
+
const url = str(args, "url").trim();
|
|
624
|
+
return {
|
|
625
|
+
name,
|
|
626
|
+
transport,
|
|
627
|
+
...command ? { command } : {},
|
|
628
|
+
...rawArgs === void 0 ? {} : { args: rawArgs },
|
|
629
|
+
...url ? { url } : {},
|
|
630
|
+
...rawEnv === void 0 ? {} : { env: rawEnv }
|
|
631
|
+
};
|
|
632
|
+
}
|
|
633
|
+
function isStringArray(value) {
|
|
634
|
+
return Array.isArray(value) && value.every((v) => typeof v === "string");
|
|
635
|
+
}
|
|
636
|
+
function isStringRecord(value) {
|
|
637
|
+
if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
|
|
638
|
+
return Object.values(value).every((v) => typeof v === "string");
|
|
639
|
+
}
|
|
640
|
+
function listTool(deps) {
|
|
641
|
+
return {
|
|
642
|
+
name: "mcp_list",
|
|
643
|
+
description: "\u5217\u51FA\u8FD9\u4E2A\u8FDB\u7A0B\u91CC\u6240\u6709 MCP server \u7684\u72B6\u6001\uFF1A\u540D\u5B57\u3001\u6765\u6E90\u3001\u8FDE\u4E0A\u6CA1\u6709\u3001\u51E0\u4E2A\u5DE5\u5177\u3001\u4E0A\u6B21\u9519\u5728\u54EA\u3002\u6539\u4E00\u53F0\u4E4B\u524D\u5148\u770B\u4E00\u773C\u3002\nsource \u51B3\u5B9A\u6539\u4E0D\u6539\u5F97\u52A8\uFF1A\u53EA\u6709 user \u90A3\u6279\u662F\u7528\u6237\u914D\u7684\uFF1Bhost / plugin \u4E24\u6279\u4E0D\u5728 mcp.json \u91CC\uFF0C\u6539\u4E0D\u4E86\u3002\n\u26A0\uFE0F \u4E0D\u56DE command / url / env \u2014\u2014 \u90A3\u51E0\u683C\u5E38\u85CF\u4EE4\u724C\uFF0C\u8981\u770B\u8BA9\u7528\u6237\u81EA\u5DF1\u6253\u5F00 ~/.epoch/mcp.json\u3002",
|
|
644
|
+
parameters: { type: "object", properties: {} },
|
|
645
|
+
exposure: ToolExposure.Direct,
|
|
646
|
+
supportsParallel: true,
|
|
647
|
+
annotations: { readOnlyHint: true, destructiveHint: false, idempotentHint: true },
|
|
648
|
+
operation: "file_read",
|
|
649
|
+
describeTarget: () => MCP_LIST_TOOL,
|
|
650
|
+
execute() {
|
|
651
|
+
const start = Date.now();
|
|
652
|
+
const rows = deps.servers();
|
|
653
|
+
if (rows.length === 0) {
|
|
654
|
+
return Promise.resolve(
|
|
655
|
+
done("No MCP servers are configured in this process. Use mcp_add to add one.", start)
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
const lines = rows.map((s) => {
|
|
659
|
+
const state = s.connected ? "connected" : "disconnected";
|
|
660
|
+
const login = s.needsLogin === true ? ", needs login" : "";
|
|
661
|
+
const err = s.lastError === void 0 ? "" : ` \u2014 ${s.lastError}`;
|
|
662
|
+
return `${s.name} [${s.source}] ${state}, ${s.toolCount} tools${login}${err}`;
|
|
663
|
+
});
|
|
664
|
+
return Promise.resolve(done(lines.join("\n"), start));
|
|
665
|
+
}
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
function runWrite(deps, args, kind) {
|
|
669
|
+
const start = Date.now();
|
|
670
|
+
const input = toAddInput(args);
|
|
671
|
+
if (typeof input === "string") return Promise.resolve(fail(input, start, 3201));
|
|
672
|
+
const call = kind === "add" ? deps.control.add(input) : deps.control.update(input);
|
|
673
|
+
return call.then((outcome) => {
|
|
674
|
+
if (!outcome.ok) {
|
|
675
|
+
return fail(
|
|
676
|
+
`${outcome.reason}: ${outcome.detail}`,
|
|
677
|
+
start,
|
|
678
|
+
3202,
|
|
679
|
+
HINT_BY_REASON[outcome.reason]
|
|
680
|
+
);
|
|
681
|
+
}
|
|
682
|
+
return done(describeWrite(kind, outcome.configPath, outcome.status), start);
|
|
683
|
+
});
|
|
684
|
+
}
|
|
685
|
+
function describeWrite(kind, configPath2, status) {
|
|
686
|
+
const verb = kind === "add" ? "Added" : "Updated";
|
|
687
|
+
const why = status.lastError === void 0 ? "" : ` \u2014 ${status.lastError}`;
|
|
688
|
+
const login = status.needsLogin === true ? " It needs a login: run `epoch mcp login`." : "";
|
|
689
|
+
const live = status.connected ? `It is connected and exposes ${status.toolCount} tools now.` : `It is NOT connected right now${why}. The config is saved either way \u2014 the server may just not be up yet.`;
|
|
690
|
+
return `${verb} MCP server "${status.name}" in ${configPath2}. ${live}${login}`;
|
|
691
|
+
}
|
|
692
|
+
function addTool(deps) {
|
|
693
|
+
return {
|
|
694
|
+
name: "mcp_add",
|
|
695
|
+
description: "\u52A0\u4E00\u53F0 MCP server\uFF1A\u5199\u8FDB ~/.epoch/mcp.json\uFF0C\u7136\u540E\u5F53\u573A\u8FDE\u4E00\u6B21\u3002\n\u26A0\uFE0F \u5F71\u54CD\u7684\u4E0D\u53EA\u662F\u8FD9\u6BB5\u4F1A\u8BDD \u2014\u2014 \u90A3\u53F0\u4EE5\u540E\u6BCF\u4E2A epoch \u8FDB\u7A0B\u90FD\u4F1A\u8FDE\u5B83\uFF08stdio \u90A3\u6863\u8981\u8D77\u5B50\u8FDB\u7A0B\uFF09\u3002\u52A0\u4E4B\u524D\u5148\u8DDF\u7528\u6237\u8BF4\u6E05\u4F60\u8981\u52A0\u4EC0\u4E48\u3002\n\u8FDE\u4E0D\u4E0A\u4E0D\u7B97\u5931\u8D25\uFF08\u914D\u7F6E\u5BF9\u3001\u5BF9\u9762\u6CA1\u8D77\u6765\u7167\u6837\u52A0\u5F97\u8FDB\u53BB\uFF0C\u56DE\u6267\u4F1A\u8BF4\u6E05\uFF09\u3002\u540D\u5B57\u88AB\u5360\u4F1A\u88AB\u62D2 \u2014\u2014 \u90A3\u65F6\u6539\u7528 mcp_update\u3002",
|
|
696
|
+
parameters: {
|
|
697
|
+
type: "object",
|
|
698
|
+
properties: SERVER_PROPERTIES,
|
|
699
|
+
required: ["name", "transport"]
|
|
700
|
+
},
|
|
701
|
+
exposure: ToolExposure.Direct,
|
|
702
|
+
supportsParallel: false,
|
|
703
|
+
annotations: ADMIN_ANNOTATIONS,
|
|
704
|
+
operation: "command",
|
|
705
|
+
describeTarget: (args) => `${MCP_ADD_TOOL}:${String(args["name"] ?? "")} ${String(args["command"] ?? args["url"] ?? "")}`.trim(),
|
|
706
|
+
execute: (args) => runWrite(deps, args, "add")
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
function updateTool(deps) {
|
|
710
|
+
return {
|
|
711
|
+
name: "mcp_update",
|
|
712
|
+
description: "\u6539\u4E00\u53F0\u5DF2\u6709 MCP server\uFF1A\u5C31\u5730\u6362\u6389 ~/.epoch/mcp.json \u91CC\u90A3\u4E00\u6BB5\uFF0C\u518D\u6309\u65B0\u914D\u7F6E\u91CD\u8FDE\u5B83\u3002\u522B\u7684 server \u548C\u6587\u4EF6\u91CC\u7684\u6CE8\u91CA\u6392\u7248\u4E00\u4E2A\u5B57\u8282\u4E0D\u52A8\u3002\n\u26A0\uFE0F \u6536\u7684\u662F**\u6574\u5957**\u914D\u7F6E\u4E0D\u662F\u8865\u4E01 \u2014\u2014 \u6CA1\u7ED9\u7684\u683C\u5B50\uFF08args / url / env\uFF09\u4F1A\u88AB\u5F53\u6210\u300C\u4E0D\u8981\u4E86\u300D\uFF0C\u8981\u4FDD\u4F4F\u7684\u6BCF\u6B21\u90FD\u5F97\u539F\u6837\u518D\u7ED9\u3002\n\u540D\u5B57\u6539\u4E0D\u4E86\uFF08\u90A3\u7B49\u4E8E\u6362\u4E00\u6279\u5DE5\u5177\u540D\uFF0C\u8981\u6539\u540D\u5C31 mcp_remove \u518D mcp_add\uFF09\u3002\u6587\u4EF6\u91CC\u6CA1\u8FD9\u4E2A\u540D\u5B57\u4F1A\u88AB\u62D2\u3002",
|
|
713
|
+
parameters: {
|
|
714
|
+
type: "object",
|
|
715
|
+
properties: SERVER_PROPERTIES,
|
|
716
|
+
required: ["name", "transport"]
|
|
717
|
+
},
|
|
718
|
+
exposure: ToolExposure.Direct,
|
|
719
|
+
supportsParallel: false,
|
|
720
|
+
annotations: ADMIN_ANNOTATIONS,
|
|
721
|
+
operation: "command",
|
|
722
|
+
describeTarget: (args) => `${MCP_UPDATE_TOOL}:${String(args["name"] ?? "")} ${String(args["command"] ?? args["url"] ?? "")}`.trim(),
|
|
723
|
+
execute: (args) => runWrite(deps, args, "update")
|
|
724
|
+
};
|
|
725
|
+
}
|
|
726
|
+
function removeTool(deps) {
|
|
727
|
+
return {
|
|
728
|
+
name: "mcp_remove",
|
|
729
|
+
description: "\u5220\u6389\u4E00\u53F0 MCP server\uFF1A\u4ECE ~/.epoch/mcp.json \u91CC\u53BB\u6389\u90A3\u4E00\u6BB5\uFF0C\u5E76\u65AD\u5F00\u5B83\u3001\u628A\u5B83\u90A3\u6279\u5DE5\u5177\u6458\u6389\u3002\u522B\u7684 server \u548C\u6CE8\u91CA\u6392\u7248\u4E00\u4E2A\u5B57\u8282\u4E0D\u52A8\u3002\n\u26A0\uFE0F \u4E0D\u53EF\u64A4\u9500 \u2014\u2014 \u90A3\u4E00\u6BB5\u914D\u7F6E\uFF08\u542B env \u91CC\u7684\u4EE4\u724C\uFF09\u4ECE\u6587\u4EF6\u91CC\u6D88\u5931\uFF0C\u8981\u56DE\u6765\u53EA\u80FD\u91CD\u586B\u3002\u5220\u4E4B\u524D\u5148\u8DDF\u7528\u6237\u786E\u8BA4\u3002\n\u6587\u4EF6\u91CC\u6CA1\u8FD9\u4E2A\u540D\u5B57\u4F1A\u88AB\u62D2\uFF08host / plugin \u90A3\u4E24\u6279\u5220\u4E0D\u6389\uFF09\u3002",
|
|
730
|
+
parameters: {
|
|
731
|
+
type: "object",
|
|
732
|
+
properties: { name: SERVER_PROPERTIES.name },
|
|
733
|
+
required: ["name"]
|
|
734
|
+
},
|
|
735
|
+
exposure: ToolExposure.Direct,
|
|
736
|
+
supportsParallel: false,
|
|
737
|
+
annotations: ADMIN_ANNOTATIONS,
|
|
738
|
+
operation: "command",
|
|
739
|
+
describeTarget: (args) => `${MCP_REMOVE_TOOL}:${String(args["name"] ?? "")}`,
|
|
740
|
+
async execute(args) {
|
|
741
|
+
const start = Date.now();
|
|
742
|
+
const name = str(args, "name").trim();
|
|
743
|
+
if (!name) return fail("name must not be empty", start, 3201);
|
|
744
|
+
const outcome = await deps.control.remove(name);
|
|
745
|
+
if (!outcome.ok) {
|
|
746
|
+
return fail(
|
|
747
|
+
`${outcome.reason}: ${outcome.detail}`,
|
|
748
|
+
start,
|
|
749
|
+
3202,
|
|
750
|
+
HINT_BY_REASON[outcome.reason]
|
|
751
|
+
);
|
|
752
|
+
}
|
|
753
|
+
return done(
|
|
754
|
+
`removed MCP server "${name}" from ${outcome.configPath}. Its tools are gone from this process.`,
|
|
755
|
+
start
|
|
756
|
+
);
|
|
757
|
+
}
|
|
758
|
+
};
|
|
759
|
+
}
|
|
760
|
+
function createMcpTools(deps) {
|
|
761
|
+
return [listTool(deps), addTool(deps), updateTool(deps), removeTool(deps)];
|
|
762
|
+
}
|
|
763
|
+
var MCP_LIST_TOOL, MCP_ADD_TOOL, MCP_UPDATE_TOOL, MCP_REMOVE_TOOL, SERVER_PROPERTIES, ADMIN_ANNOTATIONS, HINT_BY_REASON;
|
|
764
|
+
var init_mcp_tools = __esm({
|
|
765
|
+
"src/mcp-tools.ts"() {
|
|
766
|
+
MCP_LIST_TOOL = "mcp_list";
|
|
767
|
+
MCP_ADD_TOOL = "mcp_add";
|
|
768
|
+
MCP_UPDATE_TOOL = "mcp_update";
|
|
769
|
+
MCP_REMOVE_TOOL = "mcp_remove";
|
|
770
|
+
SERVER_PROPERTIES = {
|
|
771
|
+
name: {
|
|
772
|
+
type: "string",
|
|
773
|
+
description: "server \u540D\u3002\u4F1A\u539F\u6837\u53D8\u6210\u5DE5\u5177\u540D\u7684\u4E00\u90E8\u5206\uFF08mcp__<name>__<\u5DE5\u5177>\uFF09"
|
|
774
|
+
},
|
|
775
|
+
transport: {
|
|
776
|
+
type: "string",
|
|
777
|
+
enum: ["stdio", "sse", "http"],
|
|
778
|
+
description: "\u4F20\u8F93\u7C7B\u578B\uFF1Astdio \u8D77\u4E00\u4E2A\u672C\u5730\u5B50\u8FDB\u7A0B\uFF0Csse / http \u8FDE\u4E00\u4E2A\u5730\u5740"
|
|
779
|
+
},
|
|
780
|
+
command: { type: "string", description: "stdio \u90A3\u4E00\u6863\u7684\u53EF\u6267\u884C\u6587\u4EF6\uFF0C\u5982 npx" },
|
|
781
|
+
args: {
|
|
782
|
+
type: "array",
|
|
783
|
+
items: { type: "string" },
|
|
784
|
+
description: "stdio \u90A3\u4E00\u6863\u7684\u547D\u4EE4\u884C\u53C2\u6570\uFF0C\u5143\u7D20\u5FC5\u987B\u5168\u662F\u5B57\u7B26\u4E32"
|
|
785
|
+
},
|
|
786
|
+
url: { type: "string", description: "sse / http \u90A3\u4E24\u6863\u7684\u5730\u5740" },
|
|
787
|
+
env: {
|
|
788
|
+
type: "object",
|
|
789
|
+
additionalProperties: { type: "string" },
|
|
790
|
+
description: "\u5B50\u8FDB\u7A0B\u73AF\u5883\u53D8\u91CF\uFF0C\u503C\u5FC5\u987B\u5168\u662F\u5B57\u7B26\u4E32"
|
|
791
|
+
}
|
|
792
|
+
};
|
|
793
|
+
ADMIN_ANNOTATIONS = {
|
|
794
|
+
readOnlyHint: false,
|
|
795
|
+
destructiveHint: true,
|
|
796
|
+
idempotentHint: false,
|
|
797
|
+
openWorldHint: true
|
|
798
|
+
};
|
|
799
|
+
HINT_BY_REASON = {
|
|
800
|
+
"invalid-name": 'Use only letters, digits, "_" and "-", starting with a letter or digit (max 48 chars) \u2014 the name becomes part of every tool name this server exposes.',
|
|
801
|
+
"invalid-config": 'stdio needs "command"; sse and http need a valid "url". Fix that one field and retry.',
|
|
802
|
+
duplicate: "That name is already taken \u2014 call mcp_list, then use mcp_update instead of mcp_add.",
|
|
803
|
+
"not-found": "That name is not in ~/.epoch/mcp.json \u2014 call mcp_list first, or use mcp_add.",
|
|
804
|
+
unwritable: "The config file could not be read, written, or parsed. Do not retry \u2014 tell the user to look at ~/.epoch/mcp.json themselves.",
|
|
805
|
+
"apply-failed": "The config file was written but this process could not be brought in line. The change takes effect on the next epoch start."
|
|
806
|
+
};
|
|
807
|
+
}
|
|
808
|
+
});
|
|
592
809
|
function providerLabel(info, lang) {
|
|
593
810
|
switch (info.type) {
|
|
594
811
|
case "ollama":
|
|
@@ -607,12 +824,16 @@ function buildModelCatalogControl(opts) {
|
|
|
607
824
|
};
|
|
608
825
|
return {
|
|
609
826
|
providers() {
|
|
610
|
-
return PROVIDER_INFOS.filter((p) => p.interactive).map((p) =>
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
827
|
+
return PROVIDER_INFOS.filter((p) => p.interactive).map((p) => {
|
|
828
|
+
const key = p.apiKeyEnv ? keyOf(p.apiKeyEnv) : "";
|
|
829
|
+
return {
|
|
830
|
+
type: p.type,
|
|
831
|
+
label: p.label,
|
|
832
|
+
envVar: p.apiKeyEnv,
|
|
833
|
+
hasKey: p.apiKeyEnv === null || key !== "",
|
|
834
|
+
keyHint: key === "" ? null : maskApiKey(key)
|
|
835
|
+
};
|
|
836
|
+
});
|
|
616
837
|
},
|
|
617
838
|
async discover(provider, discoverOpts) {
|
|
618
839
|
const envVar = getProviderInfo(provider)?.apiKeyEnv ?? null;
|
|
@@ -632,6 +853,19 @@ function buildModelCatalogControl(opts) {
|
|
|
632
853
|
source: result.source,
|
|
633
854
|
status: probeStatus(result, { needsKey: envVar !== null, hasKey: apiKey !== "" })
|
|
634
855
|
};
|
|
856
|
+
},
|
|
857
|
+
async setKey(provider, apiKey) {
|
|
858
|
+
const envVar = getProviderInfo(provider)?.apiKeyEnv ?? null;
|
|
859
|
+
if (!envVar) throw new Error(t("provider.key_not_applicable", { provider }));
|
|
860
|
+
const written = await writeProviderSecret(envVar, apiKey, envPath(homeDir));
|
|
861
|
+
clearModelCache(homeDir, provider);
|
|
862
|
+
return {
|
|
863
|
+
envVar,
|
|
864
|
+
backend: written.backend,
|
|
865
|
+
encrypted: written.encrypted,
|
|
866
|
+
envPath: written.envPath,
|
|
867
|
+
keyHint: maskApiKey(apiKey)
|
|
868
|
+
};
|
|
635
869
|
}
|
|
636
870
|
};
|
|
637
871
|
}
|
|
@@ -761,6 +995,15 @@ function createPluginControl(deps) {
|
|
|
761
995
|
}
|
|
762
996
|
return { ok: true, source: hit.ref.source, marketplace: hit.ref.marketplace };
|
|
763
997
|
};
|
|
998
|
+
const resolveLocal = (path, lang) => {
|
|
999
|
+
const refuse2 = {
|
|
1000
|
+
ok: false,
|
|
1001
|
+
reason: "not-local",
|
|
1002
|
+
detail: t("plugin_control.not_local_path", { path: path === "" ? "(empty)" : path }, lang)
|
|
1003
|
+
};
|
|
1004
|
+
if (path === "" || !isAbsolute(path)) return refuse2;
|
|
1005
|
+
return resolveSource(path)?.type === "local" ? { ok: true, source: path } : refuse2;
|
|
1006
|
+
};
|
|
764
1007
|
const recordOf = (name, lang) => {
|
|
765
1008
|
const record = readPluginRecords(deps.statePath).records.find((r) => r.name === name);
|
|
766
1009
|
if (!record) {
|
|
@@ -817,6 +1060,58 @@ function createPluginControl(deps) {
|
|
|
817
1060
|
const target = resolveRef(ref, lang);
|
|
818
1061
|
return target.ok ? mark(await flow.install(target.source, token, { marketplace: target.marketplace }, lang)) : target;
|
|
819
1062
|
},
|
|
1063
|
+
previewLocal: async (path, lang) => {
|
|
1064
|
+
const target = resolveLocal(path, lang);
|
|
1065
|
+
return target.ok ? flow.preview(target.source, {}, lang) : target;
|
|
1066
|
+
},
|
|
1067
|
+
installLocal: async (path, token, lang) => {
|
|
1068
|
+
const target = resolveLocal(path, lang);
|
|
1069
|
+
return target.ok ? mark(await flow.install(target.source, token, {}, lang)) : target;
|
|
1070
|
+
},
|
|
1071
|
+
marketplaces: () => readMarketplaces(deps.marketplacesPath).records.map((r) => ({
|
|
1072
|
+
name: r.name,
|
|
1073
|
+
source: r.source,
|
|
1074
|
+
entries: r.catalog.plugins.length,
|
|
1075
|
+
addedAt: r.addedAt,
|
|
1076
|
+
local: isLocalSource(r.source)
|
|
1077
|
+
})),
|
|
1078
|
+
addMarketplace: async (source, lang) => {
|
|
1079
|
+
const raw = source.trim();
|
|
1080
|
+
if (raw === "") {
|
|
1081
|
+
return {
|
|
1082
|
+
ok: false,
|
|
1083
|
+
reason: "unknown-ref",
|
|
1084
|
+
detail: t("plugin_control.market_source_empty", void 0, lang)
|
|
1085
|
+
};
|
|
1086
|
+
}
|
|
1087
|
+
if (!deps.allowRemote && !isLocalSource(raw)) {
|
|
1088
|
+
return {
|
|
1089
|
+
ok: false,
|
|
1090
|
+
reason: "remote-disabled",
|
|
1091
|
+
detail: t("plugin_control.market_add_remote_off", { source: raw }, lang)
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
const outcome = await addMarketplace(raw, { statePath: deps.marketplacesPath });
|
|
1095
|
+
if (!outcome.ok) return { ok: false, reason: "failed", detail: outcome.reason };
|
|
1096
|
+
return {
|
|
1097
|
+
ok: true,
|
|
1098
|
+
marketplace: {
|
|
1099
|
+
name: outcome.record.name,
|
|
1100
|
+
source: outcome.record.source,
|
|
1101
|
+
entries: outcome.record.catalog.plugins.length,
|
|
1102
|
+
addedAt: outcome.record.addedAt,
|
|
1103
|
+
local: isLocalSource(outcome.record.source)
|
|
1104
|
+
}
|
|
1105
|
+
};
|
|
1106
|
+
},
|
|
1107
|
+
removeMarketplace: async (name, lang) => {
|
|
1108
|
+
const outcome = await removeMarketplace(name, { statePath: deps.marketplacesPath });
|
|
1109
|
+
return outcome.ok ? { ok: true } : {
|
|
1110
|
+
ok: false,
|
|
1111
|
+
reason: "not-installed",
|
|
1112
|
+
detail: outcome.reason ?? t("plugin_control.market_missing", { name }, lang)
|
|
1113
|
+
};
|
|
1114
|
+
},
|
|
820
1115
|
previewUpdate: async (name, lang) => {
|
|
821
1116
|
const found = recordOf(name, lang);
|
|
822
1117
|
return found.ok ? flow.preview(found.record.source, replaceOpts(found.record), lang) : found;
|
|
@@ -871,6 +1166,7 @@ function readOne(ref, diags) {
|
|
|
871
1166
|
for (const issue of parsed.issues) {
|
|
872
1167
|
diags.warn(`${MODULE} ${issue.path}`, issue.message, "mcp-config-invalid");
|
|
873
1168
|
}
|
|
1169
|
+
const root = dirname(ref.path);
|
|
874
1170
|
const servers = [];
|
|
875
1171
|
for (const server of parsed.servers) {
|
|
876
1172
|
if (!AGENT_ROLE_NAME_PATTERN.test(server.name)) {
|
|
@@ -881,10 +1177,21 @@ function readOne(ref, diags) {
|
|
|
881
1177
|
);
|
|
882
1178
|
continue;
|
|
883
1179
|
}
|
|
884
|
-
servers.push({ ...server, name: `${ref.plugin}__${server.name}` });
|
|
1180
|
+
servers.push(withPluginRoot({ ...server, name: `${ref.plugin}__${server.name}` }, root));
|
|
885
1181
|
}
|
|
886
1182
|
return servers;
|
|
887
1183
|
}
|
|
1184
|
+
function withPluginRoot(server, root) {
|
|
1185
|
+
const out = { ...server };
|
|
1186
|
+
if (out.command !== void 0) out.command = substitutePluginRoot(out.command, root);
|
|
1187
|
+
if (out.args !== void 0) out.args = out.args.map((a) => substitutePluginRoot(a, root));
|
|
1188
|
+
if (out.env !== void 0) {
|
|
1189
|
+
out.env = Object.fromEntries(
|
|
1190
|
+
Object.entries(out.env).map(([k, v]) => [k, substitutePluginRoot(v, root)])
|
|
1191
|
+
);
|
|
1192
|
+
}
|
|
1193
|
+
return out;
|
|
1194
|
+
}
|
|
888
1195
|
function reason(err) {
|
|
889
1196
|
return err instanceof Error ? err.message : String(err);
|
|
890
1197
|
}
|
|
@@ -1204,7 +1511,7 @@ async function runOnce(store, def, opts, startedAt, now) {
|
|
|
1204
1511
|
});
|
|
1205
1512
|
return { status: "failed", exitCode: SCHEDULE_EXIT_CODES.FAILURE, run, detail: reason2 };
|
|
1206
1513
|
}
|
|
1207
|
-
const level = runtime.permissions.setLevel(def.permission);
|
|
1514
|
+
const level = runtime.permissions.setLevel(def.permission, { remember: false });
|
|
1208
1515
|
if (!level.ok) {
|
|
1209
1516
|
const reason2 = `managed-policy: ${def.permission}`;
|
|
1210
1517
|
const run = store.recordRun({
|
|
@@ -1498,7 +1805,6 @@ function resolveModelCapability(config, diags) {
|
|
|
1498
1805
|
const cap = getCapability(config.model, config.homeDir);
|
|
1499
1806
|
return {
|
|
1500
1807
|
contextLength: cap.contextLength,
|
|
1501
|
-
supportsImages: cap.supportsImages,
|
|
1502
1808
|
...cap.pricing ? { pricing: cap.pricing } : {}
|
|
1503
1809
|
};
|
|
1504
1810
|
} catch {
|
|
@@ -1507,7 +1813,7 @@ function resolveModelCapability(config, diags) {
|
|
|
1507
1813
|
t("boot.no_model_metadata", { model: config.model }),
|
|
1508
1814
|
"no-model-metadata"
|
|
1509
1815
|
);
|
|
1510
|
-
return { contextLength: config.contextLength
|
|
1816
|
+
return { contextLength: config.contextLength };
|
|
1511
1817
|
}
|
|
1512
1818
|
}
|
|
1513
1819
|
function reportUtilityModel(info, diags) {
|
|
@@ -1534,7 +1840,7 @@ function buildTrust(config, workDir, diags, interactive) {
|
|
|
1534
1840
|
const gate2 = createAllowAllGate(t("boot.trust_disabled"));
|
|
1535
1841
|
setTrustGate(gate2);
|
|
1536
1842
|
const trust2 = gate2.decide(workDir);
|
|
1537
|
-
diags.
|
|
1843
|
+
diags.skipped("Trust", trust2.reason);
|
|
1538
1844
|
const disabled = safeInit("Trust", () => new TrustManager(trustPath(config.homeDir)), diags);
|
|
1539
1845
|
for (const err of disabled?.loadErrors ?? []) diags.warn("Trust", err, "trust-load-error");
|
|
1540
1846
|
return { manager: disabled, trust: trust2, root, gate: gate2 };
|
|
@@ -1630,11 +1936,19 @@ function buildServices(baseConfig, diags, workDir = process.cwd(), opts = {}) {
|
|
|
1630
1936
|
}
|
|
1631
1937
|
}
|
|
1632
1938
|
if (provider) provider.refreshMetadata();
|
|
1633
|
-
const
|
|
1634
|
-
|
|
1635
|
-
|
|
1939
|
+
const router = provider;
|
|
1940
|
+
const providerBridge = router ? {
|
|
1941
|
+
generate: router.generate.bind(router),
|
|
1942
|
+
stream: router.stream.bind(router),
|
|
1636
1943
|
get capabilities() {
|
|
1637
|
-
|
|
1944
|
+
try {
|
|
1945
|
+
return { supportsImages: router.getCapability().supportsImages };
|
|
1946
|
+
} catch {
|
|
1947
|
+
return { supportsImages: false };
|
|
1948
|
+
}
|
|
1949
|
+
},
|
|
1950
|
+
get modelId() {
|
|
1951
|
+
return router.getSelection().model;
|
|
1638
1952
|
}
|
|
1639
1953
|
} : null;
|
|
1640
1954
|
const session = safeInit("Session", () => new SessionManager(config.dbPath), diags);
|
|
@@ -1765,6 +2079,7 @@ function buildServices(baseConfig, diags, workDir = process.cwd(), opts = {}) {
|
|
|
1765
2079
|
}
|
|
1766
2080
|
}
|
|
1767
2081
|
}
|
|
2082
|
+
const compliance = buildCompliance(config, projectContext.rootDir, trust.trusted, diags);
|
|
1768
2083
|
let contextEngine = null;
|
|
1769
2084
|
if (provider) {
|
|
1770
2085
|
reportUtilityModel(provider.getUtilityInfo(), diags);
|
|
@@ -1816,9 +2131,53 @@ function buildServices(baseConfig, diags, workDir = process.cwd(), opts = {}) {
|
|
|
1816
2131
|
policy: policyStatus,
|
|
1817
2132
|
hook: hookStatus,
|
|
1818
2133
|
plugins,
|
|
2134
|
+
compliance,
|
|
1819
2135
|
cleanups
|
|
1820
2136
|
};
|
|
1821
2137
|
}
|
|
2138
|
+
function buildCompliance(config, projectRoot, trusted, diags) {
|
|
2139
|
+
const settings = resolveComplianceSettings(config.compliance);
|
|
2140
|
+
if (!settings.enabled) {
|
|
2141
|
+
diags.skipped("Compliance", t("boot.compliance_off"));
|
|
2142
|
+
return null;
|
|
2143
|
+
}
|
|
2144
|
+
const rules = [];
|
|
2145
|
+
const exempt = [];
|
|
2146
|
+
try {
|
|
2147
|
+
const dirs = resolveComplianceDirs({
|
|
2148
|
+
userDir: resolveComplianceDir(config.homeDir),
|
|
2149
|
+
...projectRoot ? { projectRoot } : {},
|
|
2150
|
+
trusted,
|
|
2151
|
+
diags
|
|
2152
|
+
});
|
|
2153
|
+
for (const spec of dirs.dirs) {
|
|
2154
|
+
const loaded = loadLexiconFiles(spec.dir, spec.source, diags);
|
|
2155
|
+
rules.push(...loaded.rules);
|
|
2156
|
+
exempt.push(...loaded.exempt);
|
|
2157
|
+
}
|
|
2158
|
+
} catch {
|
|
2159
|
+
diags.warn("Compliance", t("boot.compliance_load_failed"), "compliance-load-failed");
|
|
2160
|
+
}
|
|
2161
|
+
const engine = new ComplianceEngine({ settings, rules, exempt });
|
|
2162
|
+
diags.ok(
|
|
2163
|
+
"Compliance",
|
|
2164
|
+
t("boot.compliance_ready", {
|
|
2165
|
+
active: engine.activeRuleCount,
|
|
2166
|
+
builtin: engine.counts.builtin,
|
|
2167
|
+
external: engine.counts.user + engine.counts.project
|
|
2168
|
+
})
|
|
2169
|
+
);
|
|
2170
|
+
return engine;
|
|
2171
|
+
}
|
|
2172
|
+
function resolveComplianceSettings(raw) {
|
|
2173
|
+
return {
|
|
2174
|
+
enabled: raw?.enabled ?? DEFAULT_COMPLIANCE_SETTINGS.enabled,
|
|
2175
|
+
scope: { ...DEFAULT_COMPLIANCE_SETTINGS.scope, ...raw?.scope },
|
|
2176
|
+
skipCodeBlocks: raw?.skipCodeBlocks ?? DEFAULT_COMPLIANCE_SETTINGS.skipCodeBlocks,
|
|
2177
|
+
maxHoldChars: raw?.maxHoldChars ?? DEFAULT_COMPLIANCE_SETTINGS.maxHoldChars,
|
|
2178
|
+
actions: { ...DEFAULT_COMPLIANCE_SETTINGS.actions, ...raw?.actions }
|
|
2179
|
+
};
|
|
2180
|
+
}
|
|
1822
2181
|
var init_services = __esm({
|
|
1823
2182
|
"src/services.ts"() {
|
|
1824
2183
|
}
|
|
@@ -1966,11 +2325,11 @@ var init_agent_session = __esm({
|
|
|
1966
2325
|
}
|
|
1967
2326
|
};
|
|
1968
2327
|
return {
|
|
1969
|
-
onApprovalRequest: (req) => new Promise((
|
|
1970
|
-
enqueue({ kind: "approval", req, resolve:
|
|
2328
|
+
onApprovalRequest: (req) => new Promise((resolve4) => {
|
|
2329
|
+
enqueue({ kind: "approval", req, resolve: resolve4 });
|
|
1971
2330
|
}),
|
|
1972
|
-
onQuestionRequest: (req) => new Promise((
|
|
1973
|
-
enqueue({ kind: "question", req, resolve:
|
|
2331
|
+
onQuestionRequest: (req) => new Promise((resolve4) => {
|
|
2332
|
+
enqueue({ kind: "question", req, resolve: resolve4 });
|
|
1974
2333
|
}),
|
|
1975
2334
|
attach: (session) => {
|
|
1976
2335
|
target = session;
|
|
@@ -2090,8 +2449,8 @@ var init_agent_session = __esm({
|
|
|
2090
2449
|
}
|
|
2091
2450
|
continue;
|
|
2092
2451
|
}
|
|
2093
|
-
const { value: event, done } = await pendingNext;
|
|
2094
|
-
if (
|
|
2452
|
+
const { value: event, done: done2 } = await pendingNext;
|
|
2453
|
+
if (done2) break;
|
|
2095
2454
|
pendingNext = iter.next();
|
|
2096
2455
|
if (event.type === "text-delta") assistantText += event.text;
|
|
2097
2456
|
turn.observe(event);
|
|
@@ -2208,7 +2567,7 @@ function buildSessionGuards(input) {
|
|
|
2208
2567
|
permission,
|
|
2209
2568
|
planState,
|
|
2210
2569
|
managed,
|
|
2211
|
-
|
|
2570
|
+
defaultLevel,
|
|
2212
2571
|
requestApproval,
|
|
2213
2572
|
interactive,
|
|
2214
2573
|
deferredTools
|
|
@@ -2218,7 +2577,7 @@ function buildSessionGuards(input) {
|
|
|
2218
2577
|
const reveal = own ? createToolRevealState() : bootstrapReveal;
|
|
2219
2578
|
return {
|
|
2220
2579
|
permission: perm,
|
|
2221
|
-
permissions: perm ? buildSessionPermissions(perm, managed, state) : null,
|
|
2580
|
+
permissions: perm ? buildSessionPermissions(perm, managed, state, defaultLevel) : null,
|
|
2222
2581
|
plan: state ? buildPlanControl(state) : null,
|
|
2223
2582
|
planState: state,
|
|
2224
2583
|
tools: own ? [
|
|
@@ -2233,22 +2592,24 @@ function buildSessionGuards(input) {
|
|
|
2233
2592
|
bootstrap: () => bootstrapScope,
|
|
2234
2593
|
open: () => {
|
|
2235
2594
|
if (!permission) return scopeOf(null, null, true);
|
|
2236
|
-
const own = permission.scoped(
|
|
2595
|
+
const own = permission.scoped(defaultLevel.get());
|
|
2237
2596
|
return scopeOf(own, planState ? new PlanModeState(own) : null, true);
|
|
2238
2597
|
}
|
|
2239
2598
|
};
|
|
2240
2599
|
}
|
|
2241
|
-
function buildSessionPermissions(permission, managed, planState) {
|
|
2600
|
+
function buildSessionPermissions(permission, managed, planState, defaultLevel) {
|
|
2242
2601
|
return {
|
|
2243
2602
|
level: () => permission.getLevel(),
|
|
2244
2603
|
managed: () => ({ bypassDisabled: managed.disableBypassPermissionsMode }),
|
|
2245
|
-
setLevel: (level) => {
|
|
2604
|
+
setLevel: (level, opts) => {
|
|
2246
2605
|
if (level === "bypass" && managed.disableBypassPermissionsMode) {
|
|
2247
2606
|
return { ok: false, reason: "managed-bypass-disabled" };
|
|
2248
2607
|
}
|
|
2249
2608
|
permission.setLevel(level);
|
|
2250
2609
|
planState?.forget();
|
|
2251
|
-
return { ok: true };
|
|
2610
|
+
if (!opts.remember) return { ok: true };
|
|
2611
|
+
const failure = defaultLevel.remember(level);
|
|
2612
|
+
return failure ? { ok: true, notRemembered: failure } : { ok: true };
|
|
2252
2613
|
}
|
|
2253
2614
|
};
|
|
2254
2615
|
}
|
|
@@ -2699,9 +3060,6 @@ var init_mcp_config_apply = __esm({
|
|
|
2699
3060
|
init_mcp_config_file();
|
|
2700
3061
|
}
|
|
2701
3062
|
});
|
|
2702
|
-
function isWritableName(name) {
|
|
2703
|
-
return /^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/.test(name);
|
|
2704
|
-
}
|
|
2705
3063
|
function skipTrivia(raw, from) {
|
|
2706
3064
|
let i = from;
|
|
2707
3065
|
for (; ; ) {
|
|
@@ -2765,13 +3123,15 @@ function walkObject(raw, braceAt) {
|
|
|
2765
3123
|
const entries = [];
|
|
2766
3124
|
let i = skipTrivia(raw, braceAt + 1);
|
|
2767
3125
|
while (i < raw.length && raw[i] !== "}") {
|
|
3126
|
+
const keyAt = i;
|
|
2768
3127
|
const key = readString(raw, i);
|
|
2769
3128
|
if (!key) return { entries, clean: false };
|
|
2770
3129
|
const afterKey = skipTrivia(raw, key[1]);
|
|
2771
3130
|
if (raw[afterKey] !== ":") return { entries, clean: false };
|
|
2772
3131
|
const valueAt2 = skipTrivia(raw, afterKey + 1);
|
|
2773
|
-
|
|
2774
|
-
|
|
3132
|
+
const valueEnd = skipValue(raw, valueAt2);
|
|
3133
|
+
entries.push({ key: key[0], keyAt, valueAt: valueAt2, valueEnd });
|
|
3134
|
+
i = skipTrivia(raw, valueEnd);
|
|
2775
3135
|
if (raw[i] === ",") i = skipTrivia(raw, i + 1);
|
|
2776
3136
|
else if (raw[i] !== "}") return { entries, clean: false };
|
|
2777
3137
|
}
|
|
@@ -2789,10 +3149,14 @@ function locate(raw) {
|
|
|
2789
3149
|
}
|
|
2790
3150
|
const inner = walkObject(raw, servers.valueAt);
|
|
2791
3151
|
if (!inner.clean) return { ok: false, detail: t("mcp_write.servers_unreadable") };
|
|
2792
|
-
const keys = inner.entries.map((e) => e.key);
|
|
2793
3152
|
return {
|
|
2794
3153
|
ok: true,
|
|
2795
|
-
landing: {
|
|
3154
|
+
landing: {
|
|
3155
|
+
braceAt: servers.valueAt,
|
|
3156
|
+
hasMembers: inner.entries.length > 0,
|
|
3157
|
+
entries: inner.entries,
|
|
3158
|
+
kind: "servers"
|
|
3159
|
+
}
|
|
2796
3160
|
};
|
|
2797
3161
|
}
|
|
2798
3162
|
if (top.entries.some((e) => e.key === "mcpServers")) {
|
|
@@ -2800,15 +3164,29 @@ function locate(raw) {
|
|
|
2800
3164
|
}
|
|
2801
3165
|
return {
|
|
2802
3166
|
ok: true,
|
|
2803
|
-
landing: { braceAt: start, hasMembers: top.entries.length > 0,
|
|
3167
|
+
landing: { braceAt: start, hasMembers: top.entries.length > 0, entries: [], kind: "top" }
|
|
2804
3168
|
};
|
|
2805
3169
|
}
|
|
2806
3170
|
function indentOf(raw) {
|
|
2807
3171
|
return /\n([ \t]+)\S/.exec(raw)?.[1] ?? " ";
|
|
2808
3172
|
}
|
|
2809
|
-
function
|
|
2810
|
-
const
|
|
2811
|
-
|
|
3173
|
+
function eolOf(raw) {
|
|
3174
|
+
const at = raw.indexOf("\n");
|
|
3175
|
+
if (at < 0) return "\n";
|
|
3176
|
+
return raw[at - 1] === "\r" ? "\r\n" : "\n";
|
|
3177
|
+
}
|
|
3178
|
+
function withEol(block, eol) {
|
|
3179
|
+
return eol === "\n" ? block : block.replaceAll("\n", eol);
|
|
3180
|
+
}
|
|
3181
|
+
var init_mcp_json_scan = __esm({
|
|
3182
|
+
"src/mcp-json-scan.ts"() {
|
|
3183
|
+
}
|
|
3184
|
+
});
|
|
3185
|
+
function isWritableName(name) {
|
|
3186
|
+
return /^[A-Za-z0-9][A-Za-z0-9_-]{0,47}$/.test(name);
|
|
3187
|
+
}
|
|
3188
|
+
function renderEntry(input, indent, pad = indent.repeat(2)) {
|
|
3189
|
+
const inner = pad + indent;
|
|
2812
3190
|
const fields = [`${inner}"transport": ${JSON.stringify(input.transport)}`];
|
|
2813
3191
|
if (input.command) fields.push(`${inner}"command": ${JSON.stringify(input.command)}`);
|
|
2814
3192
|
if (input.args && input.args.length > 0) {
|
|
@@ -2832,7 +3210,7 @@ function insertServerText(raw, input) {
|
|
|
2832
3210
|
const scan = locate(raw);
|
|
2833
3211
|
if (!scan.ok) return { ok: false, reason: "unwritable", detail: scan.detail };
|
|
2834
3212
|
const { landing } = scan;
|
|
2835
|
-
if (landing.
|
|
3213
|
+
if (landing.entries.some((e) => e.key === input.name)) {
|
|
2836
3214
|
return { ok: false, reason: "duplicate", detail: input.name };
|
|
2837
3215
|
}
|
|
2838
3216
|
const indent = indentOf(raw);
|
|
@@ -2844,7 +3222,8 @@ ${indent}"servers": {
|
|
|
2844
3222
|
${entry}
|
|
2845
3223
|
${indent}}${landing.hasMembers ? "," : "\n"}`;
|
|
2846
3224
|
const at = landing.braceAt + 1;
|
|
2847
|
-
|
|
3225
|
+
const eol = eolOf(raw);
|
|
3226
|
+
return { ok: true, text: raw.slice(0, at) + withEol(block, eol) + raw.slice(at) };
|
|
2848
3227
|
}
|
|
2849
3228
|
function writeMcpServer(input, opts = {}) {
|
|
2850
3229
|
if (!isWritableName(input.name)) {
|
|
@@ -2853,17 +3232,8 @@ function writeMcpServer(input, opts = {}) {
|
|
|
2853
3232
|
for (const name of opts.taken ?? []) {
|
|
2854
3233
|
if (name === input.name) return { ok: false, reason: "duplicate", detail: input.name };
|
|
2855
3234
|
}
|
|
2856
|
-
const
|
|
2857
|
-
|
|
2858
|
-
"(add-form)"
|
|
2859
|
-
);
|
|
2860
|
-
if (probe.servers.length === 0) {
|
|
2861
|
-
return {
|
|
2862
|
-
ok: false,
|
|
2863
|
-
reason: "invalid-config",
|
|
2864
|
-
detail: probe.issues.map((i) => i.message).join("; ") || t("mcp_write.invalid")
|
|
2865
|
-
};
|
|
2866
|
-
}
|
|
3235
|
+
const invalid = validateAddInput(input);
|
|
3236
|
+
if (invalid !== null) return { ok: false, reason: "invalid-config", detail: invalid };
|
|
2867
3237
|
const configPath2 = mcpConfigPath(opts.homeDir);
|
|
2868
3238
|
let raw;
|
|
2869
3239
|
try {
|
|
@@ -2881,6 +3251,14 @@ function writeMcpServer(input, opts = {}) {
|
|
|
2881
3251
|
}
|
|
2882
3252
|
return { ok: true, configPath: configPath2 };
|
|
2883
3253
|
}
|
|
3254
|
+
function validateAddInput(input, lang) {
|
|
3255
|
+
const probe = parseMcpConfig(
|
|
3256
|
+
JSON.stringify({ servers: { [input.name]: rawFields(input) } }),
|
|
3257
|
+
"(add-form)"
|
|
3258
|
+
);
|
|
3259
|
+
if (probe.servers.length > 0) return null;
|
|
3260
|
+
return probe.issues.map((i) => i.message).join("; ") || t("mcp_write.invalid", {}, lang);
|
|
3261
|
+
}
|
|
2884
3262
|
function rawFields(input) {
|
|
2885
3263
|
return {
|
|
2886
3264
|
transport: input.transport,
|
|
@@ -2907,9 +3285,172 @@ function toServerConfig(input) {
|
|
|
2907
3285
|
var EMPTY_CONFIG;
|
|
2908
3286
|
var init_mcp_config_write = __esm({
|
|
2909
3287
|
"src/mcp-config-write.ts"() {
|
|
3288
|
+
init_mcp_json_scan();
|
|
2910
3289
|
EMPTY_CONFIG = '{\n "servers": {}\n}\n';
|
|
2911
3290
|
}
|
|
2912
3291
|
});
|
|
3292
|
+
function skipSpaces(raw, from) {
|
|
3293
|
+
let i = from;
|
|
3294
|
+
while (i < raw.length && /[ \t\r\n]/.test(raw[i])) i++;
|
|
3295
|
+
return i;
|
|
3296
|
+
}
|
|
3297
|
+
function lineStart(raw, at) {
|
|
3298
|
+
return raw.lastIndexOf("\n", at - 1) + 1;
|
|
3299
|
+
}
|
|
3300
|
+
function lineEnd(raw, at) {
|
|
3301
|
+
const nl = raw.indexOf("\n", at);
|
|
3302
|
+
return nl === -1 ? raw.length : nl;
|
|
3303
|
+
}
|
|
3304
|
+
function padBefore(raw, at) {
|
|
3305
|
+
const ls = lineStart(raw, at);
|
|
3306
|
+
const head = raw.slice(ls, at);
|
|
3307
|
+
return /^[ \t]*$/.test(head) ? head : "";
|
|
3308
|
+
}
|
|
3309
|
+
function findEntry(raw, name) {
|
|
3310
|
+
const scan = locate(raw);
|
|
3311
|
+
if (!scan.ok) return { detail: scan.detail };
|
|
3312
|
+
const entries = scan.landing.entries;
|
|
3313
|
+
const index = entries.findIndex((e) => e.key === name);
|
|
3314
|
+
return index === -1 ? null : { entries, index };
|
|
3315
|
+
}
|
|
3316
|
+
function replaceServerText(raw, input) {
|
|
3317
|
+
const found = findEntry(raw, input.name);
|
|
3318
|
+
if (found !== null && "detail" in found) {
|
|
3319
|
+
return { ok: false, reason: "unwritable", detail: found.detail };
|
|
3320
|
+
}
|
|
3321
|
+
if (found === null) return { ok: false, reason: "invalid-name", detail: input.name };
|
|
3322
|
+
const target = found.entries[found.index];
|
|
3323
|
+
const pad = padBefore(raw, target.keyAt);
|
|
3324
|
+
const entry = renderEntry(input, indentOf(raw), pad);
|
|
3325
|
+
const block = withEol(entry.slice(pad.length), eolOf(raw));
|
|
3326
|
+
const text = raw.slice(0, target.keyAt) + block + raw.slice(target.valueEnd);
|
|
3327
|
+
return { ok: true, text };
|
|
3328
|
+
}
|
|
3329
|
+
function removeCuts(raw, entries, index) {
|
|
3330
|
+
const self = entries[index];
|
|
3331
|
+
const afterValue = skipTrivia(raw, self.valueEnd);
|
|
3332
|
+
if (raw[afterValue] === ",") {
|
|
3333
|
+
return skipSpaces(raw, self.valueEnd) === afterValue ? [tidyLine(raw, self.keyAt, afterValue + 1)] : [
|
|
3334
|
+
[self.keyAt, self.valueEnd],
|
|
3335
|
+
[afterValue, afterValue + 1]
|
|
3336
|
+
];
|
|
3337
|
+
}
|
|
3338
|
+
const prev = index > 0 ? entries[index - 1] : void 0;
|
|
3339
|
+
const prevComma = prev === void 0 ? -1 : skipTrivia(raw, prev.valueEnd);
|
|
3340
|
+
if (prevComma < 0 || raw[prevComma] !== ",") {
|
|
3341
|
+
return [tidyLine(raw, self.keyAt, self.valueEnd)];
|
|
3342
|
+
}
|
|
3343
|
+
return [
|
|
3344
|
+
[prevComma, prevComma + 1],
|
|
3345
|
+
[lineStartIfBlank(raw, self.keyAt), self.valueEnd]
|
|
3346
|
+
];
|
|
3347
|
+
}
|
|
3348
|
+
function tidyLine(raw, keyAt, to) {
|
|
3349
|
+
const ls = lineStart(raw, keyAt);
|
|
3350
|
+
const le = lineEnd(raw, to);
|
|
3351
|
+
const headBlank = /^[ \t]*$/.test(raw.slice(ls, keyAt));
|
|
3352
|
+
const tailBlank = /^[ \t\r]*$/.test(raw.slice(to, le));
|
|
3353
|
+
if (!headBlank || !tailBlank) return [keyAt, to];
|
|
3354
|
+
return [ls, Math.min(le + 1, raw.length)];
|
|
3355
|
+
}
|
|
3356
|
+
function lineStartIfBlank(raw, at) {
|
|
3357
|
+
const ls = lineStart(raw, at);
|
|
3358
|
+
return /^[ \t]*$/.test(raw.slice(ls, at)) ? ls : at;
|
|
3359
|
+
}
|
|
3360
|
+
function removeServerText(raw, name) {
|
|
3361
|
+
const found = findEntry(raw, name);
|
|
3362
|
+
if (found !== null && "detail" in found) {
|
|
3363
|
+
return { ok: false, reason: "unwritable", detail: found.detail };
|
|
3364
|
+
}
|
|
3365
|
+
if (found === null) return { ok: false, reason: "invalid-name", detail: name };
|
|
3366
|
+
const cuts = [...removeCuts(raw, found.entries, found.index)].sort((a, b) => b[0] - a[0]);
|
|
3367
|
+
let text = raw;
|
|
3368
|
+
for (const [from, to] of cuts) text = text.slice(0, from) + text.slice(to);
|
|
3369
|
+
return { ok: true, text };
|
|
3370
|
+
}
|
|
3371
|
+
function commit(splice, expectKeys, opts) {
|
|
3372
|
+
const configPath2 = mcpConfigPath(opts.homeDir);
|
|
3373
|
+
if (!existsSync(configPath2)) return { ok: false, reason: "not-found", detail: configPath2 };
|
|
3374
|
+
let raw;
|
|
3375
|
+
try {
|
|
3376
|
+
raw = readFileSync(configPath2, "utf-8");
|
|
3377
|
+
} catch (err) {
|
|
3378
|
+
return { ok: false, reason: "unwritable", detail: describe5(err) };
|
|
3379
|
+
}
|
|
3380
|
+
const before = locate(raw);
|
|
3381
|
+
if (!before.ok) return { ok: false, reason: "unwritable", detail: before.detail };
|
|
3382
|
+
const spliced = splice(raw);
|
|
3383
|
+
if (!spliced.ok) {
|
|
3384
|
+
return spliced.reason === "invalid-name" ? { ok: false, reason: "not-found", detail: spliced.detail } : spliced;
|
|
3385
|
+
}
|
|
3386
|
+
const bad = checkSpliced(
|
|
3387
|
+
spliced.text,
|
|
3388
|
+
expectKeys(before.landing.entries.map((e) => e.key)),
|
|
3389
|
+
opts.lang
|
|
3390
|
+
);
|
|
3391
|
+
if (bad !== null) return { ok: false, reason: "unwritable", detail: bad };
|
|
3392
|
+
return write(configPath2, raw, spliced.text);
|
|
3393
|
+
}
|
|
3394
|
+
function checkSpliced(text, expect, lang) {
|
|
3395
|
+
const after = locate(text);
|
|
3396
|
+
if (!after.ok) return after.detail;
|
|
3397
|
+
const got = after.landing.entries.map((e) => e.key);
|
|
3398
|
+
if (got.length !== expect.length || got.some((k, i) => k !== expect[i])) {
|
|
3399
|
+
return `${t("mcp_write.invalid", {}, lang)} (servers: [${got.join(", ")}] != [${expect.join(", ")}])`;
|
|
3400
|
+
}
|
|
3401
|
+
return null;
|
|
3402
|
+
}
|
|
3403
|
+
function write(configPath2, expectDisk, body) {
|
|
3404
|
+
let disk;
|
|
3405
|
+
try {
|
|
3406
|
+
disk = readFileSync(configPath2, "utf-8");
|
|
3407
|
+
} catch (err) {
|
|
3408
|
+
return { ok: false, reason: "unwritable", detail: describe5(err) };
|
|
3409
|
+
}
|
|
3410
|
+
if (disk !== expectDisk) {
|
|
3411
|
+
return { ok: false, reason: "unwritable", detail: t("mcp_config.stale_disk") };
|
|
3412
|
+
}
|
|
3413
|
+
const tmp = `${configPath2}.${process.pid}.tmp`;
|
|
3414
|
+
try {
|
|
3415
|
+
mkdirSync(dirname(configPath2), { recursive: true });
|
|
3416
|
+
const mode = statSync(configPath2).mode & 511;
|
|
3417
|
+
try {
|
|
3418
|
+
writeFileSync(tmp, body, { encoding: "utf-8", mode });
|
|
3419
|
+
renameSync(tmp, configPath2);
|
|
3420
|
+
} catch (err) {
|
|
3421
|
+
rmSync(tmp, { force: true });
|
|
3422
|
+
throw err;
|
|
3423
|
+
}
|
|
3424
|
+
} catch (err) {
|
|
3425
|
+
return { ok: false, reason: "unwritable", detail: describe5(err) };
|
|
3426
|
+
}
|
|
3427
|
+
return { ok: true, configPath: configPath2 };
|
|
3428
|
+
}
|
|
3429
|
+
function updateMcpServer(input, opts = {}) {
|
|
3430
|
+
const invalid = validateAddInput(input, opts.lang);
|
|
3431
|
+
if (invalid !== null) return { ok: false, reason: "invalid-config", detail: invalid };
|
|
3432
|
+
return commit(
|
|
3433
|
+
(raw) => replaceServerText(raw, input),
|
|
3434
|
+
(before) => before,
|
|
3435
|
+
opts
|
|
3436
|
+
);
|
|
3437
|
+
}
|
|
3438
|
+
function removeMcpServer(name, opts = {}) {
|
|
3439
|
+
return commit(
|
|
3440
|
+
(raw) => removeServerText(raw, name),
|
|
3441
|
+
(before) => before.filter((k) => k !== name),
|
|
3442
|
+
opts
|
|
3443
|
+
);
|
|
3444
|
+
}
|
|
3445
|
+
function describe5(err) {
|
|
3446
|
+
return err instanceof Error ? err.message : String(err);
|
|
3447
|
+
}
|
|
3448
|
+
var init_mcp_config_edit = __esm({
|
|
3449
|
+
"src/mcp-config-edit.ts"() {
|
|
3450
|
+
init_mcp_config_write();
|
|
3451
|
+
init_mcp_json_scan();
|
|
3452
|
+
}
|
|
3453
|
+
});
|
|
2913
3454
|
function foldToolMode(mode) {
|
|
2914
3455
|
if (mode === "both") return "both";
|
|
2915
3456
|
if (mode === "code") return "code";
|
|
@@ -2948,6 +3489,12 @@ function registerLocalTools(input) {
|
|
|
2948
3489
|
const rg = resolveRipgrep();
|
|
2949
3490
|
if (rg.mode === "missing") {
|
|
2950
3491
|
diags.warn("Ripgrep", t("boot.ripgrep_missing", { hint: rg.hint ?? "" }), "ripgrep-missing");
|
|
3492
|
+
} else if (rg.mode === "not-executable") {
|
|
3493
|
+
diags.warn(
|
|
3494
|
+
"Ripgrep",
|
|
3495
|
+
t("boot.ripgrep_not_executable", { hint: rg.hint ?? "" }),
|
|
3496
|
+
"ripgrep-not-executable"
|
|
3497
|
+
);
|
|
2951
3498
|
} else if (rg.mode === "system") {
|
|
2952
3499
|
diags.ok("Ripgrep", t("boot.ripgrep_system"));
|
|
2953
3500
|
} else {
|
|
@@ -2976,6 +3523,7 @@ function registerLocalTools(input) {
|
|
|
2976
3523
|
registry.registerTool(createRunCodeTool());
|
|
2977
3524
|
}
|
|
2978
3525
|
if (skills) registry.registerTool(createSkillViewTool({ skills }));
|
|
3526
|
+
if (skills) registry.registerTools(createSkillWriteTools({ skills }));
|
|
2979
3527
|
if (input.deferMcp === true) {
|
|
2980
3528
|
registry.registerTool(
|
|
2981
3529
|
createToolSearchTool({
|
|
@@ -3063,7 +3611,7 @@ function createToolSync(mcp, registry, defer) {
|
|
|
3063
3611
|
registry.unregisterBySource(`mcp:${server}`);
|
|
3064
3612
|
registerMcpBatch(
|
|
3065
3613
|
registry,
|
|
3066
|
-
fresh.filter((
|
|
3614
|
+
fresh.filter((t25) => t25.name.startsWith(prefix)),
|
|
3067
3615
|
server,
|
|
3068
3616
|
defer
|
|
3069
3617
|
);
|
|
@@ -3132,6 +3680,32 @@ async function addServer(mcp, sync, input, homeDir) {
|
|
|
3132
3680
|
}
|
|
3133
3681
|
};
|
|
3134
3682
|
}
|
|
3683
|
+
async function updateServer(mcp, sync, input, homeDir) {
|
|
3684
|
+
const written = updateMcpServer(input, { homeDir });
|
|
3685
|
+
if (!written.ok) return written;
|
|
3686
|
+
await mcp.disconnect(input.name);
|
|
3687
|
+
const config = toServerConfig(input);
|
|
3688
|
+
const [status] = await mcp.connectAll([config], "user");
|
|
3689
|
+
await sync.replace(input.name);
|
|
3690
|
+
return {
|
|
3691
|
+
ok: true,
|
|
3692
|
+
configPath: written.configPath,
|
|
3693
|
+
status: status ?? mcp.getStatus(input.name) ?? {
|
|
3694
|
+
name: input.name,
|
|
3695
|
+
connected: false,
|
|
3696
|
+
toolCount: 0,
|
|
3697
|
+
reconnectAttempts: 0,
|
|
3698
|
+
source: "user"
|
|
3699
|
+
}
|
|
3700
|
+
};
|
|
3701
|
+
}
|
|
3702
|
+
async function removeServer(mcp, sync, name, homeDir) {
|
|
3703
|
+
const written = removeMcpServer(name, { homeDir });
|
|
3704
|
+
if (!written.ok) return written;
|
|
3705
|
+
await mcp.disconnect(name);
|
|
3706
|
+
await sync.replace(name);
|
|
3707
|
+
return { ok: true, configPath: written.configPath };
|
|
3708
|
+
}
|
|
3135
3709
|
async function connectAllServers(mcp, batches, budgetMs = MCP_CONNECT_BUDGET_MS) {
|
|
3136
3710
|
const all = await withTimeout(
|
|
3137
3711
|
Promise.all(batches.map((b) => mcp.connectAll([...b.servers], b.source))),
|
|
@@ -3179,6 +3753,12 @@ async function registerMcpTools(registry, homeDir, diags, injected = [], defer =
|
|
|
3179
3753
|
if (outcome.ok) wireToolRefresh(mcp, registry, sync);
|
|
3180
3754
|
return outcome;
|
|
3181
3755
|
},
|
|
3756
|
+
update: async (input) => {
|
|
3757
|
+
const outcome = await updateServer(mcp, sync, input, homeDir);
|
|
3758
|
+
if (outcome.ok) wireToolRefresh(mcp, registry, sync);
|
|
3759
|
+
return outcome;
|
|
3760
|
+
},
|
|
3761
|
+
remove: (name) => removeServer(mcp, sync, name, homeDir),
|
|
3182
3762
|
readConfig: () => readMcpConfigFile({ homeDir }),
|
|
3183
3763
|
writeConfig: (text, revision, lang) => writeMcpConfigFile(text, {
|
|
3184
3764
|
revision,
|
|
@@ -3228,7 +3808,7 @@ async function registerMcpTools(registry, homeDir, diags, injected = [], defer =
|
|
|
3228
3808
|
);
|
|
3229
3809
|
continue;
|
|
3230
3810
|
}
|
|
3231
|
-
const own = mcpTools.filter((
|
|
3811
|
+
const own = mcpTools.filter((t25) => t25.name.startsWith(`mcp__${st.name}__`));
|
|
3232
3812
|
const registered = dropOverlongToolNames(
|
|
3233
3813
|
own,
|
|
3234
3814
|
`MCP ${st.name}`,
|
|
@@ -3257,6 +3837,7 @@ var init_tools = __esm({
|
|
|
3257
3837
|
"src/tools.ts"() {
|
|
3258
3838
|
init_host_capabilities();
|
|
3259
3839
|
init_mcp_config_apply();
|
|
3840
|
+
init_mcp_config_edit();
|
|
3260
3841
|
init_mcp_config_file();
|
|
3261
3842
|
init_mcp_config_write();
|
|
3262
3843
|
MCP_CONNECT_BUDGET_MS = 15e3;
|
|
@@ -3443,6 +4024,7 @@ var init_session_factory = __esm({
|
|
|
3443
4024
|
contextLength: services.contextLength,
|
|
3444
4025
|
artifactRetention: resolveArtifactRetention(config.artifacts),
|
|
3445
4026
|
sandboxTerminal: config.sandbox?.terminal !== false,
|
|
4027
|
+
compliance: this.opts.compliance ?? void 0,
|
|
3446
4028
|
...role ? { role: () => ({ name: role.name, ...role.prompt ? { prompt: role.prompt } : {} }) } : {},
|
|
3447
4029
|
...permission ? { permissionManager: permission } : {},
|
|
3448
4030
|
...services.hookManager ? { hookManager: services.hookManager } : {},
|
|
@@ -3524,6 +4106,7 @@ ${context}` : goal,
|
|
|
3524
4106
|
multimodal: { homeDir: config.homeDir },
|
|
3525
4107
|
artifactRetention: resolveArtifactRetention(config.artifacts),
|
|
3526
4108
|
sandboxTerminal: config.sandbox?.terminal !== false,
|
|
4109
|
+
compliance: opts.compliance ?? void 0,
|
|
3527
4110
|
maxTurns: active?.maxTurns ?? config.maxTurns,
|
|
3528
4111
|
role: () => {
|
|
3529
4112
|
const now = roleScope.current;
|
|
@@ -3636,6 +4219,9 @@ function toProvider(router) {
|
|
|
3636
4219
|
} catch {
|
|
3637
4220
|
return { supportsImages: false };
|
|
3638
4221
|
}
|
|
4222
|
+
},
|
|
4223
|
+
get modelId() {
|
|
4224
|
+
return router.getSelection().model;
|
|
3639
4225
|
}
|
|
3640
4226
|
};
|
|
3641
4227
|
}
|
|
@@ -4018,20 +4604,96 @@ var init_utility = __esm({
|
|
|
4018
4604
|
LABEL_SHAPE = /^[a-z0-9_-]{1,32}$/;
|
|
4019
4605
|
}
|
|
4020
4606
|
});
|
|
4607
|
+
function isSystemOpenable(path) {
|
|
4608
|
+
const dot = path.lastIndexOf(".");
|
|
4609
|
+
if (dot < 0) return false;
|
|
4610
|
+
return SYSTEM_OPENABLE_EXTENSIONS.has(path.slice(dot).toLowerCase());
|
|
4611
|
+
}
|
|
4612
|
+
function toWorkspaceRelative(root, userPath) {
|
|
4613
|
+
const trimmed = userPath.trim();
|
|
4614
|
+
if (trimmed === "") return null;
|
|
4615
|
+
if (!isAbsolute(trimmed)) return trimmed.replace(/^\.\/+/, "") || null;
|
|
4616
|
+
const rel = relative(resolve(root), resolve(trimmed));
|
|
4617
|
+
if (rel === "" || rel.startsWith("..")) return null;
|
|
4618
|
+
return rel.split(sep).join("/");
|
|
4619
|
+
}
|
|
4620
|
+
function insideRoot(root, abs) {
|
|
4621
|
+
let realRoot;
|
|
4622
|
+
try {
|
|
4623
|
+
realRoot = realpathSync(root);
|
|
4624
|
+
} catch {
|
|
4625
|
+
return false;
|
|
4626
|
+
}
|
|
4627
|
+
const rel = relative(realRoot, abs);
|
|
4628
|
+
return rel !== "" && !rel.startsWith("..") && !isAbsolute(rel);
|
|
4629
|
+
}
|
|
4630
|
+
function sniffKind(absPath) {
|
|
4631
|
+
let fd;
|
|
4632
|
+
try {
|
|
4633
|
+
fd = openSync(absPath, "r");
|
|
4634
|
+
const buf = Buffer.alloc(8192);
|
|
4635
|
+
const read = readSync(fd, buf, 0, buf.length, 0);
|
|
4636
|
+
return buf.subarray(0, read).includes(0) ? "binary" : "text";
|
|
4637
|
+
} catch {
|
|
4638
|
+
return null;
|
|
4639
|
+
} finally {
|
|
4640
|
+
if (fd !== void 0) {
|
|
4641
|
+
try {
|
|
4642
|
+
closeSync(fd);
|
|
4643
|
+
} catch {
|
|
4644
|
+
}
|
|
4645
|
+
}
|
|
4646
|
+
}
|
|
4647
|
+
}
|
|
4021
4648
|
function withinWorkspace(resolveInWorkspace2, root, rel) {
|
|
4022
4649
|
return resolveInWorkspace2(root, rel) !== null;
|
|
4023
4650
|
}
|
|
4024
4651
|
function buildWorkspaceFilesControl(deps, permission) {
|
|
4025
4652
|
let cache = null;
|
|
4026
4653
|
const now = () => Date.now();
|
|
4654
|
+
function statFileImpl(path, root) {
|
|
4655
|
+
const rel = toWorkspaceRelative(root, path);
|
|
4656
|
+
if (rel === null) return { ok: false, reason: "denied" };
|
|
4657
|
+
const full = deps.resolveInWorkspace(root, rel);
|
|
4658
|
+
if (!full) return { ok: false, reason: "denied" };
|
|
4659
|
+
const decision = permission?.check({
|
|
4660
|
+
type: "file_read",
|
|
4661
|
+
detail: rel,
|
|
4662
|
+
workDir: root,
|
|
4663
|
+
target: full,
|
|
4664
|
+
toolName: "file_read"
|
|
4665
|
+
});
|
|
4666
|
+
if (permission && !decision?.allowed) return { ok: false, reason: "denied" };
|
|
4667
|
+
let stat;
|
|
4668
|
+
try {
|
|
4669
|
+
stat = statSync(full);
|
|
4670
|
+
} catch (err) {
|
|
4671
|
+
const code = err.code;
|
|
4672
|
+
return {
|
|
4673
|
+
ok: false,
|
|
4674
|
+
reason: code === "ENOENT" || code === "ENOTDIR" ? "missing" : "unreadable"
|
|
4675
|
+
};
|
|
4676
|
+
}
|
|
4677
|
+
if (!stat.isFile()) return { ok: false, reason: "not-a-file" };
|
|
4678
|
+
let absPath;
|
|
4679
|
+
try {
|
|
4680
|
+
absPath = realpathSync(full);
|
|
4681
|
+
} catch {
|
|
4682
|
+
return { ok: false, reason: "unreadable" };
|
|
4683
|
+
}
|
|
4684
|
+
if (!insideRoot(root, absPath)) return { ok: false, reason: "denied" };
|
|
4685
|
+
const kind = sniffKind(absPath);
|
|
4686
|
+
if (kind === null) return { ok: false, reason: "unreadable" };
|
|
4687
|
+
return { ok: true, rel, absPath, bytes: stat.size, mtimeMs: stat.mtimeMs, kind };
|
|
4688
|
+
}
|
|
4027
4689
|
function computeList(root) {
|
|
4028
4690
|
const { files, truncated } = deps.listFiles(root);
|
|
4029
4691
|
return { files, truncated, at: now(), root };
|
|
4030
4692
|
}
|
|
4031
4693
|
function listFor(root) {
|
|
4032
|
-
const
|
|
4694
|
+
const t25 = now();
|
|
4033
4695
|
if (!cache || cache.root !== root) cache = computeList(root);
|
|
4034
|
-
else if (
|
|
4696
|
+
else if (t25 - cache.at > LIST_TTL_MS) cache = computeList(root);
|
|
4035
4697
|
return cache;
|
|
4036
4698
|
}
|
|
4037
4699
|
return {
|
|
@@ -4090,12 +4752,55 @@ function buildWorkspaceFilesControl(deps, permission) {
|
|
|
4090
4752
|
return { ok: false, reason: "binary", bytes: stat.size };
|
|
4091
4753
|
}
|
|
4092
4754
|
return { ok: true, text: raw.toString("utf8"), bytes: stat.size };
|
|
4755
|
+
},
|
|
4756
|
+
statFile: statFileImpl,
|
|
4757
|
+
planSystemOpen(path, root) {
|
|
4758
|
+
const stat = statFileImpl(path, root);
|
|
4759
|
+
if (!stat.ok) return { ok: false, reason: stat.reason };
|
|
4760
|
+
if (!isSystemOpenable(stat.absPath)) return { ok: false, reason: "extension-not-allowed" };
|
|
4761
|
+
const { command, args } = deps.systemOpenArgv(stat.absPath);
|
|
4762
|
+
return { ok: true, command, args, absPath: stat.absPath };
|
|
4093
4763
|
}
|
|
4094
4764
|
};
|
|
4095
4765
|
}
|
|
4096
|
-
var LIST_TTL_MS;
|
|
4766
|
+
var SYSTEM_OPENABLE_EXTENSIONS, LIST_TTL_MS;
|
|
4097
4767
|
var init_workspace_files = __esm({
|
|
4098
4768
|
"src/workspace-files.ts"() {
|
|
4769
|
+
SYSTEM_OPENABLE_EXTENSIONS = /* @__PURE__ */ new Set([
|
|
4770
|
+
".doc",
|
|
4771
|
+
".docx",
|
|
4772
|
+
".xls",
|
|
4773
|
+
".xlsx",
|
|
4774
|
+
".ppt",
|
|
4775
|
+
".pptx",
|
|
4776
|
+
".rtf",
|
|
4777
|
+
".odt",
|
|
4778
|
+
".ods",
|
|
4779
|
+
".odp",
|
|
4780
|
+
".pages",
|
|
4781
|
+
".numbers",
|
|
4782
|
+
".key",
|
|
4783
|
+
".pdf",
|
|
4784
|
+
".tif",
|
|
4785
|
+
".tiff",
|
|
4786
|
+
".heic",
|
|
4787
|
+
".heif",
|
|
4788
|
+
".psd",
|
|
4789
|
+
".sketch",
|
|
4790
|
+
".fig",
|
|
4791
|
+
".mp4",
|
|
4792
|
+
".mov",
|
|
4793
|
+
".m4v",
|
|
4794
|
+
".mkv",
|
|
4795
|
+
".avi",
|
|
4796
|
+
".webm",
|
|
4797
|
+
".mp3",
|
|
4798
|
+
".wav",
|
|
4799
|
+
".m4a",
|
|
4800
|
+
".flac",
|
|
4801
|
+
".aac",
|
|
4802
|
+
".ogg"
|
|
4803
|
+
]);
|
|
4099
4804
|
LIST_TTL_MS = 6e4;
|
|
4100
4805
|
}
|
|
4101
4806
|
});
|
|
@@ -4414,6 +5119,9 @@ async function assembleRuntime(opts, opened, processes) {
|
|
|
4414
5119
|
config.tools?.deferMcp === true
|
|
4415
5120
|
);
|
|
4416
5121
|
cleanups.push(mcpWiring.dispose);
|
|
5122
|
+
registry.registerTools(
|
|
5123
|
+
createMcpTools({ control: mcpWiring.control, servers: () => mcpWiring.status() })
|
|
5124
|
+
);
|
|
4417
5125
|
const bridges = new SessionBridges(opts.onApprovalRequest);
|
|
4418
5126
|
const interactive = !services.permission?.isNonInteractive();
|
|
4419
5127
|
const requestPlanApproval = (req, sessionId2) => bridges.approve(sessionId2, req);
|
|
@@ -4423,13 +5131,14 @@ async function assembleRuntime(opts, opened, processes) {
|
|
|
4423
5131
|
requestPlanApproval,
|
|
4424
5132
|
interactive
|
|
4425
5133
|
);
|
|
5134
|
+
const defaultLevel = buildDefaultLevelSlot(services);
|
|
4426
5135
|
const guards = buildSessionGuards({
|
|
4427
5136
|
permission: services.permission,
|
|
4428
5137
|
planState,
|
|
4429
5138
|
managed: services.managedPolicy,
|
|
4430
5139
|
deferredTools: () => registry.byExposure(ToolExposure.Deferred),
|
|
4431
5140
|
bootstrapReveal,
|
|
4432
|
-
|
|
5141
|
+
defaultLevel,
|
|
4433
5142
|
requestApproval: requestPlanApproval,
|
|
4434
5143
|
interactive
|
|
4435
5144
|
});
|
|
@@ -4503,6 +5212,7 @@ async function assembleRuntime(opts, opened, processes) {
|
|
|
4503
5212
|
deferredTools: () => registry.byExposure(ToolExposure.Deferred),
|
|
4504
5213
|
codeModeTools: () => registry.codeModeDeclared(),
|
|
4505
5214
|
telemetry,
|
|
5215
|
+
compliance: services.compliance,
|
|
4506
5216
|
budget: budgetWiring,
|
|
4507
5217
|
...opts.onApprovalRequest ? { hostApproval: opts.onApprovalRequest } : {},
|
|
4508
5218
|
workspaces,
|
|
@@ -4604,7 +5314,7 @@ async function assembleRuntime(opts, opened, processes) {
|
|
|
4604
5314
|
sessionStore: store ?? null,
|
|
4605
5315
|
sessionReferences: services.session?.references ?? null,
|
|
4606
5316
|
workspaceFiles: buildWorkspaceFilesControl(
|
|
4607
|
-
{ listFiles: listWorkspaceFiles, resolveInWorkspace },
|
|
5317
|
+
{ listFiles: listWorkspaceFiles, resolveInWorkspace, systemOpenArgv },
|
|
4608
5318
|
services.permission
|
|
4609
5319
|
),
|
|
4610
5320
|
checkpoints: bootstrap?.checkpoints ?? buildCheckpointControl(checkpoints, null, () => sessionId),
|
|
@@ -4631,6 +5341,10 @@ async function assembleRuntime(opts, opened, processes) {
|
|
|
4631
5341
|
}));
|
|
4632
5342
|
},
|
|
4633
5343
|
usageScope: budgetWiring.budgetStore ? "session" : "run",
|
|
5344
|
+
sessionUsage: (sessionId2) => {
|
|
5345
|
+
const saved = budgetWiring.budgetStore?.load(sessionId2);
|
|
5346
|
+
return saved ? persistedUsage(saved) : void 0;
|
|
5347
|
+
},
|
|
4634
5348
|
compression: compressionLine(config, services),
|
|
4635
5349
|
get providerInfo() {
|
|
4636
5350
|
return provider ? { provider: provider.getProviderType(), apiKeyHint: provider.getApiKeyHint() } : null;
|
|
@@ -4657,7 +5371,7 @@ async function assembleRuntime(opts, opened, processes) {
|
|
|
4657
5371
|
skillIndexResidency: (role) => services.skill?.indexResidency({
|
|
4658
5372
|
only: role === null ? void 0 : findRole(agentRoles, role)?.skills
|
|
4659
5373
|
}) ?? /* @__PURE__ */ new Map(),
|
|
4660
|
-
|
|
5374
|
+
skillWrite: buildSkillControl(services),
|
|
4661
5375
|
roleWrite: createRoleWriteControl({
|
|
4662
5376
|
userDir: agentsDir(config.homeDir),
|
|
4663
5377
|
roles: () => agentRoles,
|
|
@@ -4945,6 +5659,34 @@ function buildSkillControl(services) {
|
|
|
4945
5659
|
const outcome = await importSkills(source, { ...opts, token });
|
|
4946
5660
|
if (outcome.ok && outcome.imported.length > 0) services.skill?.invalidateCache();
|
|
4947
5661
|
return outcome;
|
|
5662
|
+
},
|
|
5663
|
+
remove: async (name, lang) => {
|
|
5664
|
+
const skill = services.skill;
|
|
5665
|
+
if (!skill) {
|
|
5666
|
+
return {
|
|
5667
|
+
ok: false,
|
|
5668
|
+
reason: "missing",
|
|
5669
|
+
detail: t("skill.remove_no_system", void 0, lang)
|
|
5670
|
+
};
|
|
5671
|
+
}
|
|
5672
|
+
const found = skill.list().find((s) => s.name === name);
|
|
5673
|
+
if (!found) {
|
|
5674
|
+
return {
|
|
5675
|
+
ok: false,
|
|
5676
|
+
reason: "not-found",
|
|
5677
|
+
detail: t("skill.remove_unknown", { name }, lang)
|
|
5678
|
+
};
|
|
5679
|
+
}
|
|
5680
|
+
try {
|
|
5681
|
+
const path = await skill.delete(name);
|
|
5682
|
+
return { ok: true, name, path };
|
|
5683
|
+
} catch (err) {
|
|
5684
|
+
return {
|
|
5685
|
+
ok: false,
|
|
5686
|
+
reason: found.scope === "user" ? "failed" : "readonly",
|
|
5687
|
+
detail: err instanceof Error ? err.message : String(err)
|
|
5688
|
+
};
|
|
5689
|
+
}
|
|
4948
5690
|
}
|
|
4949
5691
|
};
|
|
4950
5692
|
}
|
|
@@ -5080,7 +5822,7 @@ function buildPermissionsControl(services, bootstrap, tools) {
|
|
|
5080
5822
|
bypassDisabled: p.disableBypassPermissionsMode
|
|
5081
5823
|
};
|
|
5082
5824
|
},
|
|
5083
|
-
setLevel: (level) => bootstrap?.setLevel(level) ?? { ok: true },
|
|
5825
|
+
setLevel: (level, opts) => bootstrap?.setLevel(level, opts) ?? { ok: true },
|
|
5084
5826
|
gates: (level) => {
|
|
5085
5827
|
const rules = compileRuleLists(services.config.permissions);
|
|
5086
5828
|
const isolation = services.permission?.getSandboxIsolation() ?? "process";
|
|
@@ -5125,15 +5867,20 @@ function buildSettingsControl(services, workspaces, opts) {
|
|
|
5125
5867
|
row.scalar ? { key: row.scalar, layers: scope.layers } : void 0
|
|
5126
5868
|
);
|
|
5127
5869
|
const overridable = row.scalar !== void 0;
|
|
5870
|
+
const writes = scope.paths === null ? [] : settingWriteTargets({
|
|
5871
|
+
key: row.key,
|
|
5872
|
+
winner: chain.layer,
|
|
5873
|
+
paths: scope.paths,
|
|
5874
|
+
trusted: scope.trusted
|
|
5875
|
+
});
|
|
5876
|
+
const kind = writes.length === 0 ? null : settingValueKind(row.key);
|
|
5877
|
+
const choices = writes.length === 0 ? null : settingValueChoices(row.key);
|
|
5128
5878
|
return {
|
|
5129
5879
|
...chain,
|
|
5130
5880
|
overridable,
|
|
5131
|
-
writes
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
paths: scope.paths,
|
|
5135
|
-
trusted: scope.trusted
|
|
5136
|
-
})
|
|
5881
|
+
writes,
|
|
5882
|
+
...kind === null ? {} : { valueKind: kind },
|
|
5883
|
+
...choices === null ? {} : { choices }
|
|
5137
5884
|
};
|
|
5138
5885
|
});
|
|
5139
5886
|
};
|
|
@@ -5152,7 +5899,7 @@ function buildSettingsControl(services, workspaces, opts) {
|
|
|
5152
5899
|
value: input.value
|
|
5153
5900
|
});
|
|
5154
5901
|
if (!outcome.ok) return { ok: false, reason: outcome.reason, path };
|
|
5155
|
-
const target = rows(input.sessionId).find((row) => row.key === input.key)?.writes.find((
|
|
5902
|
+
const target = rows(input.sessionId).find((row) => row.key === input.key)?.writes.find((write2) => write2.layer === input.layer);
|
|
5156
5903
|
return {
|
|
5157
5904
|
ok: true,
|
|
5158
5905
|
key: input.key,
|
|
@@ -5203,6 +5950,31 @@ function writePathsOf(layers) {
|
|
|
5203
5950
|
}
|
|
5204
5951
|
return paths;
|
|
5205
5952
|
}
|
|
5953
|
+
function buildDefaultLevelSlot(services) {
|
|
5954
|
+
let current = services.permissionLevel;
|
|
5955
|
+
return {
|
|
5956
|
+
get: () => current,
|
|
5957
|
+
remember: (level) => {
|
|
5958
|
+
current = level;
|
|
5959
|
+
const paths = writePathsOf(services.settingsLayers);
|
|
5960
|
+
if (!paths) return { reason: "no-user-config" };
|
|
5961
|
+
const outcome = writeSettingValue({
|
|
5962
|
+
layer: "user",
|
|
5963
|
+
path: paths.user,
|
|
5964
|
+
key: "permission",
|
|
5965
|
+
value: level
|
|
5966
|
+
});
|
|
5967
|
+
if (!outcome.ok) {
|
|
5968
|
+
const reason2 = outcome.reason === "unwritable-file" ? "unwritable" : "io";
|
|
5969
|
+
return { reason: reason2, path: paths.user };
|
|
5970
|
+
}
|
|
5971
|
+
if (originOf(services.configOrigins, "permission") === "env") {
|
|
5972
|
+
return { reason: "env-pinned", path: paths.user };
|
|
5973
|
+
}
|
|
5974
|
+
return void 0;
|
|
5975
|
+
}
|
|
5976
|
+
};
|
|
5977
|
+
}
|
|
5206
5978
|
function valueAt(config, key) {
|
|
5207
5979
|
let cursor = config;
|
|
5208
5980
|
for (const segment of key.split(".")) {
|
|
@@ -5236,6 +6008,7 @@ var init_build = __esm({
|
|
|
5236
6008
|
init_host_capabilities();
|
|
5237
6009
|
init_host_marketplaces();
|
|
5238
6010
|
init_host_preset();
|
|
6011
|
+
init_mcp_tools();
|
|
5239
6012
|
init_model_catalog();
|
|
5240
6013
|
init_plugin_control();
|
|
5241
6014
|
init_plugin_mcp_servers();
|
|
@@ -5339,7 +6112,7 @@ async function probeMcpServer(name, opts = {}) {
|
|
|
5339
6112
|
const tools = status?.connected ? await registry.getAllTools() : [];
|
|
5340
6113
|
return {
|
|
5341
6114
|
connected: status?.connected ?? false,
|
|
5342
|
-
toolNames: tools.map((
|
|
6115
|
+
toolNames: tools.map((t25) => t25.name),
|
|
5343
6116
|
...status?.lastError ? { error: status.lastError } : {},
|
|
5344
6117
|
needsLogin: status?.needsLogin ?? false
|
|
5345
6118
|
};
|
|
@@ -5360,5 +6133,7 @@ async function requireServer(name, opts) {
|
|
|
5360
6133
|
// src/index.ts
|
|
5361
6134
|
init_mcp_config_file();
|
|
5362
6135
|
init_mcp_config_apply();
|
|
6136
|
+
init_mcp_config_edit();
|
|
6137
|
+
init_mcp_tools();
|
|
5363
6138
|
|
|
5364
|
-
export { AgentRoleError, AgentSession, ApprovalRelay, HOST_CAPABILITIES_MODULE, HOST_MARKETPLACE_MODULE, HOST_PRESET_MODULE, QuestionRelay, SCHEDULE_EXIT_CODES, SessionStore, SessionWorkspaces, applyHostPreset, applyMcpConfig, buildModelCatalogControl, buildRuntime, buildServices, buildWorkspaceFilesControl, collectFileChanges, createScheduleControl, createUserActions, ensureSecretsReady, exitCodeFor, fireSchedule, listMcpServers, mcpConfigRevision, mcpLogin, mcpLogout, probeMcpServer, providerLabel, readMcpConfigFile, registerAskQuestion, registerLocalTools, registerMcpTools, serializeStream, splitDirList, toSerializable, utilityBudgetKey, wireCommands, wireHostCapabilities, wirePluginMcpServers, wireWorkspace, withModelScope, withRoleScope, withToolScope, writeMcpConfigFile, writeMcpServer };
|
|
6139
|
+
export { AgentRoleError, AgentSession, ApprovalRelay, HOST_CAPABILITIES_MODULE, HOST_MARKETPLACE_MODULE, HOST_PRESET_MODULE, MCP_ADD_TOOL, MCP_LIST_TOOL, MCP_REMOVE_TOOL, MCP_UPDATE_TOOL, QuestionRelay, SCHEDULE_EXIT_CODES, SessionStore, SessionWorkspaces, applyHostPreset, applyMcpConfig, buildModelCatalogControl, buildRuntime, buildServices, buildWorkspaceFilesControl, collectFileChanges, createMcpTools, createScheduleControl, createUserActions, ensureSecretsReady, exitCodeFor, fireSchedule, isSystemOpenable, listMcpServers, mcpConfigRevision, mcpLogin, mcpLogout, probeMcpServer, providerLabel, readMcpConfigFile, registerAskQuestion, registerLocalTools, registerMcpTools, removeMcpServer, removeServerText, replaceServerText, serializeStream, splitDirList, toSerializable, updateMcpServer, utilityBudgetKey, wireCommands, wireHostCapabilities, wirePluginMcpServers, wireWorkspace, withModelScope, withRoleScope, withToolScope, writeMcpConfigFile, writeMcpServer };
|