@yeaft/webchat-agent 0.1.216 → 0.1.217

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.
@@ -127,7 +127,8 @@ export async function processRoleOutput(session, roleName, roleQuery, roleState)
127
127
  // 决策者 turn 完成:检测 TASKS block 中新完成的任务
128
128
  const roleConfig = session.roles.get(roleName);
129
129
  if (roleConfig?.isDecisionMaker) {
130
- const nowCompleted = parseCompletedTasks(roleState.accumulatedText);
130
+ const knownTaskIds = Array.from(session.features.keys());
131
+ const nowCompleted = parseCompletedTasks(roleState.accumulatedText, knownTaskIds);
131
132
  if (nowCompleted.size > 0) {
132
133
  const prev = session._completedTaskIds || new Set();
133
134
  const newlyDone = [];
@@ -197,6 +198,14 @@ export async function processRoleOutput(session, roleName, roleQuery, roleState)
197
198
  if (session.status === 'paused' && roleState.accumulatedText) {
198
199
  const routes = parseRoutes(roleState.accumulatedText);
199
200
  if (routes.length > 0 && session.pendingRoutes.length === 0) {
201
+ // Fill missing taskId from roleState.currentTask (same as normal path L170-175)
202
+ const currentTask = roleState.currentTask;
203
+ for (const route of routes) {
204
+ if (!route.taskId && currentTask) {
205
+ route.taskId = currentTask.taskId;
206
+ route.taskTitle = currentTask.taskTitle;
207
+ }
208
+ }
200
209
  session.pendingRoutes = routes.map(route => ({ fromRole: roleName, route }));
201
210
  console.log(`[Crew] Saved ${routes.length} pending route(s) from aborted ${roleName}`);
202
211
  }
package/crew/routing.js CHANGED
@@ -140,7 +140,7 @@ export async function executeRoute(session, fromRole, route) {
140
140
  }
141
141
  }
142
142
  updateKanban(session, {
143
- taskId, taskTitle, assignee: to,
143
+ taskId, taskTitle, assignee: resolvedKanbanTo || to,
144
144
  status, summary: summary.substring(0, 100)
145
145
  }).catch(e => console.warn(`[Crew] Failed to update kanban:`, e.message));
146
146
  }
@@ -92,16 +92,53 @@ export async function readTaskFile(session, taskId) {
92
92
  }
93
93
 
94
94
  /**
95
- * 从 TASKS block 文本中提取已完成任务的 taskId 集合
95
+ * 从文本中提取已完成任务的 taskId 集合。
96
+ *
97
+ * Primary: parse TASKS block (---TASKS--- / ---END_TASKS---) for checked items with #taskId.
98
+ * Fallback: when no TASKS block is found AND knownTaskIds are provided,
99
+ * scan the full text for known taskId + completion keyword combinations.
100
+ * This handles cases where the PM mentions task completion in prose without
101
+ * emitting a formal TASKS block.
102
+ *
103
+ * @param {string} text - accumulated role output text
104
+ * @param {string[]} [knownTaskIds] - list of known task IDs to search for in fallback mode
105
+ * @returns {Set<string>} completed task IDs
96
106
  */
97
- export function parseCompletedTasks(text) {
107
+ export function parseCompletedTasks(text, knownTaskIds) {
98
108
  const ids = new Set();
109
+
110
+ // Primary: TASKS block parsing
99
111
  const match = text.match(/---TASKS---([\s\S]*?)---END_TASKS---/);
100
- if (!match) return ids;
101
- for (const line of match[1].split('\n')) {
102
- const m = line.match(/^-\s*\[[xX]\]\s*.+#(\S+)/);
103
- if (m) ids.add(m[1]);
112
+ if (match) {
113
+ for (const line of match[1].split('\n')) {
114
+ const m = line.match(/^-\s*\[[xX]\]\s*.+#(\S+)/);
115
+ if (m) ids.add(m[1]);
116
+ }
117
+ return ids;
104
118
  }
119
+
120
+ // Fallback: scan plain text for known taskId + completion keywords
121
+ if (!knownTaskIds || knownTaskIds.length === 0) return ids;
122
+
123
+ const completionPatterns = [
124
+ /已完成/, /完成/, /已合并/, /合并/, /DONE/, /DONE_MERGED/, /MERGED/,
125
+ /已关闭/, /关闭/, /✅/, /通过/, /passed/i, /merged/i, /completed/i
126
+ ];
127
+
128
+ for (const taskId of knownTaskIds) {
129
+ // Search for lines/sentences containing the taskId
130
+ const escaped = taskId.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
131
+ const re = new RegExp(`^.*${escaped}.*$`, 'gm');
132
+ let lineMatch;
133
+ while ((lineMatch = re.exec(text)) !== null) {
134
+ const line = lineMatch[0];
135
+ if (completionPatterns.some(p => p.test(line))) {
136
+ ids.add(taskId);
137
+ break;
138
+ }
139
+ }
140
+ }
141
+
105
142
  return ids;
106
143
  }
107
144
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.216",
3
+ "version": "0.1.217",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",