hermes-connect-services 1.2.0 → 1.3.1

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.
@@ -32,15 +32,19 @@ MIN_WEIGHTED_CHARS = 14.0
32
32
  # 请求用户裁决 / 提供输入的措辞。命中即否决。
33
33
  REQUEST_PATTERNS = [
34
34
  # 中文:把决定权交回用户
35
- r"请你?(?:确认|批准|授权|选择|决定|提供|粘贴|发送|告知|告诉)",
35
+ r"请你?(?:确认|批准|授权|选择|决定|提供|粘贴|发送|告知|告诉|现在|立刻|马上|配合|扫码|扫|在)",
36
36
  r"你(?:可以|需要|来)(?:确认|决定|选择|提供|先)",
37
37
  r"需要你(?:确认|决定|操作|提供|回复|选择)",
38
38
  r"(?:是否|要不要|需不需要|要不要我|需要我)(?:继续|执行|我|帮|先|再)?",
39
- r"等你(?:确认|回复|决定|操作)",
40
- r"告诉我(?:你|是否|要|结果|选择)",
39
+ r"等你(?:确认|回复|决定|操作|安排)",
40
+ r"告诉我(?:你|是否|要|结果|选择|一声|什么时候)",
41
+ r"(?:说|吱|喊)一声",
42
+ r"你(?:点个?头|说[「“\"']?开[」”\"']?)(?:我就|我立刻|我马上)?",
43
+ r"要我[^。!?\n]{0,24}(?:就|的话|吗)",
44
+ r"扫完(?:不用回复|告诉我|说一声)",
41
45
  r"让我知道",
42
- r"由你(?:决定|来|选)",
43
- r"你(?:先|来)(?:确认|决定|操作|选)",
46
+ r"由你(?:决定|来|选|定)",
47
+ r"你(?:先|来)(?:确认|决定|操作|选|说)",
44
48
  r"确认后(?:我)?再",
45
49
  r"(?:你|您)看(?:看|一下)?(?:是否|要不要|行不行)",
46
50
  # English: handing the decision back
@@ -55,11 +59,18 @@ REQUEST_PATTERNS = [
55
59
  r"\bwaiting (?:for|on) your\b",
56
60
  ]
57
61
 
62
+ # 确认/复述长期规则或未来条件约定(「以后遇到…我会先…」「可以,主人。以后…」)。
63
+ # 这是回答用户的规则设定,不是当前回合宣布下一步动作后漏调工具。
64
+ STANDING_RULE_PATTERNS = [
65
+ r"(?:以后|今后|之后|下次|后续|往后|接下来每次|每当|每次)[^。!?\n]{0,40}(?:提到|遇到|收到|让我|你让我|要求|需要|开始|做|时|的时候)[^。!?\n]{0,30}我(?:会|将|都会|先|就)",
66
+ r"^(?:可以|好的?|明白|没问题|收到|记下了|知道了)[,,。!!\s]*(?:主人|小主|老板)?[,,。!!\s]*(?:以后|今后|之后|下次|后续|往后|每当|每次)",
67
+ ]
68
+
58
69
  # 「已经做完」的收尾。命中且无未来标记时否决。
59
70
  DONE_PATTERNS = [
60
71
  r"(?:已|已经)(?:经)?(?:完成|修好|改好|搞定|处理完|就绪|生效|上线|发布|通过)",
61
- r"(?:全部|都)(?:完成|通过|搞定|就绪)",
62
- r"(?:搞定|收工|完事)(?:了)?[。.!!]?$",
72
+ r"(?:全部|都)(?:完成|通过|搞定|就绪|闭合)",
73
+ r"(?:搞定|收工|完事|可以收尾)(?:了)?[。.!!]?$",
63
74
  r"(?:到|就)这里(?:为止|结束)?",
64
75
  r"(?:没有|无需)(?:其他|更多|别的)(?:改动|问题|需要)",
65
76
  r"\b(?:all )?done\b",
@@ -77,6 +88,8 @@ FUTURE_STRONG = [
77
88
  r"让(?:我们|我)先",
78
89
  r"(?:接下来|下面|现在)我(?:来|将|会|要)",
79
90
  r"开始(?:执行|处理|修改|实现|写|改|跑|检查)",
91
+ r"(?:^|[。!?\n]\s*)(?:开始[。,,\s]*)?先(?:把|将|从|去|给|对|用|跑|看|查|改|写|读|登记|核对|整理|创建|执行|检查)[^。!?\n]{1,60}再",
92
+ r"(?:^|[。!?\n]\s*)现在(?:重新|开始|把|将|先|去|来|给|对|跑|开|改|写|整理|执行|检查)",
80
93
  r"\blet me\b",
81
94
  r"\bi'?ll\b",
82
95
  r"\bi will\b",
@@ -98,11 +111,11 @@ FUTURE_WEAK = [
98
111
 
99
112
  # 动作动词:把「未来时」落到「要做一件具体的事」上。
100
113
  ACTION_VERB = [
101
- r"(?:检查|查看|看看|查一下|读取|打开|搜索|定位)",
102
- r"(?:修改|改成|改掉|编辑|写入|创建|新建|删除|移除|重写)",
103
- r"(?:执行|运行|跑一下|启动|重启|安装|部署|发布|提交)",
114
+ r"(?:检查|查看|看看|查一下|读取|打开|搜索|定位|核对|登记|整理)",
115
+ r"(?:修改|改成|改掉|编辑|写入|创建|新建|删除|移除|重写|写进)",
116
+ r"(?:执行|运行|跑一下|跑起|启动|重启|安装|部署|发布|提交|开窗口|重新开)",
104
117
  r"(?:修复|实现|添加|增加|补充|更新|替换|迁移|重构)",
105
- r"(?:验证|测试|校验|确认一下|跑通|编译)",
118
+ r"(?:验证|测试|校验|确认一下|跑通|编译|上机)",
106
119
  r"\b(?:check|read|open|inspect|look at|search|grep)\b",
107
120
  r"\b(?:edit|write|create|add|remove|delete|rewrite|patch|update)\b",
108
121
  r"\b(?:run|execute|install|deploy|build|start|restart|publish)\b",
@@ -114,6 +127,11 @@ COLON_TAIL = re.compile(r"[::]\s*\Z")
114
127
  ELLIPSIS_TAIL = re.compile(r"(?:\.{3,}|…)\s*\Z")
115
128
  QUESTION_TAIL = re.compile(r"[??]\s*\Z")
116
129
 
130
+ # action-colon 判定用:末行收尾的冒号、分句切分、列表项前缀。
131
+ _COLON_AT_END = re.compile(r"[::]\s*\Z")
132
+ _CLAUSE_SPLIT = re.compile(r"[,,。;;!!??]")
133
+ _LIST_ITEM = re.compile(r"^[-*•\d|]")
134
+
117
135
  # 阈值。只有弱未来标记时必须靠额外信号才能过线。
118
136
  THRESHOLD_STRONG = 3.0
119
137
  THRESHOLD_WEAK_ONLY = 4.5
@@ -140,6 +158,7 @@ def _any_match(group: str, text: str) -> bool:
140
158
 
141
159
  _PATTERN_GROUPS = {
142
160
  "request": REQUEST_PATTERNS,
161
+ "standing_rule": STANDING_RULE_PATTERNS,
143
162
  "done": DONE_PATTERNS,
144
163
  "future_strong": FUTURE_STRONG,
145
164
  "future_weak": FUTURE_WEAK,
@@ -207,10 +226,14 @@ def detect_stall(raw_text: Any, tool_call_count: int = 0) -> Dict[str, Any]:
207
226
  if _any_match("request", tail):
208
227
  return _result("asked-user")
209
228
 
229
+ # ── 否决 ③:确认/复述长期规则约定(「以后遇到…我会先…」)──────────────
230
+ if _any_match("standing_rule", tail):
231
+ return _result("standing-rule")
232
+
210
233
  has_strong = _any_match("future_strong", tail)
211
234
  has_weak = _any_match("future_weak", tail)
212
235
 
213
- # ── 否决 ③:明确宣告完成且没有未来时 ──────────────────────────────────
236
+ # ── 否决 ④:明确宣告完成且没有未来时 ──────────────────────────────────
214
237
  if not has_strong and not has_weak and _any_match("done", tail):
215
238
  return _result("already-done")
216
239
 
@@ -224,20 +247,42 @@ def detect_stall(raw_text: Any, tool_call_count: int = 0) -> Dict[str, Any]:
224
247
  if has_weak:
225
248
  score += 1.5
226
249
  signals.append("future-weak")
227
- if _any_match("action_verb", tail):
250
+ has_verb = _any_match("action_verb", tail)
251
+ if has_verb:
228
252
  score += 1.0
229
253
  signals.append("action-verb")
230
- if COLON_TAIL.search(raw):
254
+ has_colon = bool(COLON_TAIL.search(raw))
255
+ if has_colon:
231
256
  score += 1.5
232
257
  signals.append("colon-tail")
233
258
  if ELLIPSIS_TAIL.search(raw):
234
259
  score += 0.5
235
260
  signals.append("ellipsis-tail")
236
261
 
237
- if not has_strong and not has_weak:
262
+ # 末行非列表项以「动作动词 + 冒号」收尾(例如「……整份重写干净:」):
263
+ # 即使省略了显式「我先/接下来」代词,也是典型的即将发起工具调用。
264
+ #
265
+ # 动词必须落在冒号前的**最后一个分句**里,否则「已定位问题并修好,根因有
266
+ # 三层:」这类说明性收尾会被误判 —— 它的动词讲的是已完成的事,冒号引出的
267
+ # 是名词性解释。宁可漏掉一次续跑,也不要把说明性收尾当成漏执行。
268
+ lines = [line.strip() for line in raw.split("\n") if line.strip()]
269
+ last_line = lines[-1] if lines else ""
270
+ tail_clause = _CLAUSE_SPLIT.split(_COLON_AT_END.sub("", last_line))[-1] if last_line else ""
271
+ is_action_colon = (
272
+ has_colon
273
+ and has_verb
274
+ and _any_match("action_verb", tail_clause)
275
+ and not _LIST_ITEM.match(last_line)
276
+ and len(last_line) <= 160
277
+ )
278
+ if not has_strong and not has_weak and is_action_colon:
279
+ score += 1.0
280
+ signals.append("action-colon")
281
+
282
+ if not has_strong and not has_weak and not is_action_colon:
238
283
  return _result("no-future-cue", score, signals)
239
284
 
240
- threshold = THRESHOLD_STRONG if has_strong else THRESHOLD_WEAK_ONLY
285
+ threshold = THRESHOLD_STRONG if (has_strong or is_action_colon) else THRESHOLD_WEAK_ONLY
241
286
  if score < threshold:
242
287
  return _result("weak-only", score, signals)
243
288
 
@@ -254,3 +299,18 @@ CONTINUE_PROMPT = "\n".join([
254
299
  "如果它其实已经完成,或不需要执行,用一句话说明结论即可,不要重述计划。",
255
300
  "如果需要用户先决定,提出一个具体的问题,不要继续描述待办。",
256
301
  ])
302
+
303
+ # 空回合的续跑提示:模型只留下思考过程(thinking),既没有正文也没有工具调用。
304
+ #
305
+ # 实测故障模式(2026-10-30,pi 会话 01a1880e,provider=workbuddy / hy4-preview):
306
+ # usage 显示 output 有 286 / 667 个 token、reasoning 145 / 457,耗时 7.6s、15.5s
307
+ # (不是超时被切),但 content 里只有 thinking 块,正文为空。用户看到的就是
308
+ # 「思考着,突然不工作了」。
309
+ #
310
+ # 与 CONTINUE_PROMPT 的区别:那一条假定还有可见文本可以判断意图;这里什么都
311
+ # 没有,所以不能说「执行你宣布的操作」—— 只能要求它把结论说出来。
312
+ EMPTY_TURN_PROMPT = "\n".join([
313
+ "你上一条回复只留下了思考过程,没有给出任何可见内容,也没有调用任何工具。",
314
+ "请把你的结论直接说出来:已经做了什么、接下来要做什么,或者一句话说明这一轮的结果。",
315
+ "不要重复思考过程,也不要只描述计划而不动手。",
316
+ ])
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "hermes-connect-services",
3
- "version": "1.2.0",
3
+ "version": "1.3.1",
4
4
  "description": "Bring zcode-ai-connect's four loopback endpoints to hermes, and add the missing turn-end gate that re-runs a turn when the model announces a next step but calls no tool.",
5
5
  "type": "module",
6
6
  "license": "MIT",