clay-server 5.2.0-beta.4 → 5.2.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +1 -239
- package/lib/claude-jsonl-watcher.js +1 -204
- 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-skill-install.js +1 -33
- 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/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- 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 +78 -276
- package/lib/default-ai-runtime.js +4 -8
- package/lib/email-mcp-server.js +1 -1
- package/lib/issues-service.js +29 -15
- package/lib/kiro-defaults.js +1 -22
- 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/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/notes-checklist.js +50 -0
- package/lib/notes.js +49 -3
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +24 -70
- package/lib/project-admin-overview.js +51 -0
- package/lib/project-ask-user.js +4 -2
- package/lib/project-connection.js +8 -7
- 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-http.js +4 -4
- 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-mcp.js +2 -2
- 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-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +4 -1
- 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 +167 -0
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +64 -0
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +55 -0
- package/lib/project-sessions.js +82 -267
- package/lib/project-user-message.js +31 -1
- package/lib/project-vendor-login.js +6 -36
- 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/app.js +1 -0
- package/lib/public/css/clay-tool-activity.css +51 -0
- package/lib/public/css/home-mate-creation.css +5 -0
- package/lib/public/css/messages.css +6 -2
- 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-favicon.js +11 -2
- 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 +1 -1
- package/lib/public/modules/app-messages.js +54 -8
- package/lib/public/modules/app-notifications.js +1 -2
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-projects.js +5 -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-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/mcp-connection-view.js +1 -1
- package/lib/public/modules/permission-control.js +5 -4
- 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/scheduler.js +2 -0
- package/lib/public/modules/session-create-form.js +10 -10
- 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/settings-defaults.js +27 -28
- 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 +36 -7
- package/lib/public/modules/tui-grab.js +1 -2
- 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/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/muse-avatar.svg +4 -0
- package/lib/public/style.css +2 -0
- package/lib/safe-bash-commands.js +3 -45
- package/lib/scheduled-task-records.js +3 -1
- package/lib/scheduler.js +2 -0
- package/lib/sdk-bridge.js +334 -403
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +52 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -0
- package/lib/sdk-worker.js +2 -677
- 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 +100 -30
- package/lib/session-config-transition.js +45 -0
- package/lib/session-folders.js +7 -2
- package/lib/session-fork-history.js +21 -0
- package/lib/session-github.js +9 -2
- 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/session-visibility.js +5 -2
- package/lib/sessions.js +27 -89
- package/lib/terminal-manager.js +52 -0
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/users-permissions.js +2 -8
- package/lib/users.js +4 -0
- package/lib/vendor-login-browser.js +1 -11
- package/lib/workspace-query-access.js +4 -1
- package/lib/ws-schema.js +8 -2
- 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 +54 -50
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +53 -111
- 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 +267 -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/claude-worker-startup.js +28 -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/codex-idle-recovery.js +81 -1
- package/lib/yoke/codex-recovery-admission.js +164 -0
- 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 +34 -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
|
@@ -118,6 +118,12 @@ function attachUserMessage(ctx) {
|
|
|
118
118
|
requestId: msg.requestId || null, ok: !!ok, error: error || null });
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
function noteUpdateResult(ws, msg, ok, note, error) {
|
|
122
|
+
if (!msg.requestId) return;
|
|
123
|
+
sendTo(ws, { type: "note_update_result", projectSlug: slug, noteId: msg.id || null,
|
|
124
|
+
requestId: msg.requestId, ok: !!ok, note: note || null, error: error || null });
|
|
125
|
+
}
|
|
126
|
+
|
|
121
127
|
// --------------- Main handler ---------------
|
|
122
128
|
|
|
123
129
|
function handleUserMessage(ws, msg, forcedSession, internal) {
|
|
@@ -155,7 +161,10 @@ function attachUserMessage(ctx) {
|
|
|
155
161
|
}
|
|
156
162
|
|
|
157
163
|
if (msg.type === "note_update") {
|
|
158
|
-
if (!msg.id)
|
|
164
|
+
if (!msg.id) {
|
|
165
|
+
noteUpdateResult(ws, msg, false, null, "Note id is required");
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
159
168
|
// A legacy `hidden` update is a lifecycle transition, so the actor is
|
|
160
169
|
// derived from the bound socket rather than taken from the message.
|
|
161
170
|
var noteChanges = msg;
|
|
@@ -166,10 +175,24 @@ function attachUserMessage(ctx) {
|
|
|
166
175
|
if (updated) {
|
|
167
176
|
send({ type: "note_updated", note: updated });
|
|
168
177
|
if (msg.text !== undefined || msg.hidden !== undefined) syncNotesKnowledge();
|
|
178
|
+
noteUpdateResult(ws, msg, true, updated, null);
|
|
179
|
+
} else {
|
|
180
|
+
noteUpdateResult(ws, msg, false, null, "Note update could not be saved");
|
|
169
181
|
}
|
|
170
182
|
return true;
|
|
171
183
|
}
|
|
172
184
|
|
|
185
|
+
if (msg.type === "note_close" && msg.humanClose === true) {
|
|
186
|
+
if (!canHumanManageNotes(ws)) { noteUpdateResult(ws, msg, false, null, "You are not authorized to close notes in this project."); return true; }
|
|
187
|
+
var result = nm.closeHuman(msg.id, msg, notesLifecycle.userActor(ws && ws._clayUser));
|
|
188
|
+
if (!result) { noteUpdateResult(ws, msg, false, null, "The note changed or could not be saved. Review it and retry."); return true; }
|
|
189
|
+
if (result.removed) send({ type: "note_deleted", id: result.removed.id });
|
|
190
|
+
else send({ type: "note_updated", note: result.note });
|
|
191
|
+
noteUpdateResult(ws, msg, true, result.note || null, null);
|
|
192
|
+
syncNotesKnowledge();
|
|
193
|
+
return true;
|
|
194
|
+
}
|
|
195
|
+
|
|
173
196
|
// Completing a note closes it. `note_delete` is the retired spelling and is
|
|
174
197
|
// deliberately routed to the same non-destructive close, so an older client
|
|
175
198
|
// still on the wire cannot erase a record.
|
|
@@ -443,6 +466,10 @@ function attachUserMessage(ctx) {
|
|
|
443
466
|
finishPendingEarly(false, "Pending messages cannot be delivered to a Driver-operated Split Worker");
|
|
444
467
|
return true;
|
|
445
468
|
}
|
|
469
|
+
if (!pendingInternal && ctx.resumeLifecycleProject) {
|
|
470
|
+
try { ctx.resumeLifecycleProject(ws); }
|
|
471
|
+
catch (error) { sendTo(ws, { type: "error", text: error.message }); return true; }
|
|
472
|
+
}
|
|
446
473
|
if (!pendingInternal && session._driverContinuationLease && pendingMessageQueue) {
|
|
447
474
|
var continuationQueued = pendingMessageQueue.admit(session, msg, ws && ws._clayUser);
|
|
448
475
|
if (continuationQueued.ok) {
|
|
@@ -461,6 +488,7 @@ function attachUserMessage(ctx) {
|
|
|
461
488
|
if (queuedResult.ok) {
|
|
462
489
|
messageDelivery.acknowledge(ws, deliveryReceipt, null, session);
|
|
463
490
|
sendTo(ws, { type: "message_queued", clientMessageId: msg.clientMessageId || null, sessionId: session.localId, projectSlug: slug, state: "pending", paused: pendingMessageQueue.isPaused(session), revision: pendingMessageQueue.getRevision() });
|
|
491
|
+
if (require('./sdk-foreground-admission').available(session)) setImmediate(function () { consumePendingMessage(session); });
|
|
464
492
|
} else {
|
|
465
493
|
sendTo(ws, { type: "error", text: queuedResult.error || "Unable to queue message" });
|
|
466
494
|
}
|
|
@@ -719,6 +747,7 @@ function attachUserMessage(ctx) {
|
|
|
719
747
|
|
|
720
748
|
function pendingBlockReason() {
|
|
721
749
|
if (!pendingInternal) return null;
|
|
750
|
+
if (ctx.isLifecycleExecutionPaused && ctx.isLifecycleExecutionPaused()) return "Pending message dispatch paused";
|
|
722
751
|
if (!pendingMessageQueue.isClaimed(session, pendingInternal.item.id)) return "Pending message was cancelled";
|
|
723
752
|
if (pendingMessageQueue.isPaused(session) || session._pendingMessageDrainPaused || session.taskStopRequested) return "Pending message dispatch paused";
|
|
724
753
|
if (typeof ctx.authorizePendingDispatch !== "function" || !ctx.authorizePendingDispatch(session, ws && ws._clayUser, pendingInternal.item)) return "Pending message authorization changed";
|
|
@@ -941,6 +970,7 @@ function attachUserMessage(ctx) {
|
|
|
941
970
|
}
|
|
942
971
|
|
|
943
972
|
function consumePendingMessage(session) {
|
|
973
|
+
if (ctx.isLifecycleExecutionPaused && ctx.isLifecycleExecutionPaused()) return false;
|
|
944
974
|
if (!pendingMessageQueue) return false;
|
|
945
975
|
var consumed = pendingMessageQueue.consumeOne(session, function (item, complete) {
|
|
946
976
|
var actor = item.actorId && usersModule.findUserById(item.actorId);
|
|
@@ -25,37 +25,11 @@ var yoke = require("./yoke");
|
|
|
25
25
|
// Login CLIs print a success line and then hand the shell back, so the PTY
|
|
26
26
|
// itself does not exit. Watch the output stream instead. Deliberately narrow:
|
|
27
27
|
// "Successfully logged out" and MCP-server logins must not match.
|
|
28
|
-
var LOGIN_SUCCESS_PATTERNS = [
|
|
29
|
-
/successfully logged in(?![ \t]+to[ \t]+mcp)/i,
|
|
30
|
-
/logged in successfully/i,
|
|
31
|
-
/login successful/i,
|
|
32
|
-
/successfully authenticated/i,
|
|
33
|
-
/authentication successful/i,
|
|
34
|
-
/signed in successfully/i,
|
|
35
|
-
/signed in with your [a-z ]+ account/i,
|
|
36
|
-
/you(?:'re| are) (?:now )?(?:logged|signed) in/i,
|
|
37
|
-
];
|
|
38
|
-
|
|
39
|
-
// Rolling window kept per flow so a success line split across PTY chunks is
|
|
40
|
-
// still matched, without buffering the whole login transcript.
|
|
41
28
|
var OUTPUT_TAIL_MAX = 4096;
|
|
42
|
-
|
|
43
|
-
// Grace period between detecting success and killing the terminal, so the user
|
|
44
|
-
// actually sees the confirmation line before the modal closes.
|
|
45
29
|
var CLOSE_AFTER_SUCCESS_MS = 2000;
|
|
46
|
-
|
|
47
|
-
var
|
|
48
|
-
|
|
49
|
-
function stripAnsi(text) {
|
|
50
|
-
return String(text).replace(ANSI_PATTERN, "");
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
function looksLikeLoginSuccess(text) {
|
|
54
|
-
for (var i = 0; i < LOGIN_SUCCESS_PATTERNS.length; i++) {
|
|
55
|
-
if (LOGIN_SUCCESS_PATTERNS[i].test(text)) return true;
|
|
56
|
-
}
|
|
57
|
-
return false;
|
|
58
|
-
}
|
|
30
|
+
var loginProtocol = require("./yoke/login-protocol");
|
|
31
|
+
var stripAnsi = loginProtocol.stripAnsi;
|
|
32
|
+
var looksLikeLoginSuccess = loginProtocol.successful;
|
|
59
33
|
|
|
60
34
|
/**
|
|
61
35
|
* ctx fields:
|
|
@@ -96,11 +70,7 @@ function attachVendorLogin(ctx) {
|
|
|
96
70
|
cancel: function (vendor) { closeFlowTerminal(discardFlow(vendor)); broadcastState(); },
|
|
97
71
|
});
|
|
98
72
|
|
|
99
|
-
function loginCommandFor(vendor) {
|
|
100
|
-
if (vendor === "claude") return "claude auth login";
|
|
101
|
-
var info = vendorRegistry.getVendorInfo(vendor);
|
|
102
|
-
return (info && info.loginCommand) || "claude login";
|
|
103
|
-
}
|
|
73
|
+
function loginCommandFor(vendor) { return loginProtocol.command(vendor, false); }
|
|
104
74
|
|
|
105
75
|
function vendorDisplayName(vendor) {
|
|
106
76
|
var info = vendorRegistry.getVendorInfo(vendor);
|
|
@@ -284,8 +254,8 @@ function attachVendorLogin(ctx) {
|
|
|
284
254
|
var key = flowKey(ws, vendor);
|
|
285
255
|
var identity = resolveLoginIdentity(ws, msg);
|
|
286
256
|
var command = loginCommandFor(vendor);
|
|
287
|
-
var browserMode = msg.mode === "browser" && (vendor
|
|
288
|
-
if (browserMode) command = vendor
|
|
257
|
+
var browserMode = msg.mode === "browser" && !!(vendorRegistry.getVendorInfo(vendor) || {}).browserLoginCommand;
|
|
258
|
+
if (browserMode) command = loginProtocol.command(vendor, true);
|
|
289
259
|
var flow = {
|
|
290
260
|
vendor: vendor,
|
|
291
261
|
key: key,
|
|
@@ -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,
|
package/lib/public/app.js
CHANGED
|
@@ -583,6 +583,7 @@ import { initDebate, handleDebatePreparing, handleDebateStarted, handleDebateRes
|
|
|
583
583
|
// rendering
|
|
584
584
|
currentMsgTs: null,
|
|
585
585
|
richContextUsage: null,
|
|
586
|
+
contextMeasurement: "unavailable",
|
|
586
587
|
currentMsgEl: null,
|
|
587
588
|
currentFullText: "",
|
|
588
589
|
matePreThinkingEl: null,
|
|
@@ -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); }
|
|
@@ -675,7 +675,8 @@ pre.mermaid-error {
|
|
|
675
675
|
|
|
676
676
|
.thinking-live .thinking-chevron { display: none; }
|
|
677
677
|
|
|
678
|
-
.thinking-live .thinking-label
|
|
678
|
+
.thinking-live .thinking-label,
|
|
679
|
+
.activity-inline .activity-text.activity-state-label {
|
|
679
680
|
background: linear-gradient(100deg, var(--text-muted) 40%, var(--text-dimmer) 50%, var(--text-muted) 60%);
|
|
680
681
|
background-size: 300% 100%;
|
|
681
682
|
background-clip: text;
|
|
@@ -690,7 +691,10 @@ pre.mermaid-error {
|
|
|
690
691
|
}
|
|
691
692
|
|
|
692
693
|
@media (prefers-reduced-motion: reduce) {
|
|
693
|
-
.thinking-live .thinking-label
|
|
694
|
+
.thinking-live .thinking-label,
|
|
695
|
+
.activity-inline .activity-text.activity-state-label {
|
|
696
|
+
animation: none; background: none; color: var(--text-muted); -webkit-text-fill-color: currentColor;
|
|
697
|
+
}
|
|
694
698
|
}
|
|
695
699
|
|
|
696
700
|
.thinking-header[aria-disabled="true"] { cursor: default; }
|
|
@@ -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 {
|