clay-server 5.2.0-beta.5 → 5.2.0-beta.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/claude-hook-installer.js +1 -267
- package/lib/claude-jsonl-watcher.js +1 -204
- package/lib/clay-control-presentation-binding.js +96 -0
- package/lib/clay-control-presentation.js +128 -0
- package/lib/clay-control-target-projection.js +26 -0
- package/lib/clay-mate-chat.js +1 -0
- package/lib/clay-mate-knowledge.js +1 -1
- package/lib/clay-product-control-tools.js +1 -1
- package/lib/clay-product-control.js +22 -12
- package/lib/clay-project-records.js +2 -1
- package/lib/clay-scheduling.js +1 -1
- package/lib/clay-screen-controls.js +13 -5
- package/lib/clay-skill-install.js +1 -33
- package/lib/clay-workspace-controls.js +2 -1
- package/lib/cli-sessions.js +1 -300
- package/lib/codex-defaults.js +1 -28
- package/lib/context-usage.js +3 -2
- package/lib/contextual-screen-snapshot.js +38 -0
- package/lib/daemon.js +5 -55
- package/lib/default-ai-runtime.js +4 -8
- package/lib/email-mcp-server.js +1 -1
- package/lib/kiro-defaults.js +1 -22
- package/lib/mate-context-service.js +138 -0
- package/lib/mate-context-tools.js +21 -0
- package/lib/mcp-proxy.js +2 -32
- package/lib/mcp-query-bridge.js +1 -1
- package/lib/mcp-source-adapters.js +1 -91
- package/lib/mcp-source-context.js +1 -42
- package/lib/mcp-source-discovery.js +1 -169
- package/lib/mcp-source-plugins.js +1 -68
- package/lib/mcp-source-restrictions.js +1 -47
- package/lib/mcp-tool-policy.js +1 -26
- package/lib/os-user-diagnostics.js +2 -4
- package/lib/os-users.js +4 -65
- package/lib/project-connection.js +14 -10
- package/lib/project-control-git-view.js +12 -0
- package/lib/project-filesystem.js +5 -1
- package/lib/project-http.js +7 -4
- package/lib/project-mcp.js +2 -2
- package/lib/project-notifications.js +5 -4
- package/lib/project-pair-result-delivery.js +2 -2
- package/lib/project-pair-usage.js +12 -13
- package/lib/project-pending-message-queue.js +3 -1
- package/lib/project-product-control.js +3 -1
- package/lib/project-scheduled-messages.js +4 -4
- package/lib/project-scheduled-task-control.js +1 -1
- package/lib/project-session-document.js +20 -5
- package/lib/project-session-history-actions.js +5 -3
- package/lib/project-session-pair.js +1 -1
- package/lib/project-session-runtime-actions.js +2 -1
- package/lib/project-session-spawn.js +2 -3
- package/lib/project-session-vendor.js +2 -1
- package/lib/project-sessions.js +18 -26
- package/lib/project-shared-browser.js +22 -5
- package/lib/project-studio-presentation.js +58 -0
- package/lib/project-user-message.js +37 -0
- package/lib/project-vendor-login.js +6 -36
- package/lib/project-workspace-query.js +11 -1
- package/lib/project.js +24 -7
- package/lib/public/app.js +25 -0
- package/lib/public/appearance-bootstrap.js +2 -0
- package/lib/public/css/contextual-clay.css +240 -0
- package/lib/public/css/messages.css +6 -2
- package/lib/public/modules/app-favicon.js +18 -9
- package/lib/public/modules/app-header.js +4 -2
- package/lib/public/modules/app-loop-ui.js +2 -0
- package/lib/public/modules/app-message-router.js +5 -1
- package/lib/public/modules/app-messages.js +50 -6
- package/lib/public/modules/app-notifications.js +11 -5
- package/lib/public/modules/app-panels.js +61 -123
- package/lib/public/modules/app-projects.js +3 -0
- package/lib/public/modules/app-rate-limit.js +7 -27
- package/lib/public/modules/chat-render-runtime.js +3 -0
- package/lib/public/modules/clay-control-activity.js +214 -0
- package/lib/public/modules/clay-control-copy.js +36 -0
- package/lib/public/modules/clay-control-detail-targets.js +44 -0
- package/lib/public/modules/clay-control-object-targets.js +52 -0
- package/lib/public/modules/clay-control-target-visibility.js +12 -0
- package/lib/public/modules/clay-control-targets.js +44 -0
- package/lib/public/modules/clay-navigation-render.js +52 -0
- package/lib/public/modules/clay-owner-navigation.js +11 -2
- package/lib/public/modules/clay-session-links.js +31 -0
- package/lib/public/modules/clay-studio-bridge.js +20 -0
- package/lib/public/modules/clay-studio-document.js +67 -0
- package/lib/public/modules/clay-studio-render.js +144 -0
- package/lib/public/modules/clay-tool-presentation.js +4 -0
- package/lib/public/modules/command-palette.js +26 -31
- package/lib/public/modules/contextual-ask-session.js +26 -0
- package/lib/public/modules/contextual-clay-geometry.js +155 -0
- package/lib/public/modules/contextual-clay-session.js +130 -0
- package/lib/public/modules/contextual-clay-workspace.js +157 -0
- package/lib/public/modules/contextual-clay.js +237 -0
- package/lib/public/modules/contextual-composer.js +91 -0
- package/lib/public/modules/contextual-mate-context.js +115 -0
- package/lib/public/modules/contextual-mate-hint.js +40 -0
- package/lib/public/modules/contextual-mate-identity.js +23 -0
- package/lib/public/modules/contextual-mate-sessions.js +134 -0
- package/lib/public/modules/contextual-search.js +137 -0
- package/lib/public/modules/contextual-session-citations.js +58 -0
- package/lib/public/modules/filebrowser.js +20 -1
- package/lib/public/modules/generation-stop.js +8 -5
- package/lib/public/modules/git-panel.js +3 -1
- package/lib/public/modules/icon-strip-tooltip.js +2 -1
- package/lib/public/modules/input.js +35 -1
- package/lib/public/modules/issues-render.js +3 -2
- package/lib/public/modules/knowledge-db-workbench.js +4 -4
- package/lib/public/modules/markdown.js +2 -34
- package/lib/public/modules/mate-knowledge-workbench.js +1 -1
- package/lib/public/modules/mcp-connection-view.js +1 -1
- package/lib/public/modules/message-delivery.js +10 -0
- package/lib/public/modules/pane-bridge.js +2 -2
- package/lib/public/modules/pending-message-queue.js +1 -0
- package/lib/public/modules/permission-control.js +5 -4
- package/lib/public/modules/product-review.js +26 -0
- package/lib/public/modules/project-chat-identity.js +13 -4
- package/lib/public/modules/project-logs-render.js +3 -0
- package/lib/public/modules/project-mate-navigation.js +58 -21
- package/lib/public/modules/scheduled-tasks.js +2 -0
- package/lib/public/modules/scheduler.js +2 -0
- package/lib/public/modules/search-clay-chat.js +5 -454
- package/lib/public/modules/session-create-form.js +10 -10
- package/lib/public/modules/settings-defaults.js +27 -28
- package/lib/public/modules/shared-browser.js +2 -0
- package/lib/public/modules/sidebar-mobile.js +4 -3
- package/lib/public/modules/sidebar-projects.js +1 -0
- package/lib/public/modules/sidebar-sessions.js +1 -0
- package/lib/public/modules/split-view.js +7 -16
- package/lib/public/modules/terminal.js +9 -0
- package/lib/public/modules/tools.js +6 -2
- package/lib/public/modules/tui-grab.js +1 -2
- package/lib/public/modules/vendor-login-browser.js +5 -3
- package/lib/public/modules/vendor-login.js +2 -2
- package/lib/public/modules/vendor-priority.js +15 -1
- package/lib/public/modules/wireframe-workbench.js +5 -2
- package/lib/public/muse-avatar.svg +4 -0
- package/lib/public/style.css +2 -0
- package/lib/safe-bash-commands.js +3 -45
- package/lib/sdk-bridge.js +131 -395
- package/lib/sdk-foreground-admission.js +16 -0
- package/lib/sdk-mcp-descriptors.js +2 -37
- package/lib/sdk-message-processor.js +50 -17
- package/lib/sdk-quota-snapshot.js +1 -1
- package/lib/sdk-response-recovery.js +1 -155
- package/lib/sdk-worker.js +2 -677
- package/lib/server-contextual-clay-settings.js +40 -0
- package/lib/server-contextual-mate-sessions.js +70 -0
- package/lib/server-home-chat.js +7 -1
- package/lib/server-home-clay-entry.js +23 -4
- package/lib/server-home-clay-session-links.js +12 -0
- package/lib/server-owner-ui.js +49 -8
- package/lib/server-settings.js +2 -0
- package/lib/server.js +18 -1
- package/lib/session-fork-history.js +21 -0
- package/lib/session-hygiene.js +3 -35
- package/lib/session-permission-mode.js +4 -3
- package/lib/session-skill-scope.js +11 -0
- package/lib/sessions.js +24 -89
- package/lib/terminal-manager.js +18 -11
- package/lib/tool-llm.js +1 -1
- package/lib/tui-transcript-index.js +1 -132
- package/lib/users-contextual-clay-preferences.js +31 -0
- package/lib/users.js +3 -0
- package/lib/vendor-login-browser.js +1 -11
- package/lib/workspace-assignment-service.js +25 -0
- package/lib/workspace-query-mcp-server.js +1 -1
- package/lib/ws-request.js +1 -0
- package/lib/ws-schema.js +10 -0
- package/lib/yoke/acp-agent-profiles.js +5 -3
- package/lib/yoke/adapters/antigravity.js +3 -14
- package/lib/yoke/adapters/claude-query-contract.js +9 -0
- package/lib/yoke/adapters/claude.js +7 -42
- package/lib/yoke/adapters/codex-query-contract.js +24 -0
- package/lib/yoke/adapters/codex.js +1 -0
- package/lib/yoke/adapters/kiro.js +3 -1
- package/lib/yoke/adapters/muse-query-contract.js +7 -0
- package/lib/yoke/adapters/muse.js +122 -0
- package/lib/yoke/auth-events.js +17 -0
- package/lib/yoke/claude-hook-installer.js +271 -0
- package/lib/yoke/claude-jsonl-watcher.js +204 -0
- package/lib/yoke/claude-maintenance.js +55 -0
- package/lib/yoke/claude-permission-patterns.js +53 -0
- package/lib/yoke/claude-response-recovery.js +155 -0
- package/lib/yoke/claude-transcript-hygiene.js +34 -0
- package/lib/yoke/clay-skill-install.js +33 -0
- package/lib/yoke/cli-sessions.js +300 -0
- package/lib/yoke/codex-app-server.js +7 -12
- package/lib/yoke/configuration.js +13 -0
- package/lib/yoke/discovery.js +70 -0
- package/lib/yoke/errors.js +8 -0
- package/lib/yoke/host-environment.js +7 -0
- package/lib/yoke/index.js +9 -51
- package/lib/yoke/interface.js +116 -0
- package/lib/yoke/kiro-acp-server.js +5 -5
- package/lib/yoke/legacy-sessions.js +89 -0
- package/lib/yoke/login-protocol.js +25 -0
- package/lib/yoke/maintenance.js +12 -0
- package/lib/yoke/mcp-proxy.js +32 -0
- package/lib/yoke/mcp-source-adapters.js +91 -0
- package/lib/yoke/mcp-source-context.js +42 -0
- package/lib/yoke/mcp-source-discovery.js +169 -0
- package/lib/yoke/mcp-source-plugins.js +68 -0
- package/lib/yoke/mcp-source-restrictions.js +47 -0
- package/lib/yoke/mcp-tool-policy-normalize.js +28 -0
- package/lib/yoke/model-preferences.js +6 -0
- package/lib/yoke/muse-artifacts.js +36 -0
- package/lib/yoke/muse-controls.js +50 -0
- package/lib/yoke/muse-input.js +64 -0
- package/lib/yoke/muse-items.js +157 -0
- package/lib/yoke/muse-policy.js +52 -0
- package/lib/yoke/muse-query.js +428 -0
- package/lib/yoke/muse-runtime.js +211 -0
- package/lib/yoke/muse-services.js +79 -0
- package/lib/yoke/muse-skills.js +79 -0
- package/lib/yoke/muse-turns.js +71 -0
- package/lib/yoke/options/acp.js +2 -0
- package/lib/yoke/options/antigravity.js +2 -0
- package/lib/yoke/options/claude.js +21 -0
- package/lib/yoke/options/codex-defaults.js +29 -0
- package/lib/yoke/options/codex-settings.js +21 -0
- package/lib/yoke/options/codex.js +6 -0
- package/lib/yoke/options/kiro-defaults.js +22 -0
- package/lib/yoke/options/kiro.js +5 -0
- package/lib/yoke/options/muse.js +5 -0
- package/lib/yoke/provider-contract.js +5 -0
- package/lib/yoke/providers/antigravity-executable.js +8 -0
- package/lib/yoke/providers/claude-executable.js +40 -0
- package/lib/yoke/providers/claude-model-descriptions.js +14 -0
- package/lib/yoke/providers/claude-process.js +97 -0
- package/lib/yoke/providers/claude-provision.js +75 -0
- package/lib/yoke/providers/codex-errors.js +8 -0
- package/lib/yoke/providers/muse-executable.js +17 -0
- package/lib/yoke/provisioning.js +6 -0
- package/lib/yoke/query-options.js +10 -0
- package/lib/yoke/response-recovery.js +41 -0
- package/lib/yoke/sdk-mcp-descriptors.js +37 -0
- package/lib/yoke/sdk-server-factory.js +7 -0
- package/lib/yoke/session-history.js +8 -0
- package/lib/yoke/session-tool-transport.js +31 -13
- package/lib/yoke/settings.js +23 -0
- package/lib/yoke/source-locations.js +4 -0
- package/lib/yoke/title-query.js +18 -7
- package/lib/yoke/tui-transcript-index.js +132 -0
- package/lib/yoke/usage.js +48 -0
- package/lib/yoke/user-input.js +13 -0
- package/lib/yoke/vendor-registry.js +81 -1
- package/package.json +3 -2
package/lib/sessions.js
CHANGED
|
@@ -3,7 +3,7 @@ var path = require("path");
|
|
|
3
3
|
var config = require("./config");
|
|
4
4
|
var utils = require("./utils");
|
|
5
5
|
var users = require("./users");
|
|
6
|
-
var
|
|
6
|
+
var providerDefaults = require("./yoke/settings").defaults();
|
|
7
7
|
var hygiene = require("./session-hygiene");
|
|
8
8
|
var backgroundTaskTiming = require("./background-task-timing");
|
|
9
9
|
var sessionProvenance = require("./session-provenance");
|
|
@@ -39,16 +39,16 @@ function createSessionManager(opts) {
|
|
|
39
39
|
var permissionRequestIndex = {}; // requestId -> sessionLocalId (O(1) lookup)
|
|
40
40
|
var capabilitiesByVendor = null; // set by sdk-bridge after adapter init
|
|
41
41
|
var defaultVendor = null; // set by sdk-bridge
|
|
42
|
-
var codexApproval =
|
|
43
|
-
var codexSandbox =
|
|
44
|
-
var codexWebSearch =
|
|
42
|
+
var codexApproval = providerDefaults.codexApproval;
|
|
43
|
+
var codexSandbox = providerDefaults.codexSandbox;
|
|
44
|
+
var codexWebSearch = providerDefaults.codexWebSearch;
|
|
45
45
|
|
|
46
46
|
// --- Session persistence (centralized in ~/.clay/sessions/{encoded-cwd}/) ---
|
|
47
47
|
var sessionsBase = opts.sessionsBase || path.join(config.CONFIG_DIR, "sessions");
|
|
48
48
|
var encodedCwd = utils.resolveEncodedDir(sessionsBase, cwd);
|
|
49
49
|
var sessionsDir = path.join(sessionsBase, encodedCwd);
|
|
50
50
|
fs.mkdirSync(sessionsDir, { recursive: true });
|
|
51
|
-
var cliSessionsDir = opts.cliSessionsDir ||
|
|
51
|
+
var cliSessionsDir = opts.cliSessionsDir || require("./yoke/legacy-sessions").directory(config.REAL_HOME, cwd);
|
|
52
52
|
var deletedCliSessionsPath = path.join(sessionsDir, "deleted-cli-sessions.json");
|
|
53
53
|
var deletedCliSessionIds = new Set();
|
|
54
54
|
try {
|
|
@@ -77,29 +77,6 @@ function createSessionManager(opts) {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
function readCliTranscriptHead(filePath) {
|
|
81
|
-
var MAX_READ = 64 * 1024;
|
|
82
|
-
try {
|
|
83
|
-
var stat = fs.statSync(filePath);
|
|
84
|
-
var fd = fs.openSync(filePath, "r");
|
|
85
|
-
var bytesToRead = Math.min(stat.size, MAX_READ);
|
|
86
|
-
var buf = Buffer.alloc(bytesToRead);
|
|
87
|
-
fs.readSync(fd, buf, 0, bytesToRead, 0);
|
|
88
|
-
fs.closeSync(fd);
|
|
89
|
-
var lines = buf.toString("utf8").split("\n");
|
|
90
|
-
var events = [];
|
|
91
|
-
for (var i = 0; i < lines.length; i++) {
|
|
92
|
-
if (!lines[i]) continue;
|
|
93
|
-
try {
|
|
94
|
-
var event = JSON.parse(lines[i]);
|
|
95
|
-
if (event && typeof event === "object") events.push(event);
|
|
96
|
-
} catch (e) {}
|
|
97
|
-
}
|
|
98
|
-
return events;
|
|
99
|
-
} catch (e) {
|
|
100
|
-
return null;
|
|
101
|
-
}
|
|
102
|
-
}
|
|
103
80
|
|
|
104
81
|
// Auto-migrate sessions from legacy locations:
|
|
105
82
|
// v1: {cwd}/.claude-relay/sessions/
|
|
@@ -234,8 +211,11 @@ function createSessionManager(opts) {
|
|
|
234
211
|
if (session.claySchedulingReceipts) metaObj.claySchedulingReceipts = session.claySchedulingReceipts;
|
|
235
212
|
if (session.mateCreationMode) metaObj.mateCreationMode = true;
|
|
236
213
|
if (session.homeMateCreationPhase) metaObj.homeMateCreationPhase = session.homeMateCreationPhase;
|
|
214
|
+
if (session.contextualCreationMateStamp) metaObj.contextualCreationMateStamp = session.contextualCreationMateStamp;
|
|
215
|
+
if (session.contextualCreationRequestId) metaObj.contextualCreationRequestId = session.contextualCreationRequestId;
|
|
237
216
|
if (session.homeClayEntryRequestId) metaObj.homeClayEntryRequestId = session.homeClayEntryRequestId;
|
|
238
217
|
if (session.homeClayEntryMode) metaObj.homeClayEntryMode = session.homeClayEntryMode;
|
|
218
|
+
if (session.homeClayEntryText) metaObj.homeClayEntryText = session.homeClayEntryText;
|
|
239
219
|
metaObj.sessionOriginId = provenanceMeta.sessionOriginId;
|
|
240
220
|
if (provenanceMeta.sessionProvenance) metaObj.sessionProvenance = provenanceMeta.sessionProvenance;
|
|
241
221
|
if (Array.isArray(session._workerGenerations)) metaObj.workerGenerations = session._workerGenerations;
|
|
@@ -398,8 +378,11 @@ function createSessionManager(opts) {
|
|
|
398
378
|
if (Array.isArray(m.claySchedulingReceipts)) session.claySchedulingReceipts = m.claySchedulingReceipts;
|
|
399
379
|
if (m.mateCreationMode) session.mateCreationMode = true;
|
|
400
380
|
if (m.homeMateCreationPhase) session.homeMateCreationPhase = m.homeMateCreationPhase;
|
|
381
|
+
if (m.contextualCreationMateStamp) session.contextualCreationMateStamp = m.contextualCreationMateStamp;
|
|
382
|
+
if (m.contextualCreationRequestId) session.contextualCreationRequestId = m.contextualCreationRequestId;
|
|
401
383
|
if (m.homeClayEntryRequestId) session.homeClayEntryRequestId = m.homeClayEntryRequestId;
|
|
402
384
|
if (m.homeClayEntryMode) session.homeClayEntryMode = m.homeClayEntryMode;
|
|
385
|
+
if (m.homeClayEntryText) session.homeClayEntryText = m.homeClayEntryText;
|
|
403
386
|
if (m.ownerId) session.ownerId = m.ownerId;
|
|
404
387
|
session.sessionOriginId = m.sessionOriginId || null;
|
|
405
388
|
session._provenanceMigrationNeeded = sessionProvenance.restore(m, session);
|
|
@@ -470,9 +453,6 @@ function createSessionManager(opts) {
|
|
|
470
453
|
// - Warmup shape: 1 real "hi" prompt, optional automatic interruption,
|
|
471
454
|
// and 0 assistant messages (covered by daemon cleanup but defensive)
|
|
472
455
|
function adoptOrphanedCliSessions() {
|
|
473
|
-
var files;
|
|
474
|
-
try { files = fs.readdirSync(cliSessionsDir); } catch (e) { return; }
|
|
475
|
-
|
|
476
456
|
// Build set of cliSessionIds Clay already tracks
|
|
477
457
|
var knownCliIds = new Set();
|
|
478
458
|
sessions.forEach(function (s) {
|
|
@@ -481,56 +461,10 @@ function createSessionManager(opts) {
|
|
|
481
461
|
|
|
482
462
|
var adopted = 0;
|
|
483
463
|
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
var
|
|
487
|
-
|
|
488
|
-
if (deletedCliSessionIds.has(cliSid)) continue;
|
|
489
|
-
|
|
490
|
-
var fp = path.join(cliSessionsDir, files[i]);
|
|
491
|
-
var events = readCliTranscriptHead(fp);
|
|
492
|
-
if (!events) continue;
|
|
493
|
-
var userCount = 0;
|
|
494
|
-
var firstUserText = null;
|
|
495
|
-
var createdAtIso = null;
|
|
496
|
-
for (var li = 0; li < events.length; li++) {
|
|
497
|
-
var ev = events[li];
|
|
498
|
-
if (ev.type === "user" && ev.message && ev.message.role === "user") {
|
|
499
|
-
userCount++;
|
|
500
|
-
if (firstUserText == null) {
|
|
501
|
-
var c = ev.message.content;
|
|
502
|
-
if (typeof c === "string") {
|
|
503
|
-
firstUserText = c;
|
|
504
|
-
} else if (Array.isArray(c)) {
|
|
505
|
-
var parts = [];
|
|
506
|
-
for (var ci = 0; ci < c.length; ci++) {
|
|
507
|
-
if (c[ci] && c[ci].type === "text" && typeof c[ci].text === "string") parts.push(c[ci].text);
|
|
508
|
-
}
|
|
509
|
-
firstUserText = parts.join("");
|
|
510
|
-
}
|
|
511
|
-
if (ev.timestamp && !createdAtIso) createdAtIso = ev.timestamp;
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
if (userCount === 0) continue;
|
|
517
|
-
// Defensive warmup skip (daemon also cleans these)
|
|
518
|
-
if (hygiene.isClaudeWarmupTranscript(events)) continue;
|
|
519
|
-
|
|
520
|
-
// Title: first user message trimmed/truncated. Fall back to a
|
|
521
|
-
// neutral label so the sidebar entry is identifiable.
|
|
522
|
-
var title = (firstUserText || "").trim().replace(/\s+/g, " ");
|
|
523
|
-
if (title.length > 60) title = title.slice(0, 57) + "...";
|
|
524
|
-
if (!title) title = "Imported CLI session";
|
|
525
|
-
|
|
526
|
-
var createdAt = Date.now();
|
|
527
|
-
if (createdAtIso) {
|
|
528
|
-
var t = Date.parse(createdAtIso);
|
|
529
|
-
if (!isNaN(t)) createdAt = t;
|
|
530
|
-
}
|
|
531
|
-
var lastActivity = createdAt;
|
|
532
|
-
try { lastActivity = fs.statSync(fp).mtimeMs; } catch (e) {}
|
|
533
|
-
|
|
464
|
+
var candidates = require("./yoke/legacy-sessions").candidates(cliSessionsDir, knownCliIds, deletedCliSessionIds);
|
|
465
|
+
for (var i = 0; i < candidates.length; i++) {
|
|
466
|
+
var candidate = candidates[i], cliSid = candidate.id;
|
|
467
|
+
var title = candidate.title, createdAt = candidate.createdAt, lastActivity = candidate.lastActivity;
|
|
534
468
|
var localId = nextLocalId++;
|
|
535
469
|
var session = {
|
|
536
470
|
localId: localId,
|
|
@@ -548,7 +482,7 @@ function createSessionManager(opts) {
|
|
|
548
482
|
history: [],
|
|
549
483
|
messageUUIDs: [],
|
|
550
484
|
lastRewindUuid: null,
|
|
551
|
-
vendor:
|
|
485
|
+
vendor: candidate.vendor,
|
|
552
486
|
mode: "tui",
|
|
553
487
|
// Adopted from an external `claude` run in this cwd, not started by
|
|
554
488
|
// Clay. Notification emitters must stay silent for these until the
|
|
@@ -580,10 +514,7 @@ function createSessionManager(opts) {
|
|
|
580
514
|
var stale = [];
|
|
581
515
|
sessions.forEach(function (session) {
|
|
582
516
|
if (!session.adopted || !session.cliSessionId) return;
|
|
583
|
-
|
|
584
|
-
var events = readCliTranscriptHead(transcriptPath);
|
|
585
|
-
var missingEmptySource = !fs.existsSync(transcriptPath) && (!session.history || session.history.length === 0);
|
|
586
|
-
if (missingEmptySource || (events && hygiene.isClaudeWarmupTranscript(events))) stale.push(session.localId);
|
|
517
|
+
if (require("./yoke/legacy-sessions").obsolete(cliSessionsDir, session)) stale.push(session.localId);
|
|
587
518
|
});
|
|
588
519
|
for (var i = 0; i < stale.length; i++) deleteSessionQuiet(stale[i]);
|
|
589
520
|
if (stale.length > 0) {
|
|
@@ -636,6 +567,7 @@ function createSessionManager(opts) {
|
|
|
636
567
|
var unreadMap = wsUnread || singleUserUnread;
|
|
637
568
|
return {
|
|
638
569
|
id: s.localId,
|
|
570
|
+
sessionOriginId: s.sessionOriginId || null,
|
|
639
571
|
cliSessionId: s.cliSessionId || null,
|
|
640
572
|
createdAt: s.createdAt || 0,
|
|
641
573
|
title: s.title || "New Session",
|
|
@@ -877,7 +809,8 @@ function createSessionManager(opts) {
|
|
|
877
809
|
if (_item && (_item.type === "mention_user" || _item.type === "mention_response")) {
|
|
878
810
|
console.log("[DEBUG replayHistory] sending mention at index=" + i + " from=" + fromIndex + " total=" + total + " type=" + _item.type + " mate=" + (_item.mateName || ""));
|
|
879
811
|
}
|
|
880
|
-
var
|
|
812
|
+
var normalizedItem = require("./yoke/usage").clientEvent(_item, session.vendor || "claude");
|
|
813
|
+
var replayItem = transform ? transform(normalizedItem) : normalizedItem;
|
|
881
814
|
if (replayItem && typeof replayItem === "object") replayItem = Object.assign({}, replayItem, { _historyIndex: i });
|
|
882
815
|
_send(replayItem);
|
|
883
816
|
}
|
|
@@ -887,9 +820,11 @@ function createSessionManager(opts) {
|
|
|
887
820
|
var lastModelUsage = null;
|
|
888
821
|
var lastCost = null;
|
|
889
822
|
var lastStreamInputTokens = null;
|
|
823
|
+
var lastContext = null;
|
|
890
824
|
for (var j = total - 1; j >= 0; j--) {
|
|
891
825
|
if (session.history[j].type === "result") {
|
|
892
|
-
var r = session.history[j];
|
|
826
|
+
var r = require("./yoke/usage").clientEvent(session.history[j], session.vendor || "claude");
|
|
827
|
+
lastContext = r.context || null;
|
|
893
828
|
lastUsage = r.usage || null;
|
|
894
829
|
lastModelUsage = r.modelUsage || null;
|
|
895
830
|
lastCost = r.cost != null ? r.cost : null;
|
|
@@ -898,7 +833,7 @@ function createSessionManager(opts) {
|
|
|
898
833
|
}
|
|
899
834
|
}
|
|
900
835
|
|
|
901
|
-
_send({ type: "history_done", lastUsage: lastUsage, lastModelUsage: lastModelUsage, lastCost: lastCost, lastStreamInputTokens: lastStreamInputTokens, contextUsage: session.lastContextUsage ||
|
|
836
|
+
_send({ type: "history_done", controlHistoryRevision: require("./clay-control-target-projection").digest(session.history || []), sessionId: session.localId, sessionOriginId: session.sessionOriginId || null, lastUsage: lastUsage, lastModelUsage: lastModelUsage, lastCost: lastCost, lastStreamInputTokens: lastStreamInputTokens, context: lastContext, contextUsage: require("./yoke/usage").context(session.lastContextUsage, session.vendor || "claude") });
|
|
902
837
|
for (var vi = 0; vi < sessionViewedListeners.length; vi++) {
|
|
903
838
|
try { sessionViewedListeners[vi](session, targetWs, _send); } catch (e) {}
|
|
904
839
|
}
|
package/lib/terminal-manager.js
CHANGED
|
@@ -83,7 +83,7 @@ function createTerminalManager(opts) {
|
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
// Broadcast to subscribers
|
|
86
|
-
var msg = JSON.stringify({ type: "term_output", id: id, data: data });
|
|
86
|
+
var msg = JSON.stringify(Object.assign({ type: "term_output", id: id, data: data }, controlProjection(id)));
|
|
87
87
|
for (var ws of session.subscribers) {
|
|
88
88
|
if (ws.readyState === 1) ws.send(msg);
|
|
89
89
|
}
|
|
@@ -108,7 +108,7 @@ function createTerminalManager(opts) {
|
|
|
108
108
|
}
|
|
109
109
|
|
|
110
110
|
// Broadcast updated list
|
|
111
|
-
send({ type: "term_list", terminals: list() });
|
|
111
|
+
send({ type: "term_list", terminals: list(true) });
|
|
112
112
|
|
|
113
113
|
// Caller-supplied hook: e.g. TUI session manager uses this to delete
|
|
114
114
|
// its session record once `claude` exits, so stale entries don't pile
|
|
@@ -181,10 +181,11 @@ function createTerminalManager(opts) {
|
|
|
181
181
|
session.cols = cols;
|
|
182
182
|
session.rows = rows;
|
|
183
183
|
// Notify other subscribers about the resize so their xterm stays in sync
|
|
184
|
-
var msg = JSON.stringify({ type: "term_resized", id: id, cols: cols, rows: rows });
|
|
184
|
+
var msg = JSON.stringify(Object.assign({ type: "term_resized", id: id, cols: cols, rows: rows }, controlProjection(id)));
|
|
185
185
|
for (var ws of session.subscribers) {
|
|
186
186
|
if (ws.readyState === 1 && ws !== sourceWs) ws.send(msg);
|
|
187
187
|
}
|
|
188
|
+
if (sourceWs && sourceWs.readyState === 1) sourceWs.send(JSON.stringify(Object.assign({ type: "term_control_projection", id: id }, controlProjection(id))));
|
|
188
189
|
} catch (e) {}
|
|
189
190
|
}
|
|
190
191
|
}
|
|
@@ -244,15 +245,24 @@ function createTerminalManager(opts) {
|
|
|
244
245
|
session.title = String(title).substring(0, 50);
|
|
245
246
|
}
|
|
246
247
|
|
|
247
|
-
function
|
|
248
|
+
function inspect(id) {
|
|
249
|
+
var terminal = terminals.get(id);
|
|
250
|
+
return terminal ? { id: id, identity: terminal.identity, ownerId: terminal.ownerId, osIdentity: terminal.osIdentity, title: terminal.title, kind: terminal.kind, exited: terminal.exited, cols: terminal.cols, rows: terminal.rows, totalBytesWritten: terminal.totalBytesWritten } : null;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
function controlProjection(id) { var item = inspect(id); return item ? { controlIdentity: item.identity, controlRevision: require('./clay-control-target-projection').digest(item) } : {}; }
|
|
254
|
+
|
|
255
|
+
function list(control) {
|
|
248
256
|
var result = [];
|
|
249
257
|
for (var session of terminals.values()) {
|
|
250
|
-
|
|
258
|
+
var entry = {
|
|
251
259
|
id: session.id,
|
|
252
260
|
title: session.title,
|
|
253
261
|
kind: session.kind,
|
|
254
262
|
exited: session.exited,
|
|
255
|
-
}
|
|
263
|
+
};
|
|
264
|
+
if (control) { entry.controlIdentity = session.identity; entry.controlRevision = require('./clay-control-target-projection').digest(inspect(session.id)); }
|
|
265
|
+
result.push(entry);
|
|
256
266
|
}
|
|
257
267
|
return result;
|
|
258
268
|
}
|
|
@@ -333,10 +343,7 @@ function createTerminalManager(opts) {
|
|
|
333
343
|
resize: resize,
|
|
334
344
|
close: close,
|
|
335
345
|
closeAndWait: closeAndWait,
|
|
336
|
-
inspect:
|
|
337
|
-
var terminal = terminals.get(id);
|
|
338
|
-
return terminal ? { id: id, identity: terminal.identity, ownerId: terminal.ownerId, osIdentity: terminal.osIdentity, title: terminal.title, kind: terminal.kind, exited: terminal.exited, cols: terminal.cols, rows: terminal.rows, totalBytesWritten: terminal.totalBytesWritten } : null;
|
|
339
|
-
},
|
|
346
|
+
inspect: inspect,
|
|
340
347
|
resizeExact: function (id, cols, rows, identity, revalidate) {
|
|
341
348
|
revalidate();
|
|
342
349
|
var terminal = terminals.get(id);
|
|
@@ -345,7 +352,7 @@ function createTerminalManager(opts) {
|
|
|
345
352
|
revalidate();
|
|
346
353
|
if (terminals.get(id) !== terminal) throw new Error('Terminal was replaced during resize.');
|
|
347
354
|
terminal.cols = cols; terminal.rows = rows;
|
|
348
|
-
var message = JSON.stringify({ type: 'term_resized', id: id, cols: cols, rows: rows });
|
|
355
|
+
var message = JSON.stringify(Object.assign({ type: 'term_resized', id: id, cols: cols, rows: rows }, controlProjection(id)));
|
|
349
356
|
terminal.subscribers.forEach(function (ws) { if (ws.readyState === 1) ws.send(message); });
|
|
350
357
|
return true;
|
|
351
358
|
},
|
package/lib/tool-llm.js
CHANGED
|
@@ -36,7 +36,7 @@ async function complete(opts) {
|
|
|
36
36
|
skipProjectInstructions: true,
|
|
37
37
|
skipSkills: true,
|
|
38
38
|
canUseTool: function () { return Promise.resolve({ behavior: "deny", message: "Capsule LLM calls cannot use tools." }); },
|
|
39
|
-
adapterOptions: {
|
|
39
|
+
adapterOptions: require("./yoke/query-options").map(adapter, { instructionSources: ["user"], readOnly: true, isolation: { user: opts.linuxUser || undefined } }),
|
|
40
40
|
},
|
|
41
41
|
});
|
|
42
42
|
return result.text;
|
|
@@ -1,132 +1 @@
|
|
|
1
|
-
|
|
2
|
-
//
|
|
3
|
-
// Build a per-session index of assistant text messages from a Claude Code
|
|
4
|
-
// TUI transcript (~/.claude/projects/{encoded-cwd}/{cliSessionId}.jsonl).
|
|
5
|
-
//
|
|
6
|
-
// The TUI message-grab feature uses this index on the client to map a
|
|
7
|
-
// hovered xterm region back to its original markdown source. We only
|
|
8
|
-
// extract assistant text blocks. Tool calls and tool results render in
|
|
9
|
-
// the terminal in a shape that's quite different from their JSONL form,
|
|
10
|
-
// so matching them would be noisy and not particularly useful.
|
|
11
|
-
//
|
|
12
|
-
// Codex sessions don't write transcripts at all; callers should skip
|
|
13
|
-
// indexing for non-claude vendors.
|
|
14
|
-
|
|
15
|
-
var fs = require("fs");
|
|
16
|
-
var path = require("path");
|
|
17
|
-
var utils = require("./utils");
|
|
18
|
-
var { REAL_HOME } = require("./config");
|
|
19
|
-
|
|
20
|
-
var encodeCwd = utils.encodeCwd;
|
|
21
|
-
|
|
22
|
-
// Min length of a normalized assistant message to bother indexing.
|
|
23
|
-
// Anything shorter ("ok", "sure", "yes") would generate too many false
|
|
24
|
-
// positives when matched against a hovered terminal block.
|
|
25
|
-
var MIN_MATCH_LEN = 20;
|
|
26
|
-
|
|
27
|
-
// Claude Code writes its JSONL under the *running user's* home, which
|
|
28
|
-
// is not the daemon's home in OS-isolation mode (where each Clay user
|
|
29
|
-
// gets a real Linux account like /home/clay-name). Callers from the
|
|
30
|
-
// project context resolve the session owner's home and pass it in;
|
|
31
|
-
// when no home is provided we fall back to REAL_HOME for single-user
|
|
32
|
-
// installs.
|
|
33
|
-
function transcriptFilePath(home, cwd, cliSessionId) {
|
|
34
|
-
if (!cwd || !cliSessionId) return null;
|
|
35
|
-
var base = home || REAL_HOME;
|
|
36
|
-
return path.join(base, ".claude", "projects", encodeCwd(cwd), cliSessionId + ".jsonl");
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// matchKey is just whitespace-normalized raw markdown. We intentionally
|
|
40
|
-
// do NOT strip markdown decoration here.
|
|
41
|
-
//
|
|
42
|
-
// Reasoning: TUI rendering drops markdown markers (**bold** -> bold,
|
|
43
|
-
// `code` -> code, [text](url) -> text, etc.) and the visible text is
|
|
44
|
-
// therefore always a substring of the JSONL source's character stream.
|
|
45
|
-
// If we strip server-side we risk getting the rules wrong on corner
|
|
46
|
-
// cases (double-backtick code spans, list-bullet rendering quirks,
|
|
47
|
-
// nested formatting) and the resulting matchKey diverges from both
|
|
48
|
-
// sides at once. Leaving the source alone and letting the client do a
|
|
49
|
-
// multi-offset substring probe against block text is more robust and
|
|
50
|
-
// much less code.
|
|
51
|
-
function normalizeForMatch(s) {
|
|
52
|
-
if (!s) return "";
|
|
53
|
-
return s.replace(/\s+/g, " ").trim();
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Stable per-message id. Claude's JSONL usually carries one of these but
|
|
57
|
-
// fall back to the parsed line index so the client always has a handle.
|
|
58
|
-
function messageId(obj, index) {
|
|
59
|
-
if (obj && obj.uuid) return String(obj.uuid);
|
|
60
|
-
if (obj && obj.message && obj.message.id) return String(obj.message.id);
|
|
61
|
-
return "msg-" + index;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
// Parse a single JSONL line into one assistant-text record, or null
|
|
65
|
-
// when the line isn't one we index (user prompts, tool_use blocks,
|
|
66
|
-
// tool_result records, etc.). Text-only assistant blocks get joined
|
|
67
|
-
// in order so a multi-block response surfaces as a single grabbable
|
|
68
|
-
// message.
|
|
69
|
-
function parseAssistantTextLine(line, index) {
|
|
70
|
-
var obj;
|
|
71
|
-
try { obj = JSON.parse(line); } catch (e) { return null; }
|
|
72
|
-
if (!obj || !obj.message) return null;
|
|
73
|
-
if (obj.message.role !== "assistant") return null;
|
|
74
|
-
if (!Array.isArray(obj.message.content)) return null;
|
|
75
|
-
|
|
76
|
-
var text = "";
|
|
77
|
-
for (var i = 0; i < obj.message.content.length; i++) {
|
|
78
|
-
var block = obj.message.content[i];
|
|
79
|
-
if (block && block.type === "text" && typeof block.text === "string") {
|
|
80
|
-
text += block.text;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
text = text.trim();
|
|
84
|
-
if (!text) return null;
|
|
85
|
-
|
|
86
|
-
var normalized = normalizeForMatch(text);
|
|
87
|
-
if (normalized.length < MIN_MATCH_LEN) return null;
|
|
88
|
-
|
|
89
|
-
return {
|
|
90
|
-
id: messageId(obj, index),
|
|
91
|
-
text: text,
|
|
92
|
-
matchKey: normalized,
|
|
93
|
-
};
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
// Read the whole transcript and return the assistant text index. The
|
|
97
|
-
// transcripts are small enough (KB to a few MB) that a sync read on
|
|
98
|
-
// session open or on a watcher-driven update is fine. We also return
|
|
99
|
-
// the mtime/size so callers can short-circuit re-reads when nothing
|
|
100
|
-
// changed.
|
|
101
|
-
function readAssistantIndex(home, cwd, cliSessionId) {
|
|
102
|
-
var file = transcriptFilePath(home, cwd, cliSessionId);
|
|
103
|
-
if (!file) return { messages: [], mtimeMs: 0, byteLength: 0 };
|
|
104
|
-
var raw;
|
|
105
|
-
var stat;
|
|
106
|
-
try {
|
|
107
|
-
raw = fs.readFileSync(file, "utf8");
|
|
108
|
-
stat = fs.statSync(file);
|
|
109
|
-
} catch (e) {
|
|
110
|
-
return { messages: [], mtimeMs: 0, byteLength: 0 };
|
|
111
|
-
}
|
|
112
|
-
var lines = raw.split("\n");
|
|
113
|
-
var messages = [];
|
|
114
|
-
for (var i = 0; i < lines.length; i++) {
|
|
115
|
-
if (!lines[i]) continue;
|
|
116
|
-
var entry = parseAssistantTextLine(lines[i], i);
|
|
117
|
-
if (entry) messages.push(entry);
|
|
118
|
-
}
|
|
119
|
-
return {
|
|
120
|
-
messages: messages,
|
|
121
|
-
mtimeMs: stat.mtimeMs || 0,
|
|
122
|
-
byteLength: stat.size || raw.length,
|
|
123
|
-
};
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
module.exports = {
|
|
127
|
-
MIN_MATCH_LEN: MIN_MATCH_LEN,
|
|
128
|
-
transcriptFilePath: transcriptFilePath,
|
|
129
|
-
readAssistantIndex: readAssistantIndex,
|
|
130
|
-
parseAssistantTextLine: parseAssistantTextLine,
|
|
131
|
-
normalizeForMatch: normalizeForMatch,
|
|
132
|
-
};
|
|
1
|
+
module.exports = require("./yoke/tui-transcript-index");
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Personal geometry is never a canonical product-control operation.
|
|
2
|
+
function validGeometry(value) {
|
|
3
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
|
|
4
|
+
var keys = Object.keys(value);
|
|
5
|
+
if (keys.length !== 4 || keys.some(function (key) { return ['left', 'top', 'width', 'height'].indexOf(key) < 0; })) return false;
|
|
6
|
+
return keys.every(function (key) { return typeof value[key] === 'number' && Number.isFinite(value[key]); }) &&
|
|
7
|
+
value.left >= 0 && value.left <= 16384 && value.top >= 0 && value.top <= 16384 &&
|
|
8
|
+
value.width >= 340 && value.width <= 2560 && value.height >= 360 && value.height <= 2160;
|
|
9
|
+
}
|
|
10
|
+
function attachContextualClayPreferences(deps) {
|
|
11
|
+
function get(userId) {
|
|
12
|
+
var user = deps.loadUsers().users.find(function (entry) { return entry.id === userId; });
|
|
13
|
+
var saved = user && user.contextualClayGeometry;
|
|
14
|
+
if (!user && userId === 'default') saved = require('./config').loadConfig().contextualClayGeometry;
|
|
15
|
+
return validGeometry(saved) ? Object.assign({}, saved) : null;
|
|
16
|
+
}
|
|
17
|
+
function set(userId, geometry) {
|
|
18
|
+
if (!validGeometry(geometry)) return { error: 'Invalid popover geometry.' };
|
|
19
|
+
var value = Object.assign({}, geometry);
|
|
20
|
+
var data = deps.loadUsers();
|
|
21
|
+
var user = data.users.find(function (entry) { return entry.id === userId; });
|
|
22
|
+
if (user) { user.contextualClayGeometry = value; deps.saveUsers(data); }
|
|
23
|
+
else if (userId === 'default') {
|
|
24
|
+
var config = require('./config');
|
|
25
|
+
config.saveConfig(Object.assign({}, config.loadConfig(), { contextualClayGeometry: value }));
|
|
26
|
+
} else return { error: 'Account no longer available.' };
|
|
27
|
+
return { geometry: value };
|
|
28
|
+
}
|
|
29
|
+
return { getContextualClayGeometry: get, setContextualClayGeometry: set };
|
|
30
|
+
}
|
|
31
|
+
module.exports = { attachContextualClayPreferences: attachContextualClayPreferences, validGeometry: validGeometry };
|
package/lib/users.js
CHANGED
|
@@ -332,6 +332,7 @@ var creation = require("./users-create").attachCreation({ loadUsers: loadUsers,
|
|
|
332
332
|
var createUser = creation.createUser;
|
|
333
333
|
var createUserWithoutPin = creation.createUserWithoutPin;
|
|
334
334
|
var permissions = attachPermissions({ loadUsers: loadUsers, saveUsers: saveUsers, findUserById: findUserById });
|
|
335
|
+
var contextualClayPreferences = require("./users-contextual-clay-preferences").attachContextualClayPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
|
|
335
336
|
var preferences = attachPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
|
|
336
337
|
var projectMateFilterPreferences = attachProjectMateFilterPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
|
|
337
338
|
var homeDockPreferences = attachHomeDockPreferences({ loadUsers: loadUsers, saveUsers: saveUsers });
|
|
@@ -478,6 +479,8 @@ module.exports = {
|
|
|
478
479
|
setToolPalette: setToolPalette,
|
|
479
480
|
getTerminalFont: getTerminalFont,
|
|
480
481
|
setTerminalFont: setTerminalFont,
|
|
482
|
+
getContextualClayGeometry: contextualClayPreferences.getContextualClayGeometry,
|
|
483
|
+
setContextualClayGeometry: contextualClayPreferences.setContextualClayGeometry,
|
|
481
484
|
getHomeDockPreference: getHomeDockPreference,
|
|
482
485
|
setHomeDockPreference: setHomeDockPreference,
|
|
483
486
|
getHomeSurfacePreference: getHomeSurfacePreference,
|
|
@@ -7,17 +7,7 @@ var spawn = require("child_process").spawn;
|
|
|
7
7
|
var buildUserEnv = require("./build-user-env").buildUserEnv;
|
|
8
8
|
var wrapSpawnAsUser = require("./os-users").wrapSpawnAsUser;
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
try {
|
|
12
|
-
var url = new URL(value);
|
|
13
|
-
var hosts = vendor === "codex" ? ["auth.openai.com"] : ["claude.ai", "claude.com", "console.anthropic.com", "platform.claude.com"];
|
|
14
|
-
if (url.protocol !== "https:" || url.username || url.password || hosts.indexOf(url.hostname) < 0) return null;
|
|
15
|
-
if (!url.searchParams.get("state") || !url.searchParams.get("code_challenge")) return null;
|
|
16
|
-
var callback = new URL(url.searchParams.get("redirect_uri"));
|
|
17
|
-
if (callback.protocol !== "http:" || ["localhost", "127.0.0.1", "[::1]"].indexOf(callback.hostname) < 0) return null;
|
|
18
|
-
return url.href;
|
|
19
|
-
} catch (e) { return null; }
|
|
20
|
-
}
|
|
10
|
+
var loginUrl = require("./yoke/login-protocol").loginUrl;
|
|
21
11
|
|
|
22
12
|
function createLoginBrowser(options) {
|
|
23
13
|
var initial = null;
|
|
@@ -19,6 +19,8 @@ function attachWorkspaceAssignmentService(ctx) {
|
|
|
19
19
|
var records = [];
|
|
20
20
|
var unsubscribers = {};
|
|
21
21
|
var pendingRestartReplay = {};
|
|
22
|
+
var presentations = new WeakMap();
|
|
23
|
+
var presentationTargets = new WeakMap();
|
|
22
24
|
|
|
23
25
|
function recordFile(userId) {
|
|
24
26
|
return storageDir ? path.join(storageDir, userStorageKey(userId)) : null;
|
|
@@ -130,7 +132,27 @@ function attachWorkspaceAssignmentService(ctx) {
|
|
|
130
132
|
return { project: project, manager: manager, session: session };
|
|
131
133
|
}
|
|
132
134
|
|
|
135
|
+
function present(record, principal) {
|
|
136
|
+
try {
|
|
137
|
+
var display = ctx.getPresentation && ctx.getPresentation(principal), project = exactTarget(record);
|
|
138
|
+
if (!display || !project) return;
|
|
139
|
+
var view = { project: project, session: null, origin: null }; presentationTargets.set(record, view);
|
|
140
|
+
if (record.delivery === 'follow_up') project.getSessionManager().sessions.forEach(function (session) { if (ctx.sessionRef(record.targetProjectSlug, session) === record.targetSessionRef) { view.session = session; view.origin = session.sessionOriginId; } });
|
|
141
|
+
var projection = require('./clay-control-target-projection');
|
|
142
|
+
var target = view.session ? { kind: 'session', projectSlug: record.targetProjectSlug, sessionId: view.session.localId, sessionOriginId: view.origin } : { kind: 'project', projectSlug: record.targetProjectSlug, runtimeId: projection.runtime(project.getSessionManager()) };
|
|
143
|
+
var observer = display.track('assignment', record.delivery === 'follow_up' ? 'follow_up' : 'assign', target, function () {
|
|
144
|
+
if (exactTarget(record) !== project || view.session && (project.getSessionManager().sessions.get(view.session.localId) !== view.session || view.session.sessionOriginId !== view.origin || (view.session.ownerId || null) !== (record.userId || null))) throw new Error('Assignment target changed.');
|
|
145
|
+
}, 'review');
|
|
146
|
+
if (observer) { presentations.set(record, observer); var expiry = setTimeout(function () { presentations.delete(record); }, 300000); expiry.unref(); }
|
|
147
|
+
} catch (error) { /* Presentation never changes the assignment decision. */ }
|
|
148
|
+
}
|
|
133
149
|
function emit(record, eventType) {
|
|
150
|
+
var observer = presentations.get(record);
|
|
151
|
+
if (observer) {
|
|
152
|
+
if (record.status === 'starting') observer.phase('queued');
|
|
153
|
+
else if (record.status === 'running') observer.phase('running');
|
|
154
|
+
else if (record.status !== 'proposed') { observer.finish({ status: record.status === 'interrupted' ? 'failed' : record.status, outcome: record.status === 'interrupted' ? 'uncertain' : undefined }); presentations.delete(record); }
|
|
155
|
+
}
|
|
134
156
|
var source = exactSource(record);
|
|
135
157
|
if (!source) return;
|
|
136
158
|
source.manager.sendAndRecord(source.session, {
|
|
@@ -193,6 +215,7 @@ function attachWorkspaceAssignmentService(ctx) {
|
|
|
193
215
|
if (!exactSource(record)) throw new Error("Source conversation is no longer available.");
|
|
194
216
|
records.push(record);
|
|
195
217
|
saveUser(record.userId);
|
|
218
|
+
present(record, principal);
|
|
196
219
|
emit(record, "project_assignment_proposal");
|
|
197
220
|
return publicRecord(record);
|
|
198
221
|
}
|
|
@@ -229,6 +252,7 @@ function attachWorkspaceAssignmentService(ctx) {
|
|
|
229
252
|
if (!exactSource(record)) throw new Error("Source conversation is no longer available.");
|
|
230
253
|
records.push(record);
|
|
231
254
|
saveUser(record.userId);
|
|
255
|
+
present(record, principal);
|
|
232
256
|
emit(record, "project_assignment_proposal");
|
|
233
257
|
return publicRecord(record);
|
|
234
258
|
}
|
|
@@ -267,6 +291,7 @@ function attachWorkspaceAssignmentService(ctx) {
|
|
|
267
291
|
}
|
|
268
292
|
|
|
269
293
|
function watch(record, manager, session) {
|
|
294
|
+
var view = presentationTargets.get(record); if (view) { view.session = session; view.origin = session.sessionOriginId; var observer = presentations.get(record); if (observer && observer.target) observer.target({ kind: 'session', projectSlug: record.targetProjectSlug, sessionId: session.localId, sessionOriginId: session.sessionOriginId }); }
|
|
270
295
|
var unsubscribe = manager.subscribeSession(session.localId, function (event) {
|
|
271
296
|
if (event.type === "session_id") {
|
|
272
297
|
record.targetSessionRef = ctx.sessionRef(record.targetProjectSlug, session);
|
|
@@ -32,7 +32,7 @@ function handler(bound, method) {
|
|
|
32
32
|
};
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
var SESSION_CITATION = "
|
|
35
|
+
var SESSION_CITATION = " Cite returned sessions as Markdown links: [short human label](session:OPAQUE_REFERENCE), replacing the entire target with the exact returned sessionRef. Do not prepend a project name, change the opaque reference, or wrap citations in backticks/code. For a specific returned entry, append #entry-<entryIndex> using its exact returned entry index. Legacy [clayos/<sessionRef> — short label] remains supported. Labels are presentation only; Clay Studio resolves the exact reference with the authenticated owner's permissions.";
|
|
36
36
|
|
|
37
37
|
function commonTools(bound) {
|
|
38
38
|
return [
|
package/lib/ws-request.js
CHANGED
package/lib/ws-schema.js
CHANGED
|
@@ -11,6 +11,11 @@
|
|
|
11
11
|
* Use it as a reference when adding new message types.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
+
// message.contextualSnapshot: version=1, viewId, nullable projectTitle,
|
|
15
|
+
// conversationTitle/projectSlug (160 characters), nullable positive sessionId,
|
|
16
|
+
// screen=Home|Project|Direct message. Private canonical owner delivery only.
|
|
17
|
+
// Persisted user_message metadata is echoed in message_ack only after recording;
|
|
18
|
+
// queued admission ACK has no snapshot. Never part of user text/pastes or authority.
|
|
14
19
|
var schema = {
|
|
15
20
|
|
|
16
21
|
// -----------------------------------------------------------------------
|
|
@@ -62,6 +67,7 @@ var schema = {
|
|
|
62
67
|
"project_assignment_response": { direction: "c2s", handler: "lib/workspace-assignment-service.js", description: "Approve or cancel an exact-session cross-project assignment proposal" },
|
|
63
68
|
"home_clay_session_resolve": { direction: "c2s", handler: "lib/server-home-chat.js", description: "Resolve an opaque Mate session reference through the exact bound source session" },
|
|
64
69
|
"home_clay_session_target": { direction: "s2c", handler: "lib/public/modules/command-palette.js", description: "Return an owner-validated navigation target for a rendered Mate session reference" },
|
|
70
|
+
"home_clay_contextual_session": { direction: "s2c", handler: "lib/public/modules/contextual-search.js", description: "Return the exact fresh canonical session for an idempotent contextual Ask request; does not submit or execute a message" },
|
|
65
71
|
"issues_list": { direction: "c2s", handler: "lib/project-issues.js", description: "Project Issues issues list" },
|
|
66
72
|
"issue_read": { direction: "c2s", handler: "lib/project-issues.js", description: "Project Issues issue read" },
|
|
67
73
|
"issue_create": { direction: "c2s", handler: "lib/project-issues.js", description: "Project Issues issue create" },
|
|
@@ -482,6 +488,7 @@ var schema = {
|
|
|
482
488
|
"term_resized": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Terminal was resized" },
|
|
483
489
|
"term_exited": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Terminal process exited" },
|
|
484
490
|
"term_closed": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Terminal session was closed" },
|
|
491
|
+
"term_control_projection": { direction: "s2c", handler: "lib/public/modules/terminal.js", description: "Authoritative terminal identity/revision after owner resize without echoing resize input" },
|
|
485
492
|
"term_list": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Full list of open terminals" },
|
|
486
493
|
"term_error": { direction: "s2c", handler: "lib/public/modules/app-messages.js", description: "Terminal error (e.g. access denied)" },
|
|
487
494
|
"shell_command": { direction: "c2s", handler: "lib/project-shell-command.js", description: "Run a one-shot shell command for agent context" },
|
|
@@ -785,6 +792,7 @@ var schema = {
|
|
|
785
792
|
// -----------------------------------------------------------------------
|
|
786
793
|
"home_mate_open": { direction: "c2s", handler: "lib/server-home-chat.js", description: "Open or restore the selected mate's embedded chat session" },
|
|
787
794
|
"home_clay_ask": { direction: "c2s", handler: "lib/server-home-chat.js", description: "Start a fresh builtin Clay conversation from the global search drawer" },
|
|
795
|
+
"contextual_mate_sessions_result": { direction: "s2c", handler: "lib/public/modules/contextual-mate-sessions.js", description: "Exact owner-private saved Mate conversation list or selection receipt; no transcript tap or underlying navigation" },
|
|
788
796
|
"home_mate_sessions_list": { direction: "c2s", handler: "lib/server-home-chat.js", description: "List the current user's conversations with the selected mate" },
|
|
789
797
|
"home_mate_session_open": { direction: "c2s", handler: "lib/server-home-chat.js", description: "Open one explicit conversation with the selected mate" },
|
|
790
798
|
"home_mate_send": { direction: "c2s", handler: "lib/server-home-chat.js", description: "Send a message to the selected mate from the home hub" },
|
|
@@ -814,6 +822,8 @@ var schema = {
|
|
|
814
822
|
"clay_navigation_offer": { direction: "s2c", handler: "lib/public/modules/clay-owner-navigation.js", description: "Offer one exact reviewed navigation to a selected owner connection" },
|
|
815
823
|
"clay_navigation_claim": { direction: "c2s", handler: "lib/server-owner-ui.js", description: "Claim an offered navigation with live source/target revalidation" },
|
|
816
824
|
"clay_navigation_apply": { direction: "s2c", handler: "lib/public/modules/clay-owner-navigation.js", description: "Invoke the shared conversation navigator after live claim" },
|
|
825
|
+
"clay_studio_rendered": { direction: "c2s", handler: "lib/project-studio-presentation.js", description: "Exact source-viewer workbench render receipt; no product effects" },
|
|
826
|
+
"clay_navigation_rendered": { direction: "c2s", handler: "lib/server-owner-ui.js", description: "Bounded exact-viewer rendered conversation evidence; acceptance alone remains separate" },
|
|
817
827
|
"clay_navigation_ack": { direction: "c2s", handler: "lib/server-owner-ui.js", description: "Client invocation acknowledgement; never proof of visible rendering" },
|
|
818
828
|
"context_view_get": { direction: "c2s", handler: "lib/server-context-view.js", description: "Read the authenticated user's context display preference" },
|
|
819
829
|
"context_view_set": { direction: "c2s", handler: "lib/server-context-view.js", description: "Persist the authenticated user's context display preference" },
|