clay-server 5.2.0-beta.4 → 5.2.0-beta.5
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/account-auth-socket.js +1 -0
- package/lib/builtin-mates.js +17 -14
- package/lib/canonical-clay-record-authority.js +15 -0
- package/lib/canonical-clay-scheduling-authority.js +19 -0
- package/lib/claude-hook-installer.js +28 -0
- package/lib/clay-mate-chat-receipts.js +26 -0
- package/lib/clay-mate-chat.js +87 -0
- package/lib/clay-mate-knowledge-input.js +26 -0
- package/lib/clay-mate-knowledge.js +95 -0
- package/lib/clay-mate-lifecycle.js +95 -0
- package/lib/clay-product-control-input.js +33 -0
- package/lib/clay-product-control-tools.js +51 -0
- package/lib/clay-product-control.js +237 -0
- package/lib/clay-project-lifecycle.js +154 -0
- package/lib/clay-project-record-input.js +44 -0
- package/lib/clay-project-records.js +83 -0
- package/lib/clay-scheduling-input.js +21 -0
- package/lib/clay-scheduling-receipts.js +26 -0
- package/lib/clay-scheduling.js +119 -0
- package/lib/clay-screen-controls.js +96 -0
- package/lib/clay-workspace-controls.js +83 -0
- package/lib/clay-workspace-input.js +17 -0
- package/lib/clay-workspace-receipts.js +26 -0
- package/lib/daemon-project-access.js +11 -0
- package/lib/daemon-project-lifecycle-callbacks.js +15 -0
- package/lib/daemon-project-lifecycle.js +262 -0
- package/lib/daemon-projects.js +1 -1
- package/lib/daemon.js +73 -221
- package/lib/issues-service.js +29 -15
- package/lib/knowledge-db-import.js +5 -4
- package/lib/knowledge-db-service.js +9 -6
- package/lib/knowledge-document-index.js +2 -1
- package/lib/knowledge-product-operations.js +46 -0
- package/lib/mate-creation-mcp-server.js +2 -0
- package/lib/mate-knowledge-canonical-access.js +10 -0
- package/lib/mate-knowledge-service.js +7 -4
- package/lib/mate-lifecycle-service.js +94 -0
- package/lib/mate-message-delivery.js +43 -0
- package/lib/mate-ready-creation.js +9 -1
- package/lib/mate-schedule-input.js +29 -0
- package/lib/mate-schedule-lifecycle.js +151 -0
- package/lib/mate-scheduling.js +103 -0
- package/lib/mates.js +4 -3
- package/lib/notes-checklist.js +50 -0
- package/lib/notes.js +49 -3
- package/lib/os-users.js +20 -5
- package/lib/project-admin-overview.js +51 -0
- package/lib/project-ask-user.js +4 -2
- package/lib/project-delegated-follow-up.js +2 -1
- package/lib/project-directory-claim.js +65 -0
- package/lib/project-file-operations.js +61 -0
- package/lib/project-filesystem.js +13 -87
- package/lib/project-full-access.js +23 -2
- package/lib/project-knowledge.js +32 -18
- package/lib/project-lifecycle-operations.js +98 -0
- package/lib/project-lifecycle-process-witness.js +32 -0
- package/lib/project-lifecycle-runtime.js +50 -0
- package/lib/project-linux-access.js +22 -0
- package/lib/project-logs-canonical-clay.js +27 -0
- package/lib/project-logs-clay-query.js +16 -0
- package/lib/project-logs-schema.js +4 -2
- package/lib/project-logs-service.js +8 -7
- package/lib/project-logs.js +1 -0
- package/lib/project-loop.js +1 -0
- package/lib/project-mate-chat-control.js +55 -0
- package/lib/project-mate-creation-proposal.js +18 -3
- package/lib/project-models.js +14 -31
- package/lib/project-notifications.js +74 -16
- package/lib/project-ownership.js +77 -0
- package/lib/project-pair-result-delivery.js +14 -3
- package/lib/project-pending-message-queue.js +1 -0
- package/lib/project-product-control.js +87 -0
- package/lib/project-product-session-queue.js +46 -0
- package/lib/project-push-access.js +28 -0
- package/lib/project-scheduled-message-queue.js +30 -0
- package/lib/project-scheduled-messages.js +63 -64
- package/lib/project-scheduled-task-control.js +83 -0
- package/lib/project-scheduled-tasks.js +13 -2
- package/lib/project-scheduling-access.js +10 -0
- package/lib/project-session-actions.js +65 -0
- package/lib/project-session-delete.js +8 -1
- package/lib/project-session-github.js +47 -12
- package/lib/project-session-history-actions.js +165 -0
- package/lib/project-session-runtime-actions.js +63 -0
- package/lib/project-session-vendor.js +54 -0
- package/lib/project-sessions.js +64 -241
- package/lib/project-user-message.js +30 -1
- package/lib/project-workspace-operations.js +126 -0
- package/lib/project-workspace-query.js +9 -2
- package/lib/project.js +95 -3
- package/lib/public/css/clay-tool-activity.css +51 -0
- package/lib/public/css/home-mate-creation.css +5 -0
- package/lib/public/css/overlays.css +4 -2
- package/lib/public/css/project-admin-overview.css +13 -0
- package/lib/public/css/session-folders.css +11 -0
- package/lib/public/css/session-github.css +3 -0
- package/lib/public/css/sticky-notes.css +61 -1
- package/lib/public/index.html +1 -1
- package/lib/public/modules/admin-settings-projects.js +6 -2
- package/lib/public/modules/app-connection.js +8 -1
- package/lib/public/modules/app-message-router.js +1 -1
- package/lib/public/modules/app-messages.js +18 -3
- package/lib/public/modules/app-projects.js +5 -0
- package/lib/public/modules/clay-owner-navigation.js +27 -0
- package/lib/public/modules/clay-tool-activity.js +140 -0
- package/lib/public/modules/clay-tool-presentation.js +338 -0
- package/lib/public/modules/command-palette.js +1 -1
- package/lib/public/modules/default-ai.js +11 -3
- package/lib/public/modules/home-debate-planning.js +5 -5
- package/lib/public/modules/home-mate-chat.js +8 -6
- package/lib/public/modules/home-mate-creation.js +73 -1
- package/lib/public/modules/personal-settings-dialog.js +7 -1
- package/lib/public/modules/project-access-recovery.js +17 -0
- package/lib/public/modules/project-admin-overview.js +21 -0
- package/lib/public/modules/session-folder-layout.js +1 -1
- package/lib/public/modules/session-folder-view-menu.js +9 -0
- package/lib/public/modules/session-github.js +65 -8
- package/lib/public/modules/sidebar-session-folders-ui.js +1 -1
- package/lib/public/modules/sticky-note-markdown.js +72 -6
- package/lib/public/modules/sticky-notes-actions.js +30 -0
- package/lib/public/modules/sticky-notes-browser.js +9 -2
- package/lib/public/modules/sticky-notes-card.js +23 -7
- package/lib/public/modules/sticky-notes-checklist.js +71 -0
- package/lib/public/modules/sticky-notes-editor.js +108 -44
- package/lib/public/modules/sticky-notes-shared.js +50 -2
- package/lib/public/modules/sticky-notes-state.js +66 -0
- package/lib/public/modules/sticky-notes.js +34 -51
- package/lib/public/modules/thinking-lifecycle.js +33 -3
- package/lib/public/modules/thinking-view.js +23 -1
- package/lib/public/modules/tools.js +34 -6
- package/lib/public/modules/user-settings-appearance.js +10 -3
- package/lib/public/modules/user-settings-editors.js +1 -1
- package/lib/public/modules/user-settings-layout.js +11 -2
- package/lib/public/modules/user-settings-preferences.js +6 -2
- package/lib/public/modules/user-settings-profile.js +12 -2
- package/lib/public/modules/user-settings.js +3 -2
- package/lib/public/style.css +2 -0
- package/lib/scheduled-task-records.js +3 -1
- package/lib/scheduler.js +2 -0
- package/lib/sdk-bridge.js +220 -21
- package/lib/sdk-message-processor.js +2 -0
- package/lib/sdk-response-recovery.js +155 -0
- package/lib/server-admin.js +63 -16
- package/lib/server-dm.js +1 -1
- package/lib/server-global-ws.js +10 -20
- package/lib/server-home-chat-events.js +7 -3
- package/lib/server-home-chat.js +4 -16
- package/lib/server-home-mate-creation.js +1 -0
- package/lib/server-home-mcp-consent.js +3 -3
- package/lib/server-mates.js +25 -67
- package/lib/server-owner-ui.js +77 -0
- package/lib/server-path-access.js +38 -0
- package/lib/server-project-removal.js +22 -0
- package/lib/server.js +99 -29
- package/lib/session-config-transition.js +45 -0
- package/lib/session-folders.js +7 -2
- package/lib/session-github.js +9 -2
- package/lib/session-visibility.js +5 -2
- package/lib/sessions.js +10 -0
- package/lib/terminal-manager.js +52 -0
- package/lib/users-permissions.js +2 -8
- package/lib/users.js +4 -0
- package/lib/workspace-query-access.js +4 -1
- package/lib/ws-schema.js +8 -2
- package/lib/yoke/adapters/claude.js +47 -8
- package/lib/yoke/adapters/codex.js +52 -111
- package/lib/yoke/claude-worker-startup.js +28 -0
- package/lib/yoke/codex-idle-recovery.js +81 -1
- package/lib/yoke/codex-recovery-admission.js +164 -0
- package/lib/yoke/session-tool-transport.js +3 -0
- package/package.json +1 -1
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
var fs = require('fs'), path = require('path'), crypto = require('crypto');
|
|
2
|
+
var git = require('./git-cli');
|
|
3
|
+
function digest(value) { return crypto.createHash('sha256').update(typeof value === 'string' || Buffer.isBuffer(value) ? value : JSON.stringify(value)).digest('hex'); }
|
|
4
|
+
function inode(file) { var s = fs.statSync(file); return [s.dev, s.ino, s.mode, s.uid, s.gid]; }
|
|
5
|
+
function attachWorkspaceOperations(ctx) {
|
|
6
|
+
var busy = false;
|
|
7
|
+
function authorize(principal, domain) {
|
|
8
|
+
var record = ctx.getAccess(), user = principal.userId && ctx.users.findUserById(principal.userId);
|
|
9
|
+
if (ctx.isMate || !record || record.lifecycleExecutionPaused || ctx.users.isMultiUser() && (!user || record.ownerId !== user.id || !ctx.users.canAccessProject(user.id, record))) throw new Error('Live selected-project ownership or access is unavailable.');
|
|
10
|
+
if (!ctx.users.isMultiUser() && record.ownerId && record.ownerId !== principal.singleUserOwnerId) throw new Error('Selected-project ownership changed.');
|
|
11
|
+
if (user && ctx.users.getEffectivePermissions(user, ctx.osUsers)[domain === 'terminal' ? 'terminal' : 'fileBrowser'] !== true) throw new Error('The account does not have permission for this product operation.');
|
|
12
|
+
var identity = null;
|
|
13
|
+
if (ctx.osUsers) {
|
|
14
|
+
if (!user || !user.linuxUser) throw new Error('A live mapped OS identity is required.');
|
|
15
|
+
identity = require('./os-users').resolveOsUserInfo(user.linuxUser);
|
|
16
|
+
if (!identity || !Number.isInteger(identity.uid) || identity.uid < 0 || !Number.isInteger(identity.gid) || identity.gid < 0 || identity.user !== user.linuxUser) throw new Error('The mapped OS identity is unavailable.');
|
|
17
|
+
}
|
|
18
|
+
return { identity: identity, projectBound: true, ownerId: user ? user.id : null };
|
|
19
|
+
}
|
|
20
|
+
function rootIdentity(principal, domain) {
|
|
21
|
+
var scope = authorize(principal, domain), root = fs.realpathSync(ctx.cwd);
|
|
22
|
+
return digest([root, inode(root), scope.ownerId, scope.identity && [scope.identity.user, scope.identity.uid, scope.identity.gid, scope.identity.home]]);
|
|
23
|
+
}
|
|
24
|
+
function bounded(requested, missing) {
|
|
25
|
+
if (typeof requested !== 'string' || !requested || path.isAbsolute(requested) || /[\u0000-\u001f\u007f]/.test(requested) || requested.split(/[\\/]/).some(function (part) { return part === '..' || ['.git', '.clay', '.claude', '.codex', '.envrc'].indexOf(part) >= 0; })) throw new Error('Use an in-project relative path outside managed settings and Git metadata.');
|
|
26
|
+
var root = fs.realpathSync(ctx.cwd), resolved = root;
|
|
27
|
+
var parts = requested.split('/').filter(function (part) { return part && part !== '.'; });
|
|
28
|
+
for (var i = 0; i < parts.length; i++) {
|
|
29
|
+
resolved = path.join(resolved, parts[i]);
|
|
30
|
+
var stat;
|
|
31
|
+
try { stat = fs.lstatSync(resolved); } catch (error) { if (missing && error.code === 'ENOENT' && i === parts.length - 1) return resolved; throw error; }
|
|
32
|
+
if (stat.isSymbolicLink() || stat.isFile() && stat.nlink !== 1) throw new Error('Symlinks and multiply linked files are unavailable in canonical project controls.');
|
|
33
|
+
}
|
|
34
|
+
if (path.relative(root, resolved).split(path.sep).indexOf('..') >= 0) throw new Error('The canonical project boundary changed.');
|
|
35
|
+
return resolved;
|
|
36
|
+
}
|
|
37
|
+
function repository(scope) {
|
|
38
|
+
var marker = fs.lstatSync(path.join(ctx.cwd, '.git'));
|
|
39
|
+
if (!marker.isDirectory() || marker.isSymbolicLink()) throw new Error('Canonical Git controls require a direct local .git directory; linked metadata and submodules are unavailable.');
|
|
40
|
+
var status = git.getStatus(ctx.cwd, scope.identity);
|
|
41
|
+
if (!status.isRepository || fs.realpathSync(status.root) !== fs.realpathSync(ctx.cwd)) throw new Error('Select the exact registered repository root, rather than a repository subdirectory.');
|
|
42
|
+
if (fs.realpathSync(status.gitDir) !== fs.realpathSync(path.join(ctx.cwd, '.git'))) throw new Error('The Git metadata directory escaped the selected project.');
|
|
43
|
+
return status;
|
|
44
|
+
}
|
|
45
|
+
function gitProjection(status) { return { isRepository: true, branch: status.branch, oid: status.oid, upstream: status.upstream, ahead: status.ahead, behind: status.behind, detached: status.detached, files: status.files.slice(0, 200), truncated: status.files.length > 200, dirty: status.dirty }; }
|
|
46
|
+
function terminal(scope, id) {
|
|
47
|
+
var item = ctx.tm.inspect(id);
|
|
48
|
+
var identity = scope.identity ? JSON.stringify([scope.identity.user, scope.identity.uid, scope.identity.gid, scope.identity.home]) : null;
|
|
49
|
+
if (!item || item.kind !== 'shell' || item.ownerId !== scope.ownerId || item.osIdentity !== identity) throw new Error('Select an exact same-owner shell terminal. Coding-agent, login and foreign terminals are excluded.');
|
|
50
|
+
return item;
|
|
51
|
+
}
|
|
52
|
+
function read(principal, op) {
|
|
53
|
+
var scope = authorize(principal, op.domain), before = rootIdentity(principal, op.domain), result;
|
|
54
|
+
if (op.domain === 'files') {
|
|
55
|
+
bounded(op.path || '.');
|
|
56
|
+
if (op.action === 'list') { var entries = ctx.files().list(scope, op.path || '.'); result = { entries: entries.slice(0, 200), truncated: entries.length > 200 }; }
|
|
57
|
+
else { result = ctx.files().read(scope, op.path); if (result.content && result.content.length > 65536) result = { error: 'Canonical text reads are limited to 64 KB.', size: result.size }; }
|
|
58
|
+
} else if (op.domain === 'git') {
|
|
59
|
+
var status = repository(scope);
|
|
60
|
+
if (op.action === 'status') result = gitProjection(status);
|
|
61
|
+
else { var working = bounded(op.path, true); if (fs.existsSync(working)) ctx.files().read(scope, op.path); result = git.getFileDiff(ctx.cwd, op.path, scope.identity); if ((result.oldContent.length + result.newContent.length) > 65536) throw new Error('Canonical diff reads are limited to 64 KB.'); }
|
|
62
|
+
} else if (op.action === 'list') result = { terminals: ctx.tm.list().filter(function (item) { try { terminal(scope, item.id); return true; } catch (error) { return false; } }).map(function (item) { return ctx.tm.inspect(item.id); }) };
|
|
63
|
+
else { var item = terminal(scope, op.id); result = Object.assign({}, item, ctx.tm.getScrollback(item.id)); }
|
|
64
|
+
if (rootIdentity(principal, op.domain) !== before) throw new Error('Project or OS identity changed during the read.');
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
function snapshot(principal, op) {
|
|
68
|
+
var scope = authorize(principal, op.domain);
|
|
69
|
+
if (op.domain === 'files') { bounded(op.path); return ctx.files().snapshot(scope, op.path); }
|
|
70
|
+
if (op.domain === 'git') {
|
|
71
|
+
var status = repository(scope);
|
|
72
|
+
if (!op.paths.length || !op.paths.every(function (name) { return status.files.some(function (item) { return item.path === name; }); })) throw new Error('Select exact currently changed Git paths.');
|
|
73
|
+
var entries = op.paths.map(function (name) {
|
|
74
|
+
var working = bounded(name, true);
|
|
75
|
+
if (fs.existsSync(working) && !fs.lstatSync(working).isFile()) throw new Error('Canonical staging requires individual regular file paths; recursive directory staging is unavailable.');
|
|
76
|
+
if (fs.existsSync(working)) ctx.files().read(scope, name);
|
|
77
|
+
var item = status.files.find(function (file) { return file.path === name; });
|
|
78
|
+
if (item.originalPath) bounded(item.originalPath, true);
|
|
79
|
+
var diff = git.getFileDiff(ctx.cwd, name, scope.identity);
|
|
80
|
+
if (diff.binary) throw new Error('Canonical staging of binary files is unavailable.');
|
|
81
|
+
return digest(diff);
|
|
82
|
+
});
|
|
83
|
+
var index = path.join(status.gitDir, 'index');
|
|
84
|
+
return { status: gitProjection(status), gitDirectory: [fs.realpathSync(status.gitDir), inode(status.gitDir)], index: fs.existsSync(index) ? digest(fs.readFileSync(index)) : null, files: entries,
|
|
85
|
+
configuration: digest(git.runGitSync(ctx.cwd, ['config', '--null', '--list', '--show-origin'], null, scope.identity)),
|
|
86
|
+
attributes: digest(git.runGitSync(ctx.cwd, ['check-attr', '-z', '--all', '--'].concat(op.paths), null, scope.identity)) };
|
|
87
|
+
}
|
|
88
|
+
return op.action === 'create' ? ctx.tm.list().map(function (item) { return ctx.tm.inspect(item.id); }) : terminal(scope, op.id);
|
|
89
|
+
}
|
|
90
|
+
async function apply(principal, op, expected, revalidate) {
|
|
91
|
+
if (busy) throw new Error('Another project file, Git or terminal change is applying. Inspect again after it settles.');
|
|
92
|
+
busy = true;
|
|
93
|
+
var attempted = false, result;
|
|
94
|
+
try {
|
|
95
|
+
var scope = authorize(principal, op.domain);
|
|
96
|
+
revalidate();
|
|
97
|
+
if (digest(snapshot(principal, op)) !== digest(expected)) throw new Error('The exact product revision changed.');
|
|
98
|
+
if (op.domain === 'files') return ctx.files().write(scope, op.path, op.content, { expected: expected, revalidate: function () { revalidate(); bounded(op.path); } });
|
|
99
|
+
attempted = true;
|
|
100
|
+
if (op.domain === 'git') {
|
|
101
|
+
await git.runAction(ctx.cwd, { action: op.action, paths: op.paths }, scope.identity);
|
|
102
|
+
result = { status: 'completed', runtimeEffect: 'index_updated', persisted: true };
|
|
103
|
+
} else if (op.action === 'create') {
|
|
104
|
+
var created = ctx.tm.create(op.cols, op.rows, scope.identity, null, { kind: 'shell', ownerId: scope.ownerId });
|
|
105
|
+
if (!created) throw new Error('The terminal transport is unavailable or its limit was reached.');
|
|
106
|
+
result = { status: 'completed', runtimeEffect: 'shell_created', terminalId: created.id, terminalIdentity: created.identity, commandSent: false, persisted: false };
|
|
107
|
+
} else if (op.action === 'rename') {
|
|
108
|
+
ctx.tm.rename(op.id, op.title); result = { status: 'completed', runtimeEffect: 'renamed', terminalId: op.id, terminalIdentity: expected.identity, persisted: false };
|
|
109
|
+
} else if (op.action === 'resize') {
|
|
110
|
+
ctx.tm.resizeExact(op.id, op.cols, op.rows, expected.identity, revalidate);
|
|
111
|
+
result = { status: 'completed', runtimeEffect: 'resized', terminalId: op.id, terminalIdentity: expected.identity, persisted: false };
|
|
112
|
+
} else {
|
|
113
|
+
await ctx.tm.closeAndWait(op.id, { identity: expected.identity, revalidate: function () { revalidate(); var live = terminal(authorize(principal, 'terminal'), op.id); if (live.identity !== expected.identity) throw new Error('Terminal identity changed during closure.'); } });
|
|
114
|
+
result = { status: 'completed', runtimeEffect: 'closed', terminalId: op.id, terminalIdentity: expected.identity, exitAcknowledged: true, persisted: false };
|
|
115
|
+
}
|
|
116
|
+
revalidate();
|
|
117
|
+
if (op.domain === 'terminal') ctx.publishTerminals();
|
|
118
|
+
return result;
|
|
119
|
+
} catch (error) {
|
|
120
|
+
error.productControlResult = Object.assign({ persisted: attempted ? 'unknown' : false, runtimeEffect: attempted ? 'unknown' : 'none', retrySafe: false }, result, error.productControlResult || {}, { status: 'failed', error: error.message });
|
|
121
|
+
throw error;
|
|
122
|
+
} finally { busy = false; }
|
|
123
|
+
}
|
|
124
|
+
return { rootIdentity: rootIdentity, read: read, snapshot: snapshot, apply: apply };
|
|
125
|
+
}
|
|
126
|
+
module.exports = { attachWorkspaceOperations: attachWorkspaceOperations };
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
var workspaceMcp = require("./workspace-query-mcp-server");
|
|
2
|
+
var productTools = require("./clay-product-control-tools");
|
|
2
3
|
var clayHistoryMcp = require("./clay-history-mcp-server");
|
|
3
4
|
|
|
4
5
|
function attachProjectWorkspaceQuery(ctx) {
|
|
@@ -25,7 +26,12 @@ function attachProjectWorkspaceQuery(ctx) {
|
|
|
25
26
|
function getToolDefs(session) {
|
|
26
27
|
var bound = session ? binding(session) : null;
|
|
27
28
|
if (session && !bound) return [];
|
|
28
|
-
|
|
29
|
+
var tools = workspaceMcp.getToolDefs(bound, !!(bound && bound.isClay));
|
|
30
|
+
if (bound && bound.isClay && ctx.controlService) {
|
|
31
|
+
var controls = ctx.controlService.bind({ projectSlug: projectSlug, projectOwnerId: getProjectOwnerId(), isMate: true, mateId: mateId, session: session });
|
|
32
|
+
if (controls) tools = tools.concat(productTools.getToolDefs(controls));
|
|
33
|
+
}
|
|
34
|
+
return tools;
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
function createMcpServer(adapter, session) {
|
|
@@ -34,7 +40,8 @@ function attachProjectWorkspaceQuery(ctx) {
|
|
|
34
40
|
if (session && !bound) return null;
|
|
35
41
|
var projectBound = binding(null);
|
|
36
42
|
if (!projectBound) return null;
|
|
37
|
-
|
|
43
|
+
if (!adapter || typeof adapter.createToolServer !== "function") return null;
|
|
44
|
+
return adapter.createToolServer({ name: "clay-workspace", version: "2.0.0", tools: session ? getToolDefs(session) : workspaceMcp.getToolDefs(null, projectBound.isClay) });
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
function createHistoryMcpServer(adapter, session) {
|
package/lib/project.js
CHANGED
|
@@ -46,6 +46,8 @@ var { attachShellCommand } = require("./project-shell-command");
|
|
|
46
46
|
var { attachVendorLogin } = require("./project-vendor-login");
|
|
47
47
|
var { attachConnection } = require("./project-connection");
|
|
48
48
|
var { attachProjectScheduledMessages } = require("./project-scheduled-messages");
|
|
49
|
+
var { attachMateScheduling } = require("./mate-scheduling");
|
|
50
|
+
var { attachMateScheduleLifecycle } = require("./mate-schedule-lifecycle");
|
|
49
51
|
var { attachMcp } = require("./project-mcp");
|
|
50
52
|
var { createSessionSkillCatalog } = require("./yoke/session-skill-catalog");
|
|
51
53
|
var { attachEmail: attachEmailModule } = require("./project-email");
|
|
@@ -229,6 +231,8 @@ function createProjectContext(opts) {
|
|
|
229
231
|
var getScheduleCount = opts.getScheduleCount || function () { return 0; };
|
|
230
232
|
var notifyProcessingChanged = opts.onProcessingChanged || function () {};
|
|
231
233
|
var _scheduledMessages = null;
|
|
234
|
+
var _mateScheduling = null;
|
|
235
|
+
var _mateLifecycle = null;
|
|
232
236
|
var _scheduledTasks = null;
|
|
233
237
|
var _scheduledTaskExecution = null;
|
|
234
238
|
function onProcessingChanged() {
|
|
@@ -819,6 +823,7 @@ function createProjectContext(opts) {
|
|
|
819
823
|
});
|
|
820
824
|
var _workspaceQuery = attachProjectWorkspaceQuery({
|
|
821
825
|
service: opts.workspaceQueryService || null,
|
|
826
|
+
controlService: opts.clayProductControl || null,
|
|
822
827
|
sm: sm,
|
|
823
828
|
projectSlug: slug,
|
|
824
829
|
getProjectOwnerId: function () { return projectOwnerId; },
|
|
@@ -1047,6 +1052,7 @@ function createProjectContext(opts) {
|
|
|
1047
1052
|
persistSession: function (session) { return sm.saveSessionFile(session); },
|
|
1048
1053
|
getVendorModelCatalog: function (ws, vendor) { return _models.getVendorCatalog(ws, vendor); },
|
|
1049
1054
|
createReadyMate: opts.createReadyMate,
|
|
1055
|
+
isLive: function (session) { return sm.sessions.get(session.localId) === session; },
|
|
1050
1056
|
});
|
|
1051
1057
|
var _askUser = attachAskUser({
|
|
1052
1058
|
record: function (session, event) { sm.sendAndRecord(session, event); },
|
|
@@ -1405,6 +1411,7 @@ function createProjectContext(opts) {
|
|
|
1405
1411
|
|
|
1406
1412
|
// --- SDK bridge ---
|
|
1407
1413
|
sdk = createSDKBridge({
|
|
1414
|
+
isLifecycleExecutionPaused: function () { return !!(opts.getProjectAccess && opts.getProjectAccess().lifecycleExecutionPaused); },
|
|
1408
1415
|
cwd: cwd,
|
|
1409
1416
|
slug: slug,
|
|
1410
1417
|
sessionManager: sm,
|
|
@@ -1480,6 +1487,7 @@ function createProjectContext(opts) {
|
|
|
1480
1487
|
_sessionGithub.getSystemPrompt(session),
|
|
1481
1488
|
_sessionLinear.getSystemPrompt(session),
|
|
1482
1489
|
_scheduledTasks ? _scheduledTasks.getSystemPrompt(session) : "",
|
|
1490
|
+
_mateScheduling ? _mateScheduling.getSystemPrompt(session) : "",
|
|
1483
1491
|
_mateKnowledge.getSystemPrompt(session),
|
|
1484
1492
|
_userKnowledge.getSystemPrompt(session),
|
|
1485
1493
|
_autonomousRun ? _autonomousRun.getSystemPrompt(session) : "",
|
|
@@ -1509,6 +1517,7 @@ function createProjectContext(opts) {
|
|
|
1509
1517
|
.concat(opts.userKnowledgeService ? _userKnowledge.getDynamicToolDefs(session) : [])
|
|
1510
1518
|
.concat(_loopInterview ? _loopInterview.getToolDefs(session) : [])
|
|
1511
1519
|
.concat(_scheduledTasks ? _scheduledTasks.getToolDefs(session) : [])
|
|
1520
|
+
.concat(_mateScheduling ? _mateScheduling.getToolDefs(session) : [])
|
|
1512
1521
|
.concat(_scheduledTaskExecution ? _scheduledTaskExecution.getToolDefs(session) : [])
|
|
1513
1522
|
.concat(_autonomousRun ? _autonomousRun.getToolDefs(session) : []);
|
|
1514
1523
|
},
|
|
@@ -1564,6 +1573,8 @@ function createProjectContext(opts) {
|
|
|
1564
1573
|
|
|
1565
1574
|
_scheduledMessages = attachProjectScheduledMessages({
|
|
1566
1575
|
scheduler: opts.durableScheduler,
|
|
1576
|
+
mateLifecycle: function () { return _mateLifecycle; },
|
|
1577
|
+
deferRehydrate: isMate,
|
|
1567
1578
|
slug: slug,
|
|
1568
1579
|
isMate: isMate,
|
|
1569
1580
|
osUsers: osUsers,
|
|
@@ -1586,6 +1597,32 @@ function createProjectContext(opts) {
|
|
|
1586
1597
|
consumePendingMessage: function (session) { return _userMessage && _userMessage.consumePendingMessage(session); },
|
|
1587
1598
|
});
|
|
1588
1599
|
|
|
1600
|
+
var _schedulingAccess = require('./project-scheduling-access').attachSchedulingAccess({ osUsers: osUsers, getLinuxUser: getLinuxUserForSession, resolveLinuxUser: getOsUserInfoForLinuxUser });
|
|
1601
|
+
_mateLifecycle = attachMateScheduleLifecycle({
|
|
1602
|
+
sm: sm, scheduler: opts.durableScheduler, slug: slug,
|
|
1603
|
+
owner: function (session) { return _mateScheduling.owner(session); },
|
|
1604
|
+
authorize: function (session, control) { return control ? _mateScheduling.authorizeControl(session, control) : _mateScheduling.authorize(session); },
|
|
1605
|
+
authorizeRuntime: function (session) { return _mateScheduling.authorizeLifecycle(session); },
|
|
1606
|
+
schedule: function () { return _scheduledMessages.schedule.apply(null, arguments); },
|
|
1607
|
+
queueItem: function (session, job) { return _scheduledMessages.queueItem(session, job); },
|
|
1608
|
+
cancelJob: function (session, job) {
|
|
1609
|
+
return _scheduledMessages.cancel(session, { id: job.ownerId }, null, "cancelled_by_mate", { jobId: job.id, revision: job.payload.revision });
|
|
1610
|
+
},
|
|
1611
|
+
});
|
|
1612
|
+
_mateScheduling = attachMateScheduling({
|
|
1613
|
+
isMate: isMate, isHostAgent: isHostAgent, projectOwnerId: projectOwnerId, slug: slug, mateId: opts.mateId,
|
|
1614
|
+
sm: sm, users: usersModule, lifecycle: _mateLifecycle,
|
|
1615
|
+
authorizeAccess: function (session, ownerId) { _schedulingAccess.ownerIdentity(session); return _scheduledMessages.authorize(session, ownerId); },
|
|
1616
|
+
userInput: function (session) { return _askUser.createHandler(session); },
|
|
1617
|
+
});
|
|
1618
|
+
if (isMate) {
|
|
1619
|
+
sm.sessions.forEach(function (session) {
|
|
1620
|
+
try { _mateLifecycle.reconcile(session); }
|
|
1621
|
+
catch (error) { console.error("[mate-schedules] Recovery failed:", error.message); }
|
|
1622
|
+
});
|
|
1623
|
+
_scheduledMessages.rehydrate();
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1589
1626
|
// --- Loop engine (delegated to project-loop.js) ---
|
|
1590
1627
|
// --- Notification center (global singleton from server.js) ---
|
|
1591
1628
|
var _notifications = opts.notificationsModule || null;
|
|
@@ -1667,6 +1704,7 @@ function createProjectContext(opts) {
|
|
|
1667
1704
|
cwd: cwd,
|
|
1668
1705
|
projectSlug: slug,
|
|
1669
1706
|
projectTitle: title || project,
|
|
1707
|
+
validateOwnerIdentity: _schedulingAccess.ownerIdentity,
|
|
1670
1708
|
isMate: isMate,
|
|
1671
1709
|
sm: sm,
|
|
1672
1710
|
sdk: sdk,
|
|
@@ -1765,8 +1803,8 @@ function createProjectContext(opts) {
|
|
|
1765
1803
|
}
|
|
1766
1804
|
|
|
1767
1805
|
// --- Schedule / cancel a message (used by WS handler and auto-continue) ---
|
|
1768
|
-
function scheduleMessage(session, text, resetsAt, actorUser, targetWs, attachments, requestId, transportAccountId) {
|
|
1769
|
-
return _scheduledMessages ? _scheduledMessages.schedule(session, text, resetsAt, actorUser, targetWs, attachments, requestId, transportAccountId) : null;
|
|
1806
|
+
function scheduleMessage(session, text, resetsAt, actorUser, targetWs, attachments, requestId, transportAccountId, options) {
|
|
1807
|
+
return _scheduledMessages ? _scheduledMessages.schedule(session, text, resetsAt, actorUser, targetWs, attachments, requestId, transportAccountId, options) : null;
|
|
1770
1808
|
}
|
|
1771
1809
|
|
|
1772
1810
|
function cancelScheduledMessage(session, actorUser, targetWs, correlation) {
|
|
@@ -1800,7 +1838,7 @@ function createProjectContext(opts) {
|
|
|
1800
1838
|
}
|
|
1801
1839
|
|
|
1802
1840
|
// --- DM messages (delegated to server-level handler) ---
|
|
1803
|
-
if (msg.type === "home_debate_question_response" || msg.type === "home_debate_control" || msg.type === "home_mate_creation_question_response" || msg.type === "default_ai_get" || msg.type === "default_ai_catalog_get" || msg.type === "default_ai_set" || msg.type === "default_vendor_get" || msg.type === "default_vendor_set" || msg.type === "context_view_get" || msg.type === "context_view_set" || msg.type === "cursor_sharing_get" || msg.type === "cursor_sharing_set") {
|
|
1841
|
+
if (msg.type === "home_mate_question_response" || msg.type === "clay_ui_ready" || msg.type === "clay_navigation_claim" || msg.type === "clay_navigation_ack" || msg.type === "home_debate_question_response" || msg.type === "home_debate_control" || msg.type === "home_mate_creation_question_response" || msg.type === "default_ai_get" || msg.type === "default_ai_catalog_get" || msg.type === "default_ai_set" || msg.type === "default_vendor_get" || msg.type === "default_vendor_set" || msg.type === "context_view_get" || msg.type === "context_view_set" || msg.type === "cursor_sharing_get" || msg.type === "cursor_sharing_set") {
|
|
1804
1842
|
if (typeof opts.onDmMessage === "function") opts.onDmMessage(ws, msg, slug);
|
|
1805
1843
|
return;
|
|
1806
1844
|
}
|
|
@@ -2096,6 +2134,8 @@ function createProjectContext(opts) {
|
|
|
2096
2134
|
});
|
|
2097
2135
|
sm.setSessionHydrator(_sessionSkillCatalog.hydrate);
|
|
2098
2136
|
var _sessions = attachSessions({
|
|
2137
|
+
adapters: adapters,
|
|
2138
|
+
isPendingQueuePaused: function (session) { return _pendingMessageQueue.isPaused(session); },
|
|
2099
2139
|
cwd: cwd,
|
|
2100
2140
|
slug: slug,
|
|
2101
2141
|
isMate: isMate,
|
|
@@ -2169,6 +2209,26 @@ function createProjectContext(opts) {
|
|
|
2169
2209
|
_notifications: _notifications,
|
|
2170
2210
|
_projectLogs: _projectLogs,
|
|
2171
2211
|
});
|
|
2212
|
+
var _productControl = require("./project-product-control").attachProjectProductControl({
|
|
2213
|
+
sm: sm, users: usersModule, clients: clients, slug: slug, isMate: isMate, osUsers: osUsers,
|
|
2214
|
+
getProjectAccess: opts.getProjectAccess || function () { return { visibility: "public", ownerId: projectOwnerId || null }; },
|
|
2215
|
+
userInput: function (session) { return _askUser.createHandler(session); },
|
|
2216
|
+
actions: _sessions.sessionActions, history: _sessions.sessionHistoryActions, selectModel: function (session, model, options) { return _models.selectSessionModel(session, model, null, options); },
|
|
2217
|
+
isDriverOperatedSession: function (session) { return _sessionPair.workerPermission.isDriverOperated(session); },
|
|
2218
|
+
deletePlan: _sessions.deleteProductControlPlan,
|
|
2219
|
+
queue: _pendingMessageQueue,
|
|
2220
|
+
requestQueueDrain: function (session) { if (_userMessage) _userMessage.consumePendingMessage(session); },
|
|
2221
|
+
vendor: _sessions.sessionVendorActions,
|
|
2222
|
+
runtime: _sessions.sessionRuntimeActions,
|
|
2223
|
+
});
|
|
2224
|
+
var _mateChatControl = require('./project-mate-chat-control').attachMateChatControl({
|
|
2225
|
+
sm: sm, sdk: sdk, isMate: isMate, osUsers: osUsers, getLinuxUser: getLinuxUserForSession,
|
|
2226
|
+
users: usersModule, getAccess: opts.getProjectAccess || function () { return { isMate: isMate, ownerId: projectOwnerId || null, visibility: 'private' }; },
|
|
2227
|
+
actions: _sessions.sessionActions, vendor: _sessions.sessionVendorActions,
|
|
2228
|
+
selectModel: function (session, model, options) { return _models.selectSessionModel(session, model, null, options); },
|
|
2229
|
+
});
|
|
2230
|
+
|
|
2231
|
+
|
|
2172
2232
|
|
|
2173
2233
|
var _models = attachModels({
|
|
2174
2234
|
cwd: cwd,
|
|
@@ -2215,6 +2275,7 @@ function createProjectContext(opts) {
|
|
|
2215
2275
|
});
|
|
2216
2276
|
var _delegatedFollowUp = attachDelegatedFollowUp({
|
|
2217
2277
|
sm: sm,
|
|
2278
|
+
isPendingQueuePaused: function (session) { return _pendingMessageQueue.isPaused(session); },
|
|
2218
2279
|
getSdk: function () { return sdk; },
|
|
2219
2280
|
getLinuxUserForSession: getLinuxUserForSession,
|
|
2220
2281
|
onProcessingChanged: onProcessingChanged,
|
|
@@ -2229,6 +2290,12 @@ function createProjectContext(opts) {
|
|
|
2229
2290
|
|
|
2230
2291
|
// --- User message handler (delegated to project-user-message.js) ---
|
|
2231
2292
|
var _userMessage = attachUserMessage({
|
|
2293
|
+
isLifecycleExecutionPaused: function () { return !!(opts.getProjectAccess && opts.getProjectAccess().lifecycleExecutionPaused); },
|
|
2294
|
+
resumeLifecycleProject: function (ws) {
|
|
2295
|
+
if (!opts.getProjectAccess || !opts.getProjectAccess().lifecycleExecutionPaused) return true;
|
|
2296
|
+
if (!clients.has(ws) || !ws._clayUser || !opts.resumeLifecycleProject) throw new Error('The current owner must explicitly resume recovered project execution.');
|
|
2297
|
+
return opts.resumeLifecycleProject(ws._clayUser.id);
|
|
2298
|
+
},
|
|
2232
2299
|
// Exact live pair state decides whether a session is Driver-operated; the
|
|
2233
2300
|
// handler never infers the role from the message.
|
|
2234
2301
|
isDriverOperatedSession: function (session) {
|
|
@@ -2351,6 +2418,13 @@ function createProjectContext(opts) {
|
|
|
2351
2418
|
FS_MAX_SIZE: FS_VIEWER_MAX_SIZE,
|
|
2352
2419
|
});
|
|
2353
2420
|
|
|
2421
|
+
var _workspaceOperations = require('./project-workspace-operations').attachWorkspaceOperations({
|
|
2422
|
+
cwd: cwd, isMate: isMate, osUsers: osUsers, users: usersModule, tm: tm,
|
|
2423
|
+
getAccess: opts.getProjectAccess || function () { return { ownerId: projectOwnerId || null, visibility: 'private' }; },
|
|
2424
|
+
files: function () { return _filesystem.productOperations; },
|
|
2425
|
+
publishTerminals: function () { send({ type: 'term_list', terminals: tm.list() }); },
|
|
2426
|
+
});
|
|
2427
|
+
|
|
2354
2428
|
// --- MCP bridge handler for Codex and session-bound Kiro tools ---
|
|
2355
2429
|
// Provides list_tools and call_tool operations over HTTP for mcp-bridge-server.js.
|
|
2356
2430
|
// The normal Codex bridge excludes local MCP servers it manages natively;
|
|
@@ -2780,6 +2854,7 @@ function createProjectContext(opts) {
|
|
|
2780
2854
|
return false;
|
|
2781
2855
|
}));
|
|
2782
2856
|
} catch (err) {
|
|
2857
|
+
shutdowns.push(Promise.resolve(false));
|
|
2783
2858
|
console.error("[project] " + vendor + " shutdown threw for " + slug + ":", err && err.message ? err.message : err);
|
|
2784
2859
|
}
|
|
2785
2860
|
});
|
|
@@ -2923,8 +2998,24 @@ function createProjectContext(opts) {
|
|
|
2923
2998
|
},
|
|
2924
2999
|
getYouLinuxUser: getLinuxUserForSession,
|
|
2925
3000
|
getStatus: getStatus,
|
|
3001
|
+
bindMateSchedule: function (session, intent, id) {
|
|
3002
|
+
return _mateLifecycle.create(session, require("./mate-schedule-input").normalize(intent), id);
|
|
3003
|
+
},
|
|
3004
|
+
cancelMateSchedule: function (session, record) { return _mateLifecycle.cancel(session, record); },
|
|
2926
3005
|
getSessionManager: function () { return sm; },
|
|
2927
3006
|
createDelegatedSession: _delegatedSession.createDelegatedSession,
|
|
3007
|
+
refreshEnvironmentRuntime: function () { return sdk.refreshEnvironmentRuntime(); },
|
|
3008
|
+
getWorkspaceProductOperations: function () { return isMate ? null : _workspaceOperations; },
|
|
3009
|
+
getMateChatControl: function () { return isMate ? _mateChatControl : null; },
|
|
3010
|
+
getMateScheduleControl: function () { return isMate ? _mateLifecycle : null; },
|
|
3011
|
+
getScheduledTaskControl: function () { return _scheduledTasks.productControl; },
|
|
3012
|
+
notifyIssueUpdate: _issues.notify,
|
|
3013
|
+
notifyLogUpdate: _projectLogs.broadcastUpdate,
|
|
3014
|
+
requestProductControlReview: _productControl.requestProductControlReview,
|
|
3015
|
+
applyProductSessionAction: _productControl.applyProductSessionAction,
|
|
3016
|
+
inspectProductSessionQueue: _productControl.inspectProductSessionQueue,
|
|
3017
|
+
inspectProductSessionRuntime: _sessions.sessionRuntimeActions.inspect,
|
|
3018
|
+
inspectMateLifecycleRuntime: _sessions.sessionRuntimeActions.inspectLifecycle,
|
|
2928
3019
|
inspectDelegatedFollowUp: _delegatedFollowUp.inspectDelegatedFollowUp,
|
|
2929
3020
|
dispatchDelegatedFollowUp: _delegatedFollowUp.dispatchDelegatedFollowUp,
|
|
2930
3021
|
deliverCapsuleTurn: _capsuleTurn.deliverCapsuleTurn,
|
|
@@ -2941,6 +3032,7 @@ function createProjectContext(opts) {
|
|
|
2941
3032
|
getMemoryState: _memory.readMemoryState,
|
|
2942
3033
|
listKnowledgeFiles: _knowledge.listKnowledgeFiles,
|
|
2943
3034
|
getKnowledgeDbService: function () { return _knowledge.databases; },
|
|
3035
|
+
getKnowledgeProductOperations: function () { return _knowledge.productOperations; },
|
|
2944
3036
|
refreshCapsuleCatalog: _capsuleCatalog.refreshSessions,
|
|
2945
3037
|
getNotificationsModule: function () { return _notifications; },
|
|
2946
3038
|
refreshVendorAuth: _vendorLogin.refreshVendorAuth,
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/* Keep internal activity in the established compact tool disclosure rows. */
|
|
2
|
+
.clay-tool-activity .tool-name {
|
|
3
|
+
flex-shrink: 1;
|
|
4
|
+
min-width: 0;
|
|
5
|
+
overflow: hidden;
|
|
6
|
+
text-overflow: ellipsis;
|
|
7
|
+
white-space: nowrap;
|
|
8
|
+
}
|
|
9
|
+
.clay-tool-activity .tool-desc {
|
|
10
|
+
font-family: var(--font-ui);
|
|
11
|
+
flex: 0 1 auto;
|
|
12
|
+
}
|
|
13
|
+
.clay-tool-activity .tool-status-icon { margin-left: auto; }
|
|
14
|
+
.clay-tool-activity .tool-subtitle { display: none; }
|
|
15
|
+
.tool-header[role="button"]:focus-visible,
|
|
16
|
+
.tool-group-header[role="button"]:focus-visible {
|
|
17
|
+
outline: 2px solid var(--accent);
|
|
18
|
+
outline-offset: 2px;
|
|
19
|
+
}
|
|
20
|
+
.clay-tool-activity[data-activity-phase="running"] .tool-bullet {
|
|
21
|
+
animation: pulse 1.5s ease-in-out infinite;
|
|
22
|
+
}
|
|
23
|
+
.clay-tool-activity[data-activity-phase="pending"] .tool-bullet,
|
|
24
|
+
.tool-group[data-activity-phase="pending"] .tool-group-bullet {
|
|
25
|
+
background: var(--text-muted);
|
|
26
|
+
animation: none;
|
|
27
|
+
}
|
|
28
|
+
.clay-tool-activity[data-activity-phase="error"] .tool-name,
|
|
29
|
+
.clay-tool-activity[data-activity-phase="cancelled"] .tool-name,
|
|
30
|
+
.tool-group[data-activity-phase="error"] .tool-group-label { color: var(--error); }
|
|
31
|
+
.clay-tool-technical-name,
|
|
32
|
+
.clay-tool-evidence-label {
|
|
33
|
+
padding: 8px 14px 0;
|
|
34
|
+
color: var(--text-muted);
|
|
35
|
+
font-size: 12px;
|
|
36
|
+
overflow-wrap: anywhere;
|
|
37
|
+
}
|
|
38
|
+
.clay-tool-technical-name { font-family: "Roboto Mono", monospace; }
|
|
39
|
+
.clay-tool-evidence-label { font-weight: 600; }
|
|
40
|
+
@media (prefers-reduced-motion: reduce) {
|
|
41
|
+
.clay-tool-activity .tool-bullet { animation: none; }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
@media (max-width: 600px) {
|
|
45
|
+
.clay-tool-activity .tool-name {
|
|
46
|
+
white-space: normal;
|
|
47
|
+
overflow-wrap: anywhere;
|
|
48
|
+
line-height: 1.4;
|
|
49
|
+
}
|
|
50
|
+
.clay-tool-activity .tool-desc { max-width: 35%; }
|
|
51
|
+
}
|
|
@@ -32,3 +32,8 @@
|
|
|
32
32
|
.home-mate-proposal-facts dd + dt { margin-top: 7px; }
|
|
33
33
|
.home-mate-proposal-actions button { flex: 1; }
|
|
34
34
|
}
|
|
35
|
+
|
|
36
|
+
.home-mate-proposal-schedule-details { margin-top: 2px; color: var(--text-dimmer); }
|
|
37
|
+
.home-mate-proposal-schedule-details summary { width: fit-content; padding-block: 3px; cursor: pointer; font-weight: 400; }
|
|
38
|
+
.home-mate-proposal-schedule-details summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
|
|
39
|
+
.home-mate-proposal-schedule-details[open] > div { padding-top: 3px; color: var(--text-muted); }
|
|
@@ -1360,8 +1360,10 @@ button.top-bar-pill.pill-accent:hover { background: color-mix(in srgb, var(--acc
|
|
|
1360
1360
|
.connect-wordmark {
|
|
1361
1361
|
display: block;
|
|
1362
1362
|
width: clamp(142px, 15vw, 184px);
|
|
1363
|
-
|
|
1364
|
-
|
|
1363
|
+
aspect-ratio: 4742 / 1156;
|
|
1364
|
+
background: var(--text-muted);
|
|
1365
|
+
-webkit-mask: url("../clay-studio-wordmark-bold.svg") center / contain no-repeat;
|
|
1366
|
+
mask: url("../clay-studio-wordmark-bold.svg") center / contain no-repeat;
|
|
1365
1367
|
}
|
|
1366
1368
|
|
|
1367
1369
|
#connect-overlay-msg {
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
.project-admin-overview { overflow: auto; padding: 8px; }
|
|
2
|
+
.project-admin-canvas { width: 100%; min-height: 100%; padding: 24px; border: 1px solid var(--border); border-radius: 8px; background: var(--bg); }
|
|
3
|
+
.project-admin-canvas h1 { font-size: 20px; margin: 0 0 16px; }
|
|
4
|
+
.project-admin-canvas h2 { font-size: 16px; }
|
|
5
|
+
.project-admin-canvas [role="tablist"] { display: flex; gap: 4px; border-bottom: 1px solid var(--border); padding-bottom: 8px; }
|
|
6
|
+
.project-admin-canvas [role="tab"] { height: 32px; padding: 0 12px; border: 1px solid var(--border); border-radius: 8px; color: inherit; background: transparent; cursor: pointer; }
|
|
7
|
+
.project-admin-canvas [aria-selected="true"] { background: #5857fc; color: white; }
|
|
8
|
+
.project-admin-canvas :focus-visible { outline: 2px solid #5857fc; outline-offset: 2px; }
|
|
9
|
+
.project-admin-canvas dl { display: grid; grid-template-columns: 110px 1fr; gap: 12px; margin: 24px 0; font-size: 13px; }
|
|
10
|
+
.project-admin-canvas dd { margin: 0; }
|
|
11
|
+
.project-admin-canvas p { max-width: 640px; line-height: 1.6; font-size: 14px; }
|
|
12
|
+
.project-admin-secondary { opacity: .65; }
|
|
13
|
+
@media (max-width: 600px) { .project-admin-canvas { padding: 16px; } }
|
|
@@ -158,6 +158,16 @@
|
|
|
158
158
|
.session-folder-viewmenu .session-folder-radio-dot { width: 12px; height: 12px; }
|
|
159
159
|
.session-folder-viewmenu .session-folder-radio-legend { margin: 2px 0 1px 6px; font-size: 10px; text-transform: uppercase; letter-spacing: 0.4px; }
|
|
160
160
|
.session-folder-viewmenu .session-folder-view-note { padding: 2px 6px 4px; font-size: 11px; line-height: 1.35; }
|
|
161
|
+
.session-folder-view-toggle {
|
|
162
|
+
display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; min-height: 30px; padding: 0 6px;
|
|
163
|
+
border: 0; border-radius: 6px; background: transparent; color: var(--text); font: inherit; font-size: 12.5px; text-align: left; cursor: pointer;
|
|
164
|
+
}
|
|
165
|
+
.session-folder-view-toggle:hover { background: rgba(var(--overlay-rgb), 0.08); }
|
|
166
|
+
.session-folder-view-toggle-track { position: relative; flex: none; width: 28px; height: 16px; border-radius: 999px; background: var(--text-dimmer); transition: background 0.15s ease; }
|
|
167
|
+
.session-folder-view-toggle-thumb { position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%; background: var(--sidebar-bg); transition: transform 0.15s ease; }
|
|
168
|
+
.session-folder-view-toggle[aria-checked="true"] .session-folder-view-toggle-track { background: var(--accent); }
|
|
169
|
+
.session-folder-view-toggle[aria-checked="true"] .session-folder-view-toggle-thumb { transform: translateX(12px); }
|
|
170
|
+
.session-folder-view-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
161
171
|
.session-folder-viewmenu .session-folder-radio-group.segmented {
|
|
162
172
|
display: flex; gap: 2px; padding: 2px; margin-top: 2px; border-radius: 8px; background: rgba(var(--overlay-rgb), 0.05);
|
|
163
173
|
}
|
|
@@ -167,6 +177,7 @@
|
|
|
167
177
|
.session-folder-viewmenu.is-mobile { padding: 6px; }
|
|
168
178
|
.session-folder-viewmenu.is-mobile .session-folder-radio { min-height: 40px; font-size: 14px; }
|
|
169
179
|
.session-folder-viewmenu.is-mobile .session-folder-radio-dot { width: 14px; height: 14px; }
|
|
180
|
+
.session-folder-viewmenu.is-mobile .session-folder-view-toggle { min-height: 40px; font-size: 14px; }
|
|
170
181
|
@media (pointer: coarse) {
|
|
171
182
|
.session-folder-viewmenu .session-folder-radio { min-height: 40px; font-size: 14px; }
|
|
172
183
|
}
|
|
@@ -168,6 +168,9 @@
|
|
|
168
168
|
.session-item:has(.session-work-column) > .session-work-links-slot { grid-column: 1 / 3; grid-row: 2; }
|
|
169
169
|
|
|
170
170
|
.github-kind-pr.github-state-closed .github-work-status svg { color: #f85149; }
|
|
171
|
+
.github-kind-pr.github-state-open .github-review-changes svg { color: var(--error); }
|
|
172
|
+
.github-kind-pr.github-state-open .github-review-requested svg { color: var(--warning); }
|
|
173
|
+
.github-kind-pr.github-state-open .github-review-approved svg { color: var(--success); }
|
|
171
174
|
@media (max-width: 600px) {
|
|
172
175
|
#header-github-links { flex: 0 0 auto; }
|
|
173
176
|
#header-github-links .session-github-link { display: none; }
|
|
@@ -514,6 +514,20 @@
|
|
|
514
514
|
background: color-mix(in srgb, var(--note-ink) 12%, transparent) !important;
|
|
515
515
|
border-radius: 4px;
|
|
516
516
|
}
|
|
517
|
+
.sticky-note-checklist-btn.active {
|
|
518
|
+
opacity: 1 !important;
|
|
519
|
+
background: color-mix(in srgb, var(--note-ink) 12%, transparent) !important;
|
|
520
|
+
border-radius: 4px;
|
|
521
|
+
}
|
|
522
|
+
.sticky-note-checklist-btn .lucide { width: 13px; height: 13px; }
|
|
523
|
+
.sticky-note-save-status {
|
|
524
|
+
color: var(--danger, #b42318);
|
|
525
|
+
font-size: 10px;
|
|
526
|
+
max-width: 90px;
|
|
527
|
+
overflow: hidden;
|
|
528
|
+
text-overflow: ellipsis;
|
|
529
|
+
white-space: nowrap;
|
|
530
|
+
}
|
|
517
531
|
|
|
518
532
|
.sn-fmt-bold { font-weight: 800; }
|
|
519
533
|
.sn-fmt-italic { font-style: italic; }
|
|
@@ -692,6 +706,7 @@
|
|
|
692
706
|
overflow-y: auto;
|
|
693
707
|
display: grid;
|
|
694
708
|
grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
|
|
709
|
+
grid-auto-rows: max-content;
|
|
695
710
|
gap: 10px;
|
|
696
711
|
padding: 12px 12px 28px;
|
|
697
712
|
align-content: start;
|
|
@@ -715,8 +730,23 @@
|
|
|
715
730
|
.notes-browser-card:hover { box-shadow: 0 2px 10px rgba(var(--shadow-rgb), 0.12); }
|
|
716
731
|
.notes-browser-card:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
717
732
|
/* A closed note is calm, not crossed out: still fully readable. */
|
|
718
|
-
.notes-browser-card.closed {
|
|
733
|
+
.notes-browser-card.closed {
|
|
734
|
+
min-height: 150px;
|
|
735
|
+
align-self: start;
|
|
736
|
+
opacity: 0.88;
|
|
737
|
+
cursor: default;
|
|
738
|
+
}
|
|
719
739
|
.notes-browser-card.closed:hover { box-shadow: none; }
|
|
740
|
+
.notes-browser-card.closed .notes-browser-card-header {
|
|
741
|
+
display: flex;
|
|
742
|
+
flex-wrap: wrap;
|
|
743
|
+
align-items: start;
|
|
744
|
+
}
|
|
745
|
+
.notes-browser-card.closed .notes-browser-card-title {
|
|
746
|
+
flex: 0 0 100%;
|
|
747
|
+
display: block;
|
|
748
|
+
overflow-wrap: anywhere;
|
|
749
|
+
}
|
|
720
750
|
|
|
721
751
|
.notes-browser-card-title { color: var(--note-ink); }
|
|
722
752
|
.notes-browser-card-body { color: var(--note-ink); }
|
|
@@ -791,6 +821,23 @@
|
|
|
791
821
|
-webkit-line-clamp: 5;
|
|
792
822
|
-webkit-box-orient: vertical;
|
|
793
823
|
}
|
|
824
|
+
.notes-browser-card.closed .notes-browser-card-body {
|
|
825
|
+
flex: 0 0 auto;
|
|
826
|
+
display: block;
|
|
827
|
+
min-width: 0;
|
|
828
|
+
overflow: visible;
|
|
829
|
+
overflow-wrap: anywhere;
|
|
830
|
+
word-break: break-word;
|
|
831
|
+
}
|
|
832
|
+
.notes-browser-card.closed .notes-browser-card-body a,
|
|
833
|
+
.notes-browser-card.closed .notes-browser-card-body code,
|
|
834
|
+
.notes-browser-card.closed .notes-browser-card-body pre {
|
|
835
|
+
overflow-wrap: anywhere;
|
|
836
|
+
word-break: break-word;
|
|
837
|
+
}
|
|
838
|
+
.notes-browser-card.closed .notes-browser-card-body pre {
|
|
839
|
+
white-space: pre-wrap;
|
|
840
|
+
}
|
|
794
841
|
.notes-browser-card-body code {
|
|
795
842
|
padding: 1px 3px;
|
|
796
843
|
border-radius: 3px;
|
|
@@ -879,3 +926,16 @@
|
|
|
879
926
|
.sticky-note.sticky-note-flight-away,
|
|
880
927
|
.sticky-note:not(.sticky-note-flight-away) { transition-duration: 0ms; }
|
|
881
928
|
}
|
|
929
|
+
|
|
930
|
+
/* Checklist inputs retain the card's compact reading rhythm. */
|
|
931
|
+
.sn-task-form { display: grid; gap: 6px; }
|
|
932
|
+
.sn-task-row { display: flex; align-items: center; gap: 6px; }
|
|
933
|
+
.sn-task-form input { min-width: 0; width: 100%; box-sizing: border-box; border: 1px solid transparent; border-radius: 4px; background: transparent; color: inherit; font: inherit; padding: 3px 4px; }
|
|
934
|
+
.sn-task-form input:hover, .sn-task-form input:focus { border-color: color-mix(in srgb, var(--note-ink) 25%, transparent); }
|
|
935
|
+
.sn-task-title { font-weight: 600 !important; }
|
|
936
|
+
.sn-task-form button { color: inherit; background: transparent; border: 0; font: inherit; cursor: pointer; }
|
|
937
|
+
.sn-task-form .sn-check { flex-shrink: 0; }
|
|
938
|
+
.sn-task-form .sn-task-save { justify-self: start; padding: 4px 8px; border: 1px solid color-mix(in srgb, var(--note-ink) 25%, transparent); border-radius: 4px; }
|
|
939
|
+
.sn-task-form button:focus-visible, .sn-task-form input:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
|
|
940
|
+
.sticky-note-header .sticky-note-md-btn[hidden] { display: none !important; }
|
|
941
|
+
.sn-task-remove .lucide { width: 13px; height: 13px; }
|
package/lib/public/index.html
CHANGED
|
@@ -332,7 +332,7 @@ body { background: transparent; color: inherit; }
|
|
|
332
332
|
<div id="connect-overlay">
|
|
333
333
|
<div class="connect-brand">
|
|
334
334
|
<img class="connect-symbol" src="clay-studio-symbol.png" alt="">
|
|
335
|
-
<
|
|
335
|
+
<span class="connect-wordmark" role="img" aria-label="Clay Studio"></span>
|
|
336
336
|
</div>
|
|
337
337
|
<p id="connect-overlay-msg">Connecting…</p>
|
|
338
338
|
</div>
|
|
@@ -16,7 +16,7 @@ export function loadProjectsTab(body) {
|
|
|
16
16
|
|
|
17
17
|
var accessPromises = projectList.map(function (p) {
|
|
18
18
|
return apiGet("/api/admin/projects/" + p.slug + "/access").then(function (access) {
|
|
19
|
-
return { slug: p.slug, title: p.title || p.project || p.slug, projectOwnerId: p.projectOwnerId || null, visibility: access.visibility || "public", allowedUsers: access.allowedUsers || [] };
|
|
19
|
+
return { slug: p.slug, title: p.title || p.project || p.slug, projectOwnerId: p.projectOwnerId || null, visibility: access.visibility || "public", allowedUsers: access.allowedUsers || [], readOnly: access.contentAccess === false || p.projectOwnerId !== store.get("myUserId") };
|
|
20
20
|
});
|
|
21
21
|
});
|
|
22
22
|
|
|
@@ -39,9 +39,13 @@ function renderProjectsTab(body, projectAccessList, allUsers) {
|
|
|
39
39
|
html += '<div class="admin-project-slug">' + escapeHtml(p.slug) + '</div>';
|
|
40
40
|
html += '</div>';
|
|
41
41
|
html += '<div class="admin-project-controls">';
|
|
42
|
+
if (p.readOnly) {
|
|
43
|
+
html += '<span class="admin-project-access-readonly">Administrative metadata only</span></div></div>';
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
42
46
|
// Owner select
|
|
43
47
|
html += '<select aria-label="Project owner" class="admin-owner-select" data-slug="' + escapeHtml(p.slug) + '">';
|
|
44
|
-
html += '<option value=""
|
|
48
|
+
if (!p.projectOwnerId) html += '<option value="" selected disabled>Ownership needs recovery</option>';
|
|
45
49
|
for (var u = 0; u < allUsers.length; u++) {
|
|
46
50
|
var user = allUsers[u];
|
|
47
51
|
var sel = p.projectOwnerId === user.id ? ' selected' : '';
|