@skaile/workspaces 0.23.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/{asset-feeds-WKIKSZ6Z.js → asset-feeds-77KLWCBP.js} +9 -9
  3. package/dist/{asset-feeds-WKIKSZ6Z.js.map → asset-feeds-77KLWCBP.js.map} +1 -1
  4. package/dist/asset-manager/index.js +7 -7
  5. package/dist/asset-manager/installer.js +6 -6
  6. package/dist/base-assets/connectors/deploy.js +7 -7
  7. package/dist/base-assets/connectors/devserver.js +7 -7
  8. package/dist/base-assets/connectors/flow/adapter.js +7 -7
  9. package/dist/base-assets/connectors/flow/run-flow.js +8 -8
  10. package/dist/base-assets/connectors/flow.js +7 -7
  11. package/dist/base-assets/connectors/git.js +7 -7
  12. package/dist/base-assets/connectors/gmail.js +7 -7
  13. package/dist/base-assets/connectors/googledrive/driver.d.ts.map +1 -1
  14. package/dist/base-assets/connectors/googledrive.js +7 -7
  15. package/dist/base-assets/connectors/local.js +7 -7
  16. package/dist/base-assets/connectors/mattermost.js +7 -7
  17. package/dist/base-assets/connectors/memory.js +7 -7
  18. package/dist/base-assets/connectors/minio.js +7 -7
  19. package/dist/base-assets/connectors/postgres.js +7 -7
  20. package/dist/base-assets/connectors/s3.js +7 -7
  21. package/dist/base-assets/connectors/sharepoint/driver.d.ts.map +1 -1
  22. package/dist/base-assets/connectors/sharepoint.js +7 -7
  23. package/dist/base-assets/connectors/sqlite.js +7 -7
  24. package/dist/base-assets/connectors/static-server.js +7 -7
  25. package/dist/base-assets/connectors/tunnel.js +7 -7
  26. package/dist/base-assets/connectors/webdav/driver.d.ts.map +1 -1
  27. package/dist/base-assets/connectors/webdav.js +7 -7
  28. package/dist/base-assets/connectors/xstate-store/adapter.d.ts +1 -1
  29. package/dist/base-assets/connectors/xstate-store/adapter.d.ts.map +1 -1
  30. package/dist/base-assets/connectors/xstate-store.js +7 -7
  31. package/dist/base-assets/connectors/xstate.js +7 -7
  32. package/dist/{chunk-542K7SR6.js → chunk-3QTZWPGH.js} +36 -7
  33. package/dist/chunk-3QTZWPGH.js.map +1 -0
  34. package/dist/{chunk-46COM7M5.js → chunk-4FADEVBN.js} +4 -4
  35. package/dist/{chunk-46COM7M5.js.map → chunk-4FADEVBN.js.map} +1 -1
  36. package/dist/{chunk-AFLH7B64.js → chunk-4FJE6BI6.js} +3 -3
  37. package/dist/{chunk-AFLH7B64.js.map → chunk-4FJE6BI6.js.map} +1 -1
  38. package/dist/{chunk-OVQZ5OKL.js → chunk-4QVFQEY2.js} +2 -2
  39. package/dist/{chunk-OVQZ5OKL.js.map → chunk-4QVFQEY2.js.map} +1 -1
  40. package/dist/{chunk-BTAC2VYT.js → chunk-B4ZXBH57.js} +7 -7
  41. package/dist/{chunk-BTAC2VYT.js.map → chunk-B4ZXBH57.js.map} +1 -1
  42. package/dist/{chunk-DZCFFTAX.js → chunk-BJWUSHC4.js} +336 -113
  43. package/dist/chunk-BJWUSHC4.js.map +1 -0
  44. package/dist/{chunk-2F3RUZXC.js → chunk-DCAWIRD6.js} +15 -6
  45. package/dist/chunk-DCAWIRD6.js.map +1 -0
  46. package/dist/{chunk-QTWA6BZK.js → chunk-FJFHJBGS.js} +5 -5
  47. package/dist/{chunk-QTWA6BZK.js.map → chunk-FJFHJBGS.js.map} +1 -1
  48. package/dist/{chunk-DH4N5AW4.js → chunk-GL45UNVS.js} +3 -3
  49. package/dist/{chunk-DH4N5AW4.js.map → chunk-GL45UNVS.js.map} +1 -1
  50. package/dist/{chunk-LJ52ZKIU.js → chunk-KT3CK26V.js} +3 -3
  51. package/dist/{chunk-LJ52ZKIU.js.map → chunk-KT3CK26V.js.map} +1 -1
  52. package/dist/{chunk-2RFOFHSM.js → chunk-QXC62DOF.js} +4 -4
  53. package/dist/{chunk-2RFOFHSM.js.map → chunk-QXC62DOF.js.map} +1 -1
  54. package/dist/{chunk-ODPII24X.js → chunk-SETTLPBD.js} +3 -3
  55. package/dist/{chunk-ODPII24X.js.map → chunk-SETTLPBD.js.map} +1 -1
  56. package/dist/{chunk-5ESCS2OS.js → chunk-UD4ZLXGS.js} +4 -4
  57. package/dist/{chunk-5ESCS2OS.js.map → chunk-UD4ZLXGS.js.map} +1 -1
  58. package/dist/{chunk-YX3UWPJ5.js → chunk-WIAHJOMG.js} +19 -49
  59. package/dist/chunk-WIAHJOMG.js.map +1 -0
  60. package/dist/{chunk-E4UJ7CVK.js → chunk-XMP6XTMF.js} +213 -57
  61. package/dist/chunk-XMP6XTMF.js.map +1 -0
  62. package/dist/{chunk-Z3M5K67G.js → chunk-XVL22AWE.js} +3 -3
  63. package/dist/{chunk-Z3M5K67G.js.map → chunk-XVL22AWE.js.map} +1 -1
  64. package/dist/cli/index.js +160 -46
  65. package/dist/cli/index.js.map +1 -1
  66. package/dist/cli/src/commands/project.d.ts.map +1 -1
  67. package/dist/cli/src/commands/validate.d.ts.map +1 -1
  68. package/dist/connectors/config.js +6 -6
  69. package/dist/connectors/index.js +7 -7
  70. package/dist/connectors/src/connector-manager.d.ts +49 -0
  71. package/dist/connectors/src/connector-manager.d.ts.map +1 -1
  72. package/dist/connectors/src/connector-types.d.ts +16 -0
  73. package/dist/connectors/src/connector-types.d.ts.map +1 -1
  74. package/dist/core/index.js +5 -5
  75. package/dist/core/manifest.js +2 -2
  76. package/dist/core/models.js +1 -1
  77. package/dist/core/runtime-assets.js +4 -4
  78. package/dist/core/src/index.d.ts +2 -2
  79. package/dist/core/src/index.d.ts.map +1 -1
  80. package/dist/core/src/models.d.ts +10 -3
  81. package/dist/core/src/models.d.ts.map +1 -1
  82. package/dist/core/src/workspace-config.d.ts +21 -0
  83. package/dist/core/src/workspace-config.d.ts.map +1 -1
  84. package/dist/core/workspace-config.js +3 -3
  85. package/dist/deploy/index.js +5 -5
  86. package/dist/discovery/index.js +3 -3
  87. package/dist/{ensure-sources-OJUBGX6Z.js → ensure-sources-7MOOKY3K.js} +9 -9
  88. package/dist/{ensure-sources-OJUBGX6Z.js.map → ensure-sources-7MOOKY3K.js.map} +1 -1
  89. package/dist/library/index.js +12 -4
  90. package/dist/library/src/install/install-from-manifest.d.ts.map +1 -1
  91. package/dist/open-library-GW7DWWNZ.js +21 -0
  92. package/dist/{open-library-67FSSQWE.js.map → open-library-GW7DWWNZ.js.map} +1 -1
  93. package/dist/{plugin-store-IZ5SCRAV.js → plugin-store-R32NH7JE.js} +7 -7
  94. package/dist/{plugin-store-IZ5SCRAV.js.map → plugin-store-R32NH7JE.js.map} +1 -1
  95. package/dist/runner/index.js +9 -9
  96. package/dist/runner/src/external-mcp.d.ts +112 -0
  97. package/dist/runner/src/external-mcp.d.ts.map +1 -0
  98. package/dist/runner/src/resources.d.ts +22 -1
  99. package/dist/runner/src/resources.d.ts.map +1 -1
  100. package/dist/runner/src/serve.d.ts.map +1 -1
  101. package/dist/runner/src/session-builder.d.ts +19 -0
  102. package/dist/runner/src/session-builder.d.ts.map +1 -1
  103. package/dist/sdk/asset-manager.js +7 -7
  104. package/dist/sdk/core.js +5 -5
  105. package/dist/sdk/index.js +9 -9
  106. package/dist/sdk/runner.js +9 -9
  107. package/dist/{setup-J7CYEQOF.js → setup-SRPBQOHY.js} +7 -7
  108. package/dist/{setup-J7CYEQOF.js.map → setup-SRPBQOHY.js.map} +1 -1
  109. package/dist/store-client-INZD2RYD.js +14 -0
  110. package/dist/{store-client-AEI6Y3KD.js.map → store-client-INZD2RYD.js.map} +1 -1
  111. package/dist/tui/index.js +9 -9
  112. package/dist/types/src/install-manifest.d.ts +1 -1
  113. package/dist/types/src/install-manifest.d.ts.map +1 -1
  114. package/dist/workspace-plugin/index.js +1 -1
  115. package/package.json +1 -1
  116. package/dist/chunk-2F3RUZXC.js.map +0 -1
  117. package/dist/chunk-542K7SR6.js.map +0 -1
  118. package/dist/chunk-DZCFFTAX.js.map +0 -1
  119. package/dist/chunk-E4UJ7CVK.js.map +0 -1
  120. package/dist/chunk-YX3UWPJ5.js.map +0 -1
  121. package/dist/open-library-67FSSQWE.js +0 -13
  122. package/dist/store-client-AEI6Y3KD.js +0 -14
