clay-server 5.2.0-beta.5 → 5.2.0-beta.6

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 (151) hide show
  1. package/lib/claude-hook-installer.js +1 -267
  2. package/lib/claude-jsonl-watcher.js +1 -204
  3. package/lib/clay-skill-install.js +1 -33
  4. package/lib/cli-sessions.js +1 -300
  5. package/lib/codex-defaults.js +1 -28
  6. package/lib/context-usage.js +3 -2
  7. package/lib/daemon.js +5 -55
  8. package/lib/default-ai-runtime.js +4 -8
  9. package/lib/email-mcp-server.js +1 -1
  10. package/lib/kiro-defaults.js +1 -22
  11. package/lib/mcp-proxy.js +2 -32
  12. package/lib/mcp-query-bridge.js +1 -1
  13. package/lib/mcp-source-adapters.js +1 -91
  14. package/lib/mcp-source-context.js +1 -42
  15. package/lib/mcp-source-discovery.js +1 -169
  16. package/lib/mcp-source-plugins.js +1 -68
  17. package/lib/mcp-source-restrictions.js +1 -47
  18. package/lib/mcp-tool-policy.js +1 -26
  19. package/lib/os-user-diagnostics.js +2 -4
  20. package/lib/os-users.js +4 -65
  21. package/lib/project-connection.js +8 -7
  22. package/lib/project-http.js +4 -4
  23. package/lib/project-mcp.js +2 -2
  24. package/lib/project-pair-result-delivery.js +2 -2
  25. package/lib/project-pair-usage.js +12 -13
  26. package/lib/project-pending-message-queue.js +3 -1
  27. package/lib/project-session-history-actions.js +5 -3
  28. package/lib/project-session-pair.js +1 -1
  29. package/lib/project-session-runtime-actions.js +2 -1
  30. package/lib/project-session-spawn.js +2 -3
  31. package/lib/project-session-vendor.js +2 -1
  32. package/lib/project-sessions.js +18 -26
  33. package/lib/project-user-message.js +1 -0
  34. package/lib/project-vendor-login.js +6 -36
  35. package/lib/public/app.js +1 -0
  36. package/lib/public/css/messages.css +6 -2
  37. package/lib/public/modules/app-favicon.js +11 -2
  38. package/lib/public/modules/app-header.js +4 -2
  39. package/lib/public/modules/app-loop-ui.js +2 -0
  40. package/lib/public/modules/app-messages.js +36 -5
  41. package/lib/public/modules/app-notifications.js +1 -2
  42. package/lib/public/modules/app-panels.js +61 -123
  43. package/lib/public/modules/app-rate-limit.js +7 -27
  44. package/lib/public/modules/chat-render-runtime.js +3 -0
  45. package/lib/public/modules/mcp-connection-view.js +1 -1
  46. package/lib/public/modules/permission-control.js +5 -4
  47. package/lib/public/modules/scheduler.js +2 -0
  48. package/lib/public/modules/session-create-form.js +10 -10
  49. package/lib/public/modules/settings-defaults.js +27 -28
  50. package/lib/public/modules/tools.js +2 -1
  51. package/lib/public/modules/tui-grab.js +1 -2
  52. package/lib/public/modules/vendor-login-browser.js +5 -3
  53. package/lib/public/modules/vendor-login.js +2 -2
  54. package/lib/public/modules/vendor-priority.js +15 -1
  55. package/lib/public/muse-avatar.svg +4 -0
  56. package/lib/safe-bash-commands.js +3 -45
  57. package/lib/sdk-bridge.js +127 -395
  58. package/lib/sdk-foreground-admission.js +16 -0
  59. package/lib/sdk-mcp-descriptors.js +2 -37
  60. package/lib/sdk-message-processor.js +50 -17
  61. package/lib/sdk-quota-snapshot.js +1 -1
  62. package/lib/sdk-response-recovery.js +1 -155
  63. package/lib/sdk-worker.js +2 -677
  64. package/lib/server.js +1 -1
  65. package/lib/session-fork-history.js +21 -0
  66. package/lib/session-hygiene.js +3 -35
  67. package/lib/session-permission-mode.js +4 -3
  68. package/lib/session-skill-scope.js +11 -0
  69. package/lib/sessions.js +17 -89
  70. package/lib/tool-llm.js +1 -1
  71. package/lib/tui-transcript-index.js +1 -132
  72. package/lib/vendor-login-browser.js +1 -11
  73. package/lib/yoke/acp-agent-profiles.js +5 -3
  74. package/lib/yoke/adapters/antigravity.js +3 -14
  75. package/lib/yoke/adapters/claude-query-contract.js +9 -0
  76. package/lib/yoke/adapters/claude.js +7 -42
  77. package/lib/yoke/adapters/codex-query-contract.js +24 -0
  78. package/lib/yoke/adapters/codex.js +1 -0
  79. package/lib/yoke/adapters/kiro.js +3 -1
  80. package/lib/yoke/adapters/muse-query-contract.js +7 -0
  81. package/lib/yoke/adapters/muse.js +122 -0
  82. package/lib/yoke/auth-events.js +17 -0
  83. package/lib/yoke/claude-hook-installer.js +267 -0
  84. package/lib/yoke/claude-jsonl-watcher.js +204 -0
  85. package/lib/yoke/claude-maintenance.js +55 -0
  86. package/lib/yoke/claude-permission-patterns.js +53 -0
  87. package/lib/yoke/claude-response-recovery.js +155 -0
  88. package/lib/yoke/claude-transcript-hygiene.js +34 -0
  89. package/lib/yoke/clay-skill-install.js +33 -0
  90. package/lib/yoke/cli-sessions.js +300 -0
  91. package/lib/yoke/codex-app-server.js +7 -12
  92. package/lib/yoke/configuration.js +13 -0
  93. package/lib/yoke/discovery.js +70 -0
  94. package/lib/yoke/errors.js +8 -0
  95. package/lib/yoke/host-environment.js +7 -0
  96. package/lib/yoke/index.js +9 -51
  97. package/lib/yoke/interface.js +116 -0
  98. package/lib/yoke/kiro-acp-server.js +5 -5
  99. package/lib/yoke/legacy-sessions.js +89 -0
  100. package/lib/yoke/login-protocol.js +25 -0
  101. package/lib/yoke/maintenance.js +12 -0
  102. package/lib/yoke/mcp-proxy.js +32 -0
  103. package/lib/yoke/mcp-source-adapters.js +91 -0
  104. package/lib/yoke/mcp-source-context.js +42 -0
  105. package/lib/yoke/mcp-source-discovery.js +169 -0
  106. package/lib/yoke/mcp-source-plugins.js +68 -0
  107. package/lib/yoke/mcp-source-restrictions.js +47 -0
  108. package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
  109. package/lib/yoke/model-preferences.js +6 -0
  110. package/lib/yoke/muse-artifacts.js +36 -0
  111. package/lib/yoke/muse-controls.js +50 -0
  112. package/lib/yoke/muse-input.js +64 -0
  113. package/lib/yoke/muse-items.js +157 -0
  114. package/lib/yoke/muse-policy.js +52 -0
  115. package/lib/yoke/muse-query.js +428 -0
  116. package/lib/yoke/muse-runtime.js +211 -0
  117. package/lib/yoke/muse-services.js +79 -0
  118. package/lib/yoke/muse-skills.js +79 -0
  119. package/lib/yoke/muse-turns.js +71 -0
  120. package/lib/yoke/options/acp.js +2 -0
  121. package/lib/yoke/options/antigravity.js +2 -0
  122. package/lib/yoke/options/claude.js +21 -0
  123. package/lib/yoke/options/codex-defaults.js +29 -0
  124. package/lib/yoke/options/codex-settings.js +21 -0
  125. package/lib/yoke/options/codex.js +6 -0
  126. package/lib/yoke/options/kiro-defaults.js +22 -0
  127. package/lib/yoke/options/kiro.js +5 -0
  128. package/lib/yoke/options/muse.js +5 -0
  129. package/lib/yoke/provider-contract.js +5 -0
  130. package/lib/yoke/providers/antigravity-executable.js +8 -0
  131. package/lib/yoke/providers/claude-executable.js +40 -0
  132. package/lib/yoke/providers/claude-model-descriptions.js +14 -0
  133. package/lib/yoke/providers/claude-process.js +97 -0
  134. package/lib/yoke/providers/claude-provision.js +75 -0
  135. package/lib/yoke/providers/codex-errors.js +8 -0
  136. package/lib/yoke/providers/muse-executable.js +17 -0
  137. package/lib/yoke/provisioning.js +6 -0
  138. package/lib/yoke/query-options.js +10 -0
  139. package/lib/yoke/response-recovery.js +41 -0
  140. package/lib/yoke/sdk-mcp-descriptors.js +37 -0
  141. package/lib/yoke/sdk-server-factory.js +7 -0
  142. package/lib/yoke/session-history.js +8 -0
  143. package/lib/yoke/session-tool-transport.js +31 -13
  144. package/lib/yoke/settings.js +23 -0
  145. package/lib/yoke/source-locations.js +4 -0
  146. package/lib/yoke/title-query.js +18 -7
  147. package/lib/yoke/tui-transcript-index.js +132 -0
  148. package/lib/yoke/usage.js +48 -0
  149. package/lib/yoke/user-input.js +13 -0
  150. package/lib/yoke/vendor-registry.js +81 -1
  151. package/package.json +3 -2
