@yeaft/webchat-agent 1.0.550 → 1.0.551
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/index.js +1 -1
- package/local-runtime/server/client-protocol.js +4 -0
- package/local-runtime/server/handlers/agent-work-center.js +2 -0
- package/local-runtime/server/handlers/client-work-center.js +7 -0
- package/local-runtime/server/handlers/client-workbench.js +36 -1
- package/local-runtime/server/work-center-workspace-cache.js +63 -0
- package/local-runtime/server/workbench-preview.js +1 -1
- package/local-runtime/server/workbench-route.js +62 -18
- package/local-runtime/server/ws-client.js +4 -0
- package/local-runtime/version.json +1 -1
- package/local-runtime/web/app.bundle.js +173 -152
- package/local-runtime/web/app.bundle.js.gz +0 -0
- package/local-runtime/web/index.html +2 -2
- package/local-runtime/web/style.bundle.css +1 -1
- package/local-runtime/web/style.bundle.css.gz +0 -0
- package/package.json +1 -1
- package/workbench/file-ops.js +25 -13
- package/workbench/file-search.js +4 -1
- package/workbench/git-ops.js +32 -26
- package/workbench/work-item-path.js +41 -0
- package/yeaft/work-center/projection.js +5 -0
package/index.js
CHANGED
|
@@ -180,7 +180,7 @@ async function detectCapabilities() {
|
|
|
180
180
|
const capabilities = ['background_tasks', 'file_editor', 'ping_session', 'plaintext-ok', 'workbench_session_routes', 'workbench_request_correlation', 'workbench_terminal_cleanup_fence', 'workbench_file_content_chunks', 'workbench_video_stream', 'session_history_search', 'session_history_outline', 'session_history_window_prefetch', 'file_reference_resolution', 'response_image_preview', 'yeaft_plugins', 'yeaft_managed_skills', 'settings_request_correlation', 'work_center_feature_settings'];
|
|
181
181
|
capabilities.push(getAgentUpgradeCapability());
|
|
182
182
|
if (ctx.CONFIG?.workCenterEnabled === true) {
|
|
183
|
-
capabilities.push('work_center', 'work_center_message_v2');
|
|
183
|
+
capabilities.push('work_center', 'work_center_message_v2', 'work_center_workbench');
|
|
184
184
|
if (process.platform === 'linux') capabilities.push('work_item_attachments');
|
|
185
185
|
}
|
|
186
186
|
const pty = await loadNodePty();
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export const WORKBENCH_ROUTE_PROTOCOL = 1;
|
|
2
|
+
export const WORK_CENTER_WORKBENCH_PROTOCOL = 1;
|
|
2
3
|
export const BROWSER_RUNTIME_PROTOCOL = 1;
|
|
3
4
|
export const BROWSER_RUNTIME_SETUP_PROTOCOL = 1;
|
|
4
5
|
|
|
@@ -12,6 +13,9 @@ export function applyClientHello(client, message) {
|
|
|
12
13
|
if (message.workbenchRouteProtocol === WORKBENCH_ROUTE_PROTOCOL) {
|
|
13
14
|
client.workbenchRouteProtocol = WORKBENCH_ROUTE_PROTOCOL;
|
|
14
15
|
}
|
|
16
|
+
if (message.workCenterWorkbenchProtocol === WORK_CENTER_WORKBENCH_PROTOCOL) {
|
|
17
|
+
client.workCenterWorkbenchProtocol = WORK_CENTER_WORKBENCH_PROTOCOL;
|
|
18
|
+
}
|
|
15
19
|
if (message.browserRuntimeProtocol === BROWSER_RUNTIME_PROTOCOL) {
|
|
16
20
|
client.browserRuntimeProtocol = BROWSER_RUNTIME_PROTOCOL;
|
|
17
21
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { updateWorkItemWorkspaces } from '../work-center-workspace-cache.js';
|
|
1
2
|
import { forwardAgentEvent } from '../ws-utils.js';
|
|
2
3
|
import { deliverWorkCenterResponse } from './client-work-center.js';
|
|
3
4
|
|
|
@@ -21,6 +22,7 @@ export async function handleAgentWorkCenter(agentId, msg) {
|
|
|
21
22
|
}
|
|
22
23
|
|
|
23
24
|
const { agentId: _untrustedAgentId, _requestUserId, ...payload } = msg;
|
|
25
|
+
updateWorkItemWorkspaces(agentId, payload.event);
|
|
24
26
|
const outgoing = { ...payload, agentId };
|
|
25
27
|
await forwardAgentEvent(agentId, outgoing);
|
|
26
28
|
return true;
|
|
@@ -2,6 +2,7 @@ import { randomUUID } from 'node:crypto';
|
|
|
2
2
|
import { CONFIG } from '../config.js';
|
|
3
3
|
import { agents, pendingFiles, previewFiles } from '../context.js';
|
|
4
4
|
import { forwardToAgent, sendToWebClient } from '../ws-utils.js';
|
|
5
|
+
import { forgetWorkItemWorkspace, rememberWorkItemWorkspace } from '../work-center-workspace-cache.js';
|
|
5
6
|
import {
|
|
6
7
|
assertSupportedWorkItemAttachment,
|
|
7
8
|
assertWorkItemAttachmentSize,
|
|
@@ -204,6 +205,7 @@ export async function handleClientWorkCenter(client, msg, checkAgentAccess) {
|
|
|
204
205
|
agentId,
|
|
205
206
|
clientRequestId: typeof msg.requestId === 'string' ? msg.requestId : null,
|
|
206
207
|
attachmentFileIds: resolved.consumedIds,
|
|
208
|
+
workItemId: typeof resolved.payload?.id === 'string' ? resolved.payload.id : null,
|
|
207
209
|
expiresAt: Date.now() + REQUEST_TIMEOUT_MS,
|
|
208
210
|
});
|
|
209
211
|
|
|
@@ -233,6 +235,11 @@ export async function deliverWorkCenterResponse(agentId, msg) {
|
|
|
233
235
|
// able to resolve the original fileId and reach the Agent receipt preflight.
|
|
234
236
|
const { agentId: _untrustedAgentId, requestId: _opaqueRequestId, _requestUserId, ...payload } = msg;
|
|
235
237
|
let response = payload;
|
|
238
|
+
if (msg.ok === true && msg.op === 'delete') {
|
|
239
|
+
forgetWorkItemWorkspace(pending.client?.userId, agentId, pending.workItemId);
|
|
240
|
+
} else if (msg.ok === true && msg.data?.id) {
|
|
241
|
+
rememberWorkItemWorkspace(pending.client?.userId, agentId, msg.data);
|
|
242
|
+
}
|
|
236
243
|
if (msg.ok === true && msg.op === 'preview_attachment') {
|
|
237
244
|
try {
|
|
238
245
|
const data = payload.data;
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
agentSupportsWorkbenchRequestCorrelation,
|
|
9
9
|
resolveWorkbenchRequest,
|
|
10
|
+
workbenchPathWithinWorkspace,
|
|
10
11
|
} from '../workbench-route.js';
|
|
11
12
|
import {
|
|
12
13
|
deleteWorkbenchRequest,
|
|
@@ -157,6 +158,35 @@ function workbenchFailureResponse({ agentId, msg, resolved, error }) {
|
|
|
157
158
|
};
|
|
158
159
|
}
|
|
159
160
|
|
|
161
|
+
const WORKBENCH_PATH_FIELDS = Object.freeze({
|
|
162
|
+
read_file: ['filePath'],
|
|
163
|
+
video_metadata: ['filePath'],
|
|
164
|
+
write_file: ['filePath'],
|
|
165
|
+
list_directory: ['dirPath'],
|
|
166
|
+
git_diff: ['filePath'],
|
|
167
|
+
git_add: ['filePath'],
|
|
168
|
+
git_reset: ['filePath'],
|
|
169
|
+
git_restore: ['filePath'],
|
|
170
|
+
file_search: ['dirPath'],
|
|
171
|
+
create_file: ['filePath'],
|
|
172
|
+
delete_files: ['paths'],
|
|
173
|
+
move_files: ['paths', 'destination'],
|
|
174
|
+
copy_files: ['paths', 'destination'],
|
|
175
|
+
upload_to_dir: ['dirPath'],
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
function workCenterPathsAreConfined(msg, resolved) {
|
|
179
|
+
if (resolved.route?.runtimeProvider !== 'work-center') return true;
|
|
180
|
+
for (const field of WORKBENCH_PATH_FIELDS[msg.type] || []) {
|
|
181
|
+
const values = Array.isArray(msg[field]) ? msg[field] : [msg[field]];
|
|
182
|
+
for (const value of values) {
|
|
183
|
+
if (value == null || value === '') continue;
|
|
184
|
+
if (!workbenchPathWithinWorkspace(value, resolved.workDir)) return false;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
|
|
160
190
|
function canonicalWorkbenchMessage(msg, resolved, { canonicalWorkDir = false } = {}) {
|
|
161
191
|
const {
|
|
162
192
|
_requestUserId: _ignoredUserId,
|
|
@@ -168,6 +198,7 @@ function canonicalWorkbenchMessage(msg, resolved, { canonicalWorkDir = false } =
|
|
|
168
198
|
...clientFields,
|
|
169
199
|
...(resolved.conversationId ? { conversationId: resolved.conversationId } : {}),
|
|
170
200
|
};
|
|
201
|
+
if (!workCenterPathsAreConfined(clientFields, resolved)) return null;
|
|
171
202
|
return {
|
|
172
203
|
...clientFields,
|
|
173
204
|
agentId: resolved.agentId,
|
|
@@ -233,6 +264,10 @@ function correlateWorkbenchRequest({ agentId, clientId, client, msg, resolved, c
|
|
|
233
264
|
}
|
|
234
265
|
|
|
235
266
|
async function forwardCorrelatedWorkbenchRequest({ agentId, clientId, client, msg, resolved, canonical }) {
|
|
267
|
+
if (!canonical) {
|
|
268
|
+
await denyWorkbenchRoute(client, msg);
|
|
269
|
+
return true;
|
|
270
|
+
}
|
|
236
271
|
const outbound = correlateWorkbenchRequest({ agentId, clientId, client, msg, resolved, canonical });
|
|
237
272
|
if (!outbound) return false;
|
|
238
273
|
try {
|
|
@@ -465,7 +500,7 @@ export async function handleClientWorkbench(clientId, client, msg, checkAgentAcc
|
|
|
465
500
|
client,
|
|
466
501
|
msg,
|
|
467
502
|
resolved,
|
|
468
|
-
canonical: { ...canonical, type: 'list_directory' },
|
|
503
|
+
canonical: canonical ? { ...canonical, type: 'list_directory' } : null,
|
|
469
504
|
});
|
|
470
505
|
break;
|
|
471
506
|
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { agents } from './context.js';
|
|
2
|
+
|
|
3
|
+
// Ephemeral routing metadata, never a second WorkItem catalog. A fresh detail
|
|
4
|
+
// request repopulates it after reconnect/restart; old Agent connections expire.
|
|
5
|
+
const workspaces = new Map();
|
|
6
|
+
const MAX_WORKSPACES = 1000;
|
|
7
|
+
|
|
8
|
+
function clean(value, maxLength = 4096) {
|
|
9
|
+
if (typeof value !== 'string') return '';
|
|
10
|
+
const result = value.trim();
|
|
11
|
+
return result && result.length <= maxLength ? result : '';
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function key(userId, agentId, workItemId) {
|
|
15
|
+
const parts = [userId, agentId, workItemId].map(value => clean(value, 300));
|
|
16
|
+
return parts.every(Boolean) ? JSON.stringify(parts) : '';
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/** Remember Agent-projected workspace metadata only after an owner-scoped request. */
|
|
20
|
+
export function rememberWorkItemWorkspace(userId, agentId, detail) {
|
|
21
|
+
const cacheKey = key(userId, agentId, detail?.id);
|
|
22
|
+
const workDir = clean(detail?.workbench?.workDir);
|
|
23
|
+
if (!cacheKey) return false;
|
|
24
|
+
workspaces.delete(cacheKey);
|
|
25
|
+
if (!workDir || !agents.has(agentId)) return false;
|
|
26
|
+
workspaces.set(cacheKey, { id: detail.id, agentId, agent: agents.get(agentId), workDir, isArchived: false });
|
|
27
|
+
while (workspaces.size > MAX_WORKSPACES) workspaces.delete(workspaces.keys().next().value);
|
|
28
|
+
return true;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function getWorkItemWorkspace(userId, agentId, workItemId) {
|
|
32
|
+
const cacheKey = key(userId, agentId, workItemId);
|
|
33
|
+
const row = workspaces.get(cacheKey);
|
|
34
|
+
if (!row || row.agent !== agents.get(agentId)) {
|
|
35
|
+
workspaces.delete(cacheKey);
|
|
36
|
+
return null;
|
|
37
|
+
}
|
|
38
|
+
return row;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function forgetWorkItemWorkspace(userId, agentId, workItemId) {
|
|
42
|
+
workspaces.delete(key(userId, agentId, workItemId));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Events may refresh existing owner grants, never create grants for new users. */
|
|
46
|
+
export function updateWorkItemWorkspaces(agentId, event) {
|
|
47
|
+
const detail = event?.workItem;
|
|
48
|
+
if (!detail?.id) return;
|
|
49
|
+
for (const [cacheKey, row] of workspaces) {
|
|
50
|
+
if (row.agentId !== agentId || row.id !== detail.id) continue;
|
|
51
|
+
if (event.type === 'work_item.deleted' || row.agent !== agents.get(agentId)) {
|
|
52
|
+
workspaces.delete(cacheKey);
|
|
53
|
+
} else if (Object.hasOwn(detail, 'workbench')) {
|
|
54
|
+
const workDir = clean(detail.workbench?.workDir);
|
|
55
|
+
if (!workDir) workspaces.delete(cacheKey);
|
|
56
|
+
else row.workDir = workDir;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function __testResetWorkItemWorkspaces() {
|
|
62
|
+
workspaces.clear();
|
|
63
|
+
}
|
|
@@ -65,7 +65,7 @@ function authorize(source) {
|
|
|
65
65
|
if (!CONFIG.skipAuth && (!user || user.deletion_state !== 'active' || !['pro', 'admin'].includes(user.role))) {
|
|
66
66
|
throw failure(403, 'Preview access denied');
|
|
67
67
|
}
|
|
68
|
-
const client = { userId: source.userId, role: user?.role, workbenchRouteProtocol: 1 };
|
|
68
|
+
const client = { userId: source.userId, role: user?.role, workbenchRouteProtocol: 1, workCenterWorkbenchProtocol: 1 };
|
|
69
69
|
const accessError = resolveAgentAccessError(source.agentId, client.userId, client.role);
|
|
70
70
|
if (accessError) throw failure(accessError === 'Agent access denied' ? 403 : 503, accessError);
|
|
71
71
|
const generation = currentWorkbenchWorkspaceGeneration({ ...client, route: source.route });
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
+
import { posix, win32 } from 'node:path';
|
|
1
2
|
import { CONFIG } from './config.js';
|
|
2
3
|
import { agents } from './context.js';
|
|
3
4
|
import { sessionDb } from './db/session-db.js';
|
|
4
5
|
import { yeaftSessionDb } from './db/yeaft-session-db.js';
|
|
6
|
+
import { getWorkItemWorkspace } from './work-center-workspace-cache.js';
|
|
5
7
|
|
|
6
8
|
export const WORKBENCH_SESSION_ROUTE_CAPABILITY = 'workbench_session_routes';
|
|
7
9
|
export const WORKBENCH_REQUEST_CORRELATION_CAPABILITY = 'workbench_request_correlation';
|
|
8
10
|
export const WORKBENCH_TERMINAL_CLEANUP_FENCE_CAPABILITY = 'workbench_terminal_cleanup_fence';
|
|
11
|
+
export const WORK_CENTER_WORKBENCH_CAPABILITY = 'work_center_workbench';
|
|
9
12
|
|
|
10
13
|
function agentHasCapability(agent, capability) {
|
|
11
14
|
return Array.isArray(agent?.capabilities) && agent.capabilities.includes(capability);
|
|
@@ -19,7 +22,7 @@ export function agentSupportsWorkbenchTerminalCleanupFence(agent) {
|
|
|
19
22
|
return agentHasCapability(agent, WORKBENCH_TERMINAL_CLEANUP_FENCE_CAPABILITY);
|
|
20
23
|
}
|
|
21
24
|
|
|
22
|
-
const PROVIDERS = new Set(['yeaft', 'claude-code', 'copilot']);
|
|
25
|
+
const PROVIDERS = new Set(['yeaft', 'claude-code', 'copilot', 'work-center']);
|
|
23
26
|
const SCOPES = new Set(['main', 'files-folder-picker', 'git-folder-picker']);
|
|
24
27
|
|
|
25
28
|
function clean(value, maxLength = 300) {
|
|
@@ -31,9 +34,11 @@ function clean(value, maxLength = 300) {
|
|
|
31
34
|
export function workbenchRouteKey(route) {
|
|
32
35
|
const runtimeProvider = clean(route?.runtimeProvider, 32);
|
|
33
36
|
const agentId = clean(route?.agentId);
|
|
34
|
-
const
|
|
35
|
-
|
|
36
|
-
|
|
37
|
+
const ownerId = runtimeProvider === 'work-center'
|
|
38
|
+
? clean(route?.workItemId)
|
|
39
|
+
: clean(route?.sessionId);
|
|
40
|
+
if (!PROVIDERS.has(runtimeProvider) || !agentId || !ownerId) return '';
|
|
41
|
+
return [runtimeProvider, agentId, ownerId]
|
|
37
42
|
.map(part => encodeURIComponent(part))
|
|
38
43
|
.join(':');
|
|
39
44
|
}
|
|
@@ -73,7 +78,9 @@ export function workbenchRouteKeyFromConversationId(conversationId, expectedAgen
|
|
|
73
78
|
const decodedRoute = {
|
|
74
79
|
runtimeProvider: decodeURIComponent(parts[0]),
|
|
75
80
|
agentId: decodeURIComponent(parts[1]),
|
|
76
|
-
|
|
81
|
+
...(decodeURIComponent(parts[0]) === 'work-center'
|
|
82
|
+
? { workItemId: decodeURIComponent(parts[2]) }
|
|
83
|
+
: { sessionId: decodeURIComponent(parts[2]) }),
|
|
77
84
|
};
|
|
78
85
|
if (expectedAgentId && decodedRoute.agentId !== expectedAgentId) return '';
|
|
79
86
|
return workbenchRouteKey(decodedRoute) === routeKey ? routeKey : '';
|
|
@@ -102,6 +109,7 @@ function resolveYeaftRow(client, route) {
|
|
|
102
109
|
// never the browser's cwd or the Server process cwd.
|
|
103
110
|
function resolveSessionWorkDir(row, route) {
|
|
104
111
|
if (!row) return '';
|
|
112
|
+
if (route.runtimeProvider === 'work-center') return clean(row.workDir, 4096);
|
|
105
113
|
return clean(route.runtimeProvider === 'yeaft' ? row.workDir : row.work_dir, 4096)
|
|
106
114
|
|| clean(agents.get(route.agentId)?.workDir, 4096);
|
|
107
115
|
}
|
|
@@ -122,9 +130,40 @@ function resolveChatRow(client, route) {
|
|
|
122
130
|
return row;
|
|
123
131
|
}
|
|
124
132
|
|
|
133
|
+
function resolveWorkCenterRoute(client, agent, route) {
|
|
134
|
+
if (client?.workCenterWorkbenchProtocol !== 1
|
|
135
|
+
|| !agentHasCapability(agent, WORK_CENTER_WORKBENCH_CAPABILITY)
|
|
136
|
+
|| !agentSupportsWorkbenchRequestCorrelation(agent)
|
|
137
|
+
|| !agentSupportsWorkbenchTerminalCleanupFence(agent)) return null;
|
|
138
|
+
const row = getWorkItemWorkspace(client?.userId, route.agentId, route.workItemId);
|
|
139
|
+
return row?.workDir ? row : null;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function workbenchPathWithinWorkspace(filePath, workDir) {
|
|
143
|
+
const path = clean(filePath, 4096);
|
|
144
|
+
const root = clean(workDir, 4096);
|
|
145
|
+
if (!path || !root) return false;
|
|
146
|
+
// The Agent can run a different OS from the Server. Never use Server cwd.
|
|
147
|
+
const paths = /^(?:[a-z]:[\\/]|\\\\)/i.test(root) ? win32 : posix;
|
|
148
|
+
if (!paths.isAbsolute(root)) return false;
|
|
149
|
+
const resolvedRoot = paths.resolve(root);
|
|
150
|
+
const candidate = paths.resolve(resolvedRoot, path);
|
|
151
|
+
const relativePath = paths.relative(resolvedRoot, candidate);
|
|
152
|
+
return relativePath !== '..'
|
|
153
|
+
&& !relativePath.startsWith(`..${paths.sep}`)
|
|
154
|
+
&& !paths.isAbsolute(relativePath);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function resolveRouteRow(client, route, agent) {
|
|
158
|
+
if (route.runtimeProvider === 'work-center') return resolveWorkCenterRoute(client, agent, route);
|
|
159
|
+
return route.runtimeProvider === 'yeaft'
|
|
160
|
+
? resolveYeaftRow(client, route)
|
|
161
|
+
: resolveChatRow(client, route);
|
|
162
|
+
}
|
|
163
|
+
|
|
125
164
|
/**
|
|
126
165
|
* Validate a browser-provided Workbench route against Server-owned Session
|
|
127
|
-
* metadata and return canonical execution fields. Browser cwd and synthetic
|
|
166
|
+
* metadata or owner-scoped, Agent-projected WorkItem workspace metadata and return canonical execution fields. Browser cwd and synthetic
|
|
128
167
|
* conversation ids are never authoritative.
|
|
129
168
|
*
|
|
130
169
|
* `legacy: true` preserves old clients that predate route-scoped Workbench.
|
|
@@ -135,10 +174,9 @@ function resolveChatRow(client, route) {
|
|
|
135
174
|
*/
|
|
136
175
|
export function currentWorkbenchWorkspaceGeneration({ route, userId, role }) {
|
|
137
176
|
if (!route || !userId) return null;
|
|
138
|
-
const client = { userId, role };
|
|
139
|
-
const
|
|
140
|
-
|
|
141
|
-
: resolveChatRow(client, route);
|
|
177
|
+
const client = { userId, role, workCenterWorkbenchProtocol: 1 };
|
|
178
|
+
const agent = agents.get(route.agentId);
|
|
179
|
+
const row = resolveRouteRow(client, route, agent);
|
|
142
180
|
if (!row || row.isArchived) return null;
|
|
143
181
|
const workDir = resolveSessionWorkDir(row, route);
|
|
144
182
|
if (!workDir) return '';
|
|
@@ -165,17 +203,18 @@ export function resolveWorkbenchRequest(client, msg, targetAgentId, { allowMissi
|
|
|
165
203
|
}
|
|
166
204
|
|
|
167
205
|
if (!clientSupportsRoutes || !agentSupportsRoutes) return null;
|
|
206
|
+
const runtimeProvider = clean(msg.workbenchRoute.runtimeProvider, 32);
|
|
168
207
|
const route = {
|
|
169
|
-
runtimeProvider
|
|
208
|
+
runtimeProvider,
|
|
170
209
|
agentId: clean(msg.workbenchRoute.agentId),
|
|
171
|
-
|
|
210
|
+
...(runtimeProvider === 'work-center'
|
|
211
|
+
? { workItemId: clean(msg.workbenchRoute.workItemId) }
|
|
212
|
+
: { sessionId: clean(msg.workbenchRoute.sessionId) }),
|
|
172
213
|
};
|
|
173
214
|
const routeKey = workbenchRouteKey(route);
|
|
174
215
|
if (!routeKey || route.agentId !== targetAgentId) return null;
|
|
175
216
|
|
|
176
|
-
const row = route
|
|
177
|
-
? resolveYeaftRow(client, route)
|
|
178
|
-
: resolveChatRow(client, route);
|
|
217
|
+
const row = resolveRouteRow(client, route, agent);
|
|
179
218
|
if (row?.isArchived && !allowMissingSession) return null;
|
|
180
219
|
if (!row && !allowMissingSession) return null;
|
|
181
220
|
|
|
@@ -189,6 +228,9 @@ export function resolveWorkbenchRequest(client, msg, targetAgentId, { allowMissi
|
|
|
189
228
|
? workbenchWorkspaceGeneration(routeKey, sessionWorkDir)
|
|
190
229
|
: clean(msg.workbenchWorkspaceGeneration, 1600);
|
|
191
230
|
if (!workspaceGeneration && !allowMissingSession) return null;
|
|
231
|
+
if (runtimeProvider === 'work-center' && !allowMissingSession
|
|
232
|
+
&& msg.workbenchWorkspaceGeneration
|
|
233
|
+
&& msg.workbenchWorkspaceGeneration !== workspaceGeneration) return null;
|
|
192
234
|
return {
|
|
193
235
|
legacy: false,
|
|
194
236
|
route,
|
|
@@ -196,10 +238,12 @@ export function resolveWorkbenchRequest(client, msg, targetAgentId, { allowMissi
|
|
|
196
238
|
scope,
|
|
197
239
|
agentId: route.agentId,
|
|
198
240
|
conversationId: workbenchConversationId(route, scope),
|
|
199
|
-
//
|
|
200
|
-
//
|
|
241
|
+
// A WorkItem owns one canonical workspace. Unlike Session Workbench routes,
|
|
242
|
+
// Files/Git cannot switch this route to an arbitrary browser-provided cwd.
|
|
201
243
|
workDir: sessionWorkDir,
|
|
202
|
-
requestedWorkDir:
|
|
244
|
+
requestedWorkDir: route.runtimeProvider === 'work-center'
|
|
245
|
+
? sessionWorkDir
|
|
246
|
+
: clean(msg.workDir, 4096) || sessionWorkDir,
|
|
203
247
|
workspaceGeneration,
|
|
204
248
|
archived: row?.isArchived === true,
|
|
205
249
|
};
|
|
@@ -16,6 +16,7 @@ import {
|
|
|
16
16
|
BROWSER_RUNTIME_PROTOCOL,
|
|
17
17
|
BROWSER_RUNTIME_SETUP_PROTOCOL,
|
|
18
18
|
WORKBENCH_ROUTE_PROTOCOL,
|
|
19
|
+
WORK_CENTER_WORKBENCH_PROTOCOL,
|
|
19
20
|
} from './client-protocol.js';
|
|
20
21
|
import {
|
|
21
22
|
clearWorkbenchCorrelationsForClient,
|
|
@@ -112,6 +113,7 @@ export function handleWebConnection(ws, url, req = {}) {
|
|
|
112
113
|
encryptOutbound: true,
|
|
113
114
|
// Explicit protocols have no omission-based downgrade for security fields.
|
|
114
115
|
workbenchRouteProtocol: 0,
|
|
116
|
+
workCenterWorkbenchProtocol: 0,
|
|
115
117
|
browserRuntimeProtocol: 0,
|
|
116
118
|
browserRuntimeSetupProtocol: 0,
|
|
117
119
|
});
|
|
@@ -141,6 +143,7 @@ export function handleWebConnection(ws, url, req = {}) {
|
|
|
141
143
|
acceptPlaintext: true,
|
|
142
144
|
yeaftSessionInventoryComplete: true,
|
|
143
145
|
workbenchRouteProtocol: WORKBENCH_ROUTE_PROTOCOL,
|
|
146
|
+
workCenterWorkbenchProtocol: WORK_CENTER_WORKBENCH_PROTOCOL,
|
|
144
147
|
browserRuntimeProtocol: BROWSER_RUNTIME_PROTOCOL,
|
|
145
148
|
browserRuntimeSetupProtocol: BROWSER_RUNTIME_SETUP_PROTOCOL,
|
|
146
149
|
browserRuntimeEnabled: CONFIG.browserRuntime.enabled,
|
|
@@ -259,6 +262,7 @@ async function handleWebMessage(clientId, msg) {
|
|
|
259
262
|
await sendToWebClient(client, {
|
|
260
263
|
type: 'client_hello_ack',
|
|
261
264
|
workbenchRouteProtocol: client.workbenchRouteProtocol,
|
|
265
|
+
workCenterWorkbenchProtocol: client.workCenterWorkbenchProtocol,
|
|
262
266
|
browserRuntimeProtocol: client.browserRuntimeProtocol,
|
|
263
267
|
browserRuntimeSetupProtocol: client.browserRuntimeSetupProtocol,
|
|
264
268
|
browserRuntimeEnabled: CONFIG.browserRuntime.enabled,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":"1.0.
|
|
1
|
+
{"version":"1.0.551"}
|