clay-server 5.3.0-beta.5 → 5.3.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/clay-product-control-tools.js +2 -2
- package/lib/clay-product-control.js +1 -1
- package/lib/clay-screen-controls.js +1 -0
- package/lib/clay-split-view-awareness.js +5 -2
- package/lib/clay-split-view-controls.js +14 -7
- package/lib/clay-split-view-input.js +5 -4
- package/lib/clay-split-view-render-proof.js +14 -4
- package/lib/clay-view-workspace-input.js +71 -0
- package/lib/project-admin-overview.js +22 -11
- package/lib/project-background-tasks.js +76 -0
- package/lib/project-connection.js +1 -1
- package/lib/project-file-history.js +11 -9
- package/lib/project-file-watch.js +26 -7
- package/lib/project-filesystem.js +5 -4
- package/lib/project-sessions.js +12 -5
- package/lib/project-shared-browser.js +7 -3
- package/lib/project-split-view-document.js +16 -2
- package/lib/project-split-views.js +27 -297
- package/lib/project-user-message.js +4 -3
- package/lib/project-vendor-login.js +2 -2
- package/lib/project-view-input.js +11 -0
- package/lib/project-view-pair.js +26 -0
- package/lib/project-view-store.js +348 -0
- package/lib/project-view-terminal-binding.js +16 -0
- package/lib/project-view-workspace.js +126 -0
- package/lib/project-worker-conversation.js +5 -0
- package/lib/project.js +1 -0
- package/lib/public/app.js +9 -0
- package/lib/public/css/contextual-clay.css +2 -1
- package/lib/public/css/issues.css +3 -0
- package/lib/public/css/mate-conversation.css +1 -1
- package/lib/public/css/pane.css +5 -1
- package/lib/public/css/pending-message-queue.css +24 -19
- package/lib/public/css/project-admin-overview.css +42 -13
- package/lib/public/css/right-workbench.css +0 -1
- package/lib/public/css/scheduled-tasks.css +7 -0
- package/lib/public/css/shared-browser.css +31 -8
- package/lib/public/css/split-view-layout.css +129 -10
- package/lib/public/css/tool-pane.css +124 -0
- package/lib/public/index.html +6 -6
- package/lib/public/modules/agent-message-bubbles.js +96 -0
- package/lib/public/modules/app-header.js +6 -0
- package/lib/public/modules/app-messages.js +26 -9
- package/lib/public/modules/background-task-previews.js +8 -0
- package/lib/public/modules/background-task-viewer.js +97 -0
- package/lib/public/modules/background-tasks-ui.js +34 -125
- package/lib/public/modules/chat-bubble-renderer.js +11 -5
- package/lib/public/modules/clay-file-links.js +2 -1
- package/lib/public/modules/clay-split-view-screen.js +6 -3
- package/lib/public/modules/clay-tool-pane-screen.js +35 -0
- package/lib/public/modules/contextual-clay-geometry.js +7 -3
- package/lib/public/modules/file-history-snapshot.js +12 -0
- package/lib/public/modules/file-pane-entrypoints.js +55 -0
- package/lib/public/modules/file-pane-history.js +78 -0
- package/lib/public/modules/file-pane-instances.js +140 -0
- package/lib/public/modules/file-pane-links.js +41 -0
- package/lib/public/modules/file-pane-render.js +48 -0
- package/lib/public/modules/file-pane-review.js +26 -0
- package/lib/public/modules/file-pane-toolbar.js +54 -0
- package/lib/public/modules/file-viewer-source.js +70 -0
- package/lib/public/modules/filebrowser.js +43 -82
- package/lib/public/modules/git-panel.js +5 -0
- package/lib/public/modules/issues.js +3 -4
- package/lib/public/modules/linear-panel.js +2 -2
- package/lib/public/modules/mate-knowledge-workbench.js +10 -6
- package/lib/public/modules/notifications.js +4 -1
- package/lib/public/modules/pane-activation.js +7 -3
- package/lib/public/modules/pane-binding.js +5 -1
- package/lib/public/modules/pane-browser-bridge.js +6 -2
- package/lib/public/modules/pane-browser-transport.js +110 -0
- package/lib/public/modules/pane-header-tooltips.js +32 -0
- package/lib/public/modules/pending-message-queue-drag.js +69 -0
- package/lib/public/modules/pending-message-queue.js +3 -33
- package/lib/public/modules/permission-control.js +6 -5
- package/lib/public/modules/plantuml-preview.js +12 -12
- package/lib/public/modules/project-admin-overview.js +125 -10
- package/lib/public/modules/project-grouping.js +9 -0
- package/lib/public/modules/right-workbench.js +34 -15
- package/lib/public/modules/scheduled-tasks.js +7 -6
- package/lib/public/modules/server-settings-controls.js +33 -4
- package/lib/public/modules/session-folder-toolbar.js +1 -1
- package/lib/public/modules/session-tool-item.js +86 -0
- package/lib/public/modules/settings-select.js +2 -1
- package/lib/public/modules/shared-browser-tabs.js +37 -21
- package/lib/public/modules/shared-browser.js +69 -9
- package/lib/public/modules/sidebar-projects.js +1 -10
- package/lib/public/modules/split-pane-renderer.js +1 -0
- package/lib/public/modules/split-session-empty-pane.js +47 -0
- package/lib/public/modules/split-view-create-menu.js +74 -65
- package/lib/public/modules/split-view-drag-preview.js +90 -0
- package/lib/public/modules/split-view-layout-client.js +1 -1
- package/lib/public/modules/split-view-layout-scope.js +4 -0
- package/lib/public/modules/split-view-picker-actions.js +71 -0
- package/lib/public/modules/split-view-picker-render.js +81 -0
- package/lib/public/modules/split-view-presentation-drag.js +36 -13
- package/lib/public/modules/split-view-presentation-drop.js +21 -18
- package/lib/public/modules/split-view-presentation-renderer.js +22 -17
- package/lib/public/modules/split-view-presentations.js +25 -33
- package/lib/public/modules/split-view-worker-activity.js +2 -2
- package/lib/public/modules/split-view-worker-transcript.js +70 -33
- package/lib/public/modules/split-view-workers.js +6 -4
- package/lib/public/modules/split-view.js +9 -2
- package/lib/public/modules/sticky-notes-browser.js +2 -2
- package/lib/public/modules/terminal-viewport.js +14 -0
- package/lib/public/modules/terminal.js +49 -23
- package/lib/public/modules/theme-palette.js +177 -0
- package/lib/public/modules/theme.js +2 -180
- package/lib/public/modules/tool-pane-chrome.js +88 -0
- package/lib/public/modules/tool-pane-drag.js +134 -0
- package/lib/public/modules/tool-pane-layout.js +95 -0
- package/lib/public/modules/tool-pane-legacy.js +60 -0
- package/lib/public/modules/tool-pane-tree.js +51 -0
- package/lib/public/modules/tools.js +5 -89
- package/lib/public/modules/tooltip.js +32 -6
- package/lib/public/modules/view-legacy-coordinates.js +16 -0
- package/lib/public/modules/view-mutations.js +24 -0
- package/lib/public/modules/view-pair-presentation.js +74 -0
- package/lib/public/modules/view-pair-validation.js +12 -0
- package/lib/public/modules/view-presentation-state.js +37 -0
- package/lib/public/modules/view-workspace-controller.js +210 -0
- package/lib/public/modules/view-workspace-state.js +103 -0
- package/lib/public/modules/view-workspace-validation.js +47 -0
- package/lib/public/modules/wireframe-workbench.js +13 -2
- package/lib/public/modules/workbench-layout-preference.js +12 -5
- package/lib/public/modules/workbench-panels.js +21 -0
- package/lib/public/modules/worker-message-previews.js +39 -0
- package/lib/public/pane-assets/app-5W6KJXK5.js +1 -0
- package/lib/public/pane-assets/chunk-2IJUTAB6.js +2 -0
- package/lib/public/pane-assets/chunk-6QFLYOVY.js +1 -0
- package/lib/public/pane-assets/chunk-7OVNC5HT.js +1 -0
- package/lib/public/pane-assets/chunk-BQN7B62H.js +3 -0
- package/lib/public/pane-assets/chunk-EMLN2Y7L.js +1 -0
- package/lib/public/pane-assets/chunk-ENPAHXLB.js +1 -0
- package/lib/public/pane-assets/chunk-EPBGGZRT.js +1 -0
- package/lib/public/pane-assets/chunk-EYTE56IA.js +1 -0
- package/lib/public/pane-assets/chunk-FQTNR4ZS.js +1 -0
- package/lib/public/pane-assets/chunk-JIV7O5GN.js +1 -0
- package/lib/public/pane-assets/chunk-L6LSA5WI.js +1 -0
- package/lib/public/pane-assets/chunk-M57UIGZV.js +1 -0
- package/lib/public/pane-assets/chunk-MGTFTEJP.js +1 -0
- package/lib/public/pane-assets/chunk-MMG6MYCN.js +1 -0
- package/lib/public/pane-assets/chunk-MZLMEXMW.js +1 -0
- package/lib/public/pane-assets/chunk-OG3E5SVA.js +166 -0
- package/lib/public/pane-assets/chunk-R7QVVFUG.js +1 -0
- package/lib/public/pane-assets/chunk-RGAFZ3T2.js +1 -0
- package/lib/public/pane-assets/chunk-RNDRURMF.js +1 -0
- package/lib/public/pane-assets/chunk-SJOT74VW.js +2 -0
- package/lib/public/pane-assets/chunk-SJTGHIN5.js +1 -0
- package/lib/public/pane-assets/chunk-SLMAFKI2.js +1 -0
- package/lib/public/pane-assets/chunk-U3I5KY3B.js +1 -0
- package/lib/public/pane-assets/chunk-UPXU2IPC.js +1 -0
- package/lib/public/pane-assets/chunk-VEBYD7TL.js +1 -0
- package/lib/public/pane-assets/chunk-VZ2OKDAO.js +1 -0
- package/lib/public/pane-assets/chunk-XBHYNXXT.js +1 -0
- package/lib/public/pane-assets/manifest.json +438 -381
- package/lib/public/pane-assets/style-BSKKK54Q.css +1 -0
- package/lib/public/style.css +1 -0
- package/lib/sdk-message-processor.js +5 -2
- package/lib/server-storage.js +27 -3
- package/lib/server-workbench-layout-settings.js +3 -3
- package/lib/server.js +4 -2
- package/lib/sessions.js +1 -1
- package/lib/split-view-layout.js +11 -7
- package/lib/storage-usage.js +29 -7
- package/lib/terminal-manager.js +2 -2
- package/lib/users-workbench-layout-preferences.js +14 -4
- package/lib/workbench-window-layout.js +30 -0
- package/lib/worker-message-previews.js +50 -0
- package/lib/ws-schema.js +1 -1
- package/lib/yoke/adapters/claude-worker.js +8 -0
- package/lib/yoke/adapters/claude.js +17 -2
- package/lib/yoke/claude-task-reader.js +94 -0
- package/lib/yoke/interface.js +1 -0
- package/package.json +1 -1
- package/lib/public/pane-assets/app-6H5S2Y3Z.js +0 -1
- package/lib/public/pane-assets/chunk-3QV5SDC4.js +0 -1
- package/lib/public/pane-assets/chunk-4FM3U3DW.js +0 -1
- package/lib/public/pane-assets/chunk-7GZH3FZY.js +0 -1
- package/lib/public/pane-assets/chunk-AGJTF5WQ.js +0 -1
- package/lib/public/pane-assets/chunk-DGOFGEUK.js +0 -1
- package/lib/public/pane-assets/chunk-I57URYSL.js +0 -1
- package/lib/public/pane-assets/chunk-NPKRTUNG.js +0 -2
- package/lib/public/pane-assets/chunk-VGA2Y3D2.js +0 -2
- package/lib/public/pane-assets/chunk-WKO3T75E.js +0 -167
- package/lib/public/pane-assets/style-TSRDU54Z.css +0 -1
package/lib/public/style.css
CHANGED
|
@@ -229,7 +229,8 @@ function attachMessageProcessor(ctx) {
|
|
|
229
229
|
// only clears ambient watchers must not rebroadcast the session list.
|
|
230
230
|
var previousBackgroundTaskCount = backgroundTaskTiming.countUserTasks(session.activeBackgroundTasks);
|
|
231
231
|
session.activeBackgroundTasks = [];
|
|
232
|
-
|
|
232
|
+
session.backgroundTaskCatalog = null;
|
|
233
|
+
sendToSession(session, { type: "active_background_tasks", tasks: [], catalog: [] });
|
|
233
234
|
if (previousBackgroundTaskCount !== 0) sm.broadcastSessionList();
|
|
234
235
|
var fsSkills = discoverSkillDirs(getSessionSkillOptions(session));
|
|
235
236
|
session.skillNames = Array.from(mergeSkills(parsed.skills, fsSkills));
|
|
@@ -463,6 +464,7 @@ function attachMessageProcessor(ctx) {
|
|
|
463
464
|
|
|
464
465
|
} else if (parsed.yokeType === "subagent_message") {
|
|
465
466
|
// Sub-agent messages: extract tool_use blocks for activity display
|
|
467
|
+
require("./project-background-tasks").observeMessage(session, parsed, sendToSession);
|
|
466
468
|
processSubagentMessage(session, parsed);
|
|
467
469
|
|
|
468
470
|
} else if (parsed.yokeType === "message") {
|
|
@@ -864,7 +866,8 @@ function attachMessageProcessor(ctx) {
|
|
|
864
866
|
Array.isArray(parsed.tasks) ? parsed.tasks : []
|
|
865
867
|
);
|
|
866
868
|
session.activeBackgroundTasks = activeBackgroundTasks;
|
|
867
|
-
|
|
869
|
+
var taskCatalog = require("./project-background-tasks").remember(session, activeBackgroundTasks);
|
|
870
|
+
sendToSession(session, { type: "active_background_tasks", tasks: activeBackgroundTasks, catalog: taskCatalog });
|
|
868
871
|
if (previousBackgroundTaskCount !== backgroundTaskTiming.countUserTasks(activeBackgroundTasks)) {
|
|
869
872
|
sm.broadcastSessionList();
|
|
870
873
|
}
|
package/lib/server-storage.js
CHANGED
|
@@ -6,6 +6,21 @@ function attachStorage(ctx) {
|
|
|
6
6
|
var progress = null;
|
|
7
7
|
var expires = 0;
|
|
8
8
|
var failed = false;
|
|
9
|
+
var scanIdentity = null;
|
|
10
|
+
function projectIdentity() {
|
|
11
|
+
return JSON.stringify((ctx.getProjects ? ctx.getProjects() : []).map(function (project) { return [project.slug, project.path, project.projectOwnerId, project.isMate]; }));
|
|
12
|
+
}
|
|
13
|
+
function targetProject(slug) {
|
|
14
|
+
return (ctx.getProjects ? ctx.getProjects() : []).find(function (project) { return project.slug === slug && !project.isMate && !/^mate-/.test(slug); });
|
|
15
|
+
}
|
|
16
|
+
function usageReply(req, res, result, slug) {
|
|
17
|
+
if (!authorized(req, res)) return;
|
|
18
|
+
if (!slug) { reply(res, result.scanning ? 202 : 200, result); return; }
|
|
19
|
+
if (!targetProject(slug)) { reply(res, 404, { error: 'Project is no longer registered.' }); return; }
|
|
20
|
+
if (scanIdentity !== projectIdentity()) { reply(res, 409, { error: 'Project registration changed. Refresh usage.' }); return; }
|
|
21
|
+
var usage = (result.projects || []).find(function (project) { return project.slug === slug; });
|
|
22
|
+
reply(res, result.scanning ? 202 : 200, { slug: slug, scanning: !!result.scanning, partial: !!result.partial, checkedAt: result.checkedAt || null, usage: usage || null });
|
|
23
|
+
}
|
|
9
24
|
function reply(res, status, data) {
|
|
10
25
|
res.writeHead(status, { 'Content-Type': 'application/json', 'Cache-Control': 'no-store' });
|
|
11
26
|
res.end(JSON.stringify(data));
|
|
@@ -19,14 +34,23 @@ function attachStorage(ctx) {
|
|
|
19
34
|
return true;
|
|
20
35
|
}
|
|
21
36
|
function handleRequest(req, res, url) {
|
|
22
|
-
|
|
37
|
+
var pathname = url.split('?')[0];
|
|
38
|
+
var match = /^\/api\/server\/storage\/project\/([^/]+)$/.exec(pathname);
|
|
39
|
+
if (pathname !== '/api/server/storage' && !match) return false;
|
|
40
|
+
var slug = null;
|
|
23
41
|
var refresh = new URL(url, 'http://localhost').searchParams.get('refresh') === '1';
|
|
24
42
|
if (!authorized(req, res)) return true;
|
|
25
43
|
if (req.method !== 'GET') { reply(res, 405, { error: 'Method not allowed.' }); return true; }
|
|
44
|
+
if (match) {
|
|
45
|
+
try { slug = decodeURIComponent(match[1]); } catch (error) { reply(res, 400, { error: 'Invalid project.' }); return true; }
|
|
46
|
+
if (!targetProject(slug)) { reply(res, 404, { error: 'Project not found.' }); return true; }
|
|
47
|
+
}
|
|
48
|
+
if (!pending && scanIdentity !== projectIdentity()) { cached = null; failed = false; }
|
|
26
49
|
if (failed && Date.now() < expires) { reply(res, 500, { error: 'Disk usage could not be measured. Try again in a few seconds.' }); return true; }
|
|
27
|
-
if (!pending && !refresh && cached && Date.now() < expires) {
|
|
50
|
+
if (!pending && !refresh && cached && Date.now() < expires) { usageReply(req, res, cached, slug); return true; }
|
|
28
51
|
if (!pending) {
|
|
29
52
|
progress = null; failed = false;
|
|
53
|
+
scanIdentity = projectIdentity();
|
|
30
54
|
var users = ctx.users.getAllUsers();
|
|
31
55
|
var projects = ctx.getProjects ? ctx.getProjects() : [];
|
|
32
56
|
if (!ctx.users.isMultiUser() && users.length <= 1) {
|
|
@@ -45,7 +69,7 @@ function attachStorage(ctx) {
|
|
|
45
69
|
timer = setTimeout(function () { resolve(progress || { scanning: true }); }, 750);
|
|
46
70
|
})]).then(function (result) {
|
|
47
71
|
clearTimeout(timer);
|
|
48
|
-
if (!res.destroyed
|
|
72
|
+
if (!res.destroyed) usageReply(req, res, result, slug);
|
|
49
73
|
}).catch(function () {
|
|
50
74
|
clearTimeout(timer);
|
|
51
75
|
if (!res.destroyed) reply(res, 500, { error: 'Disk usage could not be measured. Try refreshing.' });
|
|
@@ -32,7 +32,7 @@ function attachWorkbenchLayoutSettings(ctx) {
|
|
|
32
32
|
req.on('data', function (chunk) {
|
|
33
33
|
if (finished) return;
|
|
34
34
|
body += chunk;
|
|
35
|
-
if (Buffer.byteLength(body) >
|
|
35
|
+
if (Buffer.byteLength(body) > 65536) { finished = true; body = ''; reply(res, 413, { error: 'Request too large.' }); }
|
|
36
36
|
});
|
|
37
37
|
req.on('aborted', function () { finished = true; });
|
|
38
38
|
req.on('error', function () { finished = true; });
|
|
@@ -41,11 +41,11 @@ function attachWorkbenchLayoutSettings(ctx) {
|
|
|
41
41
|
if (identity(req) !== accountId) { reply(res, 401, { error: 'Account changed. Sign in again.' }); return; }
|
|
42
42
|
var input;
|
|
43
43
|
try { input = JSON.parse(body); } catch (error) { reply(res, 400, { error: 'Invalid JSON.' }); return; }
|
|
44
|
-
if (!input || Array.isArray(input) || Object.keys(input).
|
|
44
|
+
if (!input || Array.isArray(input) || Object.keys(input).some(function (key) { return ['mode', 'revision', 'layout'].indexOf(key) < 0; }) || !Object.prototype.hasOwnProperty.call(input, 'mode') || !Object.prototype.hasOwnProperty.call(input, 'revision')) {
|
|
45
45
|
reply(res, 400, { error: 'Invalid workbench preference.' }); return;
|
|
46
46
|
}
|
|
47
47
|
try {
|
|
48
|
-
var result = ctx.users.setWorkbenchLayoutPreference(accountId, input.mode, input.revision);
|
|
48
|
+
var result = ctx.users.setWorkbenchLayoutPreference(accountId, input.mode, input.revision, input.layout);
|
|
49
49
|
reply(res, result.error ? result.status || 400 : 200, result);
|
|
50
50
|
} catch (error) { reply(res, 500, { error: 'Workbench layout could not be saved. Your previous layout is retained.' }); }
|
|
51
51
|
});
|
package/lib/server.js
CHANGED
|
@@ -519,7 +519,7 @@ function createServer(opts) {
|
|
|
519
519
|
} : null;
|
|
520
520
|
var payload = JSON.stringify(seed).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
|
|
521
521
|
var script = '<script>window.__clayAppearanceSeed=' + payload + ';</script>';
|
|
522
|
-
return html.replace(
|
|
522
|
+
return html.replace(/<script src="\/?appearance-bootstrap.js"><\/script>/, function (tag) { return script + tag; });
|
|
523
523
|
}
|
|
524
524
|
|
|
525
525
|
function sendAppShell(req, res, slug) {
|
|
@@ -1103,8 +1103,10 @@ function createServer(opts) {
|
|
|
1103
1103
|
if (!httpUser || !canUserAccessSlug(httpUser.id, slug)) {
|
|
1104
1104
|
var overview = httpUser && administrativeMetadata(httpUser.id, slug);
|
|
1105
1105
|
if (overview && req.method === "GET" && (projectUrlForAuth === "/" || projectUrlForAuth === "/index.html")) {
|
|
1106
|
+
var overviewAppearance = null;
|
|
1107
|
+
try { overviewAppearance = settings.getInitialAppearance(req); } catch (error) {}
|
|
1106
1108
|
res.writeHead(200, { "Content-Type": "text/html; charset=utf-8", "Cache-Control": "no-store" });
|
|
1107
|
-
res.end(projectAdminOverview.page(overview, getVisibleProjects(httpUser.id)));
|
|
1109
|
+
res.end(injectAppearanceSeed(projectAdminOverview.page(overview, getVisibleProjects(httpUser.id)), overviewAppearance));
|
|
1108
1110
|
return;
|
|
1109
1111
|
}
|
|
1110
1112
|
res.writeHead(403, { "Content-Type": "application/json", "Cache-Control": "no-store" });
|
package/lib/sessions.js
CHANGED
|
@@ -886,7 +886,7 @@ function createSessionManager(opts) {
|
|
|
886
886
|
if (session.isProcessing) {
|
|
887
887
|
_send({ type: "status", status: "processing" });
|
|
888
888
|
}
|
|
889
|
-
_send({ type: "active_background_tasks", tasks: session.activeBackgroundTasks || [] });
|
|
889
|
+
_send({ type: "active_background_tasks", tasks: session.activeBackgroundTasks || [], catalog: session.backgroundTaskCatalog && session.backgroundTaskCatalog.tasks || [] });
|
|
890
890
|
|
|
891
891
|
// Re-send any pending permission requests
|
|
892
892
|
var pendingIds = Object.keys(session.pendingPermissions);
|
package/lib/split-view-layout.js
CHANGED
|
@@ -12,7 +12,7 @@ function validateTree(tree, expectedOrigins) {
|
|
|
12
12
|
var invalid = null;
|
|
13
13
|
function walk(node, depth) {
|
|
14
14
|
if (invalid) return;
|
|
15
|
-
if (!node || typeof node !== "object" || depth > MAX_SESSIONS) { invalid = "Layout tree is malformed"; return; }
|
|
15
|
+
if (!node || typeof node !== "object" || Array.isArray(node) || depth > MAX_SESSIONS) { invalid = "Layout tree is malformed"; return; }
|
|
16
16
|
if (Object.prototype.hasOwnProperty.call(node, "id")) {
|
|
17
17
|
if (Object.keys(node).length !== 1 || !validOrigin(node.id) || origins.indexOf(node.id) !== -1) {
|
|
18
18
|
invalid = "Layout leaves must have unique valid session origins"; return;
|
|
@@ -28,11 +28,11 @@ function validateTree(tree, expectedOrigins) {
|
|
|
28
28
|
}
|
|
29
29
|
walk(node.left, depth + 1); walk(node.right, depth + 1);
|
|
30
30
|
}
|
|
31
|
-
walk(tree, 0);
|
|
31
|
+
if (tree !== null) walk(tree, 0);
|
|
32
32
|
if (invalid) return { ok: false, error: invalid, origins: origins };
|
|
33
|
-
if (origins.length
|
|
33
|
+
if (origins.length > MAX_SESSIONS) return { ok: false, error: "A View supports zero to eight sessions", origins: origins };
|
|
34
34
|
if (expectedOrigins) {
|
|
35
|
-
if (!Array.isArray(expectedOrigins) || expectedOrigins.length !== origins.length ||
|
|
35
|
+
if (!Array.isArray(expectedOrigins) || expectedOrigins.length !== origins.length || new Set(expectedOrigins).size !== expectedOrigins.length ||
|
|
36
36
|
expectedOrigins.some(function (id) { return !validOrigin(id) || origins.indexOf(id) === -1; })) {
|
|
37
37
|
return { ok: false, error: "Layout leaves must exactly match the selected sessions", origins: origins };
|
|
38
38
|
}
|
|
@@ -100,9 +100,9 @@ function validateViewShape(view) {
|
|
|
100
100
|
var name = validateName(view.name);
|
|
101
101
|
if (!name.ok) return name;
|
|
102
102
|
if (view.nameMode != null && ["automatic", "custom"].indexOf(view.nameMode) === -1) return { ok: false, error: "Split view name mode is invalid" };
|
|
103
|
-
if (!Number.
|
|
103
|
+
if (!Number.isSafeInteger(view.revision) || view.revision < 1 || !validatePreset(view.layout)) return { ok: false, error: "Split view revision or layout is invalid" };
|
|
104
104
|
var refs = view.sessionRefs;
|
|
105
|
-
if (!Array.isArray(refs) || refs.length
|
|
105
|
+
if (!Array.isArray(refs) || refs.length > MAX_SESSIONS) return { ok: false, error: "A View supports zero to eight sessions" };
|
|
106
106
|
var ids = [];
|
|
107
107
|
for (var i = 0; i < refs.length; i++) {
|
|
108
108
|
var ref = refs[i];
|
|
@@ -113,7 +113,11 @@ function validateViewShape(view) {
|
|
|
113
113
|
}
|
|
114
114
|
var tree = validateTree(view.tree, ids);
|
|
115
115
|
if (!tree.ok) return tree;
|
|
116
|
-
if (typeof view.activeSessionOriginId !== "string" || ids.indexOf(view.activeSessionOriginId) === -1) return { ok: false, error: "Active session must belong to the Split view" };
|
|
116
|
+
if (ids.length ? typeof view.activeSessionOriginId !== "string" || ids.indexOf(view.activeSessionOriginId) === -1 : view.activeSessionOriginId !== null) return { ok: false, error: "Active session must belong to the Split view" };
|
|
117
|
+
if (view.workspace !== undefined) {
|
|
118
|
+
var checked = require("./project-view-workspace").validate(view.workspace, view);
|
|
119
|
+
if (!checked.ok) return checked;
|
|
120
|
+
}
|
|
117
121
|
return { ok: true, name: name.name, origins: ids };
|
|
118
122
|
}
|
|
119
123
|
|
package/lib/storage-usage.js
CHANGED
|
@@ -39,10 +39,26 @@ async function scanStorage(options) {
|
|
|
39
39
|
if (identity && !owners.get(identity.uid)) rows[index].mapped = false;
|
|
40
40
|
});
|
|
41
41
|
var projectRoots = new Map();
|
|
42
|
+
var projectUsage = [];
|
|
43
|
+
var usageRoots = new Map();
|
|
42
44
|
var projectErrors = 0;
|
|
43
45
|
for (var project of projects) {
|
|
46
|
+
var usage = { slug: project.slug, available: false, bytes: 0, files: 0, capacityBytes: null, availableBytes: null };
|
|
47
|
+
if (!project.isMate && project.slug) projectUsage.push(usage);
|
|
44
48
|
try {
|
|
45
49
|
var projectPath = await fs.realpath(project.path);
|
|
50
|
+
usage.available = true;
|
|
51
|
+
if (usageRoots.has(projectPath)) {
|
|
52
|
+
var priorUsage = usageRoots.get(projectPath);
|
|
53
|
+
if (priorUsage) priorUsage.available = false;
|
|
54
|
+
usage.available = false;
|
|
55
|
+
usageRoots.set(projectPath, null);
|
|
56
|
+
} else usageRoots.set(projectPath, project.isMate ? null : usage);
|
|
57
|
+
try {
|
|
58
|
+
var volume = await fs.statfs(projectPath);
|
|
59
|
+
usage.capacityBytes = volume.blocks * volume.bsize;
|
|
60
|
+
usage.availableBytes = volume.bavail * volume.bsize;
|
|
61
|
+
} catch (error) { /* Volume capacity can be unavailable independently of allocation. */ }
|
|
46
62
|
var row = rows.find(function (item) { return item.id === project.projectOwnerId; }) || null;
|
|
47
63
|
if (row) row.projects++;
|
|
48
64
|
if (projectRoots.has(projectPath) && projectRoots.get(projectPath) !== row) row = null;
|
|
@@ -50,21 +66,20 @@ async function scanStorage(options) {
|
|
|
50
66
|
} catch (error) { projectErrors++; }
|
|
51
67
|
}
|
|
52
68
|
var orderedProjects = Array.from(projectRoots.keys()).sort(function (a, b) { return b.length - a.length; });
|
|
53
|
-
function
|
|
54
|
-
|
|
55
|
-
return root ? projectRoots.get(root) : null;
|
|
69
|
+
function projectRoot(filePath) {
|
|
70
|
+
return orderedProjects.find(function (candidate) { return filePath === candidate || filePath.indexOf(candidate + path.sep) === 0; });
|
|
56
71
|
}
|
|
57
72
|
var roots = orderedProjects.concat(options.projectPaths || []);
|
|
58
73
|
identities.forEach(function (identity) { if (identity) roots.push(identity.home); });
|
|
59
74
|
roots.push(options.configDir);
|
|
60
|
-
var report = { attribution: projectMode ? "project" : "filesystem", users: rows, totalBytes: 0, totalFiles: 0, unattributedBytes: 0, unattributedFiles: 0,
|
|
75
|
+
var report = { attribution: projectMode ? "project" : "filesystem", users: rows, projects: projectUsage, totalBytes: 0, totalFiles: 0, unattributedBytes: 0, unattributedFiles: 0,
|
|
61
76
|
partial: false, errors: projectErrors + users.filter(function (user, index) { return !projectMode && options.platform === 'linux' && user.linuxUser && !identities[index]; }).length, skippedLinks: 0, skippedMounts: 0, scannedEntries: 0, roots: 0, platform: options.platform };
|
|
62
77
|
var lastProgress = 0;
|
|
63
78
|
function progress() {
|
|
64
79
|
if (!options.onProgress || Date.now() - lastProgress < 500) return;
|
|
65
80
|
lastProgress = Date.now();
|
|
66
81
|
options.onProgress(Object.assign({}, report, { scanning: true, checkedAt: new Date().toISOString(),
|
|
67
|
-
users: rows.map(function (row) { return Object.assign({}, row); }) }));
|
|
82
|
+
users: rows.map(function (row) { return Object.assign({}, row); }), projects: projectUsage.map(function (row) { return Object.assign({}, row); }) }));
|
|
68
83
|
}
|
|
69
84
|
progress();
|
|
70
85
|
var seen = new Map();
|
|
@@ -118,9 +133,15 @@ async function scanStorage(options) {
|
|
|
118
133
|
if (stat.isSymbolicLink()) { report.skippedLinks++; return; }
|
|
119
134
|
if (stat.dev !== device) { report.skippedMounts++; return; }
|
|
120
135
|
var key = stat.dev + ':' + stat.ino;
|
|
121
|
-
var
|
|
136
|
+
var usageRoot = projectRoot(filePath);
|
|
137
|
+
var owner = projectMode ? projectRoots.get(usageRoot) : owners.get(stat.uid);
|
|
138
|
+
var usage = usageRoot && usageRoots.get(usageRoot);
|
|
122
139
|
if (seen.has(key)) {
|
|
123
140
|
var previous = seen.get(key);
|
|
141
|
+
if (stat.isFile() && previous.project && usage && previous.project !== usage) {
|
|
142
|
+
previous.project.bytes -= previous.bytes; previous.project.files--;
|
|
143
|
+
previous.project = null;
|
|
144
|
+
}
|
|
124
145
|
if (projectMode && stat.isFile() && previous.owner && previous.owner !== owner) {
|
|
125
146
|
previous.owner.bytes -= previous.bytes; previous.owner.files--;
|
|
126
147
|
report.unattributedBytes += previous.bytes; report.unattributedFiles++;
|
|
@@ -132,7 +153,8 @@ async function scanStorage(options) {
|
|
|
132
153
|
if (!stat.isDirectory() && !stat.isFile()) return;
|
|
133
154
|
var bytes = typeof stat.blocks === 'number' ? stat.blocks * 512 : stat.size;
|
|
134
155
|
// Only directories and multiply-linked files need identity deduplication.
|
|
135
|
-
if (stat.isDirectory() || stat.nlink > 1) seen.set(key, { owner: owner, bytes: bytes });
|
|
156
|
+
if (stat.isDirectory() || stat.nlink > 1) seen.set(key, { owner: owner, project: usage, bytes: bytes });
|
|
157
|
+
if (usage) { usage.bytes += bytes; if (stat.isFile()) usage.files++; }
|
|
136
158
|
report.totalBytes += bytes;
|
|
137
159
|
if (stat.isFile()) report.totalFiles++;
|
|
138
160
|
if (owner) { owner.bytes += bytes; if (stat.isFile()) owner.files++; }
|
package/lib/terminal-manager.js
CHANGED
|
@@ -136,9 +136,9 @@ function createTerminalManager(opts) {
|
|
|
136
136
|
sendTo(ws, { type: "term_output", id: id, data: replay });
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
//
|
|
139
|
+
// This snapshot may arrive after the attaching client has fitted a new viewport.
|
|
140
140
|
if (!alreadySubscribed && session.cols && session.rows) {
|
|
141
|
-
sendTo(ws, { type: "term_resized", id: id, cols: session.cols, rows: session.rows });
|
|
141
|
+
sendTo(ws, { type: "term_resized", id: id, cols: session.cols, rows: session.rows, initial: true });
|
|
142
142
|
}
|
|
143
143
|
|
|
144
144
|
// If already exited, notify
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
var windowLayouts = require('./workbench-window-layout');
|
|
1
2
|
// Account UI preferences are excluded from canonical Clay controls.
|
|
2
3
|
function validMode(mode) { return mode === 'overlay' || mode === 'docked'; }
|
|
3
4
|
function attachWorkbenchLayoutPreferences(deps) {
|
|
@@ -12,16 +13,25 @@ function attachWorkbenchLayoutPreferences(deps) {
|
|
|
12
13
|
function value(accountId, saved) {
|
|
13
14
|
if (saved === undefined) return { accountId: accountId, mode: 'overlay', revision: 0 };
|
|
14
15
|
if (!saved || !validMode(saved.mode) || !Number.isSafeInteger(saved.revision) || saved.revision < 1) throw new Error('Invalid stored preference.');
|
|
15
|
-
|
|
16
|
+
if (saved.layouts && !windowLayouts.validLayouts(saved.layouts)) throw new Error('Invalid stored window layout.');
|
|
17
|
+
return Object.assign({ accountId: accountId, mode: saved.mode, revision: saved.revision }, saved.layouts ? { layouts: saved.layouts } : {});
|
|
16
18
|
}
|
|
17
19
|
function get(accountId) { return value(accountId, context(accountId).saved); }
|
|
18
|
-
function set(accountId, mode, revision) {
|
|
20
|
+
function set(accountId, mode, revision, layout) {
|
|
21
|
+
if (layout !== undefined && !windowLayouts.validLayout(layout)) return { status: 400, error: 'Invalid window layout.' };
|
|
19
22
|
if (!validMode(mode) || !Number.isSafeInteger(revision) || revision < 0) return { status: 400, error: 'Invalid workbench layout.' };
|
|
20
23
|
var current = context(accountId), prior = value(accountId, current.saved);
|
|
21
24
|
if (prior.revision !== revision) return { status: 409, error: 'Workbench layout changed elsewhere. Reload the preference and try again.' };
|
|
22
|
-
|
|
25
|
+
var layouts = Object.assign({}, prior.layouts || {});
|
|
26
|
+
var changed = layout && JSON.stringify(layouts[layout.key]) !== JSON.stringify({ base: layout.base, tree: layout.tree });
|
|
27
|
+
if (prior.mode === mode && !changed) return prior;
|
|
28
|
+
if (layout) {
|
|
29
|
+
delete layouts[layout.key];
|
|
30
|
+
if (Object.keys(layouts).length >= 32) delete layouts[Object.keys(layouts)[0]];
|
|
31
|
+
layouts[layout.key] = { base: layout.base, tree: layout.tree };
|
|
32
|
+
}
|
|
23
33
|
if (revision === Number.MAX_SAFE_INTEGER) throw new Error('Preference revision exhausted.');
|
|
24
|
-
var saved = { mode: mode, revision: revision + 1 };
|
|
34
|
+
var saved = Object.assign({ mode: mode, revision: revision + 1 }, Object.keys(layouts).length ? { layouts: layouts } : {});
|
|
25
35
|
if (current.settings) config.saveConfig(Object.assign({}, current.settings, { workbenchLayoutPreference: saved }));
|
|
26
36
|
else deps.saveUsers(Object.assign({}, current.data, { users: current.data.users.map(function (entry) {
|
|
27
37
|
return entry === current.user ? Object.assign({}, entry, { workbenchLayoutPreference: saved }) : entry;
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Display preferences only: these records never open sessions or run tools.
|
|
2
|
+
var tools = ['tool-terminal', 'tool-browser', 'tool-file-viewer'];
|
|
3
|
+
function validTree(tree, allowTools) {
|
|
4
|
+
var ids = [], nodes = 0;
|
|
5
|
+
function walk(node, depth) {
|
|
6
|
+
if (!node || typeof node !== 'object' || Array.isArray(node) || ++nodes > 21 || depth > 10) return false;
|
|
7
|
+
if (Object.prototype.hasOwnProperty.call(node, 'id')) {
|
|
8
|
+
if (Object.keys(node).length !== 1 || typeof node.id !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(node.id)) return false;
|
|
9
|
+
if (node.id.indexOf('tool-') === 0 && (!allowTools || tools.indexOf(node.id) < 0)) return false;
|
|
10
|
+
ids.push(node.id); return true;
|
|
11
|
+
}
|
|
12
|
+
return Object.keys(node).sort().join(',') === 'axis,left,ratio,right' && ['horizontal', 'vertical'].indexOf(node.axis) >= 0 &&
|
|
13
|
+
Number.isFinite(node.ratio) && node.ratio >= .05 && node.ratio <= .95 && walk(node.left, depth + 1) && walk(node.right, depth + 1);
|
|
14
|
+
}
|
|
15
|
+
return walk(tree, 0) && ids.length > 0 && ids.length <= (allowTools ? 11 : 8) && new Set(ids).size === ids.length;
|
|
16
|
+
}
|
|
17
|
+
function validLayout(input) {
|
|
18
|
+
return !!input && typeof input === 'object' && !Array.isArray(input) && Object.keys(input).sort().join(',') === 'base,key,tree' &&
|
|
19
|
+
typeof input.key === 'string' && input.key.length > 0 && input.key.length <= 512 && input.key !== '__proto__' &&
|
|
20
|
+
validTree(input.base, false) && validTree(input.tree, true) && sameSessions(input.base, input.tree);
|
|
21
|
+
}
|
|
22
|
+
function sameSessions(base, tree) {
|
|
23
|
+
function ids(node) { return node.id ? (tools.indexOf(node.id) >= 0 ? [] : [node.id]) : ids(node.left).concat(ids(node.right)); }
|
|
24
|
+
return JSON.stringify(ids(base).sort()) === JSON.stringify(ids(tree).sort());
|
|
25
|
+
}
|
|
26
|
+
function validLayouts(layouts) {
|
|
27
|
+
return !!layouts && typeof layouts === 'object' && !Array.isArray(layouts) && Object.keys(layouts).length <= 32 &&
|
|
28
|
+
Object.keys(layouts).every(function (key) { var item = layouts[key]; return item && validLayout({ key: key, base: item.base, tree: item.tree }); });
|
|
29
|
+
}
|
|
30
|
+
module.exports = { validLayout: validLayout, validLayouts: validLayouts };
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// Bounded, exact-owner message previews use the Worker reader's existing authority.
|
|
2
|
+
function latest(history) {
|
|
3
|
+
var found = null;
|
|
4
|
+
for (var i = history.length - 1, stop = Math.max(0, history.length - 1000); i >= stop; i--) {
|
|
5
|
+
var entry = history[i];
|
|
6
|
+
if (entry && (entry.type === 'delta' || entry.type === 'assistant_message') && typeof entry.text === 'string' && entry.text.trim()) {
|
|
7
|
+
var index = i, text = entry.text;
|
|
8
|
+
while (index > stop && history[index - 1].type === 'delta' && entry.type === 'delta' ) { index--; text = (history[index].text + text).slice(0, 1200); }
|
|
9
|
+
found = { id: String(index), text: text.slice(0, 1200), timestamp: history[index]._ts || null }; break;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return found;
|
|
13
|
+
}
|
|
14
|
+
function attach(ctx) {
|
|
15
|
+
var subscriptions = new WeakMap();
|
|
16
|
+
function release(ws) {
|
|
17
|
+
var old = subscriptions.get(ws); if (!old) return;
|
|
18
|
+
subscriptions.delete(ws); old.entries.forEach(function (entry) { clearTimeout(entry.timer); if (entry.release) entry.release(); });
|
|
19
|
+
if (ws.removeListener) ws.removeListener('close', old.close);
|
|
20
|
+
}
|
|
21
|
+
function handleMessage(ws, msg) {
|
|
22
|
+
if (msg.type !== 'split_worker_preview_request') return false;
|
|
23
|
+
release(ws);
|
|
24
|
+
if (typeof msg.requestId !== 'string' || msg.requestId.length > 160 || !Array.isArray(msg.targets) || msg.targets.length > 32) return true;
|
|
25
|
+
var state = { entries: [], close: function () { release(ws); } }; subscriptions.set(ws, state);
|
|
26
|
+
msg.targets.forEach(function (target) {
|
|
27
|
+
var pair = ctx.resolve(ws, target); if (!pair || state.entries.some(function (entry) { return entry.worker === pair.worker; })) return;
|
|
28
|
+
var entry = { worker: pair.worker, timer: null, last: null };
|
|
29
|
+
function send(live) {
|
|
30
|
+
var current = ctx.resolve(ws, target);
|
|
31
|
+
if (subscriptions.get(ws) !== state || ws.readyState !== 1 || !current || current.driver !== pair.driver || current.worker !== pair.worker) { if (entry.release) entry.release(); return; }
|
|
32
|
+
var preview = latest(pair.worker.history || []); if (!preview) return;
|
|
33
|
+
preview.vendor = pair.worker.vendor || null;
|
|
34
|
+
if (entry.last === JSON.stringify(preview)) return;
|
|
35
|
+
entry.last = JSON.stringify(preview);
|
|
36
|
+
ctx.send(ws, { type: 'split_worker_message_preview', requestId: msg.requestId, parentSessionOriginId: target.parentSessionOriginId,
|
|
37
|
+
workerSessionOriginId: target.workerSessionOriginId, generation: target.generation, live: live, preview: preview });
|
|
38
|
+
}
|
|
39
|
+
entry.release = ctx.sm.subscribeSession(pair.worker.localId, function (event) {
|
|
40
|
+
if (!event || ['delta', 'assistant_message'].indexOf(event.type) < 0 || entry.timer) return;
|
|
41
|
+
entry.timer = setTimeout(function () { entry.timer = null; send(true); }, 60);
|
|
42
|
+
});
|
|
43
|
+
state.entries.push(entry); send(false);
|
|
44
|
+
});
|
|
45
|
+
if (ws.once) ws.once('close', state.close);
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
return { handleMessage: handleMessage };
|
|
49
|
+
}
|
|
50
|
+
module.exports = { attach: attach, latest: latest };
|
package/lib/ws-schema.js
CHANGED
|
@@ -24,7 +24,7 @@ var schema = {
|
|
|
24
24
|
"switch_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Switch the active session by local ID" },
|
|
25
25
|
"resume_tui_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Spawn the claude --resume PTY for a TUI session shown read-only (lazy resume)" },
|
|
26
26
|
"suspend_tui_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Close a live TUI session's PTY now but keep it resumable (explicit Close)" },
|
|
27
|
-
"new_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Create a
|
|
27
|
+
"new_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Create a blank session; correlated ordinary-project requests may use activate:false to preserve the current View selection" },
|
|
28
28
|
"new_session_result": { direction: "s2c", handler: "lib/public/modules/scheduled-tasks.js", description: "Correlated result for a requested new session" },
|
|
29
29
|
"delete_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Delete a session by ID" },
|
|
30
30
|
"rename_session": { direction: "c2s", handler: "lib/project-sessions.js", description: "Rename a session" },
|
|
@@ -97,6 +97,8 @@ function applyWorkerClaudeBinary(options) {
|
|
|
97
97
|
// --- State ---
|
|
98
98
|
var sdkModule = null;
|
|
99
99
|
var queryInstance = null;
|
|
100
|
+
var taskReader = null;
|
|
101
|
+
var taskReaderModule = require("../claude-task-reader");
|
|
100
102
|
var messageQueue = null;
|
|
101
103
|
var abortController = null;
|
|
102
104
|
var pendingPermissions = {}; // requestId -> resolve
|
|
@@ -284,6 +286,9 @@ function handleMessage(msg) {
|
|
|
284
286
|
case "set_permission_mode":
|
|
285
287
|
handleSetPermissionMode(msg);
|
|
286
288
|
break;
|
|
289
|
+
case "read_background_task":
|
|
290
|
+
taskReaderModule.respond(taskReader, msg, sendToDaemon);
|
|
291
|
+
break;
|
|
287
292
|
case "stop_task":
|
|
288
293
|
handleStopTask(msg);
|
|
289
294
|
break;
|
|
@@ -598,6 +603,8 @@ async function handleQueryStart(msg) {
|
|
|
598
603
|
prompt: messageQueue,
|
|
599
604
|
options: options,
|
|
600
605
|
});
|
|
606
|
+
if (taskReader) taskReader.close();
|
|
607
|
+
taskReader = taskReaderModule.createReader(getSDK, function () { return queryInstance; }, options.cwd);
|
|
601
608
|
perf("query instance created");
|
|
602
609
|
} catch (e) {
|
|
603
610
|
sendToDaemon({ type: "query_error", error: "Failed to create query: " + (e.message || e), exitCode: null, stderr: null });
|
|
@@ -618,6 +625,7 @@ async function handleQueryStart(msg) {
|
|
|
618
625
|
var firstText = true;
|
|
619
626
|
var eventCounts = {};
|
|
620
627
|
for await (var event of queryInstance) {
|
|
628
|
+
taskReader.observe(event);
|
|
621
629
|
var etype = (event && event.type || "?");
|
|
622
630
|
var esubtype = (event && event.subtype || "");
|
|
623
631
|
eventCounts[etype] = (eventCounts[etype] || 0) + 1;
|
|
@@ -18,6 +18,7 @@ var claudeUserInput = require("../claude-user-input");
|
|
|
18
18
|
var claudeCorrelation = require("../claude-message-correlation");
|
|
19
19
|
var claudeContextUsage = require("../claude-context-usage");
|
|
20
20
|
var claudeModelSwitch = require("../claude-model-switch");
|
|
21
|
+
var taskReaderModule = require("../claude-task-reader");
|
|
21
22
|
|
|
22
23
|
// SDK-specific knowledge stays in the adapter (not the relay): the host dialog
|
|
23
24
|
// kinds this client can render, declared to the SDK when an onUserDialog
|
|
@@ -580,6 +581,8 @@ function normalizeBackgroundTasks(tasks) {
|
|
|
580
581
|
task_id: task && task.task_id,
|
|
581
582
|
task_type: taskType,
|
|
582
583
|
description: (task && task.description) || "",
|
|
584
|
+
run_id: task && task.run_id || null,
|
|
585
|
+
parent_task_id: task && task.parent_task_id || null,
|
|
583
586
|
// Housekeeping the CLI does not surface as user work (skip_transcript
|
|
584
587
|
// tasks, auto-started live-update watchers). Clay keeps these out of its
|
|
585
588
|
// activity indicators and lists them as a subdued secondary group.
|
|
@@ -597,7 +600,8 @@ function normalizeBackgroundTasks(tasks) {
|
|
|
597
600
|
// --- QueryHandle ---
|
|
598
601
|
// Wraps a raw SDK query object with the YOKE QueryHandle interface.
|
|
599
602
|
// Events are flattened via flattenEvent before yielding.
|
|
600
|
-
function createQueryHandle(rawQuery, messageQueue, abortController) {
|
|
603
|
+
function createQueryHandle(rawQuery, messageQueue, abortController, cwd) {
|
|
604
|
+
var taskReader = taskReaderModule.createReader(loadSDK, function () { return closed ? null : rawQuery; }, cwd);
|
|
601
605
|
var correlation = claudeCorrelation.createCorrelation();
|
|
602
606
|
var closed = false;
|
|
603
607
|
var removeAbort = function () {};
|
|
@@ -615,6 +619,7 @@ function createQueryHandle(rawQuery, messageQueue, abortController) {
|
|
|
615
619
|
return rawIter.next().then(function(result) {
|
|
616
620
|
if (result.done) { closed = true; removeAbort(); resolveStopped(); }
|
|
617
621
|
if (result.done) return result;
|
|
622
|
+
taskReader.observe(result.value);
|
|
618
623
|
return { value: correlation.result(result.value, flattenEvent(result.value)), done: false };
|
|
619
624
|
}, function(err) {
|
|
620
625
|
closed = true;
|
|
@@ -676,6 +681,8 @@ function createQueryHandle(rawQuery, messageQueue, abortController) {
|
|
|
676
681
|
return Promise.resolve();
|
|
677
682
|
},
|
|
678
683
|
|
|
684
|
+
readBackgroundTask: taskReader.read,
|
|
685
|
+
|
|
679
686
|
stopTask: function(taskId) {
|
|
680
687
|
if (rawQuery && typeof rawQuery.stopTask === "function") {
|
|
681
688
|
return rawQuery.stopTask(taskId);
|
|
@@ -1107,6 +1114,7 @@ function serializeWorkerValue(value, seen) {
|
|
|
1107
1114
|
function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool, onUserDialog, onUserInputRequest, onModelSwitch, preToolUsePolicy) {
|
|
1108
1115
|
var interactionAbort = new AbortController();
|
|
1109
1116
|
var contextRequests = claudeContextUsage.createRequests(worker);
|
|
1117
|
+
var taskRequests = taskReaderModule.createRequests(worker);
|
|
1110
1118
|
var correlation = claudeCorrelation.createCorrelation();
|
|
1111
1119
|
// Async iterable state
|
|
1112
1120
|
var iterQueue = [];
|
|
@@ -1153,6 +1161,7 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool,
|
|
|
1153
1161
|
function endIter() {
|
|
1154
1162
|
interactionAbort.abort();
|
|
1155
1163
|
contextRequests.close();
|
|
1164
|
+
taskRequests.close();
|
|
1156
1165
|
if (iterEnded) return;
|
|
1157
1166
|
iterEnded = true;
|
|
1158
1167
|
if (iterWaiting) {
|
|
@@ -1165,6 +1174,7 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool,
|
|
|
1165
1174
|
function errorIter(err) {
|
|
1166
1175
|
interactionAbort.abort();
|
|
1167
1176
|
contextRequests.close();
|
|
1177
|
+
taskRequests.close();
|
|
1168
1178
|
if (iterEnded) return;
|
|
1169
1179
|
iterEnded = true;
|
|
1170
1180
|
iterError = err;
|
|
@@ -1292,6 +1302,9 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool,
|
|
|
1292
1302
|
case "model_switched":
|
|
1293
1303
|
if (onModelSwitch) onModelSwitch(msg.change);
|
|
1294
1304
|
break;
|
|
1305
|
+
case "background_task_response":
|
|
1306
|
+
taskRequests.receive(msg);
|
|
1307
|
+
break;
|
|
1295
1308
|
case "context_usage_response":
|
|
1296
1309
|
contextRequests.receive(msg);
|
|
1297
1310
|
break;
|
|
@@ -1458,6 +1471,8 @@ function createWorkerQueryHandle(worker, canUseTool, onElicitation, callMcpTool,
|
|
|
1458
1471
|
});
|
|
1459
1472
|
},
|
|
1460
1473
|
|
|
1474
|
+
readBackgroundTask: function(input) { return iterEnded ? Promise.reject(new Error("Task connection closed.")) : taskRequests.request(input); },
|
|
1475
|
+
|
|
1461
1476
|
stopTask: function(taskId) {
|
|
1462
1477
|
worker.send({ type: "stop_task", taskId: taskId });
|
|
1463
1478
|
return Promise.resolve();
|
|
@@ -1821,7 +1836,7 @@ function createClaudeAdapter(opts) {
|
|
|
1821
1836
|
|
|
1822
1837
|
claudeModelSwitch.attach(sdkOptions, queryOpts.onModelSwitch);
|
|
1823
1838
|
var rawQuery = sdk.query({ prompt: mq, options: sdkOptions });
|
|
1824
|
-
return createQueryHandle(rawQuery, mq, ac);
|
|
1839
|
+
return createQueryHandle(rawQuery, mq, ac, sdkOptions.cwd || _cwd);
|
|
1825
1840
|
},
|
|
1826
1841
|
|
|
1827
1842
|
createOneShotQuery: async function(queryOpts) {
|