linco-connect 1.1.1 → 1.1.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linco-connect",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "自研 IM 桥接多 Agent 服务",
5
5
  "main": "server.js",
6
6
  "bin": {
@@ -2,7 +2,7 @@ const crypto = require('crypto');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
4
  const { ensureDir } = require('./config');
5
- const { sendError, sendSystem } = require('./protocol');
5
+ const { sendError, sendSystem, sendTurnEnd } = require('./protocol');
6
6
 
7
7
  const RESERVED_WINDOWS_NAMES = new Set(['CON', 'PRN', 'AUX', 'NUL', 'COM1', 'COM2', 'COM3', 'COM4', 'COM5', 'COM6', 'COM7', 'COM8', 'COM9', 'LPT1', 'LPT2', 'LPT3', 'LPT4', 'LPT5', 'LPT6', 'LPT7', 'LPT8', 'LPT9']);
8
8
  const SUPPORTED_IMAGE_MEDIA_TYPES = new Set(['image/png', 'image/jpeg', 'image/gif', 'image/webp']);
@@ -25,6 +25,7 @@ function handleMessageWithAttachments(msg, ws, session, config, executeAgentQuer
25
25
  savedAttachments = saveAttachments(session, attachments, config);
26
26
  } catch (err) {
27
27
  sendError(ws, `❌ 附件处理失败: ${err.message}`);
28
+ sendTurnEnd(ws, session, 'error', { error: err.message });
28
29
  return;
29
30
  }
30
31
 
@@ -193,7 +193,9 @@ function ensureClaudeProcess(ws, session, config) {
193
193
  if (session.isTurnActive) {
194
194
  session.isTurnActive = false;
195
195
  session.currentInputForNoOutput = null;
196
- sendError(ws, code === 0 || code === null ? '⚠️ Claude 会话已结束。' : `❌ Claude 进程退出,退出码: ${code}`);
196
+ const message = code === 0 || code === null ? '⚠️ Claude 会话已结束。' : `❌ Claude 进程退出,退出码: ${code}`;
197
+ sendError(ws, message);
198
+ sendTurnEnd(ws, session, 'error', { error: message });
197
199
  drainMessageQueue(ws, session, config);
198
200
  }
199
201
  });
@@ -205,7 +207,9 @@ function ensureClaudeProcess(ws, session, config) {
205
207
  session.isTurnActive = false;
206
208
  session.currentInputForNoOutput = null;
207
209
  flushAssistantText(ws, session);
208
- sendError(ws, `❌ 无法启动 Claude: ${err.message}\n请确认已安装 Claude Code 并设置好 API 密钥。`);
210
+ const message = `❌ 无法启动 Claude: ${err.message}\n请确认已安装 Claude Code 并设置好 API 密钥。`;
211
+ sendError(ws, message);
212
+ sendTurnEnd(ws, session, 'error', { error: message });
209
213
  });
210
214
 
211
215
  return child;
package/src/protocol.js CHANGED
@@ -22,8 +22,21 @@ function buildTurnEndPayload(session, reason = 'completed', payload = {}) {
22
22
  };
23
23
  }
24
24
 
25
+ function turnEndKey(payload) {
26
+ return [
27
+ payload.requestId || '',
28
+ payload.streamId || '',
29
+ payload.sessionKey || '',
30
+ ].join('|');
31
+ }
32
+
25
33
  function sendTurnEnd(ws, session, reason = 'completed', payload = {}) {
26
- send(ws, 'turn_end', buildTurnEndPayload(session, reason, payload));
34
+ const turnEndPayload = buildTurnEndPayload(session, reason, payload);
35
+ const key = turnEndKey(turnEndPayload);
36
+ if (session && session.lastTurnEndKey === key) return false;
37
+ if (session) session.lastTurnEndKey = key;
38
+ send(ws, 'turn_end', turnEndPayload);
39
+ return true;
27
40
  }
28
41
 
