@yeaft/webchat-agent 0.1.967 → 0.1.969

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/conversation.js CHANGED
@@ -774,18 +774,39 @@ export async function handleUserInput(msg) {
774
774
  let state = ctx.conversations.get(conversationId);
775
775
 
776
776
  // ★ Non-Claude providers: dispatch to driver and return
777
- const providerName = state?.providerName || DEFAULT_PROVIDER;
777
+ // fix-copilot-provider-persist: after an agent process restart `state` is
778
+ // gone (ctx.conversations is in-memory only). The server now forwards the
779
+ // persisted `provider` on the execute/transfer_files payload so we can
780
+ // re-resolve it here — otherwise this falls back to claude-code, skips the
781
+ // self-heal `driver.start()` below, and a copilot conversation mis-routes
782
+ // to Claude (the dead-send bug). `isValidProvider` guards a bad value.
783
+ const providerName = state?.providerName
784
+ || (isValidProvider(msg.provider) ? msg.provider : DEFAULT_PROVIDER);
778
785
  if (providerName !== 'claude-code') {
779
786
  const driver = getProvider(providerName);
780
787
  const effectiveWorkDir = workDir || state?.workDir || ctx.CONFIG.workDir;
781
788
  if (!state) {
782
- state = await driver.start({
783
- conversationId,
784
- workDir: effectiveWorkDir,
785
- resumeSessionId: claudeSessionId || null,
786
- userId: msg.userId,
787
- username: msg.username,
788
- });
789
+ // Re-spawn the provider's session (e.g. the Copilot ACP child) that
790
+ // died with the previous agent process. If the CLI is unavailable on
791
+ // the restarted machine, surface a visible error frame instead of an
792
+ // unhandled rejection (the send was dispatched un-awaited upstream).
793
+ try {
794
+ state = await driver.start({
795
+ conversationId,
796
+ workDir: effectiveWorkDir,
797
+ resumeSessionId: claudeSessionId || null,
798
+ userId: msg.userId,
799
+ username: msg.username,
800
+ });
801
+ } catch (err) {
802
+ sendOutput(conversationId, {
803
+ type: 'result',
804
+ subtype: 'error',
805
+ is_error: true,
806
+ error: `${providerName} provider failed to start: ${err?.message || err}`,
807
+ });
808
+ return;
809
+ }
789
810
  }
790
811
  if (workDir) state.workDir = workDir;
791
812
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.967",
3
+ "version": "0.1.969",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -1,7 +1,7 @@
1
1
  import { existsSync, writeFileSync, mkdirSync } from 'fs';
2
2
  import { join, basename, extname } from 'path';
3
3
  import ctx from '../context.js';
4
- import { getProvider, DEFAULT_PROVIDER } from '../providers/index.js';
4
+ import { getProvider, DEFAULT_PROVIDER, isValidProvider } from '../providers/index.js';
5
5
  import { sendOutput, sendConversationList } from '../conversation.js';
6
6
 
7
7
  // 临时文件目录名 (不易冲突)
@@ -64,12 +64,35 @@ export async function handleTransferFiles(msg) {
64
64
  // image bytes over ACP, so images are handed to the driver as attachments
65
65
  // instead of being inlined as disk paths the way the Claude path does.
66
66
  //
67
- // Note: when state is null here, providerName resolves to DEFAULT_PROVIDER
68
- // ('claude-code'), so this branch only runs for an already-created
69
- // non-claude conversation state is guaranteed non-null inside it.
70
- const providerName = state?.providerName || DEFAULT_PROVIDER;
67
+ // Note: after an agent process restart `state` is null even for a copilot
68
+ // conversation (ctx.conversations is in-memory). The server now forwards
69
+ // the persisted `provider` on the transfer_files payload, so we re-resolve
70
+ // it here and re-spawn the driver — without this an attachment send on a
71
+ // restarted copilot conversation would fall through to the Claude path and
72
+ // mis-route. `isValidProvider` guards a bad value.
73
+ const providerName = state?.providerName
74
+ || (isValidProvider(msg.provider) ? msg.provider : DEFAULT_PROVIDER);
71
75
  if (providerName !== 'claude-code') {
72
76
  const driver = getProvider(providerName);
77
+ if (!state) {
78
+ try {
79
+ state = await driver.start({
80
+ conversationId,
81
+ workDir: effectiveWorkDir,
82
+ resumeSessionId: claudeSessionId || null,
83
+ userId: msg.userId,
84
+ username: msg.username,
85
+ });
86
+ } catch (err) {
87
+ sendOutput(conversationId, {
88
+ type: 'result',
89
+ subtype: 'error',
90
+ is_error: true,
91
+ error: `${providerName} provider failed to start: ${err?.message || err}`,
92
+ });
93
+ return;
94
+ }
95
+ }
73
96
  if (workDir) state.workDir = workDir;
74
97
 
75
98
  // Images go inline as provider attachments ([{type,data,mimeType}] — the