@yeaft/webchat-agent 1.0.413 → 1.0.415

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.
Files changed (58) hide show
  1. package/browser-runtime/browser-install.js +497 -0
  2. package/browser-runtime/cli.js +88 -0
  3. package/browser-runtime/config.js +116 -0
  4. package/browser-runtime/errors.js +8 -0
  5. package/browser-runtime/extension/manifest.json +18 -0
  6. package/browser-runtime/extension/offscreen.html +5 -0
  7. package/browser-runtime/extension/offscreen.js +101 -0
  8. package/browser-runtime/extension/popup.html +5 -0
  9. package/browser-runtime/extension/popup.js +1 -0
  10. package/browser-runtime/extension/service-worker.js +48 -0
  11. package/browser-runtime/extension.js +45 -0
  12. package/browser-runtime/index.js +5 -0
  13. package/browser-runtime/probe.js +427 -0
  14. package/browser-runtime/protocol.js +71 -0
  15. package/browser-runtime/service.js +132 -0
  16. package/browser-runtime/windows-version-job.ps1 +233 -0
  17. package/browser-runtime/windows-version-worker.js +75 -0
  18. package/browser-runtime/windows-version.js +85 -0
  19. package/cli.js +24 -7
  20. package/connection/index.js +12 -0
  21. package/context.js +1 -0
  22. package/index.js +19 -2
  23. package/llm-config-cli.js +24 -21
  24. package/local-runtime/server/client-protocol.js +14 -0
  25. package/local-runtime/server/context.js +3 -2
  26. package/local-runtime/server/handlers/agent-file-terminal.js +185 -115
  27. package/local-runtime/server/handlers/agent-output.js +3 -0
  28. package/local-runtime/server/handlers/client-misc.js +21 -4
  29. package/local-runtime/server/handlers/client-workbench.js +222 -41
  30. package/local-runtime/server/workbench-correlation.js +184 -0
  31. package/local-runtime/server/workbench-route.js +180 -0
  32. package/local-runtime/server/ws-agent.js +4 -0
  33. package/local-runtime/server/ws-client.js +25 -3
  34. package/local-runtime/version.json +1 -1
  35. package/local-runtime/web/app.bundle.js +191 -135
  36. package/local-runtime/web/app.bundle.js.gz +0 -0
  37. package/local-runtime/web/index.html +2 -2
  38. package/local-runtime/web/style.bundle.css +1 -1
  39. package/local-runtime/web/style.bundle.css.gz +0 -0
  40. package/package.json +5 -1
  41. package/service/config.js +23 -2
  42. package/service/index.js +1 -0
  43. package/service/linux.js +3 -2
  44. package/terminal.js +167 -30
  45. package/workbench/file-ops.js +21 -20
  46. package/workbench/file-search.js +4 -3
  47. package/workbench/git-ops.js +23 -22
  48. package/workbench/request-routing.js +16 -0
  49. package/yeaft/cli.js +57 -1
  50. package/yeaft/config-api.js +138 -192
  51. package/yeaft/config-store.js +192 -0
  52. package/yeaft/config.js +3 -0
  53. package/yeaft/init.js +20 -7
  54. package/yeaft/sessions/feature-flag.js +15 -33
  55. package/yeaft/sessions/session-manifest.js +114 -10
  56. package/yeaft/stdio-protocol.js +57 -0
  57. package/yeaft/storage/atomic.js +43 -17
  58. package/yeaft/tools/process-runner.js +86 -13
@@ -2,6 +2,7 @@ import { readFile, writeFile } from 'fs/promises';
2
2
  import { join, resolve } from 'path';
3
3
  import ctx from '../context.js';
4
4
  import { execAsync, resolveAndValidatePath, getGitRoot, validateGitPath } from './utils.js';
5
+ import { sendWorkbenchResult } from './request-routing.js';
5
6
 