@@ -1,4 +1,4 @@
1
- import { WorkspacePlugin } from './chunk-OVQZ5OKL.js';
1
+ import { WorkspacePlugin } from './chunk-4QVFQEY2.js';
2
2
  import { WebSocketServerTransport } from './chunk-WQ7DE5UC.js';
3
3
  import { assembleSystemPrompt, buildCapabilitiesPromptSection } from './chunk-W3UDISS2.js';
4
4
  import { PROTOCOL_VERSION } from './chunk-TDSRLMDB.js';
@@ -6,13 +6,14 @@ import { EventNormalizer } from './chunk-M5TE6YI5.js';
6
6
  import { classifyClaudeSdkError } from './chunk-DQWREFRQ.js';
7
7
  import { createDriver } from './chunk-6VTG73UY.js';
8
8
  import { deployCatalogEntry, undeployCatalogEntry } from './chunk-LV2HPH3C.js';
9
- import { registerBuiltinConnectors, findMissingPackages, installNpmPackages, ConnectorManager, ConnectorStartupError, buildConnectorPromptSection, buildSdkConnectorTools } from './chunk-E4UJ7CVK.js';
10
- import { loadConnectorDeclarations, PreMintedSecretProvider, InMemorySecretProvider } from './chunk-ODPII24X.js';
9
+ import { registerBuiltinConnectors, findMissingPackages, installNpmPackages, ConnectorManager, ConnectorStartupError, buildConnectorPromptSection, buildSdkConnectorTools } from './chunk-XMP6XTMF.js';
10
+ import { loadConnectorDeclarations, PreMintedSecretProvider, InMemorySecretProvider } from './chunk-SETTLPBD.js';
11
11
  import { renderStimulusPrompt, buildOrchestratorPrompt } from './chunk-GZWJGNNN.js';
