clay-server 4.0.0-beta.2 → 4.0.0-beta.21
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/background-task-timing.js +105 -0
- package/lib/capsule-display-floor.js +51 -0
- package/lib/capsule-frame-server.js +255 -0
- package/lib/capsule-mate-turn.js +105 -0
- package/lib/capsule-pig-logic.js +300 -0
- package/lib/capsule-server-runtimes.js +39 -0
- package/lib/capsules/pig/display.js +198 -0
- package/lib/capsules/pig/manifest.json +9 -0
- package/lib/capsules/pig/ui.json +84 -0
- package/lib/config.js +25 -1
- package/lib/daemon-projects.js +13 -8
- package/lib/daemon.js +127 -14
- package/lib/git-cli.js +95 -1
- package/lib/knowledge-import.js +436 -0
- package/lib/knowledge-record-store.js +188 -0
- package/lib/knowledge-search.js +240 -0
- package/lib/mate-knowledge-mcp-server.js +151 -0
- package/lib/mate-knowledge-migration.js +283 -0
- package/lib/mate-knowledge-service.js +488 -0
- package/lib/mate-knowledge-sync.js +482 -0
- package/lib/migrate-single-user.js +2 -0
- package/lib/notes-lifecycle.js +149 -0
- package/lib/notes.js +60 -3
- package/lib/os-users.js +3 -1
- package/lib/project-capsule-catalog.js +9 -1
- package/lib/project-capsule-turn.js +96 -0
- package/lib/project-connection.js +14 -1
- package/lib/project-http.js +15 -1
- package/lib/project-knowledge.js +22 -0
- package/lib/project-log-context.js +172 -0
- package/lib/project-log-feedback-delivery.js +118 -0
- package/lib/project-logs-comments.js +137 -0
- package/lib/project-logs-context-state.js +42 -0
- package/lib/project-logs-mcp-server.js +328 -0
- package/lib/project-logs-query.js +185 -0
- package/lib/project-logs-root.js +75 -0
- package/lib/project-logs-schema.js +259 -0
- package/lib/project-logs-service.js +463 -0
- package/lib/project-logs-snapshot.js +205 -0
- package/lib/project-logs-store.js +495 -0
- package/lib/project-logs-versioning.js +132 -0
- package/lib/project-logs.js +400 -0
- package/lib/project-mate-interaction.js +14 -0
- package/lib/project-mate-knowledge.js +110 -0
- package/lib/project-memory.js +22 -0
- package/lib/project-message-delivery.js +70 -0
- package/lib/project-pair-lifecycle.js +460 -0
- package/lib/project-session-handoff.js +1 -1
- package/lib/project-session-notes.js +64 -13
- package/lib/project-session-pair.js +159 -146
- package/lib/project-session-spawn.js +1 -1
- package/lib/project-sessions.js +58 -13
- package/lib/project-user-message.js +53 -4
- package/lib/project-vendor-login.js +20 -3
- package/lib/project-worker-permission.js +430 -0
- package/lib/project-worker-proposal.js +208 -62
- package/lib/project.js +178 -6
- package/lib/public/app.js +58 -35
- package/lib/public/css/admin.css +2 -2
- package/lib/public/css/capsule-ui.css +38 -0
- package/lib/public/css/command-palette.css +19 -9
- package/lib/public/css/git-placard.css +154 -0
- package/lib/public/css/home-hub.css +4 -0
- package/lib/public/css/home-session-actions.css +66 -0
- package/lib/public/css/home-sidebar.css +66 -0
- package/lib/public/css/input.css +163 -28
- package/lib/public/css/mobile-nav.css +80 -0
- package/lib/public/css/notifications-center.css +67 -0
- package/lib/public/css/pane.css +52 -0
- package/lib/public/css/project-logs.css +198 -0
- package/lib/public/css/scheduler.css +1 -1
- package/lib/public/css/session-actions.css +16 -101
- package/lib/public/css/sidebar.css +103 -0
- package/lib/public/css/sticky-notes.css +237 -229
- package/lib/public/css/worker-proposal.css +34 -1
- package/lib/public/index.html +45 -11
- package/lib/public/modules/admin.js +8 -8
- package/lib/public/modules/app-connection.js +109 -4
- package/lib/public/modules/app-home-hub.js +19 -7
- package/lib/public/modules/app-message-cards.js +2 -1
- package/lib/public/modules/app-messages.js +84 -7
- package/lib/public/modules/app-misc.js +2 -14
- package/lib/public/modules/app-notifications.js +11 -7
- package/lib/public/modules/app-panels.js +46 -4
- package/lib/public/modules/app-projects.js +13 -8
- package/lib/public/modules/app-rendering.js +1 -0
- package/lib/public/modules/background-tasks-ui.js +139 -16
- package/lib/public/modules/capsule-preference.js +44 -0
- package/lib/public/modules/chat-bubble-renderer.js +1 -1
- package/lib/public/modules/clay-log-links.js +70 -0
- package/lib/public/modules/command-palette.js +52 -15
- package/lib/public/modules/context-sources.js +0 -3
- package/lib/public/modules/git-agent-sessions.js +85 -0
- package/lib/public/modules/git-panel.js +111 -84
- package/lib/public/modules/git-placard.js +161 -0
- package/lib/public/modules/home-chat-empty-state.js +39 -0
- package/lib/public/modules/home-chat-identity.js +9 -0
- package/lib/public/modules/home-conversations-sheet.js +102 -48
- package/lib/public/modules/home-mate-chat.js +15 -35
- package/lib/public/modules/home-session-actions.js +6 -0
- package/lib/public/modules/home-sidebar-chat-list.js +101 -41
- package/lib/public/modules/home-sidebar.js +7 -0
- package/lib/public/modules/home-tool-frame.js +182 -0
- package/lib/public/modules/home-tools.js +138 -1
- package/lib/public/modules/input.js +38 -7
- package/lib/public/modules/markdown.js +2 -0
- package/lib/public/modules/mate-sidebar.js +0 -8
- package/lib/public/modules/message-delivery.js +97 -0
- package/lib/public/modules/paste-modal.js +84 -0
- package/lib/public/modules/project-logs-render.js +405 -0
- package/lib/public/modules/project-logs.js +461 -0
- package/lib/public/modules/project-removal-target.js +26 -0
- package/lib/public/modules/scheduler.js +17 -3
- package/lib/public/modules/session-actions.js +29 -105
- package/lib/public/modules/session-hierarchy.js +54 -0
- package/lib/public/modules/sidebar-mobile.js +40 -15
- package/lib/public/modules/sidebar-session-hierarchy.js +214 -0
- package/lib/public/modules/sidebar-sessions.js +77 -11
- package/lib/public/modules/sidebar.js +17 -5
- package/lib/public/modules/split-group-helpers.js +8 -0
- package/lib/public/modules/split-view.js +5 -2
- package/lib/public/modules/sticky-note-markdown.js +5 -0
- package/lib/public/modules/sticky-notes-browser.js +326 -0
- package/lib/public/modules/sticky-notes-card.js +360 -0
- package/lib/public/modules/sticky-notes-editor.js +291 -0
- package/lib/public/modules/sticky-notes-shared.js +90 -0
- package/lib/public/modules/sticky-notes.js +170 -946
- package/lib/public/modules/tool-palette-order.js +143 -0
- package/lib/public/modules/tool-palette-overlays.js +159 -0
- package/lib/public/modules/tool-palette.js +21 -188
- package/lib/public/modules/tools.js +21 -1
- package/lib/public/modules/update-snooze.js +169 -0
- package/lib/public/modules/user-settings.js +4 -0
- package/lib/public/modules/worker-pane-lock.js +176 -0
- package/lib/public/modules/worker-proposal-state.js +16 -0
- package/lib/public/modules/worker-proposal.js +49 -12
- package/lib/public/style.css +3 -1
- package/lib/sdk-bridge.js +132 -46
- package/lib/sdk-message-processor.js +60 -14
- package/lib/server-experimental-settings.js +49 -0
- package/lib/server-global-ws.js +11 -0
- package/lib/server-home-chat-events.js +31 -1
- package/lib/server-home-chat.js +27 -4
- package/lib/server-home-clay-entry.js +1 -1
- package/lib/server-home-clay-session-links.js +34 -5
- package/lib/server-settings.js +5 -0
- package/lib/server-tools.js +127 -9
- package/lib/server.js +124 -18
- package/lib/session-driver-eligibility.js +38 -0
- package/lib/session-driver-orchestration.js +31 -0
- package/lib/session-hygiene.js +3 -0
- package/lib/session-notes-mcp-server.js +25 -5
- package/lib/session-pair-factory.js +247 -0
- package/lib/session-pair-mcp-server.js +46 -8
- package/lib/session-pair-prompts.js +79 -0
- package/lib/session-pair-turn-control.js +153 -0
- package/lib/session-provenance.js +119 -0
- package/lib/session-spawn-mcp-server.js +1 -1
- package/lib/session-split-groups.js +4 -1
- package/lib/session-title-policy.js +60 -0
- package/lib/session-visibility.js +39 -0
- package/lib/sessions.js +61 -11
- package/lib/tools-registry.js +89 -10
- package/lib/update-snooze.js +437 -0
- package/lib/users-experimental-preferences.js +30 -0
- package/lib/users-permissions.js +2 -2
- package/lib/users.js +22 -16
- package/lib/workspace-query-access.js +102 -0
- package/lib/workspace-query-service.js +24 -17
- package/lib/worktree.js +25 -38
- package/lib/ws-schema.js +38 -7
- package/lib/yoke/adapters/claude-worker.js +16 -6
- package/lib/yoke/adapters/claude.js +81 -4
- package/lib/yoke/adapters/codex.js +63 -15
- package/lib/yoke/codex-background-tasks.js +8 -2
- package/lib/yoke/vendor-registry.js +1 -1
- package/package.json +3 -3
package/lib/server-home-chat.js
CHANGED
|
@@ -9,6 +9,8 @@ var attachHomeMateCreation = require("./server-home-mate-creation").attachHomeMa
|
|
|
9
9
|
var attachHomeDebates = require("./server-home-debates").attachHomeDebates, attachHomeClayEntry = require("./server-home-clay-entry").attachHomeClayEntry;
|
|
10
10
|
var homeChatEvents = require("./server-home-chat-events");
|
|
11
11
|
var homeCapsuleCreation = require("./server-home-capsule-creation");
|
|
12
|
+
var sessionTitlePolicy = require("./session-title-policy");
|
|
13
|
+
var sessionProvenance = require("./session-provenance");
|
|
12
14
|
var historyToHomeChat = homeChatEvents.historyToHomeChat;
|
|
13
15
|
var transformEvent = homeChatEvents.transformEvent;
|
|
14
16
|
function attachHomeChat(deps) {
|
|
@@ -82,8 +84,18 @@ function attachHomeChat(deps) {
|
|
|
82
84
|
var sessionManager = found.ctx.getSessionManager();
|
|
83
85
|
if (!sessionManager) return [];
|
|
84
86
|
var result = [];
|
|
87
|
+
var visibleSessions = [];
|
|
85
88
|
sessionManager.sessions.forEach(function (session) {
|
|
86
89
|
if (!ownsSession(session, userId)) return;
|
|
90
|
+
visibleSessions.push(session);
|
|
91
|
+
});
|
|
92
|
+
var hierarchy = sessionProvenance.hierarchyFor(visibleSessions);
|
|
93
|
+
for (var vi = 0; vi < visibleSessions.length; vi++) {
|
|
94
|
+
var session = visibleSessions[vi];
|
|
95
|
+
if (sessionTitlePolicy.repairLegacySearchTitle(session)
|
|
96
|
+
&& typeof sessionManager.saveSessionFile === "function") {
|
|
97
|
+
sessionManager.saveSessionFile(session);
|
|
98
|
+
}
|
|
87
99
|
var cliSessionId = sessionString(session.cliSessionId, 512);
|
|
88
100
|
var createdAt = sessionTimestamp(session.createdAt);
|
|
89
101
|
var lastActivity = sessionTimestamp(session.lastActivity) || createdAt;
|
|
@@ -97,12 +109,16 @@ function attachHomeChat(deps) {
|
|
|
97
109
|
createdAt: createdAt,
|
|
98
110
|
lastActivity: lastActivity,
|
|
99
111
|
isProcessing: !!session.isProcessing,
|
|
112
|
+
sessionRole: hierarchy[session.localId].role,
|
|
113
|
+
parentSessionId: hierarchy[session.localId].parentSessionId == null ? null : sessionReference(sessionManager.sessions.get(hierarchy[session.localId].parentSessionId)),
|
|
114
|
+
parentAvailable: hierarchy[session.localId].parentAvailable,
|
|
115
|
+
workerGeneration: hierarchy[session.localId].generation,
|
|
100
116
|
});
|
|
101
117
|
if (session.debateSetupMode === true) result[result.length - 1].debatePlanning = true;
|
|
102
118
|
if (session.homeDebatePhase) result[result.length - 1].debatePhase = session.homeDebatePhase;
|
|
103
119
|
if (session.mateCreationMode === true) result[result.length - 1].mateCreation = true;
|
|
104
120
|
if (session.homeMateCreationPhase) result[result.length - 1].mateCreationPhase = session.homeMateCreationPhase;
|
|
105
|
-
}
|
|
121
|
+
}
|
|
106
122
|
result.sort(function (a, b) { return b.lastActivity - a.lastActivity; });
|
|
107
123
|
return result;
|
|
108
124
|
}
|
|
@@ -188,7 +204,12 @@ function attachHomeChat(deps) {
|
|
|
188
204
|
|
|
189
205
|
var homeModels = attachHomeModels({ users: users, mates: mates, projects: projects, sendMessage: sendMessage });
|
|
190
206
|
var homeDebates = attachHomeDebates({ mates: mates, findMateProject: findMateProject, ownsSession: ownsSession, sessionReference: sessionReference, sendMessage: sendMessage });
|
|
191
|
-
var homeClaySessionLinks = attachHomeClaySessionLinks({
|
|
207
|
+
var homeClaySessionLinks = attachHomeClaySessionLinks({
|
|
208
|
+
projects: projects,
|
|
209
|
+
workspaceQueryService: deps.workspaceQueryService,
|
|
210
|
+
projectLogsService: deps.projectLogsService,
|
|
211
|
+
sendMessage: sendMessage,
|
|
212
|
+
});
|
|
192
213
|
|
|
193
214
|
function sendHistory(ws, found, session, requestId) {
|
|
194
215
|
if (ws.readyState !== 1) return;
|
|
@@ -367,7 +388,7 @@ function attachHomeChat(deps) {
|
|
|
367
388
|
|
|
368
389
|
function handleMessage(ws, msg) {
|
|
369
390
|
if (!msg || typeof msg.type !== "string") return false;
|
|
370
|
-
if (msg.type !== "home_debates_list" && msg.type !== "home_clay_ask" && msg.type !== "home_clay_session_resolve" && msg.type !== "home_mate_present" && msg.type !== "home_mate_open" && msg.type !== "home_mate_sessions_list" && msg.type !== "home_mate_session_open" && msg.type !== "home_mate_send" && msg.type !== "home_mate_new_session" && msg.type !== "home_mate_debate_plan" && msg.type !== "home_debate_proposal_response" && msg.type !== "home_debate_question_response" && msg.type !== "home_debate_control" && msg.type !== "home_mate_creation_plan" && msg.type !== "home_mate_creation_question_response" && msg.type !== "home_mate_creation_proposal_response" && msg.type !== "home_mate_close" && msg.type !== "home_mate_memory_list" && msg.type !== "home_mate_knowledge_list" && msg.type !== "home_mate_models_get" && msg.type !== "home_mate_model_set") {
|
|
391
|
+
if (msg.type !== "home_debates_list" && msg.type !== "home_clay_ask" && msg.type !== "home_clay_session_resolve" && msg.type !== "home_clay_log_resolve" && msg.type !== "home_mate_present" && msg.type !== "home_mate_open" && msg.type !== "home_mate_sessions_list" && msg.type !== "home_mate_session_open" && msg.type !== "home_mate_send" && msg.type !== "home_mate_new_session" && msg.type !== "home_mate_debate_plan" && msg.type !== "home_debate_proposal_response" && msg.type !== "home_debate_question_response" && msg.type !== "home_debate_control" && msg.type !== "home_mate_creation_plan" && msg.type !== "home_mate_creation_question_response" && msg.type !== "home_mate_creation_proposal_response" && msg.type !== "home_mate_close" && msg.type !== "home_mate_memory_list" && msg.type !== "home_mate_knowledge_list" && msg.type !== "home_mate_models_get" && msg.type !== "home_mate_model_set") {
|
|
371
392
|
return false;
|
|
372
393
|
}
|
|
373
394
|
|
|
@@ -386,6 +407,7 @@ function attachHomeChat(deps) {
|
|
|
386
407
|
if (users.isMultiUser() && !userId) {
|
|
387
408
|
if (msg.type === "home_debates_list") sendMessage(ws, { type: "home_debates_state", requestId: msg.requestId || null, status: "error", debates: [], error: "Sign in to load your debates." });
|
|
388
409
|
else if (msg.type === "home_clay_session_resolve") sendMessage(ws, { type: "home_clay_session_target", requestId: msg.requestId || null, sessionRef: msg.sessionRef || null, status: "error", error: "Sign in to open this session." });
|
|
410
|
+
else if (msg.type === "home_clay_log_resolve") sendMessage(ws, { type: "home_clay_log_target", requestId: msg.requestId || null, ref: msg.ref || null, status: "error", error: "Sign in to open this log." });
|
|
389
411
|
else if (msg.type === "home_mate_models_get" || msg.type === "home_mate_model_set") homeModels.sendAccessError(ws, msg, "Not authenticated.");
|
|
390
412
|
else sendError(ws, msg.mateId || null, "Not authenticated.", msg.requestId || null, msg.sessionId || null);
|
|
391
413
|
return true;
|
|
@@ -394,6 +416,7 @@ function attachHomeChat(deps) {
|
|
|
394
416
|
if (homeDebates.handle(ws, userId, msg)) return true;
|
|
395
417
|
if (msg.type === "home_clay_ask") { homeClayEntry.start(ws, userId, msg); return true; }
|
|
396
418
|
if (msg.type === "home_clay_session_resolve") { homeClaySessionLinks.resolve(ws, msg); return true; }
|
|
419
|
+
if (msg.type === "home_clay_log_resolve") { homeClaySessionLinks.resolveLog(ws, msg); return true; }
|
|
397
420
|
|
|
398
421
|
if (msg.type === "home_mate_debate_plan") {
|
|
399
422
|
homeDebate.start(ws, userId, msg);
|
|
@@ -513,6 +536,6 @@ function attachHomeChat(deps) {
|
|
|
513
536
|
teardownTap(ws);
|
|
514
537
|
teardownTap(ws, "_searchClayTap");
|
|
515
538
|
}
|
|
516
|
-
return { handleMessage: handleMessage, handleDisconnection: handleDisconnection };
|
|
539
|
+
return { handleMessage: handleMessage, handleDisconnection: handleDisconnection, findMateProject: findMateProject };
|
|
517
540
|
}
|
|
518
541
|
module.exports = { attachHomeChat: attachHomeChat };
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
function attachHomeClayEntry(ctx) {
|
|
4
4
|
function providerPrompt(text) {
|
|
5
|
-
return "This conversation began in Clay Studio's global search. Respond as Clay. When the user is trying to locate
|
|
5
|
+
return "This conversation began in Clay Studio's global search. Respond as Clay. When the user is trying to locate a conversation, use search_workspace_history before any other investigation. When the user asks about a project's prior work, decisions, defects, status, rationale, or unfinished work, identify the project and search_project_logs before answering; use conversation history as additional evidence when useful. Search by meaning, related terms, and context, using at most three focused search passes. Do not use Bash, filesystem scans, or generic agents to search Clay history. Give the user useful findings promptly, and do not mention tools or internal search mechanics. Do not force a workspace or Logs search when the request is a general question or task.\n\nUser request:\n" + text;
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
function findExisting(manager, userId, requestId) {
|
|
@@ -1,13 +1,19 @@
|
|
|
1
|
-
// Resolve rendered Mate session references through an exact
|
|
1
|
+
// Resolve rendered Mate session and Project Log references through an exact
|
|
2
|
+
// owner-bound source session.
|
|
2
3
|
|
|
3
4
|
function attachHomeClaySessionLinks(ctx) {
|
|
4
|
-
function
|
|
5
|
+
function source(ws, msg) {
|
|
5
6
|
var tap = msg.surface === "search" ? ws._searchClayTap : ws._homeChatTap;
|
|
6
7
|
var sourceProject = tap && tap.mateSlug ? ctx.projects.get(tap.mateSlug) : null;
|
|
7
8
|
var sourceManager = sourceProject && sourceProject.getSessionManager ? sourceProject.getSessionManager() : null;
|
|
8
9
|
var sourceSession = sourceManager && tap ? sourceManager.sessions.get(tap.sessionId) : null;
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
return { tap: tap, project: sourceProject, session: sourceSession };
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function resolve(ws, msg) {
|
|
14
|
+
var selected = source(ws, msg);
|
|
15
|
+
var bound = ctx.workspaceQueryService && selected.session
|
|
16
|
+
? ctx.workspaceQueryService.bindProjectSession({ projectSlug: selected.tap.mateSlug, session: selected.session })
|
|
11
17
|
: null;
|
|
12
18
|
var payload = { type: "home_clay_session_target", requestId: msg.requestId || null, sessionRef: msg.sessionRef || null, status: "error" };
|
|
13
19
|
try {
|
|
@@ -20,7 +26,30 @@ function attachHomeClaySessionLinks(ctx) {
|
|
|
20
26
|
ctx.sendMessage(ws, payload);
|
|
21
27
|
}
|
|
22
28
|
|
|
23
|
-
|
|
29
|
+
function resolveLog(ws, msg) {
|
|
30
|
+
var selected = source(ws, msg);
|
|
31
|
+
var status = selected.project && selected.project.getStatus ? selected.project.getStatus() : null;
|
|
32
|
+
var bound = ctx.projectLogsService && selected.session && status
|
|
33
|
+
? ctx.projectLogsService.bindMate({
|
|
34
|
+
projectSlug: selected.tap.mateSlug,
|
|
35
|
+
projectOwnerId: status.projectOwnerId || null,
|
|
36
|
+
isMate: true,
|
|
37
|
+
mateId: selected.tap.mateId,
|
|
38
|
+
session: selected.session,
|
|
39
|
+
})
|
|
40
|
+
: null;
|
|
41
|
+
var payload = { type: "home_clay_log_target", requestId: msg.requestId || null, ref: msg.ref || null, status: "error" };
|
|
42
|
+
try {
|
|
43
|
+
if (!bound) throw new Error("The source conversation is no longer available.");
|
|
44
|
+
payload.target = bound.resolveLogNavigation({ ref: msg.ref });
|
|
45
|
+
payload.status = "ready";
|
|
46
|
+
} catch (error) {
|
|
47
|
+
payload.error = error.message || "Log not available.";
|
|
48
|
+
}
|
|
49
|
+
ctx.sendMessage(ws, payload);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
return { resolve: resolve, resolveLog: resolveLog };
|
|
24
53
|
}
|
|
25
54
|
|
|
26
55
|
module.exports = { attachHomeClaySessionLinks: attachHomeClaySessionLinks };
|
package/lib/server-settings.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
var fs = require("fs");
|
|
2
2
|
var path = require("path");
|
|
3
|
+
var { attachExperimentalSettings } = require("./server-experimental-settings");
|
|
3
4
|
|
|
4
5
|
function attachSettings(ctx) {
|
|
5
6
|
var users = ctx.users;
|
|
@@ -10,8 +11,10 @@ function attachSettings(ctx) {
|
|
|
10
11
|
var CONFIG_DIR = ctx.CONFIG_DIR;
|
|
11
12
|
|
|
12
13
|
var profilePath = path.join(CONFIG_DIR, "profile.json");
|
|
14
|
+
var experimentalSettings = attachExperimentalSettings(ctx);
|
|
13
15
|
|
|
14
16
|
function handleRequest(req, res, fullUrl) {
|
|
17
|
+
if (experimentalSettings.handleRequest(req, res, fullUrl)) return true;
|
|
15
18
|
// GET /api/profile
|
|
16
19
|
if (req.method === "GET" && fullUrl === "/api/profile") {
|
|
17
20
|
if (users.isMultiUser()) {
|
|
@@ -30,6 +33,7 @@ function attachSettings(ctx) {
|
|
|
30
33
|
profile.chatLayout = mu.chatLayout || "channel";
|
|
31
34
|
profile.mateOnboardingShown = !!mu.mateOnboardingShown;
|
|
32
35
|
profile.matesEnabled = mu.matesEnabled === true;
|
|
36
|
+
profile.capsulesEnabled = users.getCapsulesEnabled(mu.id);
|
|
33
37
|
try { profile.terminalFont = users.getTerminalFont(mu.id); } catch (e) {}
|
|
34
38
|
res.writeHead(200, { "Content-Type": "application/json" });
|
|
35
39
|
res.end(JSON.stringify(profile));
|
|
@@ -53,6 +57,7 @@ function attachSettings(ctx) {
|
|
|
53
57
|
profile.chatLayout = dc.chatLayout || "channel";
|
|
54
58
|
profile.mateOnboardingShown = !!dc.mateOnboardingShown;
|
|
55
59
|
profile.matesEnabled = dc.matesEnabled === true;
|
|
60
|
+
profile.capsulesEnabled = dc.capsulesEnabled === true;
|
|
56
61
|
if (dc.terminalFont && typeof dc.terminalFont === "object") {
|
|
57
62
|
profile.terminalFont = {
|
|
58
63
|
family: dc.terminalFont.family || "'SF Mono', Menlo, Monaco, 'Courier New', monospace",
|
package/lib/server-tools.js
CHANGED
|
@@ -2,14 +2,29 @@ var toolsRegistry = require("./tools-registry");
|
|
|
2
2
|
var toolStorage = require("./tool-storage");
|
|
3
3
|
var crypto = require("crypto");
|
|
4
4
|
var toolLlm = require("./tool-llm");
|
|
5
|
+
var capsuleFloor = require("./capsule-display-floor");
|
|
6
|
+
var capsuleServerRuntimes = require("./capsule-server-runtimes");
|
|
7
|
+
var capsuleFrameServer = require("./capsule-frame-server");
|
|
8
|
+
var capsuleMateTurn = require("./capsule-mate-turn");
|
|
5
9
|
|
|
6
10
|
function attachTools(deps) {
|
|
7
11
|
var users = deps.users;
|
|
8
12
|
var projects = deps.projects;
|
|
9
|
-
var
|
|
13
|
+
var serverRuntimes = deps.serverRuntimes || capsuleServerRuntimes;
|
|
14
|
+
var frameServer = deps.frameServer || capsuleFrameServer.createCapsuleFrameServer({ tlsOptions: deps.tlsOptions || null });
|
|
15
|
+
var mateTurn = deps.mateTurn || capsuleMateTurn.attachCapsuleMateTurn({
|
|
16
|
+
users: users,
|
|
17
|
+
findMateProject: deps.findMateProject || null,
|
|
18
|
+
broadcastToUser: function (targetUserId, payload) { broadcastToUser(targetUserId, payload); },
|
|
19
|
+
});
|
|
10
20
|
var controlTimeoutMs = deps.controlTimeoutMs || 15000;
|
|
11
21
|
var llmControlTimeoutMs = deps.llmControlTimeoutMs || 90000;
|
|
12
22
|
var pendingControl = Object.create(null);
|
|
23
|
+
var isCapsulesEnabled = deps.isCapsulesEnabled || function () { return true; };
|
|
24
|
+
|
|
25
|
+
function requireCapsulesEnabled(userId) {
|
|
26
|
+
if (!isCapsulesEnabled(userId)) throw new Error("Capsules are disabled in User Settings.");
|
|
27
|
+
}
|
|
13
28
|
|
|
14
29
|
function contextFor(userId) {
|
|
15
30
|
var multiUser = users.isMultiUser();
|
|
@@ -68,6 +83,7 @@ function attachTools(deps) {
|
|
|
68
83
|
}
|
|
69
84
|
|
|
70
85
|
function installedManifests(userId) {
|
|
86
|
+
if (!isCapsulesEnabled(userId)) return [];
|
|
71
87
|
var ctx = contextFor(userId);
|
|
72
88
|
return toolsRegistry.listTools(ctx).filter(function (manifest) { return !manifest.error; });
|
|
73
89
|
}
|
|
@@ -137,20 +153,52 @@ function attachTools(deps) {
|
|
|
137
153
|
});
|
|
138
154
|
}
|
|
139
155
|
|
|
156
|
+
// One act pipeline for server-runtime Capsules. The human's button click and
|
|
157
|
+
// the Mate's clay_tool_act call both arrive here, so the same Logic enforces
|
|
158
|
+
// the same rules for both and neither gets a private path. The seat is set
|
|
159
|
+
// from the server-resolved actor, never from caller-supplied text.
|
|
160
|
+
//
|
|
161
|
+
// The caller gets state and nothing else: a Mate never sees Display. Every
|
|
162
|
+
// successful act additionally pushes its causal {seq, actor, action,
|
|
163
|
+
// previous, next} event to the user's own connected clients, so a watching
|
|
164
|
+
// Display re-renders on any Logic change no matter who caused it.
|
|
165
|
+
async function controlServerCapsule(userId, installed, actor, callerId, kind, payload) {
|
|
166
|
+
var runtime = serverRuntimes.createRuntime(installed.manifest.id, contextFor(userId));
|
|
167
|
+
if (!runtime) throw new Error("No trusted server runtime is registered for this capsule.");
|
|
168
|
+
var context = { userId: userId, actor: actor, callerId: callerId };
|
|
169
|
+
if (kind === "snapshot") return runtime.snapshot(context);
|
|
170
|
+
if (kind === "act") {
|
|
171
|
+
var result = await runtime.act(context, payload.actionId, payload.args || {});
|
|
172
|
+
if (result && result.event) {
|
|
173
|
+
broadcastToUser(userId, { type: "tool_server_event", toolId: installed.manifest.id, event: result.event });
|
|
174
|
+
// The game round-trips: a Mate act names the opponent seat, and a
|
|
175
|
+
// human act that hands the turn over wakes that Mate to play it.
|
|
176
|
+
if (actor === "mate" && callerId) mateTurn.rememberOpponent(userId, installed.manifest.id, callerId);
|
|
177
|
+
mateTurn.maybeNudge(userId, installed.manifest, actor, result.event);
|
|
178
|
+
return result.state;
|
|
179
|
+
}
|
|
180
|
+
return result;
|
|
181
|
+
}
|
|
182
|
+
if (kind === "set" && runtime.set) return runtime.set(context, payload.controlId, payload.value);
|
|
183
|
+
throw new Error("The server-runtime capsule does not support '" + kind + "'.");
|
|
184
|
+
}
|
|
185
|
+
|
|
140
186
|
function controlForMate(userId, mateId, toolId, kind, payload) {
|
|
187
|
+
requireCapsulesEnabled(userId);
|
|
141
188
|
payload = payload || {};
|
|
142
189
|
var ctx = contextFor(userId);
|
|
143
190
|
toolsRegistry.listTools(ctx);
|
|
144
191
|
var installed = toolsRegistry.getTool(ctx, toolId);
|
|
145
192
|
if (!installed) return Promise.reject(new Error("Tool is not installed."));
|
|
193
|
+
// Skills are gated by Display: the Mate loses a Capsule at the same moment
|
|
194
|
+
// the human loses the declarative floor it is operated through.
|
|
195
|
+
if (!capsuleFloor.hasUsableFloor(installed.manifest, installed.uiTree)) {
|
|
196
|
+
return Promise.reject(new Error("This Capsule has no usable human Display, so it is unavailable to Mates."));
|
|
197
|
+
}
|
|
146
198
|
if (installed.manifest.runtime === "server") {
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
if (kind === "snapshot") return Promise.resolve(adapter.snapshot(context));
|
|
151
|
-
if (kind === "act") return Promise.resolve(adapter.act(context, payload.actionId, payload.args || {}));
|
|
152
|
-
if (kind === "set" && adapter.set) return Promise.resolve(adapter.set(context, payload.controlId, payload.value));
|
|
153
|
-
return Promise.reject(new Error("The server-runtime capsule does not support '" + kind + "'."));
|
|
199
|
+
return Promise.resolve().then(function () {
|
|
200
|
+
return controlServerCapsule(userId, installed, "mate", mateId, kind, payload);
|
|
201
|
+
});
|
|
154
202
|
}
|
|
155
203
|
var permissions = installed.manifest.permissions || [];
|
|
156
204
|
var timeoutMs = kind === "act" && permissions.indexOf("llm") !== -1 ? llmControlTimeoutMs : controlTimeoutMs;
|
|
@@ -211,6 +259,7 @@ function attachTools(deps) {
|
|
|
211
259
|
}
|
|
212
260
|
|
|
213
261
|
function installForMate(userId, input) {
|
|
262
|
+
requireCapsulesEnabled(userId);
|
|
214
263
|
var installed = toolsRegistry.installTool(contextFor(userId), input);
|
|
215
264
|
refreshMateCatalogs(userId);
|
|
216
265
|
broadcastToUser(userId, { type: "tool_installed", tool: installed });
|
|
@@ -218,6 +267,7 @@ function attachTools(deps) {
|
|
|
218
267
|
}
|
|
219
268
|
|
|
220
269
|
function sourceForMate(userId, toolId) {
|
|
270
|
+
requireCapsulesEnabled(userId);
|
|
221
271
|
var ctx = contextFor(userId);
|
|
222
272
|
var installed = toolsRegistry.getTool(ctx, toolId);
|
|
223
273
|
if (!installed) throw new Error("Tool not found.");
|
|
@@ -229,6 +279,7 @@ function attachTools(deps) {
|
|
|
229
279
|
}
|
|
230
280
|
|
|
231
281
|
function updateForMate(userId, toolId, input) {
|
|
282
|
+
requireCapsulesEnabled(userId);
|
|
232
283
|
var ctx = contextFor(userId);
|
|
233
284
|
var installed = toolsRegistry.getTool(ctx, toolId);
|
|
234
285
|
if (!installed) throw new Error("Tool not found.");
|
|
@@ -243,6 +294,7 @@ function attachTools(deps) {
|
|
|
243
294
|
}
|
|
244
295
|
|
|
245
296
|
function setMateAccess(userId, toolId, allowed, requestId) {
|
|
297
|
+
requireCapsulesEnabled(userId);
|
|
246
298
|
var ctx = contextFor(userId);
|
|
247
299
|
var tool = toolsRegistry.getTool(ctx, toolId);
|
|
248
300
|
if (!tool) throw new Error("Tool not found.");
|
|
@@ -255,6 +307,7 @@ function attachTools(deps) {
|
|
|
255
307
|
}
|
|
256
308
|
|
|
257
309
|
function removeForMate(userId, toolId) {
|
|
310
|
+
requireCapsulesEnabled(userId);
|
|
258
311
|
var removed = toolsRegistry.removeTool(contextFor(userId), toolId);
|
|
259
312
|
if (!removed) throw new Error("Tool not found.");
|
|
260
313
|
refreshMateCatalogs(userId);
|
|
@@ -262,8 +315,19 @@ function attachTools(deps) {
|
|
|
262
315
|
return { removed: true, toolId: toolId };
|
|
263
316
|
}
|
|
264
317
|
|
|
318
|
+
// The human half of the shared server-Capsule pipeline. Same Logic, same
|
|
319
|
+
// rules, no AI in the path.
|
|
320
|
+
function controlForUser(userId, toolId, kind, payload) {
|
|
321
|
+
requireCapsulesEnabled(userId);
|
|
322
|
+
var ctx = contextFor(userId);
|
|
323
|
+
var installed = toolsRegistry.getTool(ctx, toolId);
|
|
324
|
+
if (!installed) throw new Error("Tool is not installed.");
|
|
325
|
+
if (installed.manifest.runtime !== "server") throw new Error("This Capsule does not run on the server.");
|
|
326
|
+
return controlServerCapsule(userId, installed, "human", "user", kind, payload || {});
|
|
327
|
+
}
|
|
328
|
+
|
|
265
329
|
function handleMessage(ws, msg) {
|
|
266
|
-
var messageTypes = ["tools_list", "tool_get", "tool_install", "tool_remove", "tool_storage_op", "tool_llm_op", "tool_llm_config_get", "tool_control_response", "tool_source_get", "tool_mate_access_set"];
|
|
330
|
+
var messageTypes = ["tools_list", "tool_get", "tool_install", "tool_remove", "tool_storage_op", "tool_llm_op", "tool_llm_config_get", "tool_control_response", "tool_source_get", "tool_mate_access_set", "tool_server_control", "tool_frame_url"];
|
|
267
331
|
if (!msg || messageTypes.indexOf(msg.type) === -1) return false;
|
|
268
332
|
var userId;
|
|
269
333
|
if (users.isMultiUser()) {
|
|
@@ -279,6 +343,12 @@ function attachTools(deps) {
|
|
|
279
343
|
return true;
|
|
280
344
|
}
|
|
281
345
|
|
|
346
|
+
if (!isCapsulesEnabled(userId)) {
|
|
347
|
+
if (msg.type === "tools_list") send(ws, { type: "tools_state", tools: [] });
|
|
348
|
+
else sendError(ws, msg, new Error("Capsules are disabled in User Settings."));
|
|
349
|
+
return true;
|
|
350
|
+
}
|
|
351
|
+
|
|
282
352
|
if (msg.type === "tools_list") {
|
|
283
353
|
run(ws, msg, function () {
|
|
284
354
|
send(ws, { type: "tools_state", tools: fullToolList(ctx) });
|
|
@@ -312,6 +382,52 @@ function attachTools(deps) {
|
|
|
312
382
|
});
|
|
313
383
|
return true;
|
|
314
384
|
}
|
|
385
|
+
if (msg.type === "tool_frame_url") {
|
|
386
|
+
// The rich element renders on a separate anonymous origin, so the frame
|
|
387
|
+
// URL is minted here, on the authenticated socket, as a short-lived
|
|
388
|
+
// one-time token bound to this user's tools root. The rich element is
|
|
389
|
+
// additive only: it exists solely when the floor already validated at
|
|
390
|
+
// registration, and losing it never costs the human the floor.
|
|
391
|
+
Promise.resolve().then(function () {
|
|
392
|
+
var ctx = contextFor(userId);
|
|
393
|
+
var installed = toolsRegistry.getTool(ctx, msg.toolId);
|
|
394
|
+
if (!installed) throw new Error("Tool is not installed.");
|
|
395
|
+
if (!installed.hasRichDisplay) throw new Error("This Capsule ships no rich Display element.");
|
|
396
|
+
return frameServer.issueFrameUrl(ctx, msg.toolId);
|
|
397
|
+
}).then(function (frame) {
|
|
398
|
+
send(ws, { type: "tool_frame_url_state", toolId: msg.toolId, requestId: msg.requestId || null, ok: true, frame: frame });
|
|
399
|
+
}).catch(function (error) {
|
|
400
|
+
send(ws, {
|
|
401
|
+
type: "tool_frame_url_state",
|
|
402
|
+
toolId: msg.toolId,
|
|
403
|
+
requestId: msg.requestId || null,
|
|
404
|
+
ok: false,
|
|
405
|
+
error: error && error.message ? error.message : "The rich Display is unavailable.",
|
|
406
|
+
});
|
|
407
|
+
});
|
|
408
|
+
return true;
|
|
409
|
+
}
|
|
410
|
+
if (msg.type === "tool_server_control") {
|
|
411
|
+
Promise.resolve().then(function () {
|
|
412
|
+
return controlForUser(userId, msg.toolId, msg.kind, {
|
|
413
|
+
actionId: msg.actionId,
|
|
414
|
+
args: msg.args || {},
|
|
415
|
+
controlId: msg.controlId,
|
|
416
|
+
value: msg.value,
|
|
417
|
+
});
|
|
418
|
+
}).then(function (state) {
|
|
419
|
+
send(ws, { type: "tool_server_state", toolId: msg.toolId, requestId: msg.requestId || null, ok: true, state: state });
|
|
420
|
+
}).catch(function (error) {
|
|
421
|
+
send(ws, {
|
|
422
|
+
type: "tool_server_state",
|
|
423
|
+
toolId: msg.toolId,
|
|
424
|
+
requestId: msg.requestId || null,
|
|
425
|
+
ok: false,
|
|
426
|
+
error: error && error.message ? error.message : "The Capsule action failed.",
|
|
427
|
+
});
|
|
428
|
+
});
|
|
429
|
+
return true;
|
|
430
|
+
}
|
|
315
431
|
if (msg.type === "tool_install") {
|
|
316
432
|
run(ws, msg, function () {
|
|
317
433
|
installForMate(userId, {
|
|
@@ -366,11 +482,13 @@ function attachTools(deps) {
|
|
|
366
482
|
handleMessage: handleMessage,
|
|
367
483
|
installedManifests: installedManifests,
|
|
368
484
|
controlForMate: controlForMate,
|
|
485
|
+
controlForUser: controlForUser,
|
|
369
486
|
installForMate: installForMate,
|
|
370
487
|
sourceForMate: sourceForMate,
|
|
371
488
|
updateForMate: updateForMate,
|
|
372
489
|
setMateAccess: setMateAccess,
|
|
373
490
|
removeForMate: removeForMate,
|
|
491
|
+
refreshMateCatalogs: refreshMateCatalogs,
|
|
374
492
|
};
|
|
375
493
|
}
|
|
376
494
|
|