@myassis/gateway 1.0.95 → 1.0.96

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.
@@ -577,10 +577,14 @@ class Session {
577
577
  * 因此当会话已有计划、且连续 roundsSincePlanUpdate 轮工具调用都没有同步进度时,
578
578
  * 返回一条提醒文案注入上下文;无需提醒时返回 null。
579
579
  */
580
- buildPlanReminder(roundsSincePlanUpdate) {
580
+ buildPlanReminder(roundsSincePlanUpdate, finalizing) {
581
581
  // 没有计划或计划已完成,无需提醒
582
582
  if (this.plan.length === 0 || this.isPlanCompleted())
583
583
  return null;
584
+ if (finalizing) {
585
+ // Tools are stripped when finalizing, so skip the reminder instead of telling the model to continue
586
+ return null;
587
+ }
584
588
  // 允许模型连续做 1 轮工具调用后再要求同步,避免过于频繁地打断
585
589
  if (roundsSincePlanUpdate < 2)
586
590
  return null;
@@ -928,6 +932,8 @@ class Session {
928
932
  let roundsSincePlanUpdate = 0;
929
933
  // 计划模式:收尾时提醒模型补完计划的次数,避免反复提醒导致死循环
930
934
  let planFinalizeNudges = 0;
935
+ let pseudoToolCallRetries = 0;
936
+ let finalizeStatus = 'completed';
931
937
  // 当前裁剪级别,遇到上下文超限时逐级加重
932
938
  let trimLevel = ContextBuilder_js_1.TrimLevel.OldToolPayload;
933
939
  /** 判断错误是否为上下文超限 */
@@ -1070,6 +1076,7 @@ class Session {
1070
1076
  // 还没入队,插进去会破坏配对,被严格校验的厂商判为非法请求。
1071
1077
  logger.warn(`工具调用轮次超过上限 ${index_js_2.appConfig.maxToolRounds},转入收尾`);
1072
1078
  forceFinalize = true;
1079
+ finalizeStatus = 'error';
1073
1080
  }
1074
1081
  messages.push({
1075
1082
  role: 'assistant',
@@ -1356,7 +1363,7 @@ class Session {
1356
1363
  }
1357
1364
  // 计划模式:模型常常只在开头和结尾调用 updatePlan,中间过程不同步进度。
1358
1365
  // 这里在已有计划且连续多轮未同步时,主动注入一条提醒,迫使模型推进计划状态。
1359
- const reminder = this.buildPlanReminder(roundsSincePlanUpdate);
1366
+ const reminder = this.buildPlanReminder(roundsSincePlanUpdate, forceFinalize);
1360
1367
  if (reminder) {
1361
1368
  messages.push({
1362
1369
  role: 'assistant',
@@ -1381,7 +1388,7 @@ class Session {
1381
1388
  else {
1382
1389
  // ========== 没有工具调用,准备结束本轮 ==========
1383
1390
  // 计划模式:计划还没收尾时,先提醒模型补完状态(最多提醒一次,避免死循环)
1384
- if (planFinalizeNudges < 1) {
1391
+ if (!forceFinalize && planFinalizeNudges < 1) {
1385
1392
  const finalizeReminder = this.buildPlanFinalizeReminder();
1386
1393
  if (finalizeReminder) {
1387
1394
  planFinalizeNudges++;
@@ -1398,6 +1405,34 @@ class Session {
1398
1405
  return await processModelResponse();
1399
1406
  }
1400
1407
  }
1408
+ const _rawText = llmResult.content || llmResult.reasoningContent || '';
1409
+ const PSEUDO_TOOL_RE = /<\/?(exec|edit|file|search|fetch|webfetch|screenshot|keyboard|mouse|skill|task|model|sessionsspawn|setsessiontitle|updateplan|tool|tool_call|function)\b|exec\s+command=|\btoolname\b/i;
1410
+ if (_rawText && PSEUDO_TOOL_RE.test(_rawText)) {
1411
+ if (forceFinalize === false && pseudoToolCallRetries < 1) {
1412
+ pseudoToolCallRetries = 1;
1413
+ messages.push({
1414
+ role: 'user',
1415
+ content: 'Your previous reply wrote a tool call as text (e.g. an exec tag, exec command=, or a tool_call block). Such text is NOT executed. Call tools only through the function-calling tool_calls field. Never write tool-call tags in prose. If tools are unavailable, reply in plain language about progress and emit no tool-call tags. Retry using the correct format.',
1416
+ attachments: []
1417
+ });
1418
+ return await processModelResponse();
1419
+ }
1420
+ const _m = _rawText.match(PSEUDO_TOOL_RE);
1421
+ const _idx = _m && _m.index ? _m.index : 0;
1422
+ let _cleaned = _m ? _rawText.slice(0, _idx) : _rawText;
1423
+ _cleaned = _cleaned.trimEnd();
1424
+ if (!_cleaned.trim()) {
1425
+ _cleaned = '[Reached the tool-call limit for this turn. Partial progress is shown above; please continue remaining steps in a new message.]';
1426
+ }
1427
+ else {
1428
+ _cleaned = _cleaned + '\n\n[Reached the tool-call limit; partial progress above. Continue remaining steps in a new message.]';
1429
+ }
1430
+ finalizeStatus = 'error';
1431
+ if (llmResult.content)
1432
+ llmResult.content = _cleaned;
1433
+ else
1434
+ llmResult.reasoningContent = _cleaned;
1435
+ }
1401
1436
  if (llmResult.content || llmResult.reasoningContent) {
1402
1437
  // 分段发送内容,每段约50个字符
1403
1438
  const chunkSize = 1;
@@ -1412,12 +1447,13 @@ class Session {
1412
1447
  }
1413
1448
  }
1414
1449
  if (!childAgent) {
1415
- this.addAssistantMessage(llmResult.content || llmResult.reasoningContent, toolCalls, [...new Set(modelNames)].join(','), this.currentMessageId);
1450
+ const assistantMessage = this.addAssistantMessage(llmResult.content || llmResult.reasoningContent, toolCalls, [...new Set(modelNames)].join(','), this.currentMessageId);
1451
+ assistantMessage.status = finalizeStatus;
1416
1452
  // 正文已完整产出,先落库再发 complete:
1417
1453
  // 客户端收到 complete 后就认为这条消息定稿了,此时库里必须已经有它。
1418
1454
  await this.saveMessage();
1419
1455
  }
1420
- sendSSE(res, { type: 'complete', modelName: [...new Set(modelNames)].join(",") });
1456
+ sendSSE(res, { type: 'complete', modelName: [...new Set(modelNames)].join(","), status: finalizeStatus });
1421
1457
  sendSSE(res, { type: '[DONE]' });
1422
1458
  return llmResult.content;
1423
1459
  }
@@ -1432,7 +1468,8 @@ class Session {
1432
1468
  if (toolCalls) {
1433
1469
  // 用 '' 覆盖会把已经增量保存的正文清空,这里保留已有内容
1434
1470
  const saved = this.messages.find(m => m.id === this.currentMessageId && m.role === 'assistant');
1435
- this.addAssistantMessage(saved?.content || '', toolCalls, [...new Set(modelNames)].join(','), this.currentMessageId);
1471
+ const assistantMessage = this.addAssistantMessage(saved?.content || '', toolCalls, [...new Set(modelNames)].join(','), this.currentMessageId);
1472
+ assistantMessage.status = 'error';
1436
1473
  }
1437
1474
  if (error.message !== 'aborted') {
1438
1475
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@myassis/gateway",
3
- "version": "1.0.95",
3
+ "version": "1.0.96",
4
4
  "description": "我的助手 Gateway Service - 本地 AI 网关服务,支持认证、WebSocket 实时通信和任务调度",
5
5
  "main": "dist/index.js",
6
6
  "bin": {