12
- import { resolveSettings, resolveApiKey, providerEnvKey } from './chunk-Z3M5K67G.js';
13
- import { resolveRuntimeAssets } from './chunk-5ESCS2OS.js';
14
- import { loadMcpServerDeclarations, resolveSkWorkspaceConfig, resolveAgentDir, validateAssetRecipeAttr, COMPACTION_DEFAULTS } from './chunk-542K7SR6.js';
15
- import { parseAssetRef } from './chunk-YX3UWPJ5.js';
12
+ import { resolveSettings, resolveApiKey, providerEnvKey } from './chunk-XVL22AWE.js';
13
+ import { resolveDriverPaths } from './chunk-K5GBV4SA.js';
14
+ import { resolveRuntimeAssets } from './chunk-UD4ZLXGS.js';
15
+ import { resolveSkWorkspaceConfig, resolveAgentDir, stageMaterializedSkills, loadMcpServerDeclarations, COMPACTION_DEFAULTS, validateAssetRecipeAttr } from './chunk-3QTZWPGH.js';
16
+ import { parseAssetRef } from './chunk-WIAHJOMG.js';
16
17
  import { getLogStore, resolveLogStoreConfig, createLogStoreFromConfig, OnLogBridgeSink, WsLogSink, registerLogStore, resetLogStore, createLogger } from './chunk-24UIWON4.js';
17
18
  import { __require } from './chunk-NSBPE2FW.js';
18
19
  import pc from 'picocolors';
@@ -28,6 +29,10 @@ import { createHash, randomUUID } from 'crypto';
28
29
  import { execFile, spawnSync } from 'child_process';
29
30
  import { promisify } from 'util';
30
31
  import { parse } from 'yaml';