@@ -1,42 +1 @@
1
- var path = require('path');
2
- var crypto = require('crypto');
3
- var adapters = require('./mcp-source-adapters').adapters;
4
- var KEYS = ['CODEX_HOME', 'CLAUDE_CONFIG_DIR', 'OPENCODE_CONFIG', 'OPENCODE_CONFIG_DIR', 'OPENCODE_CONFIG_CONTENT', 'XDG_CONFIG_HOME', 'KIMI_CODE_HOME', 'COPILOT_HOME', 'GROK_HOME', 'QWEN_HOME', 'QWEN_CODE_SYSTEM_SETTINGS_PATH', 'QWEN_CODE_SYSTEM_DEFAULTS_PATH'];
5
- function resolve(ctx, owner, ws, home, identity) {
6
- // Only the production runtime resolver may supply mapped-user overrides.
7
- var env = ctx.runtimeEnv ? ctx.runtimeEnv(owner, ws) : identity ? {} : ctx.home ? {} : process.env;
8
- var selected = {};
9
- KEYS.forEach(function (key) { if (env[key] !== undefined) selected[key] = env[key]; });
10
- function qwenRoot(value) { if (!value) return path.join(home, '.qwen'); return path.resolve(ctx.cwd, value.replace(/^~(?=\/|$)/, home)); }
11
- var roots = { copilot: path.resolve(home, env.COPILOT_HOME || '.copilot'), grok: path.resolve(home, env.GROK_HOME || '.grok'), qwen: qwenRoot(env.QWEN_HOME), codex: path.resolve(home, env.CODEX_HOME || '.codex'), claude: path.resolve(home, env.CLAUDE_CONFIG_DIR || '.claude'),
12
- opencode: path.resolve(home, env.XDG_CONFIG_HOME || '.config', 'opencode'), kimi: path.resolve(home, env.KIMI_CODE_HOME || '.kimi-code') };
13
- var sources = [];
14
- function add(vendor, file, scope, extra) { sources.push(Object.assign({ vendor: vendor, file: file, scope: scope, cwd: ctx.cwd }, extra)); }
15
- Object.keys(adapters).forEach(function (vendor) {
16
- var files = adapters[vendor].user;
17
- if (vendor === 'codex') files = [path.join(roots.codex, 'config.toml')];
18
- if (vendor === 'claude') files = [env.CLAUDE_CONFIG_DIR ? path.join(roots.claude, '.claude.json') : path.join(home, '.claude.json')];
19
- if (vendor === 'opencode') files = ['opencode.json', 'opencode.jsonc'].map(function (file) { return path.join(roots.opencode, file); });
20
- if (vendor === 'copilot') files = [path.join(roots.copilot, 'mcp-config.json')];
21
- if (vendor === 'grok') files = [path.join(roots.grok, 'config.toml')];
22
- if (vendor === 'qwen') files = [path.join(roots.qwen, 'settings.json')];
23
- if (vendor === 'kimi' && env.KIMI_CODE_HOME) files = [path.join(roots.kimi, 'mcp.json')];
24
- files.forEach(function (file) { add(vendor, path.resolve(home, file), 'user'); });
25
- adapters[vendor].project.forEach(function (file) { add(vendor, path.join(ctx.cwd, file), 'project'); });
26
- });
27
- ['opencode.json', 'opencode.jsonc'].forEach(function (file) { add('opencode', path.join(ctx.cwd, '.opencode', file), 'runtime', { configDirectory: true }); });
28
- if (env.OPENCODE_CONFIG) add('opencode', path.resolve(ctx.cwd, env.OPENCODE_CONFIG), 'runtime');
29
- if (env.OPENCODE_CONFIG_DIR) ['opencode.json', 'opencode.jsonc'].forEach(function (file) { add('opencode', path.resolve(ctx.cwd, env.OPENCODE_CONFIG_DIR, file), 'runtime', { configDirectory: true }); });
30
- if (env.OPENCODE_CONFIG_CONTENT) add('opencode', 'runtime:OPENCODE_CONFIG_CONTENT', 'runtime-inline', { inline: env.OPENCODE_CONFIG_CONTENT });
31
- return { roots: roots, policyPaths: { qwenSettings: env.QWEN_CODE_SYSTEM_SETTINGS_PATH && path.resolve(ctx.cwd, env.QWEN_CODE_SYSTEM_SETTINGS_PATH), qwenDefaults: env.QWEN_CODE_SYSTEM_DEFAULTS_PATH && path.resolve(ctx.cwd, env.QWEN_CODE_SYSTEM_DEFAULTS_PATH) }, sources: sources, fingerprint: crypto.createHash('sha256').update(JSON.stringify(selected)).digest('hex') };
32
- }
33
- function priority(source) {
34
- if (source.scope === 'runtime-inline') return 80;
35
- if (source.scope === 'configured') return 70;
36
- if (source.scope === 'runtime') return source.configDirectory ? 65 : 30;
37
- if (source.scope === 'local') return 60;
38
- if (source.scope === 'project') return 50 + (source.vendor === 'copilot' && path.basename(source.file) === '.mcp.json' ? 1 : 0);
39
- if (source.scope === 'project-ancestor') return 40 + source.file.split(path.sep).length * 0.01 + (path.basename(source.file) === '.mcp.json' ? 0.001 : 0);
40
- return source.scope.indexOf('plugin') === 0 ? 10 : 20;
41
- }
42
- module.exports = { resolve: resolve, priority: priority };
1
+ module.exports = require("./yoke/mcp-source-context");
@@ -1,169 +1 @@
1
- var fs = require('fs');
2
- var path = require('path');
3
- var os = require('os');
4
- var crypto = require('crypto');
5
- var adapters = require('./mcp-source-adapters');
6
- var sourceContext = require('./mcp-source-context');
7
- var restrictions = require('./mcp-source-restrictions');
8
- var fsAsUser = require('./os-users').fsAsUser;
9
- function createDiscovery(ctx) {
10
- var scans = new Map(), publicScans = new Map();
11
- function read(file, identity) {
12
- if (identity) {
13
- var stat = fsAsUser('stat', { file: file }, identity);
14
- if (stat.size > 2 * 1024 * 1024 || stat.isDir) throw new Error('Source exceeds size limit.');
15
- return fsAsUser('read', { file: file, readContent: true }, identity).content;
16
- }
17
- var info = fs.statSync(file);
18
- if (!info.isFile() || info.size > 2 * 1024 * 1024) throw new Error('Source exceeds size limit.');
19
- return fs.readFileSync(file, 'utf8');
20
- }
21
- function list(directory, identity) {
22
- try { return identity ? fsAsUser('list', { dir: directory }, identity) : fs.readdirSync(directory, { withFileTypes: true }).map(function (entry) { return { name: entry.name, isDir: entry.isDirectory() }; }); }
23
- catch (error) { return []; }
24
- }
25
- function scan(owner, ws, explicit) {
26
- ctx.authorize(owner, ws);
27
- var identity = ctx.identity(ws), home = identity ? identity.home : ctx.home || os.homedir();
28
- var context = sourceContext.resolve(ctx, owner, ws, home, identity);
29
- var rows = [], diagnostics = [], sources = context.sources, files = new Map(), directories = new Map();
30
- function listing(directory) { var entries = list(directory, identity); directories.set(directory, JSON.stringify(entries.slice().sort(function (left, right) { return left.name.localeCompare(right.name); }))); return entries; }
31
- function source(vendor, file, scope, pluginRoot, extra) { sources.push(Object.assign({ vendor: vendor, file: file, scope: scope, cwd: ctx.cwd, pluginRoot: pluginRoot }, extra)); }
32
- Object.keys(adapters.adapters).forEach(function (vendor) {
33
- var adapter = adapters.adapters[vendor];
34
- if (adapter.agents) [home, ctx.cwd].forEach(function (base) {
35
- var directory = path.join(base, adapter.agents);
36
- listing(directory).slice(0, 100).forEach(function (entry) { if (!entry.isDir && /\.(json|md)$/.test(entry.name)) source(vendor, path.join(directory, entry.name), 'agent'); });
37
- });
38
- if (adapter.plugins) {
39
- var directory = path.join(home, adapter.plugins);
40
- listing(directory).slice(0, 100).forEach(function (entry) { if (entry.isDir) source(vendor, path.join(directory, entry.name, 'mcp_config.json'), 'plugin', path.join(directory, entry.name), { blocked: 'This plugin has no locally resolved activation registry. Review its active configuration before importing.' }); });
41
- }
42
- });
43
- // Copilot reads nearer project definitions before ancestors up to the Git root.
44
- var parents = [], repository = false;
45
- var currentDirectory = ctx.cwd;
46
- while (true) {
47
- try { if (identity) fsAsUser('stat', { file: path.join(currentDirectory, '.git') }, identity); else fs.statSync(path.join(currentDirectory, '.git')); repository = true; break; } catch (error) {}
48
- var parent = path.dirname(currentDirectory);
49
- if (parent === currentDirectory) break;
50
- parents.push(parent); currentDirectory = parent;
51
- }
52
- if (repository) parents.forEach(function (directory) {
53
- source('copilot', path.join(directory, '.mcp.json'), 'project-ancestor');
54
- source('copilot', path.join(directory, '.github/mcp.json'), 'project-ancestor');
55
- });
56
- diagnostics.push({ vendor: 'all', scope: 'plugins', message: 'Remote bundles are not downloaded. Unresolved installed plugin activation and local managed policies block import; credentials and vendor OAuth stores are never inspected.' });
57
- if (explicit) {
58
- if (!adapters.adapters[explicit.vendor] || typeof explicit.file !== 'string' || !path.isAbsolute(explicit.file)) throw new Error('Choose a supported format and an absolute source file path.');
59
- source(explicit.vendor, explicit.file, 'configured');
60
- }
61
- function load(file) {
62
- if (files.has(file)) { var cached = files.get(file); if (cached.error) throw cached.error; return cached.value; }
63
- try {
64
- var content = read(file, identity), value = adapters.parse(file, content);
65
- files.set(file, { value: value, digest: crypto.createHash('sha256').update(content).digest('hex') }); return value;
66
- } catch (error) { files.set(file, { error: error, digest: null }); throw error; }
67
- }
68
- require('./mcp-source-plugins').discover({ load: load, source: source, list: listing, roots: context.roots, cwd: ctx.cwd, diagnostics: diagnostics });
69
- var blockedVendors = restrictions.inspect({ load: load, roots: context.roots, policyPaths: context.policyPaths, cwd: ctx.cwd, home: home, username: identity ? identity.username : process.env.USER });
70
- var openCodeTools = {}, openCodeDefinitions = {};
71
- function collect(source, definitions, root) {
72
- if (!definitions || typeof definitions !== 'object') return;
73
- var names = Array.isArray(definitions) ? definitions.map(function (definition) { return definition.name; }) : Object.keys(definitions);
74
- names.slice(0, 100).forEach(function (name, index) {
75
- if (typeof name !== 'string' || !name || name.length > 100) return;
76
- try {
77
- var definition = Array.isArray(definitions) ? definitions[index] : definitions[name];
78
- if (source.vendor === 'opencode') {
79
- definition = Object.assign({}, openCodeDefinitions[name] || {}, definition);
80
- openCodeDefinitions[name] = definition;
81
- }
82
- if (source.overrides && source.overrides[name]) definition = Object.assign({}, definition, source.overrides[name]);
83
- var provenance = Object.assign({}, source); delete provenance.inline; delete provenance.overrides;
84
- var config = adapters.normalize(source.vendor, name, definition, provenance);
85
- var blocked = source.blocked || blockedVendors[source.vendor];
86
- if (root && Object.keys(root).some(function (key) { return restrictions.securityKey(key); })) blocked = 'This source contains runtime or agent restrictions Clay cannot resolve. Review its effective policy before importing.';
87
- if (root && root.tools !== undefined && source.vendor !== 'opencode') blocked = 'This source contains runtime tool restrictions Clay cannot resolve. Review its effective policy before importing.';
88
- var known = ['type', 'transport', 'command', 'args', 'env', 'environment', 'cwd', 'url', 'httpUrl', 'serverUrl', 'headers', 'http_headers', 'env_http_headers', 'bearer_token_env_var', 'bearerTokenEnvVar', 'includeTools', 'excludeTools', 'env_vars', 'enabled', 'disabled', 'autoApprove', 'disabledTools', 'disabled_tools', 'enabledTools', 'enabled_tools', 'timeout', 'startup_timeout_sec', 'tool_timeout_sec', 'oauth', 'oauthScopes', 'trust', 'name', 'description', 'tools', 'startupTimeoutMs'];
89
- var unknown = Object.keys(definition).filter(function (key) { return known.indexOf(key) < 0; });
90
- if (unknown.length) blocked = 'Unrecognized server fields require deliberate review in a direct Clay connection: ' + unknown.slice(0, 10).join(', ');
91
- if (unknown.length) diagnostics.push({ vendor: source.vendor, scope: source.scope, name: name, message: 'Unrecognized fields are not imported: ' + unknown.slice(0, 10).join(', ') });
92
- var hash = adapters.signature(config);
93
- rows.push({ id: crypto.randomUUID(), name: name, config: config, signature: hash, blocked: blocked });
94
- } catch (error) { diagnostics.push({ vendor: source.vendor, scope: source.scope, name: name, message: error.message }); }
95
- });
96
- }
97
- if (sources.length > 500) diagnostics.push({ vendor: 'all', scope: 'discovery', message: 'Source limit reached; inspect additional files individually.' });
98
- sources.sort(function (left, right) { return sourceContext.priority(left) - sourceContext.priority(right); });
99
- sources.slice(0, 500).forEach(function (source) {
100
- try {
101
- var value = source.inline ? adapters.parse('inline.json', source.inline) : load(source.file), adapter = adapters.adapters[source.vendor];
102
- if (Object.keys(value).some(function (key) { return restrictions.securityKey(key); })) blockedVendors[source.vendor] = 'A configuration layer contains unresolved security restrictions. Review the effective vendor policy before importing.';
103
- if (source.vendor === 'opencode' && value.agent && Object.keys(value.agent).some(function (name) { return Object.keys(value.agent[name] || {}).some(function (key) { return restrictions.securityKey(key) || key === 'tools'; }); })) blockedVendors.opencode = 'OpenCode agent-specific restrictions require review. Configure a direct Clay connection with the intended restrictions.';
104
- collect(source, source.scope === 'plugin-inline' ? value.mcpServers : value[adapter.key] || (source.vendor === 'codex' && !source.file.endsWith('.toml') ? value.mcpServers || value : source.vendor === 'copilot' || source.scope === 'plugin' ? value : null), value);
105
- if (source.vendor === 'opencode' && value.tools !== undefined) {
106
- if (!value.tools || typeof value.tools !== 'object' || Array.isArray(value.tools) || Object.values(value.tools).some(function (enabled) { return typeof enabled !== 'boolean'; })) blockedVendors.opencode = 'Invalid OpenCode tool policy. Review the runtime configuration before importing.';
107
- else Object.assign(openCodeTools, value.tools);
108
- }
109
- if (source.vendor === 'claude' && source.scope === 'user' && value.projects && value.projects[ctx.cwd]) collect(Object.assign({}, source, { scope: 'local' }), value.projects[ctx.cwd].mcpServers, value.projects[ctx.cwd]);
110
- } catch (error) {
111
- if (error.code !== 'ENOENT' && !(identity && /ENOENT|no such file/i.test(error.message))) diagnostics.push({ vendor: source.vendor, scope: source.scope, message: 'Could not read or parse source; check permissions and format.' });
112
- }
113
- });
114
- rows.forEach(function (row) {
115
- var source = row.config.provenance;
116
- row.blocked = row.blocked || blockedVendors[source.vendor];
117
- if (source.vendor === 'opencode') {
118
- row.config.toolPrefix = row.name + '_';
119
- row.config.toolPatterns = Object.keys(openCodeTools).filter(function (pattern) { return openCodeTools[pattern] === false; });
120
- row.signature = adapters.signature(row.config);
121
- }
122
- var effective = rows.filter(function (other) { return other.name === row.name && other.config.provenance.vendor === source.vendor && other.config.provenance.scope !== 'agent' && other.config.provenance.scope.indexOf('plugin') !== 0; }).pop();
123
- if (source.scope !== 'agent' && source.scope.indexOf('plugin') !== 0 && effective && effective !== row) row.blocked = 'Shadowed by the effective ' + effective.config.provenance.scope + ' source. Import that definition instead.';
124
- });
125
- var current = ctx.snapshot(owner);
126
- rows.forEach(function (row) {
127
- var same = rows.filter(function (other) { return other !== row && (other.name === row.name || row.config.url && other.config.url === row.config.url); });
128
- row.duplicate = rows.some(function (other) { return other !== row && other.signature === row.signature; });
129
- row.conflict = same.some(function (other) { return other.signature !== row.signature; });
130
- row.imported = current.records.some(function (saved) { return saved.importSignature === row.signature || row.config.url && saved.url === row.config.url; });
131
- });
132
- var scanId = crypto.randomUUID();
133
- scans.set(owner, { id: scanId, ws: ws, rows: rows, files: files, directories: directories, identity: identity, revision: current.revision, context: context.fingerprint, home: home, created: Date.now() });
134
- var publicScan = { scanId: scanId, candidates: rows.map(function (row) {
135
- return { id: row.id, name: row.name, vendor: row.config.provenance.vendor, scope: row.config.provenance.scope, file: row.config.provenance.file,
136
- blocked: row.blocked || null, pluginName: row.config.provenance.pluginName, pluginVersion: row.config.provenance.pluginVersion, transport: row.config.transport, dependency: row.config.command ? (/^[a-zA-Z0-9_./\\-]+$/.test(row.config.command) ? path.basename(row.config.command) : 'Configured executable') : new URL(row.config.url).hostname,
137
- references: require('./mcp-import-credentials').requiredNames(row.config), environmentNames: row.config.environmentNames, sourceEnabled: row.config.sourceEnabled,
138
- duplicate: row.duplicate, conflict: row.conflict, imported: row.imported };
139
- }), diagnostics: diagnostics };
140
- publicScans.set(owner, publicScan); return publicScan;
141
- }
142
- function selected(owner, ws, msg) {
143
- ctx.authorize(owner, ws);
144
- var scan = scans.get(owner);
145
- if (!scan || scan.ws !== ws || scan.id !== msg.scanId || Date.now() - scan.created > 10 * 60 * 1000) throw new Error('Discovery expired. Scan again.');
146
- if (JSON.stringify(ctx.identity(ws)) !== JSON.stringify(scan.identity)) throw new Error('OS identity changed. Scan again.');
147
- if (sourceContext.resolve(ctx, owner, ws, scan.home, scan.identity).fingerprint !== scan.context) throw new Error('Runtime configuration changed. Scan again.');
148
- scan.directories.forEach(function (state, directory) {
149
- var entries = list(directory, scan.identity).slice().sort(function (left, right) { return left.name.localeCompare(right.name); });
150
- if (JSON.stringify(entries) !== state) throw new Error('Installed source selection changed. Scan again.');
151
- });
152
- scan.files.forEach(function (state, file) {
153
- var hash = null;
154
- try { hash = crypto.createHash('sha256').update(read(file, scan.identity)).digest('hex'); } catch (error) {
155
- if (!state.error || (error.code || error.message) !== (state.error.code || state.error.message)) throw new Error('Vendor configuration changed. Scan again.');
156
- }
157
- if (hash !== state.digest) throw new Error('Vendor configuration changed. Scan again.');
158
- });
159
- var row = scan.rows.find(function (candidate) { return candidate.id === msg.candidateId; });
160
- if (row && row.blocked) throw new Error(row.blocked);
161
- if (!row || row.imported) throw new Error('Source is unavailable or already imported.');
162
- return { config: JSON.parse(JSON.stringify(row.config)), signature: row.signature, revision: scan.revision };
163
- }
164
- return { scan: scan, selected: selected, candidate: function (owner, subject, args) {
165
- selected(owner, subject, args);
166
- return publicScans.get(owner).candidates.find(function (row) { return row.id === args.candidateId; });
167
- }, destroy: function () { scans.clear(); publicScans.clear(); } };
168
- }
169
- module.exports = { createDiscovery: createDiscovery };
1
+ module.exports = require("./yoke/mcp-source-discovery");
@@ -1,68 +1 @@
1
- var path = require('path');
2
- function discover(ctx) {
3
- var load = ctx.load, source = ctx.source, list = ctx.list, roots = ctx.roots;
4
- function optional(file) { try { return load(file); } catch (error) { return {}; } }
5
- var enabled = {};
6
- var settingsFiles = [path.join(roots.claude, 'settings.json'), path.join(ctx.cwd, '.claude/settings.json'), path.join(ctx.cwd, '.claude/settings.local.json')];
7
- settingsFiles.forEach(function (file) { Object.assign(enabled, optional(file).enabledPlugins || {}); });
8
- var installed = optional(path.join(roots.claude, 'plugins/installed_plugins.json'));
9
- Object.keys(installed.plugins || {}).slice(0, 100).forEach(function (name) {
10
- var entries = (installed.plugins[name] || []).filter(function (entry) { return entry.installPath && (!entry.projectPath || path.resolve(entry.projectPath) === path.resolve(ctx.cwd)); });
11
- var scoped = entries.filter(function (entry) { return entry.scope === 'local' || entry.scope === 'project'; });
12
- if (scoped.length) entries = scoped;
13
- var local = entries.filter(function (entry) { return entry.scope === 'local'; }); if (local.length) entries = local;
14
- // The registry is authoritative only when it selects one installation.
15
- entries.forEach(function (entry) {
16
- var extra = { pluginName: name, pluginVersion: entry.version || null, sourceEnabled: enabled[name] === true ? true : enabled[name] === false ? false : null,
17
- blocked: entries.length > 1 ? 'Claude registry selects multiple installations. Resolve the active plugin installation before importing.' : typeof enabled[name] !== 'boolean' ? 'Claude plugin enablement is unknown. Enable or disable it explicitly in vendor settings and scan again.' : null };
18
- var root = entry.installPath;
19
- source('claude', path.join(root, '.mcp.json'), 'plugin', root, extra);
20
- var manifest = optional(path.join(root, '.claude-plugin/plugin.json'));
21
- var definitions = Array.isArray(manifest.mcpServers) ? manifest.mcpServers : [manifest.mcpServers];
22
- definitions.forEach(function (definition) {
23
- if (typeof definition === 'string' && definition.endsWith('.json') && !definition.includes('://')) source('claude', path.resolve(root, definition), 'plugin', root, extra);
24
- else if (definition && typeof definition === 'object') source('claude', path.join(root, '.claude-plugin/plugin.json'), 'plugin-inline', root, extra);
25
- else if (definition) ctx.diagnostics.push({ vendor: 'claude', scope: 'plugin', message: 'Remote plugin bundles are not downloaded. Install and resolve the local plugin before scanning.' });
26
- });
27
- });
28
- });
29
- var codex = {};
30
- [path.join(roots.codex, 'config.toml'), path.join(ctx.cwd, '.codex/config.toml')].forEach(function (file) {
31
- var plugins = optional(file).plugins || {};
32
- Object.keys(plugins).forEach(function (name) {
33
- var previous = codex[name] || {}, next = plugins[name] || {};
34
- codex[name] = Object.assign({}, previous, next, { mcp_servers: Object.assign({}, previous.mcp_servers || {}, next.mcp_servers || {}) });
35
- });
36
- });
37
- var cache = path.join(roots.codex, 'plugins/cache');
38
- list(cache).slice(0, 50).forEach(function (market) {
39
- if (!market.isDir) return;
40
- list(path.join(cache, market.name)).slice(0, 50).forEach(function (plugin) {
41
- if (!plugin.isDir) return;
42
- var name = plugin.name + '@' + market.name, configured = codex[name] || {};
43
- var versions = list(path.join(cache, market.name, plugin.name)).filter(function (entry) { return entry.isDir; });
44
- // Codex core-plugin-common/installed.rs selects local first, otherwise the greatest installed version.
45
- versions = versions.filter(function (entry) { return /^[A-Za-z0-9._+-]+$/.test(entry.name); }).sort(function (left, right) {
46
- var semver = require('semver');
47
- return semver.valid(left.name) && semver.valid(right.name) ? semver.compareBuild(left.name, right.name) : left.name < right.name ? -1 : left.name > right.name ? 1 : 0;
48
- });
49
- var active = versions.find(function (entry) { return entry.name === 'local'; }) || versions[versions.length - 1];
50
- versions = active ? [active] : [];
51
- versions.slice(0, 50).forEach(function (version) {
52
- var root = path.join(cache, market.name, plugin.name, version.name);
53
- var extra = { pluginName: name, pluginVersion: version.name, overrides: configured.mcp_servers,
54
- sourceEnabled: Object.prototype.hasOwnProperty.call(codex, name) ? configured.enabled !== false : false,
55
- blocked: configured.enabled !== undefined && typeof configured.enabled !== 'boolean' ? 'Invalid Codex plugin enabled state. Review its vendor configuration before importing.' : null };
56
- var portable = optional(path.join(root, 'plugin.json'));
57
- var manifest = portable.extensions && portable.extensions['com.openai'] || optional(path.join(root, '.codex-plugin/plugin.json'));
58
- if (portable.name) source('codex', path.join(root, 'mcp.json'), 'plugin', root, extra);
59
- else {
60
- source('codex', path.join(root, '.mcp.json'), 'plugin', root, extra);
61
- source('codex', path.join(root, 'mcp.json'), 'plugin', root, extra);
62
- }
63
- if (typeof manifest.mcpServers === 'string' && !manifest.mcpServers.includes('://')) source('codex', path.resolve(root, manifest.mcpServers), 'plugin', root, extra);
64
- });
65
- });
66
- });
67
- }
68
- module.exports = { discover: discover };
1
+ module.exports = require("./yoke/mcp-source-plugins");
@@ -1,47 +1 @@
1
- var path = require('path');
2
- var SECURITY = /permission|policy|security|sandbox|registry|disabledMcp|restrict|approval|allowed|denied|managedMcp|strictPlugin|includeTools|excludeTools/i;
3
- function inspect(ctx) {
4
- var blocked = {}, files = [];
5
- function add(vendor, file, exclusive) { files.push({ vendor: vendor, file: file, exclusive: exclusive }); }
6
- var claudeManaged = process.platform === 'darwin' ? '/Library/Application Support/ClaudeCode' : process.platform === 'win32' ? path.join(process.env.ProgramFiles || 'C:\\Program Files', 'ClaudeCode') : '/etc/claude-code';
7
- ['managed-mcp.json', 'managed-settings.json'].forEach(function (file) { add('claude', path.join(claudeManaged, file), true); });
8
- add('claude', path.join(claudeManaged, 'managed-settings.d'), true);
9
- if (process.platform === 'darwin') {
10
- add('claude', '/Library/Managed Preferences/com.anthropic.claudecode.plist', true);
11
- if (ctx.username) add('claude', path.join('/Library/Managed Preferences', ctx.username, 'com.anthropic.claudecode.plist'), true);
12
- }
13
- [path.join(ctx.roots.claude, 'settings.json'), path.join(ctx.cwd, '.claude/settings.json'), path.join(ctx.cwd, '.claude/settings.local.json')].forEach(function (file) { add('claude', file); });
14
- [path.join(ctx.roots.codex, 'requirements.toml'), '/etc/codex/requirements.toml', '/etc/codex/managed_config.toml', path.join(ctx.roots.codex, 'managed_config.toml')].forEach(function (file) { add('codex', file, true); });
15
- var openCodeManaged = process.platform === 'darwin' ? '/Library/Application Support/opencode' : process.platform === 'win32' ? path.join(process.env.ProgramData || 'C:\\ProgramData', 'opencode') : '/etc/opencode';
16
- ['opencode.json', 'opencode.jsonc'].forEach(function (file) { add('opencode', path.join(openCodeManaged, file), true); });
17
- if (process.platform === 'darwin') {
18
- add('opencode', '/Library/Managed Preferences/ai.opencode.managed.plist', true);
19
- if (ctx.username) add('opencode', path.join('/Library/Managed Preferences', ctx.username, 'ai.opencode.managed.plist'), true);
20
- }
21
- ['settings.json', 'permissions-config.json'].forEach(function (file) { add('copilot', path.join(ctx.roots.copilot, file)); });
22
- ['settings.json', 'settings.local.json'].forEach(function (file) { add('copilot', path.join(ctx.cwd, '.github/copilot', file)); });
23
- var copilotManaged = process.platform === 'darwin' ? '/Library/Application Support/GitHubCopilot/managed-settings.json' : process.platform === 'win32' ? path.join(process.env.ProgramFiles || 'C:\\Program Files', 'GitHubCopilot/managed-settings.json') : '/etc/github-copilot/managed-settings.json';
24
- add('copilot', copilotManaged, true);
25
- if (process.platform === 'darwin') {
26
- add('copilot', '/Library/Managed Preferences/com.github.copilot.plist', true);
27
- if (ctx.username) add('copilot', path.join('/Library/Managed Preferences', ctx.username, 'com.github.copilot.plist'), true);
28
- }
29
- add('kimi', path.join(ctx.roots.kimi, 'config.toml'));
30
- var qwenSystem = process.platform === 'darwin' ? '/Library/Application Support/QwenCode' : process.platform === 'win32' ? 'C:\\ProgramData\\qwen-code' : '/etc/qwen-code';
31
- add('qwen', ctx.policyPaths.qwenSettings || path.join(qwenSystem, 'settings.json'), true);
32
- add('qwen', ctx.policyPaths.qwenDefaults || path.join(qwenSystem, 'system-defaults.json'), true);
33
- add('grok', '/etc/grok/requirements.toml', true);
34
- add('grok', '/etc/grok/managed_config.toml', true);
35
- add('grok', path.join(ctx.roots.grok, 'requirements.toml'), true);
36
- add('grok', path.join(ctx.roots.grok, 'managed_config.toml'), true);
37
- files.forEach(function (entry) {
38
- try {
39
- var value = ctx.load(entry.file);
40
- if (entry.exclusive || path.basename(entry.file) === 'permissions-config.json' && Object.keys(value).length || Object.keys(value).some(function (key) { return SECURITY.test(key); })) blocked[entry.vendor] = 'Local vendor security or managed policy is present. Clay cannot resolve its effective restrictions. Review that policy and configure an explicitly authorized Clay connection; vendor import is blocked.';
41
- } catch (error) {
42
- if (error.code !== 'ENOENT' && !/ENOENT|no such file/i.test(error.message)) blocked[entry.vendor] = 'Could not resolve a local vendor policy source. Check permissions and format before importing.';
43
- }
44
- });
45
- return blocked;
46
- }
47
- module.exports = { inspect: inspect, securityKey: function (key) { return SECURITY.test(key); } };
1
+ module.exports = require("./yoke/mcp-source-restrictions");
@@ -1,29 +1,4 @@
1
- // Restrictive policies are intersected; an empty allowlist means no tools.
2
- var ALLOW = ['enabled_tools', 'enabledTools', 'includeTools'];
3
- var DENY = ['disabled_tools', 'disabledTools', 'excludeTools'];
4
- function list(value, key) {
5
- if (!Array.isArray(value) || value.some(function (item) { return typeof item !== 'string' || !item || item.indexOf('\0') >= 0; })) throw new Error('Invalid tool policy: ' + key + '. Review the vendor configuration before importing.');
6
- return Array.from(new Set(value));
7
- }
8
- function normalize(vendor, value) {
9
- var allow = null, deny = [], keys = ALLOW.slice();
10
- if (vendor === 'copilot') keys.push('tools');
11
- keys.forEach(function (key) {
12
- if (!Object.prototype.hasOwnProperty.call(value, key)) return;
13
- var next = list(value[key], key);
14
- if (vendor === 'copilot' && key === 'tools' && next.indexOf('*') >= 0) next = null;
15
- if (next !== null) allow = allow === null ? next : allow.filter(function (name) { return next.indexOf(name) >= 0; });
16
- });
17
- DENY.forEach(function (key) { if (Object.prototype.hasOwnProperty.call(value, key)) deny = deny.concat(list(value[key], key)); });
18
- var unsupported = Object.keys(value).filter(function (key) {
19
- if (ALLOW.concat(DENY, vendor === 'copilot' ? ['tools'] : [], ['autoApprove', 'trust', 'oauth', 'oauthScopes']).indexOf(key) >= 0) return false;
20
- return /tool|permission|policy|security|authProvider|allowed|denied|restrict|approval/i.test(key) && ['tool_timeout_sec'].indexOf(key) < 0;
21
- });
22
- if (unsupported.length) throw new Error('Unsupported restriction or security configuration: ' + unsupported.join(', ') + '. Configure and review a direct Clay connection instead.');
23
- if (value.autoApprove !== undefined && (!Array.isArray(value.autoApprove) || value.autoApprove.some(function (item) { return typeof item !== 'string'; }))) throw new Error('Invalid autoApprove policy.');
24
- if (value.trust !== undefined && typeof value.trust !== 'boolean') throw new Error('Invalid trust policy.');
25
- return { enabledTools: allow, disabledTools: Array.from(new Set(deny)) };
26
- }
1
+ var normalize = require("./yoke/mcp-tool-policy-normalize").normalize;
27
2
  function matches(pattern, name) {
28
3
  return new RegExp('^' + pattern.split('').map(function (letter) { return letter === '*' ? '.*' : letter === '?' ? '.' : letter.replace(/[\\^$+.[\]{}()|]/g, '\\$&'); }).join('') + '$').test(name);
29
4
  }
