@pellux/goodvibes-sdk 0.33.25 → 0.33.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/dist/contracts/artifacts/operator-contract.json +1090 -4
  2. package/dist/platform/agents/worktree.d.ts +3 -0
  3. package/dist/platform/agents/worktree.d.ts.map +1 -1
  4. package/dist/platform/agents/worktree.js +64 -2
  5. package/dist/platform/agents/wrfc-controller.d.ts +3 -1
  6. package/dist/platform/agents/wrfc-controller.d.ts.map +1 -1
  7. package/dist/platform/agents/wrfc-controller.js +59 -9
  8. package/dist/platform/control-plane/method-catalog-runtime.d.ts.map +1 -1
  9. package/dist/platform/control-plane/method-catalog-runtime.js +150 -2
  10. package/dist/platform/daemon/facade-composition.d.ts.map +1 -1
  11. package/dist/platform/daemon/facade-composition.js +2 -0
  12. package/dist/platform/daemon/http/mcp-routes.d.ts +13 -0
  13. package/dist/platform/daemon/http/mcp-routes.d.ts.map +1 -0
  14. package/dist/platform/daemon/http/mcp-routes.js +128 -0
  15. package/dist/platform/daemon/http/router.d.ts +2 -0
  16. package/dist/platform/daemon/http/router.d.ts.map +1 -1
  17. package/dist/platform/daemon/http/router.js +12 -0
  18. package/dist/platform/git/service.d.ts +4 -0
  19. package/dist/platform/git/service.d.ts.map +1 -1
  20. package/dist/platform/git/service.js +33 -0
  21. package/dist/platform/mcp/config.d.ts +27 -0
  22. package/dist/platform/mcp/config.d.ts.map +1 -1
  23. package/dist/platform/mcp/config.js +165 -46
  24. package/dist/platform/mcp/index.d.ts +3 -3
  25. package/dist/platform/mcp/index.d.ts.map +1 -1
  26. package/dist/platform/mcp/index.js +1 -1
  27. package/dist/platform/mcp/mcp-api.d.ts +24 -0
  28. package/dist/platform/mcp/mcp-api.d.ts.map +1 -1
  29. package/dist/platform/mcp/mcp-api.js +12 -0
  30. package/dist/platform/mcp/registry.d.ts +27 -2
  31. package/dist/platform/mcp/registry.d.ts.map +1 -1
  32. package/dist/platform/mcp/registry.js +106 -8
  33. package/dist/platform/version.js +1 -1
  34. package/package.json +9 -9
@@ -8,11 +8,11 @@
8
8
  * Tool namespace: mcp:<server-name>:<tool-name>
9
9
  */
10
10
  import { logger } from '../utils/logger.js';
11
- import { loadMcpConfig } from './config.js';
11
+ import { loadMcpEffectiveConfig, removeMcpServerConfig, upsertMcpServerConfig, } from './config.js';
12
12
  import { McpClient } from './client.js';
13
13
  import { McpPermissionManager } from '../runtime/mcp/permissions.js';
14
14
  import { McpSchemaFreshnessTracker } from '../runtime/mcp/schema-freshness.js';
15
- import { emitMcpConfigured, emitMcpPolicyUpdated, emitMcpSchemaQuarantineApproved, emitMcpSchemaQuarantined, } from '../runtime/emitters/mcp.js';
15
+ import { emitMcpConfigured, emitMcpDisconnected, emitMcpPolicyUpdated, emitMcpSchemaQuarantineApproved, emitMcpSchemaQuarantined, } from '../runtime/emitters/mcp.js';
16
16
  import { getSandboxConfigSnapshot } from '../runtime/sandbox/manager.js';
17
17
  import {} from '../runtime/sandbox/session-registry.js';
18
18
  import { resolveSandboxCommandPlan } from '../runtime/sandbox/backend.js';
@@ -20,8 +20,12 @@ import { summarizeError } from '../utils/error-display.js';
20
20
  function compactEnv(env) {
21
21
  return Object.fromEntries(Object.entries(env).filter((entry) => typeof entry[1] === 'string'));
22
22
  }