29
42
  module.exports = {
@@ -1,7 +1,7 @@
1
1
  const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { resetOutgoingAttachments, startOutboxWatcher, stopOutboxWatcher } = require('./outgoingAttachmentHandler');
4
- const { sendError, sendSystem } = require('./protocol');
4
+ const { sendError, sendSystem, sendTurnEnd } = require('./protocol');
5
5
  const { removeAgentSessionFromHistory, saveSessionMetadata } = require('./session');
6
6
  const { resolvePendingDanger, resolvePendingPermission, stopAgentProcess } = require('./agentRunner');
7
7
  const { clearPendingPermissions, pendingPermissionIds } = require('./permissionState');
@@ -35,7 +35,7 @@ function handleSlashCommand(text, ws, session, config) {
35
35
  📎 支持附件:默认允许普通文件(如 csv、xlsx、sql、txt、md、pdf、docx 等),高风险可执行/脚本文件默认拦截
36
36
  📤 Agent 生成文件后可放入当前会话 outbox 目录自动发送到对话框
37
37
  🔁 其他 /xxx 命令会透传给当前 Agent,优先使用当前 Agent 原生斜杠命令能力`);
38
- return true;
38
+ return completeLocalCommand(ws, session);
39
39
 
40
40
  case '/commands':
41
41
  sendSystem(ws, `${localCommandsHelp()}
@@ -44,38 +44,38 @@ function handleSlashCommand(text, ws, session, config) {
44
44
  除上述本地命令外,其他 /xxx 会直接发送给当前 Agent,例如 /model、/status、/memory、/compact 等。
45
45
 
46
46
  ⚠️ 注意:部分 Agent 原生命令只在交互式 CLI/TUI 中更新界面;在 Linco 的 stream-json 模式下可能返回“无输出”或提示当前环境不可用。遇到这种情况不是连接失败,而是该命令不适合当前桥接模式。`);
47
- return true;
47
+ return completeLocalCommand(ws, session);
48
48
 
49
49
  case '/status':
50
50
  sendStatus(ws, session);
51
- return true;
51
+ return completeLocalCommand(ws, session);
52
52
 
53
53
  case '/pwd':
54
54
  if (isHermesSession(session)) {
55
55
  sendHermesWorkspaceNotice(ws);
56
- return true;
56
+ return completeLocalCommand(ws, session);
57
57
  }
58
58
  sendSystem(ws, `📂 ${session.workspace}`);
59
- return true;
59
+ return completeLocalCommand(ws, session);
60
60
 
61
61
  case '/cd':
62
62
  if (isHermesSession(session)) {
63
63
  sendHermesWorkspaceNotice(ws);
64
- return true;
64
+ return completeLocalCommand(ws, session);
65
65
  }
66
66
  handleCd(parts[1], ws, session, config);
67
- return true;
67
+ return completeLocalCommand(ws, session);
68
68
 
69
69
  case '/new':
70
70
  stopAgentProcess(session, { clearAgentSession: true });
71
71
  resetOutgoingAttachments(session, config);
72
72
  sendSystem(ws, '🆕 已开启新会话,之前上下文已清除。');
73
- return true;
73
+ return completeLocalCommand(ws, session);
74
74
 
75
75
  case '/stop':
76
76
  stopAgentProcess(session, { clearAgentSession: false });
77
77
  sendSystem(ws, '⏹️ 已停止当前 Agent 进程,下次消息会尝试恢复当前会话。');
78
- return true;
78
+ return completeLocalCommand(ws, session);
79
79
 
80
80
  case '/base':
81
81
  sendSystem(ws, `🗄️ Linco 运行信息:
@@ -84,33 +84,38 @@ Linco Home: ${config.lincoHome}
84
84
  会话运行目录: ${session.runtimeDir}
85
85
  附件目录: ${session.attachmentsDir}
86
86
  outbox 目录: ${session.outboxDir}`);
87
- return true;
87
+ return completeLocalCommand(ws, session);
88
88
 
89
89
  case '/list':
90
90
  handleList(parts[1], ws, session);
91
- return true;
91
+ return completeLocalCommand(ws, session);
92
92
 
93
93
  case '/switch':
94
94
  handleSwitch(parts[1], ws, session, config);
95
- return true;
95
+ return completeLocalCommand(ws, session);
96
96
 
97
97
  case '/delete':
98
98
  handleDelete(parts[1], ws, session);
99
- return true;
99
+ return completeLocalCommand(ws, session);
100
100
 
101
101
  case '/approve':
102
102
  handleApprove(parts[1], ws, session, config);
103
- return true;
103
+ return completeLocalCommand(ws, session);
104
104
 
105
105
  case '/usage':
106
106
  handleUsage(ws, session);
107
- return true;
107
+ return completeLocalCommand(ws, session);
108
108
 
109
109
  default:
110
110
  return false;
111
111
  }
112
112
  }
113
113
 
114
+ function completeLocalCommand(ws, session) {
115
+ sendTurnEnd(ws, session);
116
+ return true;
117
+ }
118
+
114
119
  function sendStatus(ws, session) {
115
120
  const processRunning = !!(
116
121
  (session.agentProcess || session.claudeProcess) &&