@yeaft/webchat-agent 0.1.490 → 0.1.491
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/routing.js +33 -9
- package/package.json +1 -1
package/crew/routing.js
CHANGED
|
@@ -43,10 +43,13 @@ export function parseRoutes(text) {
|
|
|
43
43
|
// Replaces ```...``` content with whitespace of same length to preserve positions
|
|
44
44
|
text = text.replace(/```[\s\S]*?```/g, m => ' '.repeat(m.length));
|
|
45
45
|
|
|
46
|
-
// ─── Phase 1: Standard ROUTE blocks (with
|
|
47
|
-
// ★ Tolerate
|
|
48
|
-
//
|
|
49
|
-
|
|
46
|
+
// ─── Phase 1: Standard ROUTE blocks (with closing marker) ─────
|
|
47
|
+
// ★ Tolerate closer variants:
|
|
48
|
+
// ---END_ROUTE--- (underscore)
|
|
49
|
+
// ---END ROUTE--- (space)
|
|
50
|
+
// ---END--- (bare — users / PM often write this)
|
|
51
|
+
// ★ Use negative lookahead to not cross another ---ROUTE--- boundary.
|
|
52
|
+
const regex = /---ROUTE---\s*\n((?:(?!---ROUTE---)[\s\S])*?)---END(?:[_ ]ROUTE)?---/g;
|
|
50
53
|
let match;
|
|
51
54
|
const matchedRanges = []; // track matched ranges to avoid double-parsing
|
|
52
55
|
|
|
@@ -56,9 +59,12 @@ export function parseRoutes(text) {
|
|
|
56
59
|
if (parsed) routes.push(parsed);
|
|
57
60
|
}
|
|
58
61
|
|
|
59
|
-
// ─── Phase 2: Fallback — ROUTE block missing
|
|
60
|
-
//
|
|
61
|
-
//
|
|
62
|
+
// ─── Phase 2: Fallback — ROUTE block missing any closing marker ──
|
|
63
|
+
// Take content until (a) next ---ROUTE--- boundary, (b) first blank
|
|
64
|
+
// line (summary is almost always a single paragraph — anything after
|
|
65
|
+
// a blank line is kanban/recent-routes/task-context noise injected
|
|
66
|
+
// by the crew runtime), or (c) EOF. The blank-line cutoff prevents
|
|
67
|
+
// the whole back-injected blob from being swallowed as the summary.
|
|
62
68
|
const openRegex = /---ROUTE---\s*\n/g;
|
|
63
69
|
while ((match = openRegex.exec(text)) !== null) {
|
|
64
70
|
// Skip if this range was already captured by Phase 1
|
|
@@ -68,7 +74,10 @@ export function parseRoutes(text) {
|
|
|
68
74
|
const blockStart = pos + match[0].length;
|
|
69
75
|
// End at next ---ROUTE--- or EOF
|
|
70
76
|
const nextRoute = text.indexOf('---ROUTE---', blockStart);
|
|
71
|
-
const
|
|
77
|
+
const hardEnd = nextRoute !== -1 ? nextRoute : text.length;
|
|
78
|
+
// Soft end: first blank line (two or more newlines with only whitespace in between).
|
|
79
|
+
const blank = text.slice(blockStart, hardEnd).search(/\n[ \t]*\n/);
|
|
80
|
+
const blockEnd = blank !== -1 ? blockStart + blank : hardEnd;
|
|
72
81
|
const block = text.slice(blockStart, blockEnd);
|
|
73
82
|
|
|
74
83
|
const parsed = _parseRouteBlock(block);
|
|
@@ -88,7 +97,8 @@ export function parseRoutes(text) {
|
|
|
88
97
|
const lastRouteOpen = precedingText.lastIndexOf('---ROUTE---');
|
|
89
98
|
const lastRouteClose = Math.max(
|
|
90
99
|
precedingText.lastIndexOf('---END_ROUTE---'),
|
|
91
|
-
precedingText.lastIndexOf('---END ROUTE---')
|
|
100
|
+
precedingText.lastIndexOf('---END ROUTE---'),
|
|
101
|
+
precedingText.lastIndexOf('---END---')
|
|
92
102
|
);
|
|
93
103
|
if (lastRouteOpen > lastRouteClose) continue; // inside an open block
|
|
94
104
|
|
|
@@ -122,6 +132,20 @@ function _parseRouteBlock(block) {
|
|
|
122
132
|
const taskTitleMatch = block.match(/^taskTitle:\s*(.+)/im);
|
|
123
133
|
|
|
124
134
|
let summary = summaryMatch ? summaryMatch[1].trim() : '';
|
|
135
|
+
|
|
136
|
+
// ★ Bare-body fallback — PM / humans often omit the `summary:` label and
|
|
137
|
+
// just write the message as free text AFTER the known fields. Collect
|
|
138
|
+
// everything that is NOT a recognised field line as the body.
|
|
139
|
+
if (!summary) {
|
|
140
|
+
const KNOWN_FIELD = /^\s*(?:to|task|taskTitle|summary)\s*:/i;
|
|
141
|
+
const bare = block
|
|
142
|
+
.split(/\r?\n/)
|
|
143
|
+
.filter(line => !KNOWN_FIELD.test(line))
|
|
144
|
+
.join('\n')
|
|
145
|
+
.trim();
|
|
146
|
+
if (bare) summary = bare;
|
|
147
|
+
}
|
|
148
|
+
|
|
125
149
|
if (!summary) {
|
|
126
150
|
summary = '[该角色未提供消息摘要]';
|
|
127
151
|
}
|