@luckydraw/cumulus 0.30.29 → 0.30.31
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/CHANGELOG.md +4 -0
- package/README.md +40 -43
- package/dist/gateway/daemon.js +0 -0
- package/dist/gateway/static/widget.js +263 -6
- package/dist/lib/projects.d.ts.map +1 -0
- package/package.json +1 -1
- package/dist/lib/intent-reviewer.d.ts +0 -103
- package/dist/lib/intent-reviewer.d.ts.map +0 -1
- package/dist/lib/intent-reviewer.js +0 -363
- package/dist/lib/intent-reviewer.js.map +0 -1
package/CHANGELOG.md
CHANGED
package/README.md
CHANGED
|
@@ -2,15 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
A self-hosted multi-channel AI gateway built around Claude and other LLMs. Runs as a long-lived daemon, speaks to you through a web chat widget, Slack, Discord, iOS push, email webhooks, and more — with unlimited conversation context via the **Recursive Language Model (RLM)** pattern.
|
|
4
4
|
|
|
5
|
-
Originally a CLI wrapper for Claude, Cumulus has grown into a full gateway platform that coordinates agents, channels, models
|
|
5
|
+
Originally a CLI wrapper for Claude, Cumulus has grown into a full gateway platform that coordinates agents, channels, and models behind one persistent process.
|
|
6
6
|
|
|
7
7
|
## What you get
|
|
8
8
|
|
|
9
9
|
- **Gateway daemon** (`cumulus-gateway`) — HTTP + WebSocket server with per-thread conversations, streaming responses, and an admin API.
|
|
10
|
-
- **Web chat widget** — embeddable `/chat` interface with voice mode, push notifications,
|
|
10
|
+
- **Web chat widget** — embeddable `/chat` interface with voice mode, push notifications, file uploads with progress, and rich [blex block](https://www.npmjs.com/package/@luckydraw/blex) rendering (tables, forms, charts, kanban, diagrams).
|
|
11
11
|
- **Channel adapters** — Slack and Discord bots, inbound email webhooks (Resend), and generic HTTP webhooks — all injecting into the same thread model.
|
|
12
12
|
- **Inter-agent messaging** — threads can talk to each other via `send_to_agent`, with support for CC/BCC visibility.
|
|
13
|
-
- **Federation** — hub-and-spoke mesh so agents on different machines can message each other across NATs.
|
|
14
13
|
- **Per-thread model selection** — Claude (via CLI) or any HuggingFace model (GLM-5, Kimi-K2.5, Qwen3, etc.) with tool calling.
|
|
15
14
|
- **Scheduled triggers, email, push, media serving** — built-in MCP tools so agents can send emails, schedule themselves, notify you, and upload files.
|
|
16
15
|
- **Unlimited history** — JSONL per thread + vector store + adaptive context budget. Conversations never truncate.
|
|
@@ -32,19 +31,19 @@ Originally a CLI wrapper for Claude, Cumulus has grown into a full gateway platf
|
|
|
32
31
|
│ │ (HTTP / WS daemon) │ │
|
|
33
32
|
│ └──────────┬───────────┘ │
|
|
34
33
|
│ │ │
|
|
35
|
-
│
|
|
36
|
-
│
|
|
37
|
-
│
|
|
38
|
-
│
|
|
39
|
-
│
|
|
40
|
-
│
|
|
41
|
-
│
|
|
42
|
-
│
|
|
43
|
-
│
|
|
44
|
-
│
|
|
45
|
-
│
|
|
46
|
-
│
|
|
47
|
-
│
|
|
34
|
+
│ ┌─────────────┴─────────────┐ │
|
|
35
|
+
│ ▼ ▼ │
|
|
36
|
+
│ ┌─────────┐ ┌──────────────┐ │
|
|
37
|
+
│ │ Thread │ │ Model router │ │
|
|
38
|
+
│ │ store │ │ Claude / HF │ │
|
|
39
|
+
│ │ (JSONL) │ │ MCP tools │ │
|
|
40
|
+
│ └────┬────┘ └──────┬───────┘ │
|
|
41
|
+
│ │ │ │
|
|
42
|
+
│ ▼ ▼ │
|
|
43
|
+
│ ~/.cumulus/ Claude CLI │
|
|
44
|
+
│ threads/ HuggingFace API │
|
|
45
|
+
│ content/ MCP stdio + in-process │
|
|
46
|
+
│ media/ │
|
|
48
47
|
└──────────────────────────────────────────────────────────────┘
|
|
49
48
|
```
|
|
50
49
|
|
|
@@ -108,12 +107,6 @@ Open `http://localhost:8080/chat`, paste the API key, and start talking. Message
|
|
|
108
107
|
},
|
|
109
108
|
"resend": { "apiKey": "re_…", "defaultFrom": "you@example.com" },
|
|
110
109
|
"vapid": { "publicKey": "…", "privateKey": "…", "subject": "mailto:you@example.com" },
|
|
111
|
-
"federation": {
|
|
112
|
-
"enabled": true,
|
|
113
|
-
"role": "hub", // "hub" or "spoke"
|
|
114
|
-
"allowedSpokes": ["mac-karl"], // hub only
|
|
115
|
-
// spoke config: { role:"spoke", hub:"wss://host/federation", apiKey:"…", name:"mac-…" }
|
|
116
|
-
},
|
|
117
110
|
}
|
|
118
111
|
```
|
|
119
112
|
|
|
@@ -131,7 +124,7 @@ curl -X PUT http://localhost:8080/api/thread/my-thread/config \
|
|
|
131
124
|
-d '{"model": "zai-org/GLM-5"}'
|
|
132
125
|
```
|
|
133
126
|
|
|
134
|
-
- `claude` — spawns `claude --print` per turn. Gets the full Claude Code tool surface.
|
|
127
|
+
- `claude` — spawns `claude --print` per turn. Gets the full Claude Code tool surface. Per-thread `effort` selector (`low` → `max`) maps to the CLI's `--effort` flag.
|
|
135
128
|
- HuggingFace models — routed through an OpenAI-compatible endpoint with a built-in agentic loop that handles tool use, truncation recovery, and error retry.
|
|
136
129
|
|
|
137
130
|
### Web chat widget
|
|
@@ -139,12 +132,17 @@ curl -X PUT http://localhost:8080/api/thread/my-thread/config \
|
|
|
139
132
|
At `/chat`. Features:
|
|
140
133
|
|
|
141
134
|
- **Streaming** responses over WebSocket, with interjection support (type while streaming to interrupt and redirect).
|
|
142
|
-
- **
|
|
143
|
-
- **
|
|
135
|
+
- **Multiple threads, side by side** — Cmd/Ctrl+Click any thread in the sidebar to open it in a second panel alongside the current one. Useful for cross-referencing or driving two agents in parallel. Mobile auto-collapses to a single panel.
|
|
136
|
+
- **Inline annotations** — highlight any chat text, leave a comment via the popover, and send it back as a quoted chip. Chips can be edited or removed before sending. Works like leaving a margin note on what the agent just said.
|
|
137
|
+
- **[Blex blocks](https://www.npmjs.com/package/@luckydraw/blex)** — `~~~blex:TYPE` fenced JSON renders as a rich, interactive component. 22 block types including:
|
|
138
|
+
- **Interactive input** — `poll` (multi-question carousels, multi-select, write-in answers), `confirm` (Yes/No/Cancel), `form` (typed fields with validation). User responses serialize back into the chat input.
|
|
139
|
+
- **Embedded content** — `embed` (sandboxed iframe for hosted apps and webpages, inline in the chat), `image`/`gallery` (with `upload_media`-served URLs), `mermaid` and `svg` diagrams.
|
|
140
|
+
- **Live data** — `table` (sortable/selectable), `chart`, `kanban`, `calendar`, `timeline`, `status`, `metric`, `progress`, `file-tree`, `terminal`, `code`, `diff` (with Apply/Reject buttons), `layout` (composes other blocks), `branch` (step-through flowcharts).
|
|
141
|
+
- **Voice mode** — hands-free conversation using browser STT + server-side Piper TTS, with sentence-by-sentence playback and barge-in.
|
|
144
142
|
- **Push notifications** — PWA install + VAPID subscriptions. Agents call `notify_user` to alert you while you're away.
|
|
145
|
-
- **
|
|
146
|
-
- **Annotations** — highlight text, attach comments, send back as chips.
|
|
143
|
+
- **File attachments** — drag or pick any file type. Non-image files upload via XHR with a per-chip progress bar and cancel; agents receive the absolute disk path and can `read_file` it directly. Images stay on the inline-base64 path for vision-capable models.
|
|
147
144
|
- **Texitool integration** — edit Unicode-art diagrams in-place via an embedded canvas.
|
|
145
|
+
- **Update banner** — auto-detects when a newer version is on npm and offers a one-click update.
|
|
148
146
|
|
|
149
147
|
### Channel adapters
|
|
150
148
|
|
|
@@ -154,7 +152,7 @@ At `/chat`. Features:
|
|
|
154
152
|
|
|
155
153
|
### Inter-agent messaging
|
|
156
154
|
|
|
157
|
-
Any thread can message another thread using the `send_to_agent` MCP tool:
|
|
155
|
+
Any thread can message another thread on the same gateway using the `send_to_agent` MCP tool:
|
|
158
156
|
|
|
159
157
|
```
|
|
160
158
|
send_to_agent(target="devops", message="Deploy the new build", visibility="cc")
|
|
@@ -166,17 +164,6 @@ send_to_agent(target="devops", message="Deploy the new build", visibility="cc")
|
|
|
166
164
|
|
|
167
165
|
If the target is busy, the message is queued and delivered as a batch when that thread is idle ("while you were busy, 3 messages arrived…").
|
|
168
166
|
|
|
169
|
-
### Federation
|
|
170
|
-
|
|
171
|
-
Two gateways can be linked in a hub-and-spoke topology. The hub runs at a stable URL; spokes connect outbound via WSS, so NAT doesn't matter.
|
|
172
|
-
|
|
173
|
-
```
|
|
174
|
-
send_to_agent("thundercat:cumulus", "…") # cross-gateway addressing
|
|
175
|
-
list_agents() # aggregates threads across all spokes
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
Heartbeats every 25s with bidirectional WebSocket pings; dead connections are torn down within 75s.
|
|
179
|
-
|
|
180
167
|
### Scheduled triggers
|
|
181
168
|
|
|
182
169
|
Agents can schedule themselves:
|
|
@@ -200,6 +187,16 @@ list_emails(limit=10)
|
|
|
200
187
|
|
|
201
188
|
Rate-limited per thread (default 10/hour). All sends are logged to thread history. First email from a new thread triggers a `notify_user` ping.
|
|
202
189
|
|
|
190
|
+
### Reliability
|
|
191
|
+
|
|
192
|
+
- **Graceful restart** — `cumulus-gateway reload` (SIGHUP) drains active Claude/HF streams up to 120s before restarting; no truncated responses on deploy.
|
|
193
|
+
- **Auto-resume after restart** — interrupted threads get a resume nudge on startup so the agent picks back up with full RAG context.
|
|
194
|
+
- **Persistent streaming buffer** — partial responses are flushed to disk every 5s during streaming and recovered on restart.
|
|
195
|
+
- **Truncation continuation** — `finish_reason: "length"` triggers max-token escalation (8k → 16k → 32k) and seamless continuation stitching.
|
|
196
|
+
- **WebSocket keepalive** — server-side ping/pong every 30s; clients reload history if a stream goes silent for >120s.
|
|
197
|
+
- **Policy-error retry** — Claude CLI transient "Usage Policy" refusals auto-retry up to 3 times with a visible "Retrying…" indicator.
|
|
198
|
+
- **HF transient-error retry** — `[Error: terminated]`, connection resets, and similar stream/network errors retry with exponential backoff.
|
|
199
|
+
|
|
203
200
|
### Self-update
|
|
204
201
|
|
|
205
202
|
```bash
|
|
@@ -208,7 +205,7 @@ cumulus-gateway update # bumps to latest, saves previous for rollbac
|
|
|
208
205
|
cumulus-gateway rollback # restores the previous version
|
|
209
206
|
```
|
|
210
207
|
|
|
211
|
-
The widget's top bar also shows an "Update available" indicator when a new version lands on npm.
|
|
208
|
+
The widget's top bar also shows an "Update available" indicator (with a manual ↻ check button) when a new version lands on npm.
|
|
212
209
|
|
|
213
210
|
## Classic CLI mode
|
|
214
211
|
|
|
@@ -258,7 +255,7 @@ The `cumulus-mcp` server exposes history and content tools. Usable from any MCP-
|
|
|
258
255
|
|
|
259
256
|
**Gateway-only tools** (available to agents running inside the daemon):
|
|
260
257
|
|
|
261
|
-
`send_to_agent`, `list_agents`, `notify_user`, `schedule_trigger`, `cancel_schedule`, `list_schedules`, `send_email`, `list_emails`, `upload_media
|
|
258
|
+
`send_to_agent`, `list_agents`, `notify_user`, `schedule_trigger`, `cancel_schedule`, `list_schedules`, `send_email`, `list_emails`, `upload_media`.
|
|
262
259
|
|
|
263
260
|
## RAG & context management
|
|
264
261
|
|
|
@@ -292,7 +289,7 @@ The `cumulus-mcp` server exposes history and content tools. Usable from any MCP-
|
|
|
292
289
|
|
|
293
290
|
All `/api/*` routes require `X-API-Key: <key>` (from `apiKeys[]`).
|
|
294
291
|
|
|
295
|
-
WebSocket (`/chat/ws`) carries the same semantics with streaming, interjection, `inject`,
|
|
292
|
+
WebSocket (`/chat/ws`) carries the same semantics with streaming, interjection, `inject`, and voice-mode audio frames.
|
|
296
293
|
|
|
297
294
|
## Development
|
|
298
295
|
|
|
@@ -307,7 +304,7 @@ npm run type-check
|
|
|
307
304
|
```
|
|
308
305
|
|
|
309
306
|
- **Build:** `dist/` compiled TypeScript plus static assets (widget HTML/CSS/JS, blex bundles).
|
|
310
|
-
- **Tests:** vitest, 700+ tests covering agentic loop, retriever, adapters,
|
|
307
|
+
- **Tests:** vitest, 700+ tests covering agentic loop, retriever, adapters, scheduler, push.
|
|
311
308
|
- **Deploy workflow:** bump version, `npm publish`, then `cumulus-gateway reload` on the host (SIGHUP drains active streams — see `docs/tasks/050-graceful-restart.md`).
|
|
312
309
|
|
|
313
310
|
## Background
|
package/dist/gateway/daemon.js
CHANGED
|
File without changes
|
|
@@ -494,6 +494,78 @@
|
|
|
494
494
|
' 30% { transform: translateY(-0.4em); opacity: 1; }',
|
|
495
495
|
'}',
|
|
496
496
|
|
|
497
|
+
/* ── Activity chip (task 070) ── */
|
|
498
|
+
'.cumulus-activity-chip {',
|
|
499
|
+
' display: block;',
|
|
500
|
+
' margin: 0 0 0.5em 0;',
|
|
501
|
+
' font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;',
|
|
502
|
+
' font-size: 0.82em;',
|
|
503
|
+
' background: #2a2a2a;',
|
|
504
|
+
' border: 1px solid #3a3a3a;',
|
|
505
|
+
' border-radius: 6px;',
|
|
506
|
+
' color: #b8b8b8;',
|
|
507
|
+
' user-select: none;',
|
|
508
|
+
' overflow: hidden;',
|
|
509
|
+
'}',
|
|
510
|
+
'.cumulus-activity-chip-header {',
|
|
511
|
+
' display: flex;',
|
|
512
|
+
' align-items: center;',
|
|
513
|
+
' gap: 0.5em;',
|
|
514
|
+
' padding: 0.35em 0.6em;',
|
|
515
|
+
' cursor: pointer;',
|
|
516
|
+
'}',
|
|
517
|
+
'.cumulus-activity-chip-header:hover { background: #303030; }',
|
|
518
|
+
'.cumulus-activity-chip-icon {',
|
|
519
|
+
' flex: 0 0 auto;',
|
|
520
|
+
' width: 1.2em;',
|
|
521
|
+
' text-align: center;',
|
|
522
|
+
'}',
|
|
523
|
+
'.cumulus-activity-chip-text {',
|
|
524
|
+
' flex: 1 1 auto;',
|
|
525
|
+
' overflow: hidden;',
|
|
526
|
+
' text-overflow: ellipsis;',
|
|
527
|
+
' white-space: nowrap;',
|
|
528
|
+
'}',
|
|
529
|
+
'.cumulus-activity-chip-chevron {',
|
|
530
|
+
' flex: 0 0 auto;',
|
|
531
|
+
' font-size: 0.7em;',
|
|
532
|
+
' color: #888;',
|
|
533
|
+
' transition: transform 0.15s ease;',
|
|
534
|
+
'}',
|
|
535
|
+
'.cumulus-activity-chip.expanded .cumulus-activity-chip-chevron {',
|
|
536
|
+
' transform: rotate(90deg);',
|
|
537
|
+
'}',
|
|
538
|
+
'.cumulus-activity-chip-body {',
|
|
539
|
+
' border-top: 1px solid #3a3a3a;',
|
|
540
|
+
' background: #232323;',
|
|
541
|
+
' padding: 0.4em 0.6em;',
|
|
542
|
+
' display: flex;',
|
|
543
|
+
' flex-direction: column;',
|
|
544
|
+
' gap: 0.3em;',
|
|
545
|
+
'}',
|
|
546
|
+
'.cumulus-activity-row {',
|
|
547
|
+
' display: flex;',
|
|
548
|
+
' gap: 0.5em;',
|
|
549
|
+
' align-items: flex-start;',
|
|
550
|
+
' line-height: 1.4;',
|
|
551
|
+
'}',
|
|
552
|
+
'.cumulus-activity-row-icon {',
|
|
553
|
+
' flex: 0 0 auto;',
|
|
554
|
+
' width: 1.2em;',
|
|
555
|
+
' text-align: center;',
|
|
556
|
+
'}',
|
|
557
|
+
'.cumulus-activity-row-text {',
|
|
558
|
+
' flex: 1 1 auto;',
|
|
559
|
+
' word-break: break-word;',
|
|
560
|
+
' white-space: pre-wrap;',
|
|
561
|
+
' color: #c8c8c8;',
|
|
562
|
+
'}',
|
|
563
|
+
'.cumulus-activity-row.thinking .cumulus-activity-row-text { color: #9a9aff; font-style: italic; }',
|
|
564
|
+
'.cumulus-activity-row.tool_use .cumulus-activity-row-text { color: #d4b86a; }',
|
|
565
|
+
'.cumulus-activity-row.tool_result .cumulus-activity-row-text { color: #8fbf8f; }',
|
|
566
|
+
'.cumulus-activity-row.tool_result.error .cumulus-activity-row-text { color: #d97a7a; }',
|
|
567
|
+
'.cumulus-activity-row.system .cumulus-activity-row-text { color: #888; }',
|
|
568
|
+
|
|
497
569
|
/* ── Loading indicator ── */
|
|
498
570
|
'.cumulus-loading {',
|
|
499
571
|
' color: #666; font-style: italic; padding: 0.4em 0;',
|
|
@@ -3841,6 +3913,10 @@
|
|
|
3841
3913
|
hasOlderMessages: false,
|
|
3842
3914
|
oldestOffset: -1, // -1 = unknown, set on first history response
|
|
3843
3915
|
model: 'claude', // default model
|
|
3916
|
+
// Activity chip (task 070) — accumulates segments during in-flight turn,
|
|
3917
|
+
// moves onto the assistant message at `done` time
|
|
3918
|
+
currentSegments: [],
|
|
3919
|
+
expandedChips: {}, // msgKey → true if chip expanded
|
|
3844
3920
|
};
|
|
3845
3921
|
}
|
|
3846
3922
|
return threadStates[threadName];
|
|
@@ -5542,7 +5618,140 @@
|
|
|
5542
5618
|
return el;
|
|
5543
5619
|
}
|
|
5544
5620
|
|
|
5545
|
-
|
|
5621
|
+
// ── Activity chip helpers (task 070) ──
|
|
5622
|
+
function truncateForChip(s, n) {
|
|
5623
|
+
s = String(s == null ? '' : s);
|
|
5624
|
+
return s.length > n ? s.slice(0, n - 1) + '…' : s;
|
|
5625
|
+
}
|
|
5626
|
+
function iconForSegment(s) {
|
|
5627
|
+
if (!s) return '';
|
|
5628
|
+
switch (s.type) {
|
|
5629
|
+
case 'thinking':
|
|
5630
|
+
return '💭';
|
|
5631
|
+
case 'tool_use':
|
|
5632
|
+
return '⚙';
|
|
5633
|
+
case 'tool_result':
|
|
5634
|
+
return s.isError ? '✗' : '✓';
|
|
5635
|
+
case 'system':
|
|
5636
|
+
return '⚙';
|
|
5637
|
+
default:
|
|
5638
|
+
return '';
|
|
5639
|
+
}
|
|
5640
|
+
}
|
|
5641
|
+
function labelForSegment(s, expanded) {
|
|
5642
|
+
if (!s) return '';
|
|
5643
|
+
var maxLen = expanded ? 400 : 80;
|
|
5644
|
+
switch (s.type) {
|
|
5645
|
+
case 'thinking': {
|
|
5646
|
+
var t = String(s.content || '')
|
|
5647
|
+
.replace(/\s+/g, ' ')
|
|
5648
|
+
.trim();
|
|
5649
|
+
return 'Thinking — ' + truncateForChip(t, maxLen);
|
|
5650
|
+
}
|
|
5651
|
+
case 'tool_use': {
|
|
5652
|
+
var tool = s.tool || 'tool';
|
|
5653
|
+
var argStr = '';
|
|
5654
|
+
try {
|
|
5655
|
+
var keys = Object.keys(s.input || {});
|
|
5656
|
+
if (keys.length > 0) {
|
|
5657
|
+
var v = s.input[keys[0]];
|
|
5658
|
+
argStr = typeof v === 'string' ? JSON.stringify(v) : JSON.stringify(v);
|
|
5659
|
+
}
|
|
5660
|
+
} catch (e) {
|
|
5661
|
+
argStr = '';
|
|
5662
|
+
}
|
|
5663
|
+
var headroom = Math.max(8, maxLen - tool.length - 2);
|
|
5664
|
+
return tool + '(' + (argStr ? truncateForChip(argStr, headroom) : '') + ')';
|
|
5665
|
+
}
|
|
5666
|
+
case 'tool_result': {
|
|
5667
|
+
var c = String(s.content || '').split('\n')[0] || '';
|
|
5668
|
+
return truncateForChip(c, maxLen);
|
|
5669
|
+
}
|
|
5670
|
+
case 'system': {
|
|
5671
|
+
var c2 = String(s.content || '')
|
|
5672
|
+
.replace(/\s+/g, ' ')
|
|
5673
|
+
.trim();
|
|
5674
|
+
return 'system: ' + truncateForChip(c2, maxLen);
|
|
5675
|
+
}
|
|
5676
|
+
default:
|
|
5677
|
+
return '';
|
|
5678
|
+
}
|
|
5679
|
+
}
|
|
5680
|
+
function buildActivityChip(segments, msgKey, st) {
|
|
5681
|
+
if (!segments || segments.length === 0) return null;
|
|
5682
|
+
var meaningful = [];
|
|
5683
|
+
for (var i = 0; i < segments.length; i++) {
|
|
5684
|
+
var seg = segments[i];
|
|
5685
|
+
if (seg && seg.type !== 'text' && seg.type !== 'result') meaningful.push(seg);
|
|
5686
|
+
}
|
|
5687
|
+
if (meaningful.length === 0) return null;
|
|
5688
|
+
|
|
5689
|
+
var key = msgKey || '__streaming__';
|
|
5690
|
+
var expanded = !!(st && st.expandedChips && st.expandedChips[key]);
|
|
5691
|
+
|
|
5692
|
+
var chip = document.createElement('div');
|
|
5693
|
+
chip.className = 'cumulus-activity-chip' + (expanded ? ' expanded' : '');
|
|
5694
|
+
chip.setAttribute('data-testid', 'webchat-activity-chip');
|
|
5695
|
+
|
|
5696
|
+
var header = document.createElement('div');
|
|
5697
|
+
header.className = 'cumulus-activity-chip-header';
|
|
5698
|
+
|
|
5699
|
+
var latest = meaningful[meaningful.length - 1];
|
|
5700
|
+
var headerIcon = document.createElement('span');
|
|
5701
|
+
headerIcon.className = 'cumulus-activity-chip-icon';
|
|
5702
|
+
headerIcon.textContent = iconForSegment(latest);
|
|
5703
|
+
|
|
5704
|
+
var headerText = document.createElement('span');
|
|
5705
|
+
headerText.className = 'cumulus-activity-chip-text';
|
|
5706
|
+
if (expanded) {
|
|
5707
|
+
headerText.textContent =
|
|
5708
|
+
'Activity (' + meaningful.length + ' step' + (meaningful.length === 1 ? '' : 's') + ')';
|
|
5709
|
+
} else {
|
|
5710
|
+
headerText.textContent = labelForSegment(latest, false);
|
|
5711
|
+
}
|
|
5712
|
+
|
|
5713
|
+
var chevron = document.createElement('span');
|
|
5714
|
+
chevron.className = 'cumulus-activity-chip-chevron';
|
|
5715
|
+
chevron.textContent = '▸';
|
|
5716
|
+
|
|
5717
|
+
header.appendChild(headerIcon);
|
|
5718
|
+
header.appendChild(headerText);
|
|
5719
|
+
header.appendChild(chevron);
|
|
5720
|
+
chip.appendChild(header);
|
|
5721
|
+
|
|
5722
|
+
if (expanded) {
|
|
5723
|
+
var body = document.createElement('div');
|
|
5724
|
+
body.className = 'cumulus-activity-chip-body';
|
|
5725
|
+
for (var j = 0; j < meaningful.length; j++) {
|
|
5726
|
+
var s2 = meaningful[j];
|
|
5727
|
+
var row = document.createElement('div');
|
|
5728
|
+
var rowClass = 'cumulus-activity-row ' + s2.type;
|
|
5729
|
+
if (s2.type === 'tool_result' && s2.isError) rowClass += ' error';
|
|
5730
|
+
row.className = rowClass;
|
|
5731
|
+
var rowIcon = document.createElement('span');
|
|
5732
|
+
rowIcon.className = 'cumulus-activity-row-icon';
|
|
5733
|
+
rowIcon.textContent = iconForSegment(s2);
|
|
5734
|
+
var rowText = document.createElement('span');
|
|
5735
|
+
rowText.className = 'cumulus-activity-row-text';
|
|
5736
|
+
rowText.textContent = labelForSegment(s2, true);
|
|
5737
|
+
row.appendChild(rowIcon);
|
|
5738
|
+
row.appendChild(rowText);
|
|
5739
|
+
body.appendChild(row);
|
|
5740
|
+
}
|
|
5741
|
+
chip.appendChild(body);
|
|
5742
|
+
}
|
|
5743
|
+
|
|
5744
|
+
if (st) {
|
|
5745
|
+
header.addEventListener('click', function () {
|
|
5746
|
+
st.expandedChips[key] = !st.expandedChips[key];
|
|
5747
|
+
refreshThreadPanel(threadName);
|
|
5748
|
+
});
|
|
5749
|
+
}
|
|
5750
|
+
|
|
5751
|
+
return chip;
|
|
5752
|
+
}
|
|
5753
|
+
|
|
5754
|
+
function buildAssistantMsgEl(content, isStreaming, msgKey, segments) {
|
|
5546
5755
|
content = stripThinkingBlocks(content);
|
|
5547
5756
|
var el = document.createElement('div');
|
|
5548
5757
|
el.className = 'cumulus-msg assistant';
|
|
@@ -5563,6 +5772,11 @@
|
|
|
5563
5772
|
el.innerHTML =
|
|
5564
5773
|
'<span class="cumulus-typing-dots"><span></span><span></span><span></span></span>';
|
|
5565
5774
|
}
|
|
5775
|
+
// Prepend activity chip if we have segments
|
|
5776
|
+
var chipEl = buildActivityChip(segments, msgKey, state);
|
|
5777
|
+
if (chipEl) {
|
|
5778
|
+
el.insertBefore(chipEl, el.firstChild);
|
|
5779
|
+
}
|
|
5566
5780
|
return el;
|
|
5567
5781
|
}
|
|
5568
5782
|
|
|
@@ -5629,7 +5843,9 @@
|
|
|
5629
5843
|
row.appendChild(buildUserMsgEl(msg));
|
|
5630
5844
|
}
|
|
5631
5845
|
} else {
|
|
5632
|
-
row.appendChild(
|
|
5846
|
+
row.appendChild(
|
|
5847
|
+
buildAssistantMsgEl(msg.content, false, String(msg.timestamp), msg.segments)
|
|
5848
|
+
);
|
|
5633
5849
|
}
|
|
5634
5850
|
appendTimestamp(row, msg.timestamp);
|
|
5635
5851
|
messagesEl.appendChild(row);
|
|
@@ -5637,7 +5853,9 @@
|
|
|
5637
5853
|
if (state.streaming) {
|
|
5638
5854
|
var row = document.createElement('div');
|
|
5639
5855
|
row.className = 'cumulus-msg-row';
|
|
5640
|
-
row.appendChild(
|
|
5856
|
+
row.appendChild(
|
|
5857
|
+
buildAssistantMsgEl(state.streamBuffer, true, null, state.currentSegments)
|
|
5858
|
+
);
|
|
5641
5859
|
messagesEl.appendChild(row);
|
|
5642
5860
|
}
|
|
5643
5861
|
scrollToBottom();
|
|
@@ -5920,14 +6138,16 @@
|
|
|
5920
6138
|
if (streamRow) {
|
|
5921
6139
|
var parentRow = streamRow.closest('.cumulus-msg-row') || streamRow.parentElement;
|
|
5922
6140
|
if (parentRow) {
|
|
5923
|
-
var newEl = buildAssistantMsgEl(state.streamBuffer, true);
|
|
6141
|
+
var newEl = buildAssistantMsgEl(state.streamBuffer, true, null, state.currentSegments);
|
|
5924
6142
|
parentRow.replaceChild(newEl, streamRow);
|
|
5925
6143
|
}
|
|
5926
6144
|
} else if (state.streaming) {
|
|
5927
6145
|
// No streaming element yet — append one
|
|
5928
6146
|
var row = document.createElement('div');
|
|
5929
6147
|
row.className = 'cumulus-msg-row';
|
|
5930
|
-
row.appendChild(
|
|
6148
|
+
row.appendChild(
|
|
6149
|
+
buildAssistantMsgEl(state.streamBuffer, true, null, state.currentSegments)
|
|
6150
|
+
);
|
|
5931
6151
|
messagesEl.appendChild(row);
|
|
5932
6152
|
}
|
|
5933
6153
|
scrollToBottom();
|
|
@@ -5961,9 +6181,14 @@
|
|
|
5961
6181
|
role: 'assistant',
|
|
5962
6182
|
content: state.streamBuffer,
|
|
5963
6183
|
timestamp: Date.now(),
|
|
6184
|
+
segments:
|
|
6185
|
+
state.currentSegments && state.currentSegments.length
|
|
6186
|
+
? state.currentSegments
|
|
6187
|
+
: undefined,
|
|
5964
6188
|
});
|
|
5965
6189
|
state.streamBuffer = '';
|
|
5966
6190
|
}
|
|
6191
|
+
state.currentSegments = [];
|
|
5967
6192
|
renderPanelMessages();
|
|
5968
6193
|
return;
|
|
5969
6194
|
}
|
|
@@ -5975,10 +6200,15 @@
|
|
|
5975
6200
|
role: 'assistant',
|
|
5976
6201
|
content: state.streamBuffer,
|
|
5977
6202
|
timestamp: Date.now(),
|
|
6203
|
+
segments:
|
|
6204
|
+
state.currentSegments && state.currentSegments.length
|
|
6205
|
+
? state.currentSegments
|
|
6206
|
+
: undefined,
|
|
5978
6207
|
});
|
|
5979
6208
|
}
|
|
5980
6209
|
// Reset streaming state — the server will send 'interjected' for the old stream
|
|
5981
6210
|
state.streamBuffer = '';
|
|
6211
|
+
state.currentSegments = [];
|
|
5982
6212
|
state.interjecting = true;
|
|
5983
6213
|
state.interjectionStreamId = (state.interjectionStreamId || 0) + 1;
|
|
5984
6214
|
// Don't set streaming=false — the new message will keep streaming
|
|
@@ -6516,9 +6746,12 @@
|
|
|
6516
6746
|
role: 'assistant',
|
|
6517
6747
|
content: s.streamBuffer,
|
|
6518
6748
|
timestamp: Date.now(),
|
|
6749
|
+
segments:
|
|
6750
|
+
s.currentSegments && s.currentSegments.length ? s.currentSegments : undefined,
|
|
6519
6751
|
});
|
|
6520
6752
|
s.streamBuffer = '';
|
|
6521
6753
|
}
|
|
6754
|
+
s.currentSegments = [];
|
|
6522
6755
|
s.streaming = false;
|
|
6523
6756
|
s.stopRequested = false;
|
|
6524
6757
|
s.interjecting = false;
|
|
@@ -6717,7 +6950,14 @@
|
|
|
6717
6950
|
break;
|
|
6718
6951
|
|
|
6719
6952
|
case 'segment':
|
|
6720
|
-
//
|
|
6953
|
+
// Activity chip (task 070): accumulate segment for the in-flight turn
|
|
6954
|
+
if (data.threadName && data.segment) {
|
|
6955
|
+
var segState = getThreadState(data.threadName);
|
|
6956
|
+
if (!segState.currentSegments) segState.currentSegments = [];
|
|
6957
|
+
segState.currentSegments.push(data.segment);
|
|
6958
|
+
// Re-render the streaming row so the chip reflects the latest activity
|
|
6959
|
+
refreshThreadPanel(data.threadName, true);
|
|
6960
|
+
}
|
|
6721
6961
|
break;
|
|
6722
6962
|
|
|
6723
6963
|
case 'voice_info':
|
|
@@ -6752,9 +6992,14 @@
|
|
|
6752
6992
|
role: 'assistant',
|
|
6753
6993
|
content: state.streamBuffer,
|
|
6754
6994
|
timestamp: Date.now(),
|
|
6995
|
+
segments:
|
|
6996
|
+
state.currentSegments && state.currentSegments.length
|
|
6997
|
+
? state.currentSegments
|
|
6998
|
+
: undefined,
|
|
6755
6999
|
});
|
|
6756
7000
|
}
|
|
6757
7001
|
state.streamBuffer = '';
|
|
7002
|
+
state.currentSegments = [];
|
|
6758
7003
|
updateThreadActivity(data.threadName);
|
|
6759
7004
|
refreshThreadPanel(data.threadName);
|
|
6760
7005
|
break;
|
|
@@ -6765,8 +7010,13 @@
|
|
|
6765
7010
|
role: 'assistant',
|
|
6766
7011
|
content: responseContent,
|
|
6767
7012
|
timestamp: Date.now(),
|
|
7013
|
+
segments:
|
|
7014
|
+
state.currentSegments && state.currentSegments.length
|
|
7015
|
+
? state.currentSegments
|
|
7016
|
+
: undefined,
|
|
6768
7017
|
});
|
|
6769
7018
|
state.streamBuffer = '';
|
|
7019
|
+
state.currentSegments = [];
|
|
6770
7020
|
updateThreadActivity(data.threadName);
|
|
6771
7021
|
refreshThreadPanel(data.threadName);
|
|
6772
7022
|
// Voice mode: speak the assistant response
|
|
@@ -6788,21 +7038,28 @@
|
|
|
6788
7038
|
var state = getThreadState(data.threadName);
|
|
6789
7039
|
state.stopRequested = false;
|
|
6790
7040
|
state.streaming = false;
|
|
7041
|
+
var hadSegments =
|
|
7042
|
+
state.currentSegments && state.currentSegments.length
|
|
7043
|
+
? state.currentSegments
|
|
7044
|
+
: undefined;
|
|
6791
7045
|
if (state.streamBuffer) {
|
|
6792
7046
|
state.messages.push({
|
|
6793
7047
|
role: 'assistant',
|
|
6794
7048
|
content:
|
|
6795
7049
|
state.streamBuffer + '\n\n[Error: ' + (data.error || 'Unknown error') + ']',
|
|
6796
7050
|
timestamp: Date.now(),
|
|
7051
|
+
segments: hadSegments,
|
|
6797
7052
|
});
|
|
6798
7053
|
} else {
|
|
6799
7054
|
state.messages.push({
|
|
6800
7055
|
role: 'assistant',
|
|
6801
7056
|
content: '[Error: ' + (data.error || 'Unknown error') + ']',
|
|
6802
7057
|
timestamp: Date.now(),
|
|
7058
|
+
segments: hadSegments,
|
|
6803
7059
|
});
|
|
6804
7060
|
}
|
|
6805
7061
|
state.streamBuffer = '';
|
|
7062
|
+
state.currentSegments = [];
|
|
6806
7063
|
refreshThreadPanel(data.threadName);
|
|
6807
7064
|
}
|
|
6808
7065
|
break;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"projects.d.ts","sourceRoot":"","sources":["../../src/lib/projects.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAaH,MAAM,WAAW,eAAe;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,oBAAoB;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oDAAoD;IACpD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,mDAAmD;IACnD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACrE;AA4BD;;GAEG;AACH,wBAAsB,aAAa,CAAC,OAAO,EAAE,oBAAoB,GAAG,OAAO,CAAC,eAAe,CAAC,CAkE3F;AAED;;GAEG;AACH,wBAAsB,YAAY,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CA4BlF;AAED;;GAEG;AACH,wBAAsB,UAAU,CAC9B,IAAI,EAAE,MAAM,EACZ,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAUjC;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,OAAO,CAAC,IAAI,CAAC,eAAe,EAAE,SAAS,GAAG,eAAe,GAAG,WAAW,CAAC,CAAC,EAClF,WAAW,CAAC,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC,CAoC1B;AAED;;GAEG;AACH,wBAAsB,aAAa,CACjC,IAAI,EAAE,MAAM,EACZ,YAAY,UAAQ,EACpB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,IAAI,CAAC,CAgBf;AAED;;GAEG;AACH,wBAAsB,kBAAkB,CACtC,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,MAAM,EAClB,WAAW,CAAC,EAAE,MAAM,EACpB,UAAU,CAAC,EAAE,MAAM,GAClB,OAAO,CAAC,eAAe,CAAC,CAS1B;AAED;;;GAGG;AACH,wBAAsB,uBAAuB,CAC3C,UAAU,EAAE,MAAM,EAClB,UAAU,CAAC,EAAE,MAAM,EACnB,WAAW,CAAC,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAexB"}
|
package/package.json
CHANGED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Intent Reviewer — adversarial quality gate for Write/Edit tool calls.
|
|
3
|
-
*
|
|
4
|
-
* Intercepts Write/Edit results, compares stated intent vs actual execution
|
|
5
|
-
* using GLM-5 via HuggingFace, and either passes or rejects with actionable feedback.
|
|
6
|
-
* After 3 failures on the same task+file, escalates to the user.
|
|
7
|
-
*/
|
|
8
|
-
export interface ReviewVerdict {
|
|
9
|
-
verdict: 'pass' | 'provisional_pass' | 'fail';
|
|
10
|
-
score: number;
|
|
11
|
-
claims: Array<{
|
|
12
|
-
claim: string;
|
|
13
|
-
finding: string;
|
|
14
|
-
result: 'true' | 'false' | 'partially_false';
|
|
15
|
-
}>;
|
|
16
|
-
limitations: string;
|
|
17
|
-
feedback: string;
|
|
18
|
-
}
|
|
19
|
-
export interface ReviewInput {
|
|
20
|
-
intent: string;
|
|
21
|
-
execution: {
|
|
22
|
-
type: 'edit' | 'write';
|
|
23
|
-
file: string;
|
|
24
|
-
old?: string;
|
|
25
|
-
new_content: string;
|
|
26
|
-
};
|
|
27
|
-
taskContext?: string;
|
|
28
|
-
}
|
|
29
|
-
export interface ReviewLog {
|
|
30
|
-
threadId: string;
|
|
31
|
-
model: string;
|
|
32
|
-
toolName: string;
|
|
33
|
-
filePath: string;
|
|
34
|
-
verdict: 'pass' | 'provisional_pass' | 'fail' | 'skipped' | 'timeout' | 'error';
|
|
35
|
-
score: number;
|
|
36
|
-
attempt: number;
|
|
37
|
-
latencyMs: number;
|
|
38
|
-
timestamp: number;
|
|
39
|
-
}
|
|
40
|
-
export declare function shouldReview(toolName: string, params: Record<string, unknown>): boolean;
|
|
41
|
-
interface MessageLike {
|
|
42
|
-
role: string;
|
|
43
|
-
content: string | Array<{
|
|
44
|
-
type: string;
|
|
45
|
-
text?: string;
|
|
46
|
-
}>;
|
|
47
|
-
}
|
|
48
|
-
/**
|
|
49
|
-
* Extract intent from the last assistant message.
|
|
50
|
-
* Priority: <thinking> > active <todo> item > plain text.
|
|
51
|
-
*/
|
|
52
|
-
export declare function extractIntent(messages: MessageLike[]): string | null;
|
|
53
|
-
/**
|
|
54
|
-
* Get the currently-active todo text (first [ ] item in last <todo> block).
|
|
55
|
-
* Used for failure counter keys and reset detection.
|
|
56
|
-
*/
|
|
57
|
-
export declare function getActiveTodoText(messages: MessageLike[]): string | null;
|
|
58
|
-
export declare class IntentReviewer {
|
|
59
|
-
private hfApiKey;
|
|
60
|
-
private states;
|
|
61
|
-
private reviewLogs;
|
|
62
|
-
constructor(hfApiKey: string);
|
|
63
|
-
/**
|
|
64
|
-
* Get or create per-thread review state.
|
|
65
|
-
*/
|
|
66
|
-
private getState;
|
|
67
|
-
/**
|
|
68
|
-
* Reset failure counters when user sends a new message.
|
|
69
|
-
*/
|
|
70
|
-
resetOnNewUserMessage(threadId: string): void;
|
|
71
|
-
/**
|
|
72
|
-
* Check if active todo changed (reset counters for affected files).
|
|
73
|
-
*/
|
|
74
|
-
private checkTodoReset;
|
|
75
|
-
/**
|
|
76
|
-
* Evaluate a Write/Edit execution against stated intent.
|
|
77
|
-
* Returns the modified tool result (pass-through, annotated, or error).
|
|
78
|
-
*/
|
|
79
|
-
evaluate(threadId: string, model: string, toolName: string, params: Record<string, unknown>, toolResult: string, messages: MessageLike[]): Promise<{
|
|
80
|
-
output: string;
|
|
81
|
-
isError: boolean;
|
|
82
|
-
}>;
|
|
83
|
-
/**
|
|
84
|
-
* Call GLM-5 via HuggingFace to evaluate intent vs execution.
|
|
85
|
-
*/
|
|
86
|
-
private callReviewModel;
|
|
87
|
-
/**
|
|
88
|
-
* Log a review for analytics.
|
|
89
|
-
*/
|
|
90
|
-
private logReview;
|
|
91
|
-
/**
|
|
92
|
-
* Get review statistics for analytics.
|
|
93
|
-
*/
|
|
94
|
-
getStats(): {
|
|
95
|
-
total: number;
|
|
96
|
-
byVerdict: Record<string, number>;
|
|
97
|
-
avgLatencyMs: number;
|
|
98
|
-
p95LatencyMs: number;
|
|
99
|
-
p99LatencyMs: number;
|
|
100
|
-
};
|
|
101
|
-
}
|
|
102
|
-
export {};
|
|
103
|
-
//# sourceMappingURL=intent-reviewer.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"intent-reviewer.d.ts","sourceRoot":"","sources":["../../src/lib/intent-reviewer.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,MAAM,CAAC;IAC9C,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,KAAK,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,GAAG,OAAO,GAAG,iBAAiB,CAAA;KAAE,CAAC,CAAC;IAChG,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,SAAS,EAAE;QACT,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC;QACvB,IAAI,EAAE,MAAM,CAAC;QACb,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,WAAW,EAAE,MAAM,CAAC;KACrB,CAAC;IACF,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,OAAO,EAAE,MAAM,GAAG,kBAAkB,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;IAChF,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;CACnB;AA2CD,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAC9B,OAAO,CAmBT;AAID,UAAU,WAAW;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,GAAG,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC1D;AAED;;;GAGG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,GAAG,IAAI,CAmDpE;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,WAAW,EAAE,GAAG,MAAM,GAAG,IAAI,CAyBxE;AAID,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAS;IACzB,OAAO,CAAC,MAAM,CAAuC;IACrD,OAAO,CAAC,UAAU,CAAmB;gBAEzB,QAAQ,EAAE,MAAM;IAI5B;;OAEG;IACH,OAAO,CAAC,QAAQ;IAWhB;;OAEG;IACH,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI;IAQ7C;;OAEG;IACH,OAAO,CAAC,cAAc;IAUtB;;;OAGG;IACG,QAAQ,CACZ,QAAQ,EAAE,MAAM,EAChB,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,MAAM,EAChB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,UAAU,EAAE,MAAM,EAClB,QAAQ,EAAE,WAAW,EAAE,GACtB,OAAO,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,OAAO,CAAA;KAAE,CAAC;IAiHhD;;OAEG;YACW,eAAe;IA8D7B;;OAEG;IACH,OAAO,CAAC,SAAS;IAmCjB;;OAEG;IACH,QAAQ,IAAI;QACV,KAAK,EAAE,MAAM,CAAC;QACd,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAClC,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,MAAM,CAAC;KACtB;CAiBF"}
|
|
@@ -1,363 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Intent Reviewer — adversarial quality gate for Write/Edit tool calls.
|
|
3
|
-
*
|
|
4
|
-
* Intercepts Write/Edit results, compares stated intent vs actual execution
|
|
5
|
-
* using GLM-5 via HuggingFace, and either passes or rejects with actionable feedback.
|
|
6
|
-
* After 3 failures on the same task+file, escalates to the user.
|
|
7
|
-
*/
|
|
8
|
-
// ─── Constants ───────────────────────────────────────────────────────────────
|
|
9
|
-
const REVIEW_MODEL = 'zai-org/GLM-5';
|
|
10
|
-
const REVIEW_TIMEOUT_MS = 5_000; // GLM-5 via HF is slower than Haiku
|
|
11
|
-
const MAX_STRIKES = 3;
|
|
12
|
-
const HF_API_URL = 'https://router.huggingface.co/v1/chat/completions';
|
|
13
|
-
const REVIEWER_SYSTEM_PROMPT = `You are an Intent Reviewer. Your job is to DISPROVE that the output matches the stated intent. Assume the output is wrong until proven otherwise.
|
|
14
|
-
|
|
15
|
-
## What You Receive
|
|
16
|
-
- INTENT: What was planned (from <thinking>, <todo>, or surrounding text)
|
|
17
|
-
- EXECUTION: What was actually written (the diff or file content)
|
|
18
|
-
- TASK CONTEXT: The user's original request (if available)
|
|
19
|
-
|
|
20
|
-
For each claim in the intent, challenge it against the literal code. Does the implementation match both the letter and spirit of the claim?
|
|
21
|
-
|
|
22
|
-
## What You Cannot Evaluate
|
|
23
|
-
Subjective quality (fun, beautiful, engaging) is beyond your scope. State this limitation explicitly. Give a provisional pass when all verifiable claims check out but subjective assessment is needed.
|
|
24
|
-
|
|
25
|
-
## Response Format
|
|
26
|
-
Respond with ONLY valid JSON (no markdown fencing, no extra text):
|
|
27
|
-
{
|
|
28
|
-
"verdict": "pass" | "provisional_pass" | "fail",
|
|
29
|
-
"score": 0-10,
|
|
30
|
-
"claims": [
|
|
31
|
-
{ "claim": "description of what was intended", "finding": "what was actually done", "result": "true" | "false" | "partially_false" }
|
|
32
|
-
],
|
|
33
|
-
"limitations": "any aspects you cannot evaluate",
|
|
34
|
-
"feedback": "specific actionable feedback if failing, empty string if passing"
|
|
35
|
-
}`;
|
|
36
|
-
// ─── Skip Logic ──────────────────────────────────────────────────────────────
|
|
37
|
-
const SKIP_EXTENSIONS = /\.(json|yml|yaml|toml|env|lock|md|txt|csv|log)$/i;
|
|
38
|
-
export function shouldReview(toolName, params) {
|
|
39
|
-
if (toolName !== 'Write' && toolName !== 'Edit')
|
|
40
|
-
return false;
|
|
41
|
-
const file = params.file_path || '';
|
|
42
|
-
if (SKIP_EXTENSIONS.test(file))
|
|
43
|
-
return false;
|
|
44
|
-
// Skip tiny edits (< 5 lines changed)
|
|
45
|
-
if (params.old_string && params.new_string) {
|
|
46
|
-
const oldLines = params.old_string.split('\n').length;
|
|
47
|
-
const newLines = params.new_string.split('\n').length;
|
|
48
|
-
if (Math.max(oldLines, newLines) < 5)
|
|
49
|
-
return false;
|
|
50
|
-
}
|
|
51
|
-
// Skip tiny new files (< 20 lines)
|
|
52
|
-
if (toolName === 'Write' && params.content) {
|
|
53
|
-
if (params.content.split('\n').length < 20)
|
|
54
|
-
return false;
|
|
55
|
-
}
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
/**
|
|
59
|
-
* Extract intent from the last assistant message.
|
|
60
|
-
* Priority: <thinking> > active <todo> item > plain text.
|
|
61
|
-
*/
|
|
62
|
-
export function extractIntent(messages) {
|
|
63
|
-
// Walk backwards to find last assistant message
|
|
64
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
65
|
-
const msg = messages[i];
|
|
66
|
-
if (msg.role !== 'assistant')
|
|
67
|
-
continue;
|
|
68
|
-
const text = typeof msg.content === 'string'
|
|
69
|
-
? msg.content
|
|
70
|
-
: msg.content
|
|
71
|
-
.filter(b => b.type === 'text' && b.text)
|
|
72
|
-
.map(b => b.text)
|
|
73
|
-
.join('\n');
|
|
74
|
-
if (!text)
|
|
75
|
-
continue;
|
|
76
|
-
const parts = [];
|
|
77
|
-
// Extract <thinking> block
|
|
78
|
-
const thinkingMatch = text.match(/<thinking>([\s\S]*?)<\/thinking>/);
|
|
79
|
-
if (thinkingMatch) {
|
|
80
|
-
parts.push(`THINKING: ${thinkingMatch[1].trim()}`);
|
|
81
|
-
}
|
|
82
|
-
// Extract active <todo> item (first [ ] in last <todo> block)
|
|
83
|
-
const todoBlocks = [...text.matchAll(/<todo>([\s\S]*?)<\/todo>/g)];
|
|
84
|
-
if (todoBlocks.length > 0) {
|
|
85
|
-
const lastTodo = todoBlocks[todoBlocks.length - 1][1];
|
|
86
|
-
const activeItem = lastTodo
|
|
87
|
-
.split('\n')
|
|
88
|
-
.find(line => line.match(/^[-\s]*\[ \]/));
|
|
89
|
-
if (activeItem) {
|
|
90
|
-
parts.push(`ACTIVE TODO: ${activeItem.replace(/^[-\s]*\[ \]\s*/, '').trim()}`);
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
// Extract plain text (non-tag content)
|
|
94
|
-
const plainText = text
|
|
95
|
-
.replace(/<thinking>[\s\S]*?<\/thinking>/g, '')
|
|
96
|
-
.replace(/<todo>[\s\S]*?<\/todo>/g, '')
|
|
97
|
-
.trim();
|
|
98
|
-
if (plainText) {
|
|
99
|
-
parts.push(`TEXT: ${plainText.substring(0, 500)}`);
|
|
100
|
-
}
|
|
101
|
-
if (parts.length > 0) {
|
|
102
|
-
return parts.join('\n\n');
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
return null;
|
|
106
|
-
}
|
|
107
|
-
/**
|
|
108
|
-
* Get the currently-active todo text (first [ ] item in last <todo> block).
|
|
109
|
-
* Used for failure counter keys and reset detection.
|
|
110
|
-
*/
|
|
111
|
-
export function getActiveTodoText(messages) {
|
|
112
|
-
for (let i = messages.length - 1; i >= 0; i--) {
|
|
113
|
-
const msg = messages[i];
|
|
114
|
-
if (msg.role !== 'assistant')
|
|
115
|
-
continue;
|
|
116
|
-
const text = typeof msg.content === 'string'
|
|
117
|
-
? msg.content
|
|
118
|
-
: msg.content
|
|
119
|
-
.filter(b => b.type === 'text' && b.text)
|
|
120
|
-
.map(b => b.text)
|
|
121
|
-
.join('\n');
|
|
122
|
-
const todoBlocks = [...text.matchAll(/<todo>([\s\S]*?)<\/todo>/g)];
|
|
123
|
-
if (todoBlocks.length > 0) {
|
|
124
|
-
const lastTodo = todoBlocks[todoBlocks.length - 1][1];
|
|
125
|
-
const activeItem = lastTodo
|
|
126
|
-
.split('\n')
|
|
127
|
-
.find(line => line.match(/^[-\s]*\[ \]/));
|
|
128
|
-
if (activeItem) {
|
|
129
|
-
return activeItem.replace(/^[-\s]*\[ \]\s*/, '').trim();
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
return null;
|
|
134
|
-
}
|
|
135
|
-
// ─── Intent Reviewer ─────────────────────────────────────────────────────────
|
|
136
|
-
export class IntentReviewer {
|
|
137
|
-
hfApiKey;
|
|
138
|
-
states = new Map();
|
|
139
|
-
reviewLogs = [];
|
|
140
|
-
constructor(hfApiKey) {
|
|
141
|
-
this.hfApiKey = hfApiKey;
|
|
142
|
-
}
|
|
143
|
-
/**
|
|
144
|
-
* Get or create per-thread review state.
|
|
145
|
-
*/
|
|
146
|
-
getState(threadId) {
|
|
147
|
-
if (!this.states.has(threadId)) {
|
|
148
|
-
this.states.set(threadId, {
|
|
149
|
-
failureCounts: new Map(),
|
|
150
|
-
feedbackHistory: new Map(),
|
|
151
|
-
lastActiveTodo: null,
|
|
152
|
-
});
|
|
153
|
-
}
|
|
154
|
-
return this.states.get(threadId);
|
|
155
|
-
}
|
|
156
|
-
/**
|
|
157
|
-
* Reset failure counters when user sends a new message.
|
|
158
|
-
*/
|
|
159
|
-
resetOnNewUserMessage(threadId) {
|
|
160
|
-
const state = this.states.get(threadId);
|
|
161
|
-
if (state) {
|
|
162
|
-
state.failureCounts.clear();
|
|
163
|
-
state.feedbackHistory.clear();
|
|
164
|
-
}
|
|
165
|
-
}
|
|
166
|
-
/**
|
|
167
|
-
* Check if active todo changed (reset counters for affected files).
|
|
168
|
-
*/
|
|
169
|
-
checkTodoReset(threadId, currentTodo) {
|
|
170
|
-
const state = this.getState(threadId);
|
|
171
|
-
if (currentTodo && state.lastActiveTodo && currentTodo !== state.lastActiveTodo) {
|
|
172
|
-
// Active todo changed — reset all counters
|
|
173
|
-
state.failureCounts.clear();
|
|
174
|
-
state.feedbackHistory.clear();
|
|
175
|
-
}
|
|
176
|
-
state.lastActiveTodo = currentTodo;
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Evaluate a Write/Edit execution against stated intent.
|
|
180
|
-
* Returns the modified tool result (pass-through, annotated, or error).
|
|
181
|
-
*/
|
|
182
|
-
async evaluate(threadId, model, toolName, params, toolResult, messages) {
|
|
183
|
-
const filePath = params.file_path || 'unknown';
|
|
184
|
-
const activeTodo = getActiveTodoText(messages);
|
|
185
|
-
this.checkTodoReset(threadId, activeTodo);
|
|
186
|
-
// Extract intent
|
|
187
|
-
const intent = extractIntent(messages);
|
|
188
|
-
if (!intent) {
|
|
189
|
-
this.logReview(threadId, model, toolName, filePath, 'skipped', 0, 0, 0);
|
|
190
|
-
return { output: toolResult, isError: false };
|
|
191
|
-
}
|
|
192
|
-
// Build execution description
|
|
193
|
-
const execution = toolName === 'Edit'
|
|
194
|
-
? {
|
|
195
|
-
type: 'edit',
|
|
196
|
-
file: filePath,
|
|
197
|
-
old: params.old_string,
|
|
198
|
-
new_content: params.new_string,
|
|
199
|
-
}
|
|
200
|
-
: {
|
|
201
|
-
type: 'write',
|
|
202
|
-
file: filePath,
|
|
203
|
-
new_content: params.content,
|
|
204
|
-
};
|
|
205
|
-
// Truncate large content for the review (keep it fast)
|
|
206
|
-
const maxContentChars = 3000;
|
|
207
|
-
const executionDesc = execution.type === 'edit'
|
|
208
|
-
? `FILE: ${execution.file}\nOLD:\n${(execution.old || '').slice(0, maxContentChars)}\nNEW:\n${execution.new_content.slice(0, maxContentChars)}`
|
|
209
|
-
: `FILE: ${execution.file}\nCONTENT:\n${execution.new_content.slice(0, maxContentChars)}`;
|
|
210
|
-
// Call Haiku with timeout
|
|
211
|
-
const startTime = Date.now();
|
|
212
|
-
let verdict;
|
|
213
|
-
try {
|
|
214
|
-
verdict = await this.callReviewModel(intent, executionDesc, REVIEW_TIMEOUT_MS);
|
|
215
|
-
}
|
|
216
|
-
catch (err) {
|
|
217
|
-
const latency = Date.now() - startTime;
|
|
218
|
-
const isTimeout = err.message?.includes('timeout');
|
|
219
|
-
this.logReview(threadId, model, toolName, filePath, isTimeout ? 'timeout' : 'error', 0, 0, latency);
|
|
220
|
-
console.warn(`[IntentReviewer] ${isTimeout ? 'TIMEOUT' : 'ERROR'} thread=${threadId} ` +
|
|
221
|
-
`latency=${latency}ms: ${err.message}`);
|
|
222
|
-
// Default to pass on infrastructure failure
|
|
223
|
-
return { output: toolResult, isError: false };
|
|
224
|
-
}
|
|
225
|
-
const latency = Date.now() - startTime;
|
|
226
|
-
const state = this.getState(threadId);
|
|
227
|
-
const key = `${activeTodo || 'no-todo'}:${filePath}`;
|
|
228
|
-
if (verdict.verdict === 'pass') {
|
|
229
|
-
this.logReview(threadId, model, toolName, filePath, 'pass', verdict.score, 0, latency);
|
|
230
|
-
return { output: toolResult, isError: false };
|
|
231
|
-
}
|
|
232
|
-
if (verdict.verdict === 'provisional_pass') {
|
|
233
|
-
this.logReview(threadId, model, toolName, filePath, 'provisional_pass', verdict.score, 0, latency);
|
|
234
|
-
const annotation = `\n\n[Intent Review: PROVISIONAL PASS — Score ${verdict.score}/10]\n` +
|
|
235
|
-
`Verified: ${verdict.claims.filter(c => c.result === 'true').map(c => c.claim).join(', ')}\n` +
|
|
236
|
-
`Unverifiable: ${verdict.limitations}`;
|
|
237
|
-
return { output: toolResult + annotation, isError: false };
|
|
238
|
-
}
|
|
239
|
-
// FAIL
|
|
240
|
-
const count = (state.failureCounts.get(key) || 0) + 1;
|
|
241
|
-
state.failureCounts.set(key, count);
|
|
242
|
-
if (!state.feedbackHistory.has(key)) {
|
|
243
|
-
state.feedbackHistory.set(key, []);
|
|
244
|
-
}
|
|
245
|
-
state.feedbackHistory.get(key).push(verdict.feedback);
|
|
246
|
-
this.logReview(threadId, model, toolName, filePath, 'fail', verdict.score, count, latency);
|
|
247
|
-
if (count >= MAX_STRIKES) {
|
|
248
|
-
const allFeedback = state.feedbackHistory.get(key) || [];
|
|
249
|
-
return {
|
|
250
|
-
output: `ESCALATION REQUIRED: You have failed this task ${MAX_STRIKES} times.\n\n` +
|
|
251
|
-
`Previous feedback:\n${allFeedback.map((f, i) => ` Attempt ${i + 1}: ${f}`).join('\n')}\n\n` +
|
|
252
|
-
`You MUST now:\n` +
|
|
253
|
-
`1. Stop attempting this task\n` +
|
|
254
|
-
`2. Explain the core difficulty to the user\n` +
|
|
255
|
-
`3. Suggest 2-3 ways to proceed\n\n` +
|
|
256
|
-
`Do NOT attempt another revision.`,
|
|
257
|
-
isError: true,
|
|
258
|
-
};
|
|
259
|
-
}
|
|
260
|
-
const failedClaims = verdict.claims
|
|
261
|
-
.filter(c => c.result !== 'true')
|
|
262
|
-
.map(c => `- "${c.claim}" → ${c.finding}`)
|
|
263
|
-
.join('\n');
|
|
264
|
-
return {
|
|
265
|
-
output: `REVISION REQUIRED (attempt ${count}/${MAX_STRIKES}): ${verdict.feedback}\n\n` +
|
|
266
|
-
`Claims that failed:\n${failedClaims}`,
|
|
267
|
-
isError: true,
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
/**
|
|
271
|
-
* Call GLM-5 via HuggingFace to evaluate intent vs execution.
|
|
272
|
-
*/
|
|
273
|
-
async callReviewModel(intent, execution, timeoutMs) {
|
|
274
|
-
const userMessage = `## INTENT\n${intent}\n\n## EXECUTION\n${execution}`;
|
|
275
|
-
const controller = new AbortController();
|
|
276
|
-
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
277
|
-
try {
|
|
278
|
-
const response = await fetch(HF_API_URL, {
|
|
279
|
-
method: 'POST',
|
|
280
|
-
headers: {
|
|
281
|
-
'Content-Type': 'application/json',
|
|
282
|
-
Authorization: `Bearer ${this.hfApiKey}`,
|
|
283
|
-
},
|
|
284
|
-
body: JSON.stringify({
|
|
285
|
-
model: REVIEW_MODEL,
|
|
286
|
-
max_tokens: 1024,
|
|
287
|
-
stream: false,
|
|
288
|
-
messages: [
|
|
289
|
-
{ role: 'system', content: REVIEWER_SYSTEM_PROMPT },
|
|
290
|
-
{ role: 'user', content: userMessage },
|
|
291
|
-
],
|
|
292
|
-
}),
|
|
293
|
-
signal: controller.signal,
|
|
294
|
-
});
|
|
295
|
-
clearTimeout(timer);
|
|
296
|
-
if (!response.ok) {
|
|
297
|
-
const body = await response.text().catch(() => '');
|
|
298
|
-
throw Object.assign(new Error(`HF API ${response.status}: ${body.slice(0, 200)}`), {
|
|
299
|
-
status: response.status,
|
|
300
|
-
});
|
|
301
|
-
}
|
|
302
|
-
const json = (await response.json());
|
|
303
|
-
const text = json.choices?.[0]?.message?.content || '';
|
|
304
|
-
// Parse JSON — strip markdown fencing if present
|
|
305
|
-
const cleaned = text.replace(/^```(?:json)?\s*\n?/m, '').replace(/\n?```\s*$/m, '').trim();
|
|
306
|
-
const parsed = JSON.parse(cleaned);
|
|
307
|
-
// Validate required fields
|
|
308
|
-
if (!parsed.verdict || !['pass', 'provisional_pass', 'fail'].includes(parsed.verdict)) {
|
|
309
|
-
throw new Error(`Invalid verdict: ${parsed.verdict}`);
|
|
310
|
-
}
|
|
311
|
-
return parsed;
|
|
312
|
-
}
|
|
313
|
-
catch (err) {
|
|
314
|
-
clearTimeout(timer);
|
|
315
|
-
if (err.name === 'AbortError') {
|
|
316
|
-
throw new Error('Review timeout exceeded');
|
|
317
|
-
}
|
|
318
|
-
throw err;
|
|
319
|
-
}
|
|
320
|
-
}
|
|
321
|
-
/**
|
|
322
|
-
* Log a review for analytics.
|
|
323
|
-
*/
|
|
324
|
-
logReview(threadId, model, toolName, filePath, verdict, score, attempt, latencyMs) {
|
|
325
|
-
const entry = {
|
|
326
|
-
threadId,
|
|
327
|
-
model,
|
|
328
|
-
toolName,
|
|
329
|
-
filePath,
|
|
330
|
-
verdict,
|
|
331
|
-
score,
|
|
332
|
-
attempt,
|
|
333
|
-
latencyMs,
|
|
334
|
-
timestamp: Date.now(),
|
|
335
|
-
};
|
|
336
|
-
this.reviewLogs.push(entry);
|
|
337
|
-
// Keep only last 1000 entries in memory
|
|
338
|
-
if (this.reviewLogs.length > 1000) {
|
|
339
|
-
this.reviewLogs = this.reviewLogs.slice(-500);
|
|
340
|
-
}
|
|
341
|
-
console.log(`[IntentReviewer] thread=${threadId} tool=${toolName} file=${filePath.split('/').pop()} ` +
|
|
342
|
-
`verdict=${verdict} score=${score} attempt=${attempt} latency=${latencyMs}ms`);
|
|
343
|
-
}
|
|
344
|
-
/**
|
|
345
|
-
* Get review statistics for analytics.
|
|
346
|
-
*/
|
|
347
|
-
getStats() {
|
|
348
|
-
const total = this.reviewLogs.length;
|
|
349
|
-
const byVerdict = {};
|
|
350
|
-
const latencies = [];
|
|
351
|
-
for (const log of this.reviewLogs) {
|
|
352
|
-
byVerdict[log.verdict] = (byVerdict[log.verdict] || 0) + 1;
|
|
353
|
-
if (log.latencyMs > 0)
|
|
354
|
-
latencies.push(log.latencyMs);
|
|
355
|
-
}
|
|
356
|
-
latencies.sort((a, b) => a - b);
|
|
357
|
-
const avg = latencies.length > 0 ? latencies.reduce((a, b) => a + b, 0) / latencies.length : 0;
|
|
358
|
-
const p95 = latencies.length > 0 ? latencies[Math.floor(latencies.length * 0.95)] : 0;
|
|
359
|
-
const p99 = latencies.length > 0 ? latencies[Math.floor(latencies.length * 0.99)] : 0;
|
|
360
|
-
return { total, byVerdict, avgLatencyMs: Math.round(avg), p95LatencyMs: p95 ?? 0, p99LatencyMs: p99 ?? 0 };
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
//# sourceMappingURL=intent-reviewer.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"intent-reviewer.js","sourceRoot":"","sources":["../../src/lib/intent-reviewer.ts"],"names":[],"mappings":"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"}
|