clay-server 5.2.0-beta.5 → 5.2.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/claude-hook-installer.js +1 -267
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-control-presentation-binding.js +96 -0
- package/lib/clay-control-presentation.js +128 -0
- package/lib/clay-control-target-projection.js +26 -0
- package/lib/clay-mate-chat.js +1 -0
- package/lib/clay-mate-knowledge.js +1 -1
- package/lib/clay-product-control-tools.js +1 -1
- package/lib/clay-product-control.js +22 -12
- package/lib/clay-project-records.js +2 -1
- package/lib/clay-scheduling.js +1 -1
- package/lib/clay-screen-controls.js +13 -5
- package/lib/clay-skill-install.js +1 -33
- package/lib/clay-workspace-controls.js +2 -1
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/contextual-screen-snapshot.js +38 -0
- package/lib/daemon.js +5 -55
- package/lib/default-ai-runtime.js +4 -8
- package/lib/email-mcp-server.js +1 -1
- package/lib/kiro-defaults.js +1 -22
- package/lib/mate-context-service.js +138 -0
- package/lib/mate-context-tools.js +21 -0
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +1 -1
- package/lib/mcp-source-adapters.js +1 -91
- package/lib/mcp-source-context.js +1 -42
- package/lib/mcp-source-discovery.js +1 -169
- package/lib/mcp-source-plugins.js +1 -68
- package/lib/mcp-source-restrictions.js +1 -47
- package/lib/mcp-tool-policy.js +1 -26
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +4 -65
- package/lib/project-connection.js +14 -10
- package/lib/project-control-git-view.js +12 -0
- package/lib/project-filesystem.js +5 -1
- package/lib/project-http.js +7 -4
- package/lib/project-mcp.js +2 -2
- package/lib/project-notifications.js +5 -4
- package/lib/project-pair-result-delivery.js +2 -2
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +3 -1
- package/lib/project-product-control.js +3 -1
- package/lib/project-scheduled-messages.js +4 -4
- package/lib/project-scheduled-task-control.js +1 -1
- package/lib/project-session-document.js +20 -5
- package/lib/project-session-history-actions.js +5 -3
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +2 -1
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +2 -1
- package/lib/project-sessions.js +18 -26
- package/lib/project-shared-browser.js +22 -5
- package/lib/project-studio-presentation.js +58 -0
- package/lib/project-user-message.js +37 -0
- package/lib/project-vendor-login.js +6 -36
- package/lib/project-workspace-query.js +11 -1
- package/lib/project.js +24 -7
- package/lib/public/app.js +25 -0
- package/lib/public/appearance-bootstrap.js +2 -0
- package/lib/public/css/contextual-clay.css +240 -0
- package/lib/public/css/messages.css +6 -2
- package/lib/public/modules/app-favicon.js +18 -9
- package/lib/public/modules/app-header.js +4 -2
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-message-router.js +5 -1
- package/lib/public/modules/app-messages.js +50 -6
- package/lib/public/modules/app-notifications.js +11 -5
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-projects.js +3 -0
- package/lib/public/modules/app-rate-limit.js +7 -27
- package/lib/public/modules/chat-render-runtime.js +3 -0
- package/lib/public/modules/clay-control-activity.js +214 -0
- package/lib/public/modules/clay-control-copy.js +36 -0
- package/lib/public/modules/clay-control-detail-targets.js +44 -0
- package/lib/public/modules/clay-control-object-targets.js +52 -0
- package/lib/public/modules/clay-control-target-visibility.js +12 -0
- package/lib/public/modules/clay-control-targets.js +44 -0
- package/lib/public/modules/clay-navigation-render.js +52 -0
- package/lib/public/modules/clay-owner-navigation.js +11 -2
- package/lib/public/modules/clay-session-links.js +31 -0
- package/lib/public/modules/clay-studio-bridge.js +20 -0
- package/lib/public/modules/clay-studio-document.js +67 -0
- package/lib/public/modules/clay-studio-render.js +144 -0
- package/lib/public/modules/clay-tool-presentation.js +4 -0
- package/lib/public/modules/command-palette.js +26 -31
- package/lib/public/modules/contextual-ask-session.js +26 -0
- package/lib/public/modules/contextual-clay-geometry.js +155 -0
- package/lib/public/modules/contextual-clay-session.js +130 -0
- package/lib/public/modules/contextual-clay-workspace.js +157 -0
- package/lib/public/modules/contextual-clay.js +237 -0
- package/lib/public/modules/contextual-composer.js +91 -0
- package/lib/public/modules/contextual-mate-context.js +115 -0
- package/lib/public/modules/contextual-mate-hint.js +40 -0
- package/lib/public/modules/contextual-mate-identity.js +23 -0
- package/lib/public/modules/contextual-mate-sessions.js +134 -0
- package/lib/public/modules/contextual-search.js +137 -0
- package/lib/public/modules/contextual-session-citations.js +58 -0
- package/lib/public/modules/filebrowser.js +20 -1
- package/lib/public/modules/generation-stop.js +8 -5
- package/lib/public/modules/git-panel.js +3 -1
- package/lib/public/modules/icon-strip-tooltip.js +2 -1
- package/lib/public/modules/input.js +35 -1
- package/lib/public/modules/issues-render.js +3 -2
- package/lib/public/modules/knowledge-db-workbench.js +4 -4
- package/lib/public/modules/markdown.js +2 -34
- package/lib/public/modules/mate-knowledge-workbench.js +1 -1
- package/lib/public/modules/mcp-connection-view.js +1 -1
- package/lib/public/modules/message-delivery.js +10 -0
- package/lib/public/modules/pane-bridge.js +2 -2
- package/lib/public/modules/pending-message-queue.js +1 -0
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/product-review.js +26 -0
- package/lib/public/modules/project-chat-identity.js +13 -4
- package/lib/public/modules/project-logs-render.js +3 -0
- package/lib/public/modules/project-mate-navigation.js +58 -21
- package/lib/public/modules/scheduled-tasks.js +2 -0
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/search-clay-chat.js +5 -454
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/shared-browser.js +2 -0
- package/lib/public/modules/sidebar-mobile.js +4 -3
- package/lib/public/modules/sidebar-projects.js +1 -0
- package/lib/public/modules/sidebar-sessions.js +1 -0
- package/lib/public/modules/split-view.js +7 -16
- package/lib/public/modules/terminal.js +9 -0
- package/lib/public/modules/tools.js +6 -2
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/modules/wireframe-workbench.js +5 -2
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/public/style.css +2 -0
- package/lib/safe-bash-commands.js +3 -45
- package/lib/sdk-bridge.js +131 -395
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +50 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -155
- package/lib/sdk-worker.js +2 -677
- package/lib/server-contextual-clay-settings.js +40 -0
- package/lib/server-contextual-mate-sessions.js +70 -0
- package/lib/server-home-chat.js +7 -1
- package/lib/server-home-clay-entry.js +23 -4
- package/lib/server-home-clay-session-links.js +12 -0
- package/lib/server-owner-ui.js +49 -8
- package/lib/server-settings.js +2 -0
- package/lib/server.js +18 -1
- package/lib/session-fork-history.js +21 -0
- package/lib/session-hygiene.js +3 -35
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/sessions.js +24 -89
- package/lib/terminal-manager.js +18 -11
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/users-contextual-clay-preferences.js +31 -0
- package/lib/users.js +3 -0
- package/lib/vendor-login-browser.js +1 -11
- package/lib/workspace-assignment-service.js +25 -0
- package/lib/workspace-query-mcp-server.js +1 -1
- package/lib/ws-request.js +1 -0
- package/lib/ws-schema.js +10 -0
- package/lib/yoke/acp-agent-profiles.js +5 -3
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +7 -42
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +1 -0
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +271 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +79 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +14 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +31 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +81 -1
- package/package.json +3 -2
|
@@ -1,47 +1 @@
|
|
|
1
|
-
|
|
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");
|
package/lib/mcp-tool-policy.js
CHANGED
|
@@ -1,29 +1,4 @@
|
|
|
1
|
-
|
|
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
|
|
479
|
-
|
|
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
|
|
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");
|
|
@@ -143,6 +143,9 @@ function attachConnection(ctx) {
|
|
|
143
143
|
var _warmedUp = false;
|
|
144
144
|
|
|
145
145
|
function findRestoredActiveSession(ws, wsUser, allSessions) {
|
|
146
|
+
if (ws._clayContextual && ws._clayPaneSession) {
|
|
147
|
+
return { active: allSessions.find(function (session) { return session.localId === ws._clayPaneSession; }) || null, storedPresence: null };
|
|
148
|
+
}
|
|
146
149
|
var active = null;
|
|
147
150
|
var presenceKey = wsUser ? wsUser.id : "_default";
|
|
148
151
|
var storedPresence = userPresence.getPresence(slug, presenceKey);
|
|
@@ -202,7 +205,7 @@ function attachConnection(ctx) {
|
|
|
202
205
|
} else if (!usersModule.isMultiUser()) {
|
|
203
206
|
allSessions = allSessions.filter(function (s) { return !s.ownerId; });
|
|
204
207
|
}
|
|
205
|
-
var restoredState = ws._clayPane
|
|
208
|
+
var restoredState = ws._clayPane && !ws._clayContextual
|
|
206
209
|
? { active: null, storedPresence: null }
|
|
207
210
|
: findRestoredActiveSession(ws, wsUser, allSessions);
|
|
208
211
|
var restoredActive = restoredState.active;
|
|
@@ -217,7 +220,7 @@ function attachConnection(ctx) {
|
|
|
217
220
|
sm.saveSessionFile(restoredActive);
|
|
218
221
|
}
|
|
219
222
|
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.
|
|
223
|
+
sendTo(ws, { type: "info", controlRuntimeId: require("./clay-control-target-projection").runtime(sm), 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
224
|
if (typeof sendCursorSharingState === "function") sendCursorSharingState(ws);
|
|
222
225
|
// Update notifications are pushed on a scheduled interval (see
|
|
223
226
|
// scheduleUpdateBroadcast). We no longer push on connect to avoid
|
|
@@ -234,8 +237,8 @@ function attachConnection(ctx) {
|
|
|
234
237
|
var initialPermissionState = permissionModes.clientState(restoredActive, sm.currentPermissionMode, sm.defaultVendor);
|
|
235
238
|
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
239
|
sendTo(ws, { type: "last_vendor", vendor: sm.lastVendor || "" });
|
|
237
|
-
|
|
238
|
-
sendTo(ws, { type: "term_list", terminals: tm.list() });
|
|
240
|
+
providerSettings.messages(sm).forEach(function(message) { sendTo(ws, message); });
|
|
241
|
+
sendTo(ws, { type: "term_list", terminals: tm.list(true) });
|
|
239
242
|
// Any login flow already running in this project, so a reconnecting client
|
|
240
243
|
// re-attaches to the existing login terminal instead of starting another.
|
|
241
244
|
if (_vendorLogin) _vendorLogin.sendStateTo(ws);
|
|
@@ -267,7 +270,7 @@ function attachConnection(ctx) {
|
|
|
267
270
|
var presenceKey = wsUser ? wsUser.id : "_default";
|
|
268
271
|
var storedPresence = restoredState.storedPresence;
|
|
269
272
|
var autoCreated = false;
|
|
270
|
-
if (!active && !ws._clayPane) {
|
|
273
|
+
if (!active && (!ws._clayPane || (ws._clayContextual && !ws._clayPaneSession))) {
|
|
271
274
|
var autoOpts = {};
|
|
272
275
|
if (wsUser && usersModule.isMultiUser()) autoOpts.ownerId = wsUser.id;
|
|
273
276
|
active = sm.createSession(autoOpts, ws);
|
|
@@ -301,12 +304,13 @@ function attachConnection(ctx) {
|
|
|
301
304
|
}
|
|
302
305
|
sendTo(ws, { type: "history_meta", total: total, from: fromIndex });
|
|
303
306
|
for (var i = fromIndex; i < total; i++) {
|
|
304
|
-
sendTo(ws, Object.assign({}, hydrateImageRefs(active.history[i]), { _historyIndex: i }));
|
|
307
|
+
sendTo(ws, Object.assign({}, hydrateImageRefs(require("./yoke/usage").clientEvent(active.history[i], active.vendor || "claude")), { _historyIndex: i }));
|
|
305
308
|
}
|
|
306
|
-
var _lastUsage = null, _lastModelUsage = null, _lastCost = null, _lastStreamInputTokens = null;
|
|
309
|
+
var _lastContext = null, _lastUsage = null, _lastModelUsage = null, _lastCost = null, _lastStreamInputTokens = null;
|
|
307
310
|
for (var _ri = total - 1; _ri >= 0; _ri--) {
|
|
308
311
|
if (active.history[_ri].type === "result") {
|
|
309
|
-
var _r = active.history[_ri];
|
|
312
|
+
var _r = require("./yoke/usage").clientEvent(active.history[_ri], active.vendor || "claude");
|
|
313
|
+
_lastContext = _r.context;
|
|
310
314
|
_lastUsage = _r.usage || null;
|
|
311
315
|
_lastModelUsage = _r.modelUsage || null;
|
|
312
316
|
_lastCost = _r.cost != null ? _r.cost : null;
|
|
@@ -314,7 +318,7 @@ function attachConnection(ctx) {
|
|
|
314
318
|
break;
|
|
315
319
|
}
|
|
316
320
|
}
|
|
317
|
-
sendTo(ws, { type: "history_done", lastUsage: _lastUsage, lastModelUsage: _lastModelUsage, lastCost: _lastCost, lastStreamInputTokens: _lastStreamInputTokens, contextUsage: active.lastContextUsage ||
|
|
321
|
+
sendTo(ws, { type: "history_done", controlHistoryRevision: require("./clay-control-target-projection").digest(active.history || []), sessionId: active.localId, sessionOriginId: active.sessionOriginId || null, lastUsage: _lastUsage, lastModelUsage: _lastModelUsage, lastCost: _lastCost, lastStreamInputTokens: _lastStreamInputTokens, context: _lastContext, contextUsage: require("./yoke/usage").context(active.lastContextUsage, active.vendor || "claude") });
|
|
318
322
|
if (scheduledMessages) scheduledMessages.sendState(active, ws);
|
|
319
323
|
if (pairResultRecovery) pairResultRecovery.sendState(active, ws);
|
|
320
324
|
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
var fs = require('fs'), path = require('path');
|
|
2
|
+
var projection = require('./clay-control-target-projection');
|
|
3
|
+
function project(service, principal, status, manager) {
|
|
4
|
+
var before = service.rootIdentity(principal, 'git');
|
|
5
|
+
var directory = fs.realpathSync(status.gitDir), stat = fs.statSync(directory), indexPath = path.join(directory, 'index');
|
|
6
|
+
var index = fs.existsSync(indexPath) ? require('crypto').createHash('sha256').update(fs.readFileSync(indexPath)).digest('hex') : null;
|
|
7
|
+
var view = { runtimeId: projection.runtime(manager), repository: projection.digest([directory, [stat.dev, stat.ino, stat.mode, stat.uid, stat.gid]]),
|
|
8
|
+
files: status.files.slice(0, 200).map(function (file) { return { path: file.path, revision: projection.digest([status.oid, index, file]) }; }) };
|
|
9
|
+
if (service.rootIdentity(principal, 'git') !== before) throw new Error('Workspace identity changed during projection.');
|
|
10
|
+
return view;
|
|
11
|
+
}
|
|
12
|
+
module.exports = { project: project };
|
|
@@ -116,7 +116,11 @@ function attachFilesystem(ctx) {
|
|
|
116
116
|
// --- fs_read ---
|
|
117
117
|
if (msg.type === "fs_read") {
|
|
118
118
|
var metadata = { type: 'fs_read_result', path: msg.path, requestId: msg.requestId, projectSlug: msg.projectSlug, sessionId: msg.sessionId, accountId: msg.accountId };
|
|
119
|
-
try {
|
|
119
|
+
try {
|
|
120
|
+
var readResult = operations.read(fileScope, msg.path);
|
|
121
|
+
try { var fileSnapshot = operations.snapshot(fileScope, msg.path); if (fileSnapshot.hash === require('crypto').createHash('sha256').update(readResult.content).digest('hex')) readResult.controlRevision = require('./clay-control-target-projection').digest(fileSnapshot); } catch (ignored) {}
|
|
122
|
+
sendTo(ws, Object.assign(metadata, readResult));
|
|
123
|
+
}
|
|
120
124
|
catch (e) { sendTo(ws, Object.assign(metadata, { error: fileError(e).message, errorCode: fileError(e).code })); }
|
|
121
125
|
return true;
|
|
122
126
|
}
|
package/lib/project-http.js
CHANGED
|
@@ -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(
|
|
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) :
|
|
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 =
|
|
532
|
-
var projectSkillsDir =
|
|
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
|
|
|
@@ -663,6 +663,9 @@ function attachHTTP(ctx) {
|
|
|
663
663
|
var gitUserInfo = getOsUserInfoForReq(req);
|
|
664
664
|
try {
|
|
665
665
|
var gitStatus = gitCli.getStatus(cwd, gitUserInfo);
|
|
666
|
+
if (ctx.workspaceOperations) {
|
|
667
|
+
try { gitStatus.controlView = require('./project-control-git-view').project(ctx.workspaceOperations, { userId: req._clayUser && req._clayUser.id || null, singleUserOwnerId: ctx.getProjectOwnerId && ctx.getProjectOwnerId() || null }, gitStatus, sm); } catch (ignored) {}
|
|
668
|
+
}
|
|
666
669
|
if (gitAttribution) {
|
|
667
670
|
var visibleGitSessions = sm.sessions;
|
|
668
671
|
if (usersModule.isMultiUser() && req._clayUser) {
|
package/lib/project-mcp.js
CHANGED
|
@@ -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
|
|
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);
|
|
@@ -154,8 +154,9 @@ function attachNotifications(ctx) {
|
|
|
154
154
|
return notifications.length;
|
|
155
155
|
}
|
|
156
156
|
|
|
157
|
+
function controlProjection(item) { var value = Object.assign({}, item); delete value.controlRevision; return Object.assign(value, { controlRevision: require('./clay-control-target-projection').digest(value) }); }
|
|
157
158
|
function visible(ws) {
|
|
158
|
-
return notifications.filter(function (notification) { return !ctx.canView || ctx.canView(ws, notification); });
|
|
159
|
+
return notifications.filter(function (notification) { return !ctx.canView || ctx.canView(ws, notification); }).map(controlProjection);
|
|
159
160
|
}
|
|
160
161
|
|
|
161
162
|
function publish(payload) {
|
|
@@ -164,7 +165,7 @@ function attachNotifications(ctx) {
|
|
|
164
165
|
if (ws.readyState !== 1) return;
|
|
165
166
|
if (payload.notification && !ctx.canView(ws, payload.notification)) return;
|
|
166
167
|
var list = visible(ws);
|
|
167
|
-
ws.send(JSON.stringify(Object.assign({}, payload, { unreadCount: list.length })));
|
|
168
|
+
ws.send(JSON.stringify(Object.assign({}, payload, { notification: payload.notification ? controlProjection(payload.notification) : undefined, unreadCount: list.length })));
|
|
168
169
|
});
|
|
169
170
|
}
|
|
170
171
|
|
|
@@ -197,7 +198,7 @@ function attachNotifications(ctx) {
|
|
|
197
198
|
saveNotifications();
|
|
198
199
|
var payload = {
|
|
199
200
|
type: "notification_created",
|
|
200
|
-
notification: notif,
|
|
201
|
+
notification: controlProjection(notif),
|
|
201
202
|
unreadCount: getUnreadCount(),
|
|
202
203
|
};
|
|
203
204
|
// Project/session previews are projected for each authenticated viewer.
|
|
@@ -235,7 +236,7 @@ function attachNotifications(ctx) {
|
|
|
235
236
|
if (items.some(function (item) { return !canReadOwned(principal, item); })) throw new Error('Owned notification access changed.');
|
|
236
237
|
}
|
|
237
238
|
function listOwned(principal) {
|
|
238
|
-
return notifications.filter(function (item) { return canReadOwned(principal, item); }).slice(0, 100).map(function (item) { return JSON.parse(JSON.stringify(item)); });
|
|
239
|
+
return notifications.filter(function (item) { return canReadOwned(principal, item); }).slice(0, 100).map(function (item) { return JSON.parse(JSON.stringify(controlProjection(item))); });
|
|
239
240
|
}
|
|
240
241
|
function inspectOwned(principal, ids) {
|
|
241
242
|
var owned = listOwned(principal);
|
|
@@ -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.
|
|
129
|
-
&& caller.
|
|
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
|
}
|
|
@@ -16,16 +16,14 @@ function contextStatus(session) {
|
|
|
16
16
|
var used = null;
|
|
17
17
|
var window = null;
|
|
18
18
|
var source = "unavailable";
|
|
19
|
+
var measurement = "unavailable";
|
|
19
20
|
if (snapshot && typeof snapshot === "object") {
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
window = firstNumber(snapshot.contextWindow);
|
|
27
|
-
if (used !== null || window !== null) source = "adapter_context_usage";
|
|
28
|
-
}
|
|
21
|
+
var current = require("./yoke/usage").context(snapshot, session.vendor || "claude").context;
|
|
22
|
+
measurement = current.measurement;
|
|
23
|
+
used = firstNumber(current.usedTokens);
|
|
24
|
+
window = firstNumber(current.windowTokens);
|
|
25
|
+
if (used !== null || window !== null) source = current.source;
|
|
26
|
+
|
|
29
27
|
}
|
|
30
28
|
var history = session.history || [];
|
|
31
29
|
var cumulativeInput = 0;
|
|
@@ -46,9 +44,9 @@ function contextStatus(session) {
|
|
|
46
44
|
if (typeof item._ts === "number") lastCompactionAt = item._ts;
|
|
47
45
|
}
|
|
48
46
|
if (item.type !== "result") continue;
|
|
49
|
-
var usage = item.usage || {};
|
|
50
|
-
var input = firstNumber(usage.
|
|
51
|
-
var output = firstNumber(usage.
|
|
47
|
+
var usage = require("./yoke/usage").accounting(item.usage) || {};
|
|
48
|
+
var input = firstNumber(usage.inputTokens);
|
|
49
|
+
var output = firstNumber(usage.outputTokens);
|
|
52
50
|
if (input !== null) { cumulativeInput += input; observedInput = true; }
|
|
53
51
|
if (output !== null) { cumulativeOutput += output; observedOutput = true; }
|
|
54
52
|
results++;
|
|
@@ -59,13 +57,14 @@ function contextStatus(session) {
|
|
|
59
57
|
if (used !== null && window !== null && used > window) {
|
|
60
58
|
used = null;
|
|
61
59
|
source = "unavailable";
|
|
60
|
+
measurement = "unavailable";
|
|
62
61
|
}
|
|
63
62
|
// Older result records contain lastStreamInputTokens, but that field was
|
|
64
63
|
// populated from adapter snapshots that could be cumulative or inflated by
|
|
65
64
|
// cache accounting. It is not safe evidence of current context occupancy.
|
|
66
65
|
var ratio = used !== null && window !== null && window > 0 ? Math.min(1, Math.round((used / window) * 1000) / 1000) : null;
|
|
67
66
|
return {
|
|
68
|
-
current: { source: source, usedTokens: used, windowTokens: window, usedRatio: ratio,
|
|
67
|
+
current: { source: source, measurement: measurement, usedTokens: used, windowTokens: window, usedRatio: ratio,
|
|
69
68
|
scope: used === null && window === null ? "unknown" : (used !== null && window !== null ? "current_context" : "partial_current_context") },
|
|
70
69
|
cumulative: { inputTokens: observedInput ? cumulativeInput : null, outputTokens: observedOutput ? cumulativeOutput : null, tasksObserved: results, scope: "loaded_session_history", complete: false },
|
|
71
70
|
lastTask: lastTask,
|
|
@@ -249,7 +249,8 @@ function createPendingMessageQueue(opts) {
|
|
|
249
249
|
return true;
|
|
250
250
|
}
|
|
251
251
|
function consumeOne(session, dispatch) {
|
|
252
|
-
|
|
252
|
+
var foreground = require('./sdk-foreground-admission');
|
|
253
|
+
if (loadError || !session || session._pendingMessageClaimId || isPaused(session) || session._pendingMessageDrainPaused || session.taskStopRequested || session._queryStarting || (session._awaitingTurnResult || session.isProcessing) && !foreground.available(session)) return null;
|
|
253
254
|
if (session.rateLimitResetsAt && session.rateLimitResetsAt > Date.now()) return null;
|
|
254
255
|
for (var i = 0; i < data.items.length; i++) {
|
|
255
256
|
var item = data.items[i];
|
|
@@ -262,6 +263,7 @@ function createPendingMessageQueue(opts) {
|
|
|
262
263
|
data.revision++;
|
|
263
264
|
try { persist(); } catch (e) { restore(before); return null; }
|
|
264
265
|
session._pendingMessageClaimId = item.id;
|
|
266
|
+
foreground.take(session);
|
|
265
267
|
broadcast(session, { type: "pending_message_claimed", sessionId: session.localId, projectSlug: opts.slug, paused: isPaused(session), id: item.id, revision: data.revision });
|
|
266
268
|
var completed = false;
|
|
267
269
|
function complete(ok, error) { if (completed) return false; completed = true; return finishClaim(session, item.id, ok, error); }
|
|
@@ -18,9 +18,11 @@ function attachProjectProductControl(ctx) {
|
|
|
18
18
|
if (session.ownerId && session.ownerId !== principal.singleUserOwnerId) throw new Error("Live target ownership denied.");
|
|
19
19
|
return null;
|
|
20
20
|
}
|
|
21
|
-
function review(session, question, revalidate) {
|
|
21
|
+
function review(session, question, revalidate, presentation) {
|
|
22
22
|
var owner = session.ownerId || null;
|
|
23
23
|
return userInput.dispatchUserInput(function (request, respond) {
|
|
24
|
+
// Presentation comes only from the bound product service, after normalization.
|
|
25
|
+
if (presentation) request.questions[0].review = presentation;
|
|
24
26
|
request.authorizeResponse = function (ws) {
|
|
25
27
|
try {
|
|
26
28
|
revalidate();
|
|
@@ -234,7 +234,7 @@ function attachProjectScheduledMessages(ctx) {
|
|
|
234
234
|
images: images,
|
|
235
235
|
pastes: pastes,
|
|
236
236
|
clientMessageId: requestId ? "scheduled-request-" + requestId : "scheduled-" + job.id,
|
|
237
|
-
schedule: { jobId: job.id, revision: job.payload.revision, notBefore: job.runAt, ready: false },
|
|
237
|
+
schedule: { jobId: job.id, revision: job.payload.revision, notBefore: job.runAt, ready: false, logicalScheduleId: job.payload.logicalScheduleId || null, logicalRevision: job.payload.logicalRevision || null },
|
|
238
238
|
}, queueActor(actorId));
|
|
239
239
|
if (!queuedScheduled.ok) {
|
|
240
240
|
if (current) {
|
|
@@ -304,7 +304,7 @@ function attachProjectScheduledMessages(ctx) {
|
|
|
304
304
|
images: existing ? existing.message.images : undefined,
|
|
305
305
|
pastes: existing ? existing.message.pastes : undefined,
|
|
306
306
|
clientMessageId: existing ? existing.clientMessageId : "scheduled-" + updated.id,
|
|
307
|
-
schedule: { jobId: updated.id, revision: updated.payload.revision, notBefore: updated.runAt, ready: false },
|
|
307
|
+
schedule: { jobId: updated.id, revision: updated.payload.revision, notBefore: updated.runAt, ready: false, logicalScheduleId: updated.payload.logicalScheduleId || null, logicalRevision: updated.payload.logicalRevision || null },
|
|
308
308
|
}, queueActor(actorUser && actorUser.id));
|
|
309
309
|
if (!queued.ok) {
|
|
310
310
|
try { scheduler.replaceQueued(job.id, job); }
|
|
@@ -375,7 +375,7 @@ function attachProjectScheduledMessages(ctx) {
|
|
|
375
375
|
if (!ensured && job.payload.queueAdmissionRequired === true) throw new Error("Scheduled queue item is missing; refusing to recreate message payload.");
|
|
376
376
|
if (!ensured) ensured = pendingMessageQueue.upsertScheduled(session, {
|
|
377
377
|
type: "message", text: job.payload.text, clientMessageId: "scheduled-" + job.id,
|
|
378
|
-
schedule: { jobId: job.id, revision: job.payload.revision, notBefore: job.runAt, ready: false },
|
|
378
|
+
schedule: { jobId: job.id, revision: job.payload.revision, notBefore: job.runAt, ready: false, logicalScheduleId: job.payload.logicalScheduleId || null, logicalRevision: job.payload.logicalRevision || null },
|
|
379
379
|
}, queueActor(job.payload.actorId));
|
|
380
380
|
if (!ensured || ensured.ok === false) throw new Error(ensured.error || "Scheduled queue handoff could not be prepared.");
|
|
381
381
|
var released = pendingMessageQueue.releaseScheduled(session, job.id, job.payload.revision);
|
|
@@ -438,7 +438,7 @@ function attachProjectScheduledMessages(ctx) {
|
|
|
438
438
|
if (jobs[i].state === "queued" && !queueItemForJob(session, jobs[i], jobs[i].payload.actorId)) {
|
|
439
439
|
if (jobs[i].payload.queueAdmissionRequired !== true || (jobs[i].payload.mateScheduleId && ctx.mateLifecycle && ctx.mateLifecycle().current(session, jobs[i]))) pendingMessageQueue.upsertScheduled(session, {
|
|
440
440
|
type: "message", text: jobs[i].payload.text, clientMessageId: "scheduled-" + jobs[i].id,
|
|
441
|
-
schedule: { jobId: jobs[i].id, revision: jobs[i].payload.revision, notBefore: jobs[i].runAt, ready: false },
|
|
441
|
+
schedule: { jobId: jobs[i].id, revision: jobs[i].payload.revision, notBefore: jobs[i].runAt, ready: false, logicalScheduleId: jobs[i].payload.logicalScheduleId || null, logicalRevision: jobs[i].payload.logicalRevision || null },
|
|
442
442
|
}, actor);
|
|
443
443
|
}
|
|
444
444
|
else if (["interrupted", "completed"].indexOf(jobs[i].state) >= 0) {
|
|
@@ -31,7 +31,7 @@ function attachScheduledTaskControl(ctx) {
|
|
|
31
31
|
var begun = ctx.beginManual(session, requestId);
|
|
32
32
|
if (!begun.ok) throw new Error(begun.error);
|
|
33
33
|
var prior = session.scheduledTaskDraft;
|
|
34
|
-
var draft = Object.assign({}, prior, checked.value, { canonicalScheduling: true });
|
|
34
|
+
var draft = Object.assign({}, prior, checked.value, { canonicalScheduling: true, version: Number(prior.version || 0) + 1 });
|
|
35
35
|
session.scheduledTaskDraft = draft;
|
|
36
36
|
if (ctx.sm.saveSessionFile(session) !== true) { session.scheduledTaskDraft = prior; throw new Error('The canonical schedule proposal could not be saved.'); }
|
|
37
37
|
if (drafts.size >= 100) drafts.delete(drafts.keys().next().value);
|
|
@@ -63,23 +63,34 @@ function attachSessionDocument(ctx) {
|
|
|
63
63
|
|
|
64
64
|
function canUse(caller) { return !!caller && (!ctx.canUseSession || ctx.canUseSession(caller)); }
|
|
65
65
|
|
|
66
|
+
function activity(caller, action) {
|
|
67
|
+
if (ctx.studioPresentation) return ctx.studioPresentation.begin(caller, action);
|
|
68
|
+
try { var display = ctx.getPresentation && ctx.getPresentation(caller); return display && display.track('studio', action); } catch (error) { return null; }
|
|
69
|
+
}
|
|
66
70
|
function present(args, caller) {
|
|
67
71
|
if (!canUse(caller)) return toolError("Documents require a live authorized session.");
|
|
68
72
|
if (!caller) return toolError("present_markdown_edit requires a session-bound tool server");
|
|
69
73
|
var target = resolveMarkdownPath(args.path);
|
|
70
74
|
if (!target) return toolError("path must be a .md or .mdx file inside the project");
|
|
75
|
+
var observer;
|
|
71
76
|
try {
|
|
77
|
+
observer = activity(caller, 'document');
|
|
72
78
|
var snapshot = readSnapshot(target, caller);
|
|
73
79
|
var relativePath = path.relative(cwd, target).split(path.sep).join("/");
|
|
74
|
-
|
|
80
|
+
var message = {
|
|
75
81
|
type: "markdown_edit_present",
|
|
76
82
|
path: relativePath,
|
|
77
83
|
content: snapshot.content,
|
|
78
84
|
exists: snapshot.exists,
|
|
79
|
-
size: snapshot.size,
|
|
80
|
-
}
|
|
85
|
+
size: snapshot.size, sessionId: caller.localId,
|
|
86
|
+
};
|
|
87
|
+
var delivered;
|
|
88
|
+
if (observer && observer.present) delivered = observer.present(message, "document", relativePath, require("./clay-control-target-projection").digest(snapshot), function () { if (!canUse(caller) || require("./clay-control-target-projection").digest(readSnapshot(target, caller)) !== require("./clay-control-target-projection").digest(snapshot)) throw new Error("Document revision changed."); }, function (value, onSent) { return sendToSession(caller.localId, value, onSent); });
|
|
89
|
+
else delivered = sendToSession(caller.localId, message);
|
|
90
|
+
if (observer && !observer.present) observer.finish(delivered > 0 ? { status: 'delivered' } : { outcome: 'uncertain' });
|
|
81
91
|
return toolResult({ ready: true, path: relativePath });
|
|
82
92
|
} catch (e) {
|
|
93
|
+
if (observer) { if (observer.unknown) observer.unknown(); else observer.finish({ outcome: 'uncertain' }); }
|
|
83
94
|
return toolError(e.message || String(e));
|
|
84
95
|
}
|
|
85
96
|
}
|
|
@@ -95,11 +106,15 @@ function attachSessionDocument(ctx) {
|
|
|
95
106
|
} catch (e) {
|
|
96
107
|
return toolError(e.message || String(e));
|
|
97
108
|
}
|
|
109
|
+
var observer = activity(caller, 'sketch');
|
|
98
110
|
return wireframeRenderer.renderPlantUml(source).then(function () {
|
|
99
|
-
if (!canUse(caller)) return toolError("Document access changed.");
|
|
100
|
-
|
|
111
|
+
if (!canUse(caller)) { if (observer) { if (observer.fail) observer.fail(); else observer.cancel(); } return toolError("Document access changed."); }
|
|
112
|
+
var message = { type: "wireframe_present", source: source, id: args.id, title: String(args.title || "clay-sketch").slice(0, 100), sessionId: caller.localId };
|
|
113
|
+
var delivered = observer && observer.present ? observer.present(message, "sketch", args.id, require("./clay-control-target-projection").digest(source), function () { if (!canUse(caller)) throw new Error("Sketch source changed."); }, function (value, onSent) { return sendToSession(caller.localId, value, onSent); }) : sendToSession(caller.localId, message);
|
|
114
|
+
if (observer && !observer.present) observer.finish(delivered > 0 ? { status: 'delivered' } : { outcome: 'uncertain' });
|
|
101
115
|
return toolResult({ ready: true, id: args.id, saved: false });
|
|
102
116
|
}).catch(function (e) {
|
|
117
|
+
if (observer) { if (observer.unknown) observer.unknown(); else observer.finish({ outcome: 'uncertain' }); }
|
|
103
118
|
return toolError(e && e.message ? e.message : String(e));
|
|
104
119
|
});
|
|
105
120
|
}
|
|
@@ -105,7 +105,10 @@ function attachSessionHistoryActions(ctx) {
|
|
|
105
105
|
if (session.messageUUIDs[fi].uuid === uuid) { targetIdx = fi; break; }
|
|
106
106
|
}
|
|
107
107
|
var forkHistory = [];
|
|
108
|
-
if (
|
|
108
|
+
if (result.localHistoryEndIndex !== undefined) {
|
|
109
|
+
if (!Number.isInteger(result.localHistoryEndIndex) || result.localHistoryEndIndex < 0 || result.localHistoryEndIndex > session.history.length) throw new Error("The provider fork exists but its local history boundary is unavailable; inspect before retrying");
|
|
110
|
+
forkHistory = session.history.slice(0, result.localHistoryEndIndex);
|
|
111
|
+
} else if (targetIdx >= 0) {
|
|
109
112
|
var trimTo = session.messageUUIDs[targetIdx].historyIndex;
|
|
110
113
|
forkHistory = session.history.slice(0, trimTo);
|
|
111
114
|
} else {
|
|
@@ -133,8 +136,7 @@ function attachSessionHistoryActions(ctx) {
|
|
|
133
136
|
return forked;
|
|
134
137
|
} else {
|
|
135
138
|
// Read history from CLI session files
|
|
136
|
-
var
|
|
137
|
-
var history = await cliSess.readCliSessionHistory(resolveSessionHome(session), cwd, result.sessionId);
|
|
139
|
+
var history = await require("./yoke/session-history").read(session.vendor || "claude", resolveSessionHome(session), cwd, result.sessionId);
|
|
138
140
|
revalidate();
|
|
139
141
|
var options = {
|
|
140
142
|
history: history,
|
|
@@ -350,7 +350,7 @@ function attachSessionPair(ctx) {
|
|
|
350
350
|
if (boundRoles && boundRoles.workers.indexOf(boundSession) !== -1) return taskControl.workerToolDefs(boundSession);
|
|
351
351
|
var adHocSplit = !!(group && !group.pair);
|
|
352
352
|
if (!driverEligibility.isEligibleDriverSession(boundSession, sm)) return [];
|
|
353
|
-
var persistentCatalog = boundSession.vendor === "
|
|
353
|
+
var persistentCatalog = require("./yoke/session-tool-transport").contract(boundSession.vendor || "claude").catalogLifetime === "conversation" && (!group || !!group.pair);
|
|
354
354
|
if (!group && !persistentCatalog) {
|
|
355
355
|
return workerProposal.getToolDefs(boundSession)
|
|
356
356
|
.concat(workerPermission.getToolDefs(boundSession, { dormantDriver: true }));
|