32
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
33
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
34
+ import { getDefaultEnvironment, StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
35
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
31
36
  import mime from 'mime';
32
37
 
33
38
  // runner/src/logging-bootstrap.ts
@@ -850,8 +855,262 @@ function validateSubstitutedPaths(command, args) {
850
855
  return missing;
851
856
  }
852
857
 
858
+ // runner/src/external-mcp.ts
859
+ function resolveRecordSecrets(record, secrets) {
860
+ if (!secrets) return record;
861
+ const resolved = {};
862
+ for (const [k, v] of Object.entries(record)) {
863
+ resolved[k] = secrets.resolve(v) ?? v;
864
+ }
865
+ return resolved;
866
+ }
867
+ var RESERVED_MCP_SERVER_IDS = /* @__PURE__ */ new Set([
868
+ "skaile-connectors",
869
+ "skaile-workspace",
870
+ "skaile-capabilities"
871
+ ]);
872
+ function resolveExternalMcpDeclarations(projectDir, sessionId) {
873
+ const mcpLog = createLogger({
874
+ kind: "mcp",
875
+ subkind: "wiring",
876
+ instance: sessionId ?? "no-session"
877
+ });
878
+ const mcpDeclarations = loadMcpServerDeclarations(projectDir);
879
+ mcpLog.debug("loaded MCP declarations", { count: mcpDeclarations.length });
880
+ const recipeCache = createRecipeCache();
881
+ const nixFlakeRef = process.env.SKAILE_NIX_FLAKE_REF ?? "/etc/skaile/flake";
882
+ const resolved = [];
883
+ for (const decl of mcpDeclarations) {
884
+ if (RESERVED_MCP_SERVER_IDS.has(decl.id)) {
885
+ mcpLog.warn("MCP server skipped: reserved id", { id: decl.id });
886
+ continue;
887
+ }
888
+ let resolvedDecl = decl;
889
+ if (decl.recipe) {
890
+ const recipeLogKey = decl.recipe.flake ? { flake: decl.recipe.flake } : { attr: decl.recipe.attr };
891
+ try {
892
+ const recipeKey = decl.recipe.flake ? `mcps.${decl.id}` : decl.recipe.attr ?? `mcps.${decl.id}`;
893
+ const outPath = resolveRecipePath(nixFlakeRef, recipeKey, recipeCache);
894
+ const outPaths = /* @__PURE__ */ new Map([[decl.id, outPath]]);
895
+ const resolvedCommand = decl.command ? substituteRecipeTemplating(decl.command, outPaths) : decl.command;
896
+ const resolvedArgs = decl.args?.map((a) => substituteRecipeTemplating(a, outPaths));
897
+ const resolvedEnv = substituteRecipeMap(decl.env, outPaths);
898
+ resolvedDecl = {
899
+ ...decl,
900
+ command: resolvedCommand,
901
+ args: resolvedArgs,
902
+ env: resolvedEnv
903
+ };
904
+ const recipeLog = createLogger({
905
+ kind: "mcp",
906
+ subkind: decl.id,
907
+ instance: "recipe-resolve"
908
+ });
909
+ const allResolved = [
910
+ resolvedCommand ?? "",
911
+ ...resolvedArgs ?? [],
912
+ ...Object.values(resolvedEnv ?? {})
913
+ ];
914
+ for (const v of allResolved) {
915
+ if (v.includes("${recipe:")) {
916
+ recipeLog.warn("unresolved recipe marker after substitution", {
917
+ value: v,
918
+ declId: decl.id
919
+ });
920
+ }
921
+ }
922
+ const missingPaths = validateSubstitutedPaths(resolvedCommand, resolvedArgs);
923
+ if (missingPaths.length > 0) {
924
+ recipeLog.error("MCP server skipped: substituted path does not exist", void 0, {
925
+ id: decl.id,
926
+ ...recipeLogKey,
927
+ missing: missingPaths
928
+ });
929
+ continue;
930
+ }
931
+ recipeLog.info("recipe substitution complete", { ...recipeLogKey, outPath });
932
+ } catch (err) {
933
+ createLogger({ kind: "mcp", subkind: decl.id, instance: "recipe-resolve" }).error(
934
+ "MCP server skipped: recipe resolution failed",
935
+ err,
936
+ { id: decl.id, ...recipeLogKey, flakeRef: nixFlakeRef }
937
+ );
938
+ continue;
939
+ }
940
+ }
941
+ resolved.push(resolvedDecl);
942
+ }
943
+ return resolved;
944
+ }
945
+ var PASSTHROUGH_INPUT_ZOD = {
946
+ parse: (v) => v
947
+ };
948
+ function buildTransport(decl, secrets) {
949
+ const transport = decl.transport ?? "stdio";
950
+ if (transport === "stdio") {
951
+ if (!decl.command) {
952
+ throw new Error(`stdio MCP server '${decl.id}' is missing 'command'`);
953
+ }
954
+ const env = {
955
+ ...getDefaultEnvironment(),
956
+ ...resolveRecordSecrets(decl.env ?? {}, secrets)
957
+ };
958
+ return new StdioClientTransport({
959
+ command: decl.command,
960
+ args: decl.args ?? [],
961
+ env,
962
+ stderr: "pipe"
963
+ });
964
+ }
965
+ if (transport === "sse") {
966
+ if (!decl.url) throw new Error(`sse MCP server '${decl.id}' is missing 'url'`);
967
+ const headers = resolveRecordSecrets(decl.headers ?? {}, secrets);
968
+ return new SSEClientTransport(new URL(decl.url), { requestInit: { headers } });
969
+ }
970
+ if (transport === "http") {
971
+ if (!decl.url) throw new Error(`http MCP server '${decl.id}' is missing 'url'`);
972
+ const headers = resolveRecordSecrets(decl.headers ?? {}, secrets);
973
+ return new StreamableHTTPClientTransport(new URL(decl.url), { requestInit: { headers } });
974
+ }
975
+ throw new Error(`Unknown MCP transport '${transport}' for server '${decl.id}'`);
976
+ }
977
+ function defaultMcpClientConnector(secrets) {
978
+ return async (decl) => {
979
+ const transport = buildTransport(decl, secrets);
980
+ const client = new Client({ name: "skaile-runner", version: "1.0.0" }, { capabilities: {} });
981
+ await client.connect(transport);
982
+ return client;
983
+ };
984
+ }
985
+ var ExternalMcpManager = class {
986
+ /**
987
+ * @param registry Session capability registry to register MCP tools into.
988
+ * @param secrets Secret provider for resolving `env:` / `forge:` refs in the
989
+ * stdio subprocess env and sse/http headers.
990
+ * @param sessionId Used for the logger instance slice.
991
+ * @param connect Optional connector override (tests inject a fake client so
992
+ * no live subprocess is spawned). Defaults to the real SDK
993
+ * client + transport.
994
+ */
995
+ constructor(registry, secrets, sessionId, connect) {
996
+ this.registry = registry;
997
+ this.connect = connect ?? defaultMcpClientConnector(secrets);
998
+ this.log = createLogger({
999
+ kind: "mcp",
1000
+ subkind: "runner-managed",
1001
+ instance: sessionId ?? "no-session"
1002
+ });
1003
+ }
1004
+ registry;
1005
+ servers = [];
1006
+ log;
1007
+ connect;
1008
+ /** True when at least one external server connected and registered tools. */
1009
+ hasServers() {
1010
+ return this.servers.length > 0;
1011
+ }
1012
+ /**
1013
+ * Connect every declaration and register its tools. Per-server failures are
1014
+ * logged and skipped so a single bad server never blocks the session.
1015
+ */
1016
+ async start(declarations) {
1017
+ for (const decl of declarations) {
1018
+ try {
1019
+ await this.startServer(decl);
1020
+ } catch (err) {
1021
+ this.log.error("MCP server spawn failed; skipping", err, {
1022
+ id: decl.id,
1023
+ transport: decl.transport ?? "stdio"
1024
+ });
1025
+ }
1026
+ }
1027
+ }
1028
+ async startServer(decl) {
1029
+ const client = await this.connect(decl);
1030
+ const toolNames = [];
1031
+ try {
1032
+ const { tools } = await client.listTools();
1033
+ for (const tool of tools) {
1034
+ const cap = this.buildToolCapability(decl.id, tool, client);
1035
+ this.registry.register(cap, "agent");
1036
+ toolNames.push(cap.name);
1037
+ }
1038
+ } catch (err) {
1039
+ for (const name of toolNames) this.registry.deregister(name);
1040
+ try {
1041
+ await client.close();
1042
+ } catch {
1043
+ }
1044
+ throw err;
1045
+ }
1046
+ this.servers.push({ id: decl.id, client, toolNames });
1047
+ this.log.info("MCP server registered (runner-managed)", {
1048
+ id: decl.id,
1049
+ transport: decl.transport ?? "stdio",
1050
+ toolCount: toolNames.length,
1051
+ tools: toolNames
1052
+ });
1053
+ }
1054
+ /**
1055
+ * Wrap a single MCP tool as a {@link DefinedCapability}. The capability name
1056
+ * is `mcp__<server>__<tool>` (the naming claude-sdk wildcards and prompts
1057
+ * depend on). The handler proxies the call to the live MCP client; a tool
1058
+ * error (`isError`) is rethrown so the capability dispatch path surfaces it
1059
+ * to the LLM as a tool failure.
1060
+ */
1061
+ buildToolCapability(serverId, tool, client) {
1062
+ const name = `mcp__${serverId}__${tool.name}`;
1063
+ const inputSchema = tool.inputSchema && typeof tool.inputSchema === "object" ? tool.inputSchema : { type: "object" };
1064
+ return {
1065
+ name,
1066
+ description: tool.description ?? `MCP tool ${tool.name} from server ${serverId}`,
1067
+ inputSchema,
1068
+ side: "agent",
1069
+ origin: { kind: "mcp", serverId },
1070
+ scope: "session",
1071
+ kind: "effect",
1072
+ // LLM-only — these are agent tools, not user command-palette actions.
1073
+ audience: ["llm"],
1074
+ inputZod: PASSTHROUGH_INPUT_ZOD,
1075
+ handler: async (input, ctx) => {
1076
+ const result = await client.callTool({
1077
+ name: tool.name,
1078
+ arguments: input ?? {}
1079
+ });
1080
+ const content = result.content ?? [];
1081
+ const text = content.filter((c) => c.type === "text" && typeof c.text === "string").map((c) => c.text).join("\n");
1082
+ if (result.isError) {
1083
+ throw new Error(text || `MCP tool ${name} returned an error`);
1084
+ }
1085
+ ctx.log.debug("mcp tool ok", { tool: name, hasText: text.length > 0 });
1086
+ return text.length > 0 ? text : content;
1087
+ }
1088
+ };
1089
+ }
1090
+ /**
1091
+ * Deregister every tool and close every client (kills the subprocesses).
1092
+ * Best-effort: a failed close on one server does not prevent the rest.
1093
+ */
1094
+ async dispose() {
1095
+ for (const server of this.servers) {
1096
+ for (const toolName of server.toolNames) {
1097
+ this.registry.deregister(toolName);
1098
+ }
1099
+ try {
1100
+ await server.client.close();
1101
+ } catch (err) {
1102
+ this.log.warn("MCP server close failed", {
1103
+ id: server.id,
1104
+ error: err instanceof Error ? err.message : String(err)
1105
+ });
1106
+ }
1107
+ }
1108
+ this.servers.length = 0;
1109
+ }
1110
+ };
1111
+
853
1112
  // runner/src/resources.ts
854
- async function buildAgentResources(projectDir, driverType, _onLog, watch, secretProvider, sessionMeta, tokenMediator, preMintedSecrets) {
1113
+ async function buildAgentResources(projectDir, driverType, _onLog, watch, secretProvider, sessionMeta, tokenMediator, preMintedSecrets, deferFilesystemMounts, capabilityRegistry) {
855
1114
  const resLog = createLogger({
856
1115
  kind: "runner",
857
1116
  subkind: "resources",
@@ -891,11 +1150,13 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
891
1150
  secrets: secretProvider,
892
1151
  catalogEntries: runtimeAssets.catalogEntries,
893
1152
  tokenMediator,
894
- preMintedSecrets
1153
+ preMintedSecrets,
1154
+ onStatusChange: watch?.onConnectorStatus
895
1155
  });
896
1156
  try {
897
1157
  const report = await resourceManager.connectAll(connectorDeclarations, {
898
- failOnReadWriteError: true
1158
+ failOnReadWriteError: true,
1159
+ deferMounts: deferFilesystemMounts
899
1160
  });
900
1161
  for (const r of report.results) {
901
1162
  if (r.connected) {
@@ -918,13 +1179,9 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
918
1179
  }
919
1180
  }
920
1181
  const resourcePromptSection = resourceManager ? buildConnectorPromptSection(resourceManager) : "";
921
- const mcpLog = createLogger({
922
- kind: "mcp",
923
- subkind: "wiring",
924
- instance: sessionMeta?.sessionId ?? "no-session"
925
- });
926
1182
  let mcpServers;
927
1183
  let workspacePluginToShutdown = null;
1184
+ let externalMcpManager = null;
928
1185
  if (driverType === "claude-sdk") {
929
1186
  if (resourceManager) {
930
1187
  const connectorsLog = createLogger({ kind: "mcp", subkind: "skaile-connectors" });
@@ -955,96 +1212,34 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
955
1212
  mcpServers = { ...mcpServers ?? {}, "skaile-workspace": wsServer };
956
1213
  }
957
1214
  workspacePluginToShutdown = workspacePlugin;
958
- const mcpDeclarations = loadMcpServerDeclarations(projectDir);
959
- mcpLog.debug("loaded MCP declarations", { count: mcpDeclarations.length });
960
- const recipeCache = createRecipeCache();
961
- const nixFlakeRef = process.env.SKAILE_NIX_FLAKE_REF ?? "/etc/skaile/flake";
962
- for (const decl of mcpDeclarations) {
963
- if (RESERVED_MCP_SERVER_IDS.has(decl.id)) {
964
- mcpLog.warn("MCP server skipped: reserved id", { id: decl.id });
965
- continue;
966
- }
967
- let resolvedDecl = decl;
968
- if (decl.recipe) {
969
- const recipeLogKey = decl.recipe.flake ? { flake: decl.recipe.flake } : { attr: decl.recipe.attr };
970
- try {
971
- const recipeKey = decl.recipe.flake ? `mcps.${decl.id}` : decl.recipe.attr ?? `mcps.${decl.id}`;
972
- const outPath = resolveRecipePath(nixFlakeRef, recipeKey, recipeCache);
973
- const outPaths = /* @__PURE__ */ new Map([[decl.id, outPath]]);
974
- const resolvedCommand = decl.command ? substituteRecipeTemplating(decl.command, outPaths) : decl.command;
975
- const resolvedArgs = decl.args?.map((a) => substituteRecipeTemplating(a, outPaths));
976
- const resolvedEnv = substituteRecipeMap(decl.env, outPaths);
977
- resolvedDecl = {
978
- ...decl,
979
- command: resolvedCommand,
980
- args: resolvedArgs,
981
- env: resolvedEnv
982
- };
983
- const allResolved = [
984
- resolvedCommand ?? "",
985
- ...resolvedArgs ?? [],
986
- ...Object.values(resolvedEnv ?? {})
987
- ];
988
- for (const v of allResolved) {
989
- if (v.includes("${recipe:")) {
990
- const recipeLog2 = createLogger({
991
- kind: "mcp",
992
- subkind: decl.id,
993
- instance: "recipe-resolve"
994
- });
995
- recipeLog2.warn("unresolved recipe marker after substitution", {
996
- value: v,
997
- declId: decl.id
998
- });
999
- }
1000
- }
1001
- const missingPaths = validateSubstitutedPaths(resolvedCommand, resolvedArgs);
1002
- if (missingPaths.length > 0) {
1003
- const recipeLog2 = createLogger({
1004
- kind: "mcp",
1005
- subkind: decl.id,
1006
- instance: "recipe-resolve"
1007
- });
1008
- recipeLog2.error("MCP server skipped: substituted path does not exist", void 0, {
1009
- id: decl.id,
1010
- ...recipeLogKey,
1011
- missing: missingPaths
1012
- });
1013
- continue;
1014
- }
1015
- const recipeLog = createLogger({
1016
- kind: "mcp",
1017
- subkind: decl.id,
1018
- instance: "recipe-resolve"
1019
- });
1020
- recipeLog.info("recipe substitution complete", {
1021
- ...recipeLogKey,
1022
- outPath
1023
- });
1024
- } catch (err) {
1025
- const recipeLog = createLogger({
1026
- kind: "mcp",
1027
- subkind: decl.id,
1028
- instance: "recipe-resolve"
1029
- });
1030
- recipeLog.error("MCP server skipped: recipe resolution failed", err, {
1031
- id: decl.id,
1032
- ...recipeLogKey,
1033
- flakeRef: nixFlakeRef
1034
- });
1035
- continue;
1036
- }
1037
- }
1215
+ }
1216
+ if (capabilityRegistry) {
1217
+ const resolvedDecls = resolveExternalMcpDeclarations(projectDir, sessionMeta?.sessionId);
1218
+ if (resolvedDecls.length > 0) {
1219
+ externalMcpManager = new ExternalMcpManager(
1220
+ capabilityRegistry,
1221
+ secretProvider,
1222
+ sessionMeta?.sessionId
1223
+ );
1224
+ await externalMcpManager.start(resolvedDecls);
1225
+ }
1226
+ } else if (driverType === "claude-sdk") {
1227
+ const mcpLog = createLogger({
1228
+ kind: "mcp",
1229
+ subkind: "wiring",
1230
+ instance: sessionMeta?.sessionId ?? "no-session"
1231
+ });
1232
+ const resolvedDecls = resolveExternalMcpDeclarations(projectDir, sessionMeta?.sessionId);
1233
+ for (const decl of resolvedDecls) {
1038
1234
  const serverConfig = toSdkMcpServerConfig(
1039
- resolvedDecl,
1235
+ decl,
1040
1236
  secretProvider
1041
1237
  );
1042
1238
  if (serverConfig) {
1043
1239
  mcpServers = { ...mcpServers ?? {}, [decl.id]: serverConfig };
1044
- const declLog = createLogger({ kind: "mcp", subkind: decl.id });
1045
- declLog.info("MCP server registered", {
1240
+ createLogger({ kind: "mcp", subkind: decl.id }).info("MCP server registered", {
1046
1241
  transport: decl.transport ?? "stdio",
1047
- ...decl.transport === "stdio" || !decl.transport ? { command: resolvedDecl.command, args: resolvedDecl.args } : { url: decl.url }
1242
+ ...decl.transport === "stdio" || !decl.transport ? { command: decl.command, args: decl.args } : { url: decl.url }
1048
1243
  });
1049
1244
  } else {
1050
1245
  mcpLog.error("MCP server config invalid (translation returned null)", void 0, {
@@ -1054,7 +1249,8 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
1054
1249
  }
1055
1250
  }
1056
1251
  }
1057
- if (!resourceManager && driverType !== "claude-sdk") return noopResult;
1252
+ if (!resourceManager && driverType !== "claude-sdk" && !externalMcpManager?.hasServers())
1253
+ return noopResult;
1058
1254
  const startWatching = () => {
1059
1255
  if (resourceManager && watch?.onFileChanged) {
1060
1256
  const dedupTtlMs = watch.dedupTtlMs ?? 500;
@@ -1075,6 +1271,10 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
1075
1271
  }
1076
1272
  };
1077
1273
  const dispose = async () => {
1274
+ try {
1275
+ await externalMcpManager?.dispose();
1276
+ } catch {
1277
+ }
1078
1278
  if (workspacePluginToShutdown) {
1079
1279
  try {
1080
1280
  await workspacePluginToShutdown.shutdown();
@@ -1098,15 +1298,6 @@ async function buildAgentResources(projectDir, driverType, _onLog, watch, secret
1098
1298
  return noopResult;
1099
1299
  }
1100
1300
  }
1101
- var RESERVED_MCP_SERVER_IDS = /* @__PURE__ */ new Set(["skaile-connectors", "skaile-workspace"]);
1102
- function resolveRecordSecrets(record, secrets) {
1103
- if (!secrets) return record;
1104
- const resolved = {};
1105
- for (const [k, v] of Object.entries(record)) {
1106
- resolved[k] = secrets.resolve(v) ?? v;
1107
- }
1108
- return resolved;
1109
- }
1110
1301
  function toSdkMcpServerConfig(decl, secrets) {
1111
1302
  const transport = decl.transport ?? "stdio";
1112
1303
  if (transport === "stdio") {
@@ -1184,7 +1375,9 @@ async function createAgentSession(config) {
1184
1375
  sessionId
1185
1376
  },
1186
1377
  config.tokenMediator,
1187
- config.preMintedSecrets
1378
+ config.preMintedSecrets,
1379
+ config.deferFilesystemMounts,
1380
+ config.capabilityRegistry
1188
1381
  );
1189
1382
  const wsConfig = wsConfigOverride ?? resolveSkWorkspaceConfig(projectDir);
1190
1383
  const contextSection = buildContextSection(wsConfig.agent?.context);
@@ -2743,7 +2936,10 @@ function splitConnectorsForWire(manager) {
2743
2936
  driver: c.driver,
2744
2937
  access: c.access,
2745
2938
  mountPath: c.mountPath,
2746
- mounted: true,
2939
+ // A deferred (background) mount is pre-registered with its path but
2940
+ // status "connecting" — report it as not-yet-mounted; the platform
2941
+ // flips it on the subsequent connector_status "connected" event.
2942
+ mounted: c.status === "connected",
2747
2943
  source
2748
2944
  });
2749
2945
  } else {
@@ -3026,10 +3222,25 @@ async function startAgentServer(opts) {
3026
3222
  action: event.action,
3027
3223
  mountId: connectorId
3028
3224
  });
3225
+ },
3226
+ // Forward connector lifecycle transitions to the platform. This is the
3227
+ // channel deferred (rclone-backed) filesystem mounts report progress on
3228
+ // after session_init_ack — a wedged remote shows as "still syncing".
3229
+ onConnectorStatus: (event) => {
3230
+ sendEvent(event);
3029
3231
  }
3030
3232
  },
3233
+ // Bring rclone-backed filesystem mounts up in the background so a slow
3234
+ // remote can't blow the platform's session_init_ack deadline and trigger
3235
+ // the container restart loop.
3236
+ deferFilesystemMounts: true,
3031
3237
  onLog: (line) => log(`[serve] ${line}`),
3032
3238
  capabilities: buildCapabilityHooks(),
3239
+ // Hand the registry to the session builder so external (declarative /
3240
+ // recipe-backed) MCP servers are spawned by the RUNNER and their tools
3241
+ // registered as `mcp`-origin capabilities — works on every driver, no
3242
+ // dependence on the claude-agent-sdk `mcpServers` query option.
3243
+ capabilityRegistry,
3033
3244
  // v3: pre-minted credentials replace the tokenMediator for initial mints.
3034
3245
  // Refresh routes through `host.refresh_credential` (invoked from the
3035
3246
  // bridge driver's onAuthError callback). The legacy `tokenMediator`
@@ -3049,6 +3260,18 @@ async function startAgentServer(opts) {
3049
3260
  let secretProvider;
3050
3261
  let runtimeIdentity;
3051
3262
  let preMintedSecrets;
3263
+ try {
3264
+ const { skillsDir } = resolveDriverPaths({
3265
+ driver: opts.driver ?? wsConfig.agent_config?.default?.driver ?? "claude-sdk"
3266
+ });
3267
+ const stagedSkills = stageMaterializedSkills(opts.projectDir, skillsDir);
3268
+ if (stagedSkills.length > 0) {
3269
+ log(
3270
+ `[serve] staged ${stagedSkills.length} materialized skill(s): ${stagedSkills.join(", ")}`
3271
+ );
3272
+ }
3273
+ } catch {
3274
+ }
3052
3275
  if (secretsMode === "provisioned") {
3053
3276
  log("[serve] secrets mode: provisioned \u2014 waiting for provision_secrets command");
3054
3277
  } else {
@@ -4287,5 +4510,5 @@ function touchSession(state) {
4287
4510
  }
4288
4511
 
4289
4512
  export { CLAUDE_CODE_CREDENTIALS_KEY, COMPILE_MANIFEST_FILENAME, CapabilityRegistry, DEFAULT_CAPABILITY_CALL_TIMEOUT_MS, DEFAULT_COALESCE_MS, MarkdownStreamer, PreInitRingSink, agentDefinitionExists, bootstrapCapabilityRegistry, bootstrapRunnerLogStore, buildAgentResources, buildClientCapabilityHandler, buildContextSection, buildEnvironmentSection, builtinCapabilities, clearPreInitRingSink, clearSession, compileComposition, computeCapabilitySignature, createAgentSession, createSessionStimulusBus, defineCapability, deleteSession, emitSystemPromptComposed, ensureGitConfigInclude, extractClaudeAiOauthExpiresAt, getPreInitRingSink, handleMountResourceRequest, handleResourceRequest, installPreInitRingSink, listSessions, loadAgentManifest, loadCompileManifest, loadCompileManifestFromDir, loadSession, loadSessionById, newSession, registerCompositionCapabilities, rejectCapabilityOnApprovalDeny, resetRunnerLogStore, resolveAgentComposition, resolveAgentMixins, resolveBinding, resolveCapabilityCallTimeoutMs, resolveCapabilityResult, resolveComposition, resolveMixin, runAgentChat, saveSession, setCurrentSession, startAgentServer, touchSession, writeClaudeCodeCredentialsFile };
4290
- //# sourceMappingURL=chunk-DZCFFTAX.js.map
4291
- //# sourceMappingURL=chunk-DZCFFTAX.js.map
4513
+ //# sourceMappingURL=chunk-BJWUSHC4.js.map
4514
+ //# sourceMappingURL=chunk-BJWUSHC4.js.map