23
+ function sameServerConfig(a, b) {
24
+ return JSON.stringify(a ?? null) === JSON.stringify(b ?? null);
25
+ }
23
26
  export class McpRegistry {
24
27
  clients = new Map();
28
+ serverConfigs = new Map();
25
29
  permissions = new McpPermissionManager();
26
30
  freshness = new McpSchemaFreshnessTracker();
27
31
  runtimeBus = null;
@@ -45,16 +49,74 @@ export class McpRegistry {
45
49
  * Errors on individual servers are logged but do not abort the whole startup.
46
50
  */
47
51
  async connectAll(roots) {
48
- const mcpConfig = loadMcpConfig(roots);
49
- await Promise.allSettled(mcpConfig.servers.map((serverConfig) => this._connectServer(serverConfig)));
52
+ await this.reload(roots);
50
53
  }
51
54
  /**
52
55
  * connectServer — Connect a single MCP server by config.
53
56
  * Exposed for programmatic use (testing, dynamic registration).
54
57
  */
55
58
  async connectServer(serverConfig) {
59
+ this.serverConfigs.set(serverConfig.name, serverConfig);
56
60
  await this._connectServer(serverConfig);
57
61
  }
62
+ async reload(roots) {
63
+ const effective = loadMcpEffectiveConfig(roots);
64
+ return this.applyConfig(effective.servers.map((entry) => entry.server));
65
+ }
66
+ getEffectiveConfig(roots) {
67
+ return loadMcpEffectiveConfig(roots);
68
+ }
69
+ async upsertServerConfig(roots, scope, serverConfig) {
70
+ const written = upsertMcpServerConfig(roots, scope, serverConfig);
71
+ return { path: written.path, reload: await this.reload(roots) };
72
+ }
73
+ async removeServerConfig(roots, scope, serverName) {
74
+ const written = removeMcpServerConfig(roots, scope, serverName);
75
+ return { path: written.path, removed: written.removed, reload: await this.reload(roots) };
76
+ }
77
+ async applyConfig(serverConfigs) {
78
+ const next = new Map(serverConfigs.map((serverConfig) => [serverConfig.name, serverConfig]));
79
+ const results = [];
80
+ let added = 0;
81
+ let changed = 0;
82
+ let removed = 0;
83
+ let unchanged = 0;
84
+ for (const name of [...this.serverConfigs.keys()]) {
85
+ if (next.has(name))
86
+ continue;
87
+ await this.disconnectServer(name, 'config-removed');
88
+ this.serverConfigs.delete(name);
89
+ removed += 1;
90
+ results.push({ name, action: 'removed', connected: false });
91
+ }
92
+ for (const [name, serverConfig] of next) {
93
+ const previous = this.serverConfigs.get(name);
94
+ if (previous && sameServerConfig(previous, serverConfig)) {
95
+ unchanged += 1;
96
+ const client = this.clients.get(name);
97
+ if (!client?.isConnected) {
98
+ await this._connectServer(serverConfig);
99
+ }
100
+ results.push({ name, action: 'unchanged', connected: this.clients.get(name)?.isConnected ?? false });
101
+ continue;
102
+ }
103
+ if (previous) {
104
+ await this.disconnectServer(name, 'config-changed');
105
+ changed += 1;
106
+ }
107
+ else {
108
+ added += 1;
109
+ }
110
+ this.serverConfigs.set(name, serverConfig);
111
+ await this._connectServer(serverConfig);
112
+ results.push({
113
+ name,
114
+ action: previous ? 'changed' : 'added',
115
+ connected: this.clients.get(name)?.isConnected ?? false,
116
+ });
117
+ }
118
+ return { added, changed, removed, unchanged, servers: results };
119
+ }
58
120
  /**
59
121
  * listAllTools — Return all registered tools (name + description) from all connected servers.
60
122
  * Only loads tool names and descriptions — full schemas are NOT fetched here.
@@ -194,6 +256,38 @@ export class McpRegistry {
194
256
  }
195
257
  this.sandboxSessionByServer.clear();
196
258
  }
259
+ async disconnectServer(serverName, reason = 'manual') {
260
+ const client = this.clients.get(serverName);
261
+ if (!client)
262
+ return false;
263
+ await client.disconnect();
264
+ this.clients.delete(serverName);
265
+ const sessionId = this.sandboxSessionByServer.get(serverName);
266
+ if (sessionId) {
267
+ this.sandboxSessions.stop(sessionId);
268
+ this.sandboxSessionByServer.delete(serverName);
269
+ }
270
+ if (this.runtimeBus) {
271
+ emitMcpDisconnected(this.runtimeBus, {
272
+ sessionId: 'mcp-registry',
273
+ traceId: `mcp-registry:${serverName}:disconnected`,
274
+ source: 'mcp-registry',
275
+ }, { serverId: serverName, reason, willRetry: false });
276
+ }
277
+ const disconnectedEvent = {
278
+ path: 'Lifecycle:mcp:disconnected',
279
+ phase: 'Lifecycle',
280
+ category: 'mcp',
281
+ specific: 'disconnected',
282
+ sessionId: '',
283
+ timestamp: Date.now(),
284
+ payload: { server: serverName, reason },
285
+ };
286
+ this.hookDispatcher.fire(disconnectedEvent).catch((err) => {
287
+ logger.warn('Lifecycle:mcp:disconnected hook error', { error: summarizeError(err) });
288
+ });
289
+ return true;
290
+ }
197
291
  /**
198
292
  * getClient — Get the McpClient for a given server name (for advanced use).
199
293
  */
@@ -202,15 +296,15 @@ export class McpRegistry {
202
296
  }
203
297
  /** Connected server names. */
204
298
  get serverNames() {
205
- return Array.from(this.clients.keys());
299
+ return Array.from(new Set([...this.serverConfigs.keys(), ...this.clients.keys()])).sort();
206
300
  }
207
301
  /**
208
302
  * listServers — Return status info for all known servers (connected or not).
209
303
  */
210
304
  listServers() {
211
- return Array.from(this.clients.entries()).map(([name, client]) => ({
305
+ return this.serverNames.map((name) => ({
212
306
  name,
213
- connected: client.isConnected,
307
+ connected: this.clients.get(name)?.isConnected ?? false,
214
308
  }));
215
309
  }
216
310
  listServerSecurity() {
@@ -283,10 +377,14 @@ export class McpRegistry {
283
377
  // ---------------------------------------------------------------------------
284
378
  async _connectServer(serverConfig) {
285
379
  const { name } = serverConfig;
286
- if (this.clients.has(name)) {
380
+ const existing = this.clients.get(name);
381
+ if (existing?.isConnected) {
287
382
  logger.info('McpRegistry: server already registered', { name });
288
383
  return;
289
384
  }
385
+ if (existing) {
386
+ await this.disconnectServer(name, 'reconnect');
387
+ }
290
388
  let sandboxSessionId = null;
291
389
  let processSpec;
292
390
  if (this.sandboxConfigManager) {
@@ -1,6 +1,6 @@
1
1
  import { readFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
- let version = '0.33.25';
3
+ let version = '0.33.27';
4
4
  try {
5
5
  const pkg = JSON.parse(readFileSync(join(import.meta.dir, '..', '..', 'package.json'), 'utf-8'));
6
6
  version = pkg.version ?? version;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pellux/goodvibes-sdk",
3
- "version": "0.33.25",
3
+ "version": "0.33.27",
4
4
  "description": "TypeScript SDK for building GoodVibes operator, peer, web, mobile, and daemon-connected apps with typed contracts, auth, realtime events, and transport layers.",
5
5
  "keywords": [
6
6
  "goodvibes",
@@ -449,14 +449,14 @@
449
449
  "sideEffects": false,
450
450
  "type": "module",
451
451
  "dependencies": {
452
- "@pellux/goodvibes-contracts": "0.33.25",
453
- "@pellux/goodvibes-daemon-sdk": "0.33.25",
454
- "@pellux/goodvibes-errors": "0.33.25",
455
- "@pellux/goodvibes-operator-sdk": "0.33.25",
456
- "@pellux/goodvibes-peer-sdk": "0.33.25",
457
- "@pellux/goodvibes-transport-core": "0.33.25",
458
- "@pellux/goodvibes-transport-http": "0.33.25",
459
- "@pellux/goodvibes-transport-realtime": "0.33.25"
452
+ "@pellux/goodvibes-contracts": "0.33.27",
453
+ "@pellux/goodvibes-daemon-sdk": "0.33.27",
454
+ "@pellux/goodvibes-errors": "0.33.27",
455
+ "@pellux/goodvibes-operator-sdk": "0.33.27",
456
+ "@pellux/goodvibes-peer-sdk": "0.33.27",
457
+ "@pellux/goodvibes-transport-core": "0.33.27",
458
+ "@pellux/goodvibes-transport-http": "0.33.27",
459
+ "@pellux/goodvibes-transport-realtime": "0.33.27"
460
460
  },
461
461
  "optionalDependencies": {
462
462
  "@agentclientprotocol/sdk": "^0.21.0",