@yeaft/webchat-agent 0.1.216 → 0.1.218
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/crew/role-output.js +10 -1
- package/crew/routing.js +1 -1
- package/crew/task-files.js +43 -6
- package/package.json +1 -1
package/crew/role-output.js
CHANGED
|
@@ -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
|
|
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
|
}
|
package/crew/task-files.js
CHANGED
|
@@ -92,16 +92,53 @@ export async function readTaskFile(session, taskId) {
|
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
/**
|
|
95
|
-
*
|
|
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 (
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
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
|
|