@@ -17,12 +17,10 @@ function commandAvailable(command, execFile) {
17
17
 
18
18
  function credentialPaths(home) {
19
19
  if (!home) return [];
20
- return [
21
- home + "/.claude",
22
- home + "/.codex",
20
+ return require("./yoke/maintenance").credentialPaths(home).concat([
23
21
  home + "/.config/gh",
24
22
  home + "/.config/gcloud",
25
- ];
23
+ ]);
26
24
  }
27
25
 
28
26
  function accessStatus(checks) {
package/lib/os-users.js CHANGED
@@ -475,72 +475,11 @@ function getLinuxUserUid(username) {
475
475
  * hardcoded ~/.local/bin/claude path.
476
476
  * Returns true if claude resolves, false otherwise.
477
477
  */
478
- function claudeAvailableForUser(linuxName) {
479
- if (!isSafeLinuxUsername(linuxName)) return false;
480
- try {
481
- var out = execFileSync("su", ["-", linuxName, "-c", "command -v claude"], {
482
- encoding: "utf8",
483
- timeout: 10000,
484
- stdio: "pipe",
485
- }).trim();
486
- return out.length > 0;
487
- } catch (e) {
488
- return false;
489
- }
490
- }
491
-
492
- /**
493
- * Install Claude CLI for a Linux user account.
494
- * Downloads and runs the install script, then ensures PATH is configured.
495
- * Non-fatal: logs errors but does not throw.
496
- * Skips entirely when claude is already resolvable for the user, or when
497
- * CLAY_SKIP_CLAUDE_INSTALL is set (bring-your-own-claude).
498
- */
499
- function installClaudeCli(linuxName) {
500
- if (process.env.CLAY_SKIP_CLAUDE_INSTALL) {
501
- console.log("[os-users] CLAY_SKIP_CLAUDE_INSTALL set, skipping Claude CLI install for " + linuxName);
502
- return;
503
- }
504
- if (claudeAvailableForUser(linuxName)) {
505
- console.log("[os-users] Claude CLI already available for " + linuxName + ", skipping install");
506
- return;
507
- }
508
- try {
509
- if (!isSafeLinuxUsername(linuxName)) throw new Error("Invalid Linux username");
510
- // Download and run the Claude CLI install script as the target user
511
- execFileSync("su", ["-", linuxName, "-c", "curl -fsSL https://claude.ai/install.sh | bash"], {
512
- encoding: "utf8",
513
- timeout: 60000,
514
- stdio: "pipe",
515
- });
516
- console.log("[os-users] Claude CLI installed for " + linuxName);
517
- } catch (e) {
518
- var msg = (e.stderr || e.message || "").trim();
519
- console.error("[os-users] Failed to install Claude CLI for " + linuxName + ": " + msg);
520
- return;
521
- }
522
-
523
- // Append PATH export to the user's shell config if not already present
524
- try {
525
- var userInfo = resolveOsUserInfo(linuxName);
526
- var home = userInfo.home || ("/home/" + linuxName);
527
- var rcPath = fs.existsSync(path.join(home, ".zshrc")) ? path.join(home, ".zshrc") : path.join(home, ".bashrc");
528
- var exportLine = 'export PATH="$HOME/.local/bin:$PATH"';
529
- var existing = "";
530
- try { existing = fs.readFileSync(rcPath, "utf8"); } catch (e2) {}
531
- if (existing.indexOf(exportLine) === -1) {
532
- var prefix = existing && !/\n$/.test(existing) ? "\n" : "";
533
- fs.appendFileSync(rcPath, prefix + exportLine + "\n", "utf8");
534
- try { fs.chownSync(rcPath, userInfo.uid, userInfo.gid); } catch (e3) {}
535
- console.log("[os-users] PATH export appended to " + rcPath + " for " + linuxName);
536
- } else {
537
- console.log("[os-users] PATH already configured in " + rcPath + " for " + linuxName);
538
- }
539
- } catch (e) {
540
- var rcMsg = (e.stderr || e.message || "").trim();
541
- console.error("[os-users] Failed to configure PATH for " + linuxName + ": " + rcMsg);
542
- }
478
+ function defaultRuntimeProvisioning() {
479
+ return require("./yoke/provisioning").forProvider("claude", { isSafeLinuxUsername: isSafeLinuxUsername, resolveOsUserInfo: resolveOsUserInfo });
543
480
  }
481
+ function claudeAvailableForUser(user) { return defaultRuntimeProvisioning().available(user); }
482
+ function installClaudeCli(user) { return defaultRuntimeProvisioning().install(user); }
544
483
 
545
484
  /**
546
485
  * Provision a Linux user account for a Clay user.
@@ -3,7 +3,7 @@ var path = require("path");
3
3
  var usersModule = require("./users");
4
4
  var userPresence = require("./user-presence");
5
5
  var emailAccounts = require("./email-accounts");
6
- var { getCodexConfig } = require("./codex-defaults");
6
+ var providerSettings = require("./yoke/settings");
7
7
  var yoke = require("./yoke");
8
8
  var permissionModes = require("./session-permission-mode");
9
9
  var sessionProvenance = require("./session-provenance");
@@ -217,7 +217,7 @@ function attachConnection(ctx) {
217
217
  sm.saveSessionFile(restoredActive);
218
218
  }
219
219
  var initialModels = (sm.modelsByVendor && sm.modelsByVendor[initialVendor]) || sm.availableModels || [];
220
- sendTo(ws, { type: "info", cwd: cwd, slug: slug, project: title || project, version: currentVersion, debug: !!debug, dangerouslySkipPermissions: dangerouslySkipPermissions, osUsers: osUsers, lanHost: lanHost, projectCount: _filteredProjects.length, projects: _filteredProjects, projectOwnerId: projectOwnerId, ownerLocked: ownerLocked, vendors: yoke.VENDOR_REGISTRY, isMate: isMate, mateId: isMate ? mateId : null, mateDisplayName: isMate ? (mateDisplayName || title || project || "Mate") : "" });
220
+ sendTo(ws, { type: "info", cwd: cwd, slug: slug, project: title || project, version: currentVersion, debug: !!debug, dangerouslySkipPermissions: dangerouslySkipPermissions, osUsers: osUsers, lanHost: lanHost, projectCount: _filteredProjects.length, projects: _filteredProjects, projectOwnerId: projectOwnerId, ownerLocked: ownerLocked, vendors: require("./yoke/vendor-registry").publicInfo(), isMate: isMate, mateId: isMate ? mateId : null, mateDisplayName: isMate ? (mateDisplayName || title || project || "Mate") : "" });
221
221
  if (typeof sendCursorSharingState === "function") sendCursorSharingState(ws);
222
222
  // Update notifications are pushed on a scheduled interval (see
223
223
  // scheduleUpdateBroadcast). We no longer push on connect to avoid
@@ -234,7 +234,7 @@ function attachConnection(ctx) {
234
234
  var initialPermissionState = permissionModes.clientState(restoredActive, sm.currentPermissionMode, sm.defaultVendor);
235
235
  sendTo(ws, { type: "config_state", model: initialModelInfo.model, vendor: initialVendor, sessionId: restoredActive ? restoredActive.localId : null, mode: initialPermissionState.requestedPermissionMode, effectivePermissionMode: initialPermissionState.effectivePermissionMode, permissionCapabilities: initialPermissionState.permissionCapabilities, mcpPermissionModeOverrides: initialPermissionState.mcpPermissionModeOverrides, effort: initialEffort || "medium", betas: sm.currentBetas || [], thinking: sm.currentThinking || "adaptive", thinkingBudget: sm.currentThinkingBudget || 10000 });
236
236
  sendTo(ws, { type: "last_vendor", vendor: sm.lastVendor || "" });
237
- sendTo(ws, Object.assign({ type: "codex_config" }, getCodexConfig(sm)));
237
+ providerSettings.messages(sm).forEach(function(message) { sendTo(ws, message); });
238
238
  sendTo(ws, { type: "term_list", terminals: tm.list() });
239
239
  // Any login flow already running in this project, so a reconnecting client
240
240
  // re-attaches to the existing login terminal instead of starting another.
@@ -301,12 +301,13 @@ function attachConnection(ctx) {
301
301
  }
302
302
  sendTo(ws, { type: "history_meta", total: total, from: fromIndex });
303
303
  for (var i = fromIndex; i < total; i++) {
304
- sendTo(ws, Object.assign({}, hydrateImageRefs(active.history[i]), { _historyIndex: i }));
304
+ sendTo(ws, Object.assign({}, hydrateImageRefs(require("./yoke/usage").clientEvent(active.history[i], active.vendor || "claude")), { _historyIndex: i }));
305
305
  }
306
- var _lastUsage = null, _lastModelUsage = null, _lastCost = null, _lastStreamInputTokens = null;
306
+ var _lastContext = null, _lastUsage = null, _lastModelUsage = null, _lastCost = null, _lastStreamInputTokens = null;
307
307
  for (var _ri = total - 1; _ri >= 0; _ri--) {
308
308
  if (active.history[_ri].type === "result") {
309
- var _r = active.history[_ri];
309
+ var _r = require("./yoke/usage").clientEvent(active.history[_ri], active.vendor || "claude");
310
+ _lastContext = _r.context;
310
311
  _lastUsage = _r.usage || null;
311
312
  _lastModelUsage = _r.modelUsage || null;
312
313
  _lastCost = _r.cost != null ? _r.cost : null;
@@ -314,7 +315,7 @@ function attachConnection(ctx) {
314
315
  break;
315
316
  }
316
317
  }
317
- sendTo(ws, { type: "history_done", lastUsage: _lastUsage, lastModelUsage: _lastModelUsage, lastCost: _lastCost, lastStreamInputTokens: _lastStreamInputTokens, contextUsage: active.lastContextUsage || null });
318
+ sendTo(ws, { type: "history_done", lastUsage: _lastUsage, lastModelUsage: _lastModelUsage, lastCost: _lastCost, lastStreamInputTokens: _lastStreamInputTokens, context: _lastContext, contextUsage: require("./yoke/usage").context(active.lastContextUsage, active.vendor || "claude") });
318
319
  if (scheduledMessages) scheduledMessages.sendState(active, ws);
319
320
  if (pairResultRecovery) pairResultRecovery.sendState(active, ws);
320
321
 
@@ -419,9 +419,9 @@ function attachHTTP(ctx) {
419
419
  var uninstallUserInfo = getOsUserInfoForReq(req);
420
420
  var baseDir = scope === "global" ? (uninstallUserInfo ? uninstallUserInfo.home : require("./config").REAL_HOME) : cwd;
421
421
  if (osUsers && !uninstallUserInfo) throw new Error("A mapped OS user is required to uninstall skills");
422
- var skillDir = claySkillStorage.isClaySkill(skill) ? path.join(claySkillStorage.privateRoot(baseDir), skill) : path.join(baseDir, ".claude", "skills", skill);
422
+ var skillDir = claySkillStorage.isClaySkill(skill) ? path.join(claySkillStorage.privateRoot(baseDir), skill) : path.join(require("./yoke/source-locations").legacySkillRoot(baseDir), skill);
423
423
  // Safety: keep removal inside the selected skill storage root.
424
- var expectedParent = claySkillStorage.isClaySkill(skill) ? claySkillStorage.privateRoot(baseDir) : path.join(baseDir, ".claude", "skills");
424
+ var expectedParent = claySkillStorage.isClaySkill(skill) ? claySkillStorage.privateRoot(baseDir) : require("./yoke/source-locations").legacySkillRoot(baseDir);
425
425
  var resolved = path.resolve(skillDir);
426
426
  if (!resolved.startsWith(expectedParent + path.sep)) {
427
427
  res.writeHead(403, { "Content-Type": "application/json" });
@@ -528,8 +528,8 @@ function attachHTTP(ctx) {
528
528
  var sui = getOsUserInfoForReq(req);
529
529
  return sui ? sui.home : require("./config").REAL_HOME;
530
530
  })();
531
- var globalSkillsDir = path.join(skillUserHome, ".claude", "skills");
532
- var projectSkillsDir = path.join(cwd, ".claude", "skills");
531
+ var globalSkillsDir = require("./yoke/source-locations").legacySkillRoot(skillUserHome);
532
+ var projectSkillsDir = require("./yoke/source-locations").legacySkillRoot(cwd);
533
533
  var results = [];
534
534
  var pending = skills.length;
535
535
 
@@ -26,7 +26,7 @@ function attachMcp(ctx) {
26
26
  }
27
27
  function isSession(subject) { return !!subject && ctx.canUseSession(subject); }
28
28
  function sourceIdentity(subject) { return isSession(subject) ? ctx.ownerIdentity(ctx.isMultiUser() ? String(subject.ownerId) : 'default') : ctx.identity(subject); }
29
- var discovery = require('./mcp-source-discovery').createDiscovery({ cwd: ctx.cwd || process.cwd(), home: ctx.discoveryHome,
29
+ var discovery = require('./yoke/mcp-source-discovery').createDiscovery({ cwd: ctx.cwd || process.cwd(), home: ctx.discoveryHome,
30
30
  identity: sourceIdentity, runtimeEnv: function (owner, subject) { return isSession(subject) ? ctx.runtimeEnvForSession(subject) : ctx.runtimeEnv ? ctx.runtimeEnv(owner, subject) : {}; }, authorize: function (owner, subject) {
31
31
  var permitted = isSession(subject) ? (ctx.isMultiUser() ? String(subject.ownerId) : 'default') === owner && ctx.canDiscoverSession(subject) : ownerFor(subject) === owner && ctx.canDiscover && ctx.canDiscover(subject);
32
32
  if (!permitted) throw new Error('Vendor discovery requires your authorized OS user mapping or a sole account installation.');
@@ -152,7 +152,7 @@ function attachMcp(ctx) {
152
152
  });
153
153
  }
154
154
  function getMcpServers(session, allowManagement) {
155
- if (!session || !ctx.canUseSession(session) || (ctx.sessionVendor ? ctx.sessionVendor(session) : session.vendor) === 'antigravity') return {};
155
+ if (!session || !ctx.canUseSession(session) || !require('./yoke/session-tool-transport').capability(ctx.sessionVendor ? ctx.sessionVendor(session) : session.vendor || 'claude').supported) return {};
156
156
  var owner = ctx.isMultiUser() ? String(session.ownerId) : 'default';
157
157
  function authorize() {
158
158
  authorizeOwner(owner);
@@ -125,8 +125,8 @@ function attachPairResultDelivery(ctx) {
125
125
  if (!currentAttempt(caller, partner, token, attemptId)) return;
126
126
  // A known, cancellable provider admission wait has not sent input.
127
127
  // Keep the correlated completion eligible until admission or Stop.
128
- if (caller._queryStarting && Number(caller._codexRecoveryWaiting) > 0
129
- && caller._codexRecoveryWaiting === caller._sdkQueryGeneration) {
128
+ if (caller._queryStarting && Number(caller._providerRecoveryWaiting) > 0
129
+ && caller._providerRecoveryWaiting === caller._sdkQueryGeneration) {
130
130
  token._deliveryTimer = { attemptId: attemptId, timer: setTimeout(acceptanceTimeout, timeoutMs) };
131
131
  return;
132
132
  }