6
7
  export async function handleGitStatus(msg) {
7
8
  const { conversationId, _requestUserId } = msg;
@@ -58,7 +59,7 @@ export async function handleGitStatus(msg) {
58
59
  files.push({ path: displayPath, indexStatus, workTreeStatus });
59
60
  }
60
61
 
61
- ctx.sendToServer({
62
+ sendWorkbenchResult(ctx, msg, {
62
63
  type: 'git_status_result',
63
64
  conversationId,
64
65
  _requestUserId,
@@ -70,7 +71,7 @@ export async function handleGitStatus(msg) {
70
71
  gitRoot
71
72
  });
72
73
  } catch (e) {
73
- ctx.sendToServer({
74
+ sendWorkbenchResult(ctx, msg, {
74
75
  type: 'git_status_result',
75
76
  conversationId,
76
77
  _requestUserId,
@@ -88,7 +89,7 @@ export async function handleGitDiff(msg) {
88
89
  try {
89
90
  // 安全检查:验证 filePath 不包含 shell 注入字符
90
91
  if (!filePath || /[`$;|&><!\n\r]/.test(filePath)) {
91
- ctx.sendToServer({
92
+ sendWorkbenchResult(ctx, msg, {
92
93
  type: 'git_diff_result',
93
94
  conversationId,
94
95
  _requestUserId,
@@ -114,7 +115,7 @@ export async function handleGitDiff(msg) {
114
115
  const fullPath = resolve(gitRoot, filePath);
115
116
  const resolved = resolveAndValidatePath(fullPath, gitRoot);
116
117
  const content = await readFile(resolved, 'utf-8');
117
- ctx.sendToServer({
118
+ sendWorkbenchResult(ctx, msg, {
118
119
  type: 'git_diff_result',
119
120
  conversationId,
120
121
  _requestUserId,
@@ -148,7 +149,7 @@ export async function handleGitDiff(msg) {
148
149
  windowsHide: true
149
150
  });
150
151
  if (cachedOut.trim()) {
151
- ctx.sendToServer({
152
+ sendWorkbenchResult(ctx, msg, {
152
153
  type: 'git_diff_result',
153
154
  conversationId,
154
155
  _requestUserId,
@@ -167,7 +168,7 @@ export async function handleGitDiff(msg) {
167
168
  windowsHide: true
168
169
  });
169
170
  if (wtOut.trim()) {
170
- ctx.sendToServer({
171
+ sendWorkbenchResult(ctx, msg, {
171
172
  type: 'git_diff_result',
172
173
  conversationId,
173
174
  _requestUserId,
@@ -179,7 +180,7 @@ export async function handleGitDiff(msg) {
179
180
  }
180
181
  }
181
182
 
182
- ctx.sendToServer({
183
+ sendWorkbenchResult(ctx, msg, {
183
184
  type: 'git_diff_result',
184
185
  conversationId,
185
186
  _requestUserId,
@@ -188,7 +189,7 @@ export async function handleGitDiff(msg) {
188
189
  diff: stdout
189
190
  });
190
191
  } catch (e) {
191
- ctx.sendToServer({
192
+ sendWorkbenchResult(ctx, msg, {
192
193
  type: 'git_diff_result',
193
194
  conversationId,
194
195
  _requestUserId,
@@ -210,15 +211,15 @@ export async function handleGitAdd(msg) {
210
211
  await execAsync('git add -A', { cwd: gitRoot, timeout: 10000, windowsHide: true });
211
212
  } else {
212
213
  if (!validateGitPath(filePath)) {
213
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'add', success: false, error: 'Invalid file path' });
214
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'add', success: false, error: 'Invalid file path' });
214
215
  return;
215
216
  }
216
217
  await execAsync(`git add -- "${filePath}"`, { cwd: gitRoot, timeout: 10000, windowsHide: true });
217
218
  }
218
219
 
219
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'add', success: true, message: addAll ? 'All files staged' : `Staged: ${filePath}` });
220
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'add', success: true, message: addAll ? 'All files staged' : `Staged: ${filePath}` });
220
221
  } catch (e) {
221
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'add', success: false, error: e.message });
222
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'add', success: false, error: e.message });
222
223
  }
223
224
  }
224
225
 
@@ -234,15 +235,15 @@ export async function handleGitReset(msg) {
234
235
  await execAsync('git reset HEAD', { cwd: gitRoot, timeout: 10000, windowsHide: true });
235
236
  } else {
236
237
  if (!validateGitPath(filePath)) {
237
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'reset', success: false, error: 'Invalid file path' });
238
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'reset', success: false, error: 'Invalid file path' });
238
239
  return;
239
240
  }
240
241
  await execAsync(`git reset HEAD -- "${filePath}"`, { cwd: gitRoot, timeout: 10000, windowsHide: true });
241
242
  }
242
243
 
243
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'reset', success: true, message: resetAll ? 'All files unstaged' : `Unstaged: ${filePath}` });
244
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'reset', success: true, message: resetAll ? 'All files unstaged' : `Unstaged: ${filePath}` });
244
245
  } catch (e) {
245
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'reset', success: false, error: e.message });
246
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'reset', success: false, error: e.message });
246
247
  }
247
248
  }
248
249
 
@@ -253,15 +254,15 @@ export async function handleGitRestore(msg) {
253
254
 
254
255
  try {
255
256
  if (!validateGitPath(filePath)) {
256
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'restore', success: false, error: 'Invalid file path' });
257
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'restore', success: false, error: 'Invalid file path' });
257
258
  return;
258
259
  }
259
260
 
260
261
  const gitRoot = await getGitRoot(workDir);
261
262
  await execAsync(`git restore -- "${filePath}"`, { cwd: gitRoot, timeout: 10000, windowsHide: true });
262
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'restore', success: true, message: `Restored: ${filePath}` });
263
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'restore', success: true, message: `Restored: ${filePath}` });
263
264
  } catch (e) {
264
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'restore', success: false, error: e.message });
265
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'restore', success: false, error: e.message });
265
266
  }
266
267
  }
267
268
 
@@ -272,7 +273,7 @@ export async function handleGitCommit(msg) {
272
273
 
273
274
  try {
274
275
  if (!commitMessage || !commitMessage.trim()) {
275
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'commit', success: false, error: 'Commit message is required' });
276
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'commit', success: false, error: 'Commit message is required' });
276
277
  return;
277
278
  }
278
279
 
@@ -286,13 +287,13 @@ export async function handleGitCommit(msg) {
286
287
  const { stdout } = await execAsync(`git commit -F "${tmpFile}"`, {
287
288
  cwd: gitRoot, timeout: 30000, windowsHide: true
288
289
  });
289
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'commit', success: true, message: stdout.trim() });
290
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'commit', success: true, message: stdout.trim() });
290
291
  } finally {
291
292
  // Clean up temp file
292
293
  try { await writeFile(tmpFile, '', 'utf8'); } catch {}
293
294
  }
294
295
  } catch (e) {
295
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'commit', success: false, error: e.stderr?.trim() || e.message });
296
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'commit', success: false, error: e.stderr?.trim() || e.message });
296
297
  }
297
298
  }
298
299
 
@@ -306,8 +307,8 @@ export async function handleGitPush(msg) {
306
307
  const { stdout, stderr } = await execAsync('git push', {
307
308
  cwd: gitRoot, timeout: 60000, windowsHide: true
308
309
  });
309
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'push', success: true, message: (stdout + '\n' + stderr).trim() || 'Push complete' });
310
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'push', success: true, message: (stdout + '\n' + stderr).trim() || 'Push complete' });
310
311
  } catch (e) {
311
- ctx.sendToServer({ type: 'git_op_result', conversationId, _requestUserId, operation: 'push', success: false, error: e.stderr?.trim() || e.message });
312
+ sendWorkbenchResult(ctx, msg, { type: 'git_op_result', conversationId, _requestUserId, operation: 'push', success: false, error: e.stderr?.trim() || e.message });
312
313
  }
313
314
  }
@@ -0,0 +1,16 @@
1
+ export function workbenchRequestRouting(source) {
2
+ return {
3
+ ...(source?._workbenchRequestId ? { _workbenchRequestId: source._workbenchRequestId } : {}),
4
+ ...(source?.workbenchRouteKey ? { workbenchRouteKey: source.workbenchRouteKey } : {}),
5
+ ...(source?.workbenchWorkspaceGeneration
6
+ ? { workbenchWorkspaceGeneration: source.workbenchWorkspaceGeneration }
7
+ : {}),
8
+ };
9
+ }
10
+
11
+ export function sendWorkbenchResult(ctx, request, result) {
12
+ ctx.sendToServer({
13
+ ...result,
14
+ ...workbenchRequestRouting(request),
15
+ });
16
+ }
package/yeaft/cli.js CHANGED
@@ -25,6 +25,7 @@
25
25
 
26
26
  import { createInterface } from 'readline';
27
27
  import { randomUUID } from 'node:crypto';
28
+ import { realpathSync } from 'node:fs';
28
29
  import { resolve } from 'node:path';
29
30
  import { fileURLToPath } from 'node:url';
30
31
  import { join } from 'path';
@@ -35,13 +36,16 @@ import { listModels, resolveModel, parseModelRef, resolveContextWindow, resolveM
35
36
  import { buildSystemPrompt } from './prompts.js';
36
37
  import { searchMessages } from './conversation/search.js';
37
38
  import { ConversationStore } from './conversation/persist.js';
38
- import { snapshotSessions } from './sessions/session-crud.js';
39
+ import { sessionsRoot, snapshotSessions } from './sessions/session-crud.js';
39
40
  import { loadSessionConfig, resolveSessionConfig } from './sessions/session-config.js';
41
+ import { createSession } from './sessions/session-store.js';
42
+ import { addOrUpdateManifestSession, withSessionManifestLock } from './sessions/session-manifest.js';
40
43
  import { validateSessionId } from './sessions/ids.js';
41
44
  import {
42
45
  createJsonlWriter,
43
46
  JsonlInput,
44
47
  normalizeStreamRoutingIntent,
48
+ normalizeStreamSessionBootstrap,
45
49
  runStreamTurn,
46
50
  runStreamSessionTurn,
47
51
  } from './stdio-protocol.js';
@@ -900,6 +904,7 @@ async function runStreamJson(config, args) {
900
904
  let taskEventIntakeOpen = true;
901
905
  let hadError = false;
902
906
  let singleEngineTail = Promise.resolve();
907
+ let streamSessionBootstrapOpen = false;
903
908
 
904
909
  const loadStreamHistory = () => conversationStore.loadRecentBySession(sessionId, 20).map(message => ({
905
910
  role: message.role,
@@ -1033,6 +1038,16 @@ async function runStreamJson(config, args) {
1033
1038
 
1034
1039
  configureStreamEngine(engine, null);
1035
1040
  sessionRunner = createCliSessionRunner({ loaded, sessionId, workDir, configureEngine: configureStreamEngine });
1041
+ if (sessionRunner) {
1042
+ snapshotSessions(loaded.yeaftDir);
1043
+ const meta = sessionRunner.meta;
1044
+ if (meta) addOrUpdateManifestSession(
1045
+ loaded.yeaftDir,
1046
+ meta,
1047
+ join(sessionsRoot(loaded.yeaftDir), sessionId),
1048
+ );
1049
+ }
1050
+ streamSessionBootstrapOpen = !sessionRunner;
1036
1051
 
1037
1052
  write({
1038
1053
  type: 'system',
@@ -1091,6 +1106,47 @@ async function runStreamJson(config, args) {
1091
1106
  };
1092
1107
 
1093
1108
  const runPrompt = async (prompt, message = null) => {
1109
+ if (streamSessionBootstrapOpen) {
1110
+ streamSessionBootstrapOpen = false;
1111
+ const bootstrap = normalizeStreamSessionBootstrap(message);
1112
+ if (bootstrap) {
1113
+ let workspaceKey = '';
1114
+ try { workspaceKey = realpathSync(resolve(workDir)); } catch { /* leave empty */ }
1115
+ withSessionManifestLock(loaded.yeaftDir, () => {
1116
+ sessionRunner = createCliSessionRunner({
1117
+ loaded,
1118
+ sessionId,
1119
+ workDir,
1120
+ configureEngine: configureStreamEngine,
1121
+ });
1122
+ if (sessionRunner) return;
1123
+ const handle = createSession(sessionsRoot(loaded.yeaftDir), {
1124
+ id: sessionId,
1125
+ name: sessionId,
1126
+ roster: bootstrap.roster,
1127
+ defaultVpId: bootstrap.defaultVpId,
1128
+ workDir,
1129
+ workspaceKey,
1130
+ });
1131
+ handle.close();
1132
+ });
1133
+ if (!sessionRunner) {
1134
+ sessionRunner = createCliSessionRunner({
1135
+ loaded,
1136
+ sessionId,
1137
+ workDir,
1138
+ configureEngine: configureStreamEngine,
1139
+ });
1140
+ }
1141
+ if (!sessionRunner) throw new Error(`Failed to initialize formal stream-json Session ${sessionId}`);
1142
+ const meta = sessionRunner.meta;
1143
+ if (meta) addOrUpdateManifestSession(
1144
+ loaded.yeaftDir,
1145
+ meta,
1146
+ join(sessionsRoot(loaded.yeaftDir), sessionId),
1147
+ );
1148
+ }
1149
+ }
1094
1150
  const routingIntent = normalizeStreamRoutingIntent(message);
1095
1151
  if (routingIntent && !sessionRunner) {
1096
1152
  throw new Error('stream-json VP selectors require an existing formal Session with a persisted roster');