@c4t4/heyamigo 0.7.2 → 0.7.3
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/dist/memory/digest-flag.js +59 -12
- package/package.json +1 -1
|
@@ -1,15 +1,64 @@
|
|
|
1
|
-
|
|
1
|
+
// Trailing-marker parser. Handles tags like [DIGEST: ...], [JOURNAL:...],
|
|
2
|
+
// [JOURNAL-NEW:...], [ASYNC: ...] at the end of a reply.
|
|
3
|
+
//
|
|
4
|
+
// Uses a bracket-depth walk rather than a regex because payloads routinely
|
|
5
|
+
// contain nested square brackets (e.g. [ASYNC: read a [config] file] or
|
|
6
|
+
// [DIGEST: noted the [JOURNAL:x] pattern]). A naive /\[.*?\]/-style regex
|
|
7
|
+
// terminates at the FIRST inner `]` and either misidentifies the tag or
|
|
8
|
+
// drops it entirely. That bug leaked two real markers into user-facing
|
|
9
|
+
// replies today (DIGEST ~morning, ASYNC later). This parser closes that
|
|
10
|
+
// whole class of failure.
|
|
11
|
+
const KINDS = ['DIGEST', 'JOURNAL', 'JOURNAL-NEW', 'ASYNC'];
|
|
12
|
+
// Walk backwards from the end of the string, tracking bracket depth, to find
|
|
13
|
+
// the `[` that matches the final `]`. Returns the tag kind, its payload, and
|
|
14
|
+
// everything before the tag. Returns null if the tail doesn't cleanly look
|
|
15
|
+
// like a supported tag — caller should stop peeling.
|
|
16
|
+
function peelTrailingTag(raw) {
|
|
17
|
+
const trimmed = raw.replace(/\s+$/, '');
|
|
18
|
+
if (trimmed.length === 0)
|
|
19
|
+
return null;
|
|
20
|
+
if (trimmed[trimmed.length - 1] !== ']')
|
|
21
|
+
return null;
|
|
22
|
+
// Walk right-to-left counting depth. `]` pushes, `[` pops. The opening `[`
|
|
23
|
+
// that brings depth back to 0 is the match for the trailing `]`.
|
|
24
|
+
let depth = 0;
|
|
25
|
+
let openIdx = -1;
|
|
26
|
+
for (let i = trimmed.length - 1; i >= 0; i--) {
|
|
27
|
+
const c = trimmed[i];
|
|
28
|
+
if (c === ']') {
|
|
29
|
+
depth++;
|
|
30
|
+
}
|
|
31
|
+
else if (c === '[') {
|
|
32
|
+
depth--;
|
|
33
|
+
if (depth === 0) {
|
|
34
|
+
openIdx = i;
|
|
35
|
+
break;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
if (openIdx < 0)
|
|
40
|
+
return null; // unbalanced — don't try to interpret
|
|
41
|
+
const inside = trimmed.slice(openIdx + 1, trimmed.length - 1);
|
|
42
|
+
const colonIdx = inside.indexOf(':');
|
|
43
|
+
if (colonIdx < 0)
|
|
44
|
+
return null;
|
|
45
|
+
const tagCandidate = inside.slice(0, colonIdx).trim().toUpperCase();
|
|
46
|
+
if (!KINDS.includes(tagCandidate))
|
|
47
|
+
return null;
|
|
48
|
+
const payload = inside.slice(colonIdx + 1).trim();
|
|
49
|
+
const remaining = trimmed.slice(0, openIdx).replace(/\s+$/, '');
|
|
50
|
+
return { kind: tagCandidate, payload, remaining };
|
|
51
|
+
}
|
|
2
52
|
// Peel trailing tags off the end of a reply. Supported:
|
|
3
53
|
// [DIGEST: <reason>]
|
|
4
54
|
// [JOURNAL:<slug> — <note>] (append entry)
|
|
5
55
|
// [JOURNAL-NEW:<slug> — <purpose>] (create journal)
|
|
6
56
|
// [ASYNC: <self-sufficient task description>]
|
|
7
|
-
// Multiple tags supported
|
|
57
|
+
// Multiple tags supported in any order at the tail. Tags must be the LAST
|
|
8
58
|
// thing in the reply (after trimming trailing whitespace).
|
|
9
59
|
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
12
|
-
// Keeping the marker vocabulary small keeps Claude's context tight.
|
|
60
|
+
// Payload can contain arbitrary characters including `[` and `]` as long as
|
|
61
|
+
// the brackets are balanced within the payload.
|
|
13
62
|
export function extractFlags(reply) {
|
|
14
63
|
let current = reply;
|
|
15
64
|
let digest = null;
|
|
@@ -17,14 +66,13 @@ export function extractFlags(reply) {
|
|
|
17
66
|
const journalCreates = [];
|
|
18
67
|
const asyncTasks = [];
|
|
19
68
|
while (true) {
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
if (!match)
|
|
69
|
+
const peeled = peelTrailingTag(current);
|
|
70
|
+
if (!peeled)
|
|
23
71
|
break;
|
|
24
|
-
const kind =
|
|
25
|
-
|
|
72
|
+
const { kind, payload, remaining } = peeled;
|
|
73
|
+
current = remaining;
|
|
26
74
|
if (kind === 'DIGEST') {
|
|
27
|
-
if (digest === null)
|
|
75
|
+
if (digest === null && payload.length > 0)
|
|
28
76
|
digest = payload;
|
|
29
77
|
}
|
|
30
78
|
else if (kind === 'JOURNAL') {
|
|
@@ -43,7 +91,6 @@ export function extractFlags(reply) {
|
|
|
43
91
|
asyncTasks.unshift({ description: payload });
|
|
44
92
|
}
|
|
45
93
|
}
|
|
46
|
-
current = trimmed.slice(0, match.index).trimEnd();
|
|
47
94
|
}
|
|
48
95
|
return { clean: current, digest, journals, journalCreates, asyncTasks };
|
|
49
96
|
}
|