@luckydraw/cumulus 0.30.28 → 0.30.30

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.
@@ -563,6 +563,31 @@
563
563
  ' font-size: 0.85em; line-height: 1; cursor: pointer; padding: 0 0.1em;',
564
564
  '}',
565
565
  '.cumulus-attach-chip-remove:hover { color: #ff6666; }',
566
+ /* Upload progress bar — thin overlay at bottom of chip */
567
+ '.cumulus-attach-chip-progress {',
568
+ ' position: absolute; left: 0; right: 0; bottom: 0;',
569
+ ' height: 3px; background: rgba(255,255,255,0.08);',
570
+ ' overflow: hidden;',
571
+ '}',
572
+ '.cumulus-attach-chip-progress-fill {',
573
+ ' height: 100%; width: 0%;',
574
+ ' background: linear-gradient(90deg, #7c3aed, #a78bfa);',
575
+ ' transition: width 0.15s ease;',
576
+ '}',
577
+ /* Status badge (✓ or ✗) — small corner indicator */
578
+ '.cumulus-attach-chip-status {',
579
+ ' position: absolute; bottom: 0.15em; left: 0.2em;',
580
+ ' font-size: 0.75em; line-height: 1; font-weight: bold;',
581
+ ' pointer-events: none;',
582
+ '}',
583
+ '.cumulus-attach-chip[data-status="done"] .cumulus-attach-chip-status { color: #4ade80; }',
584
+ '.cumulus-attach-chip[data-status="error"] {',
585
+ ' border-color: #b91c1c;',
586
+ '}',
587
+ '.cumulus-attach-chip[data-status="error"] .cumulus-attach-chip-status { color: #ef4444; }',
588
+ '.cumulus-attach-chip[data-status="uploading"] {',
589
+ ' opacity: 0.85;',
590
+ '}',
566
591
 
567
592
  /* Input row (attach btn + textarea + send btn) */
568
593
  '.cumulus-input-row {',
@@ -2612,6 +2637,77 @@
2612
2637
  });
2613
2638
  }
2614
2639
 
2640
+ function formatFileSize(bytes) {
2641
+ if (bytes < 1024) return bytes + ' B';
2642
+ if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
2643
+ if (bytes < 1024 * 1024 * 1024) return (bytes / (1024 * 1024)).toFixed(1) + ' MB';
2644
+ return (bytes / (1024 * 1024 * 1024)).toFixed(2) + ' GB';
2645
+ }
2646
+
2647
+ // Upload a non-image file to /api/media/upload via XHR with progress.
2648
+ // Mutates the attachment object in place: progress, status, url, path, error, xhr.
2649
+ // Calls onChange() whenever state advances so the UI can re-render.
2650
+ function uploadAttachment(att, file, apiKey, onChange) {
2651
+ var xhr = new XMLHttpRequest();
2652
+ att.xhr = xhr;
2653
+
2654
+ var url = '/api/media/upload?filename=' + encodeURIComponent(file.name);
2655
+ xhr.open('POST', url, true);
2656
+ xhr.setRequestHeader('X-API-Key', apiKey || '');
2657
+ xhr.setRequestHeader('Content-Type', file.type || 'application/octet-stream');
2658
+
2659
+ xhr.upload.addEventListener('progress', function (e) {
2660
+ if (e.lengthComputable && att.status === 'uploading') {
2661
+ att.progress = e.loaded / e.total;
2662
+ if (onChange) onChange();
2663
+ }
2664
+ });
2665
+
2666
+ xhr.addEventListener('load', function () {
2667
+ if (att.status !== 'uploading') return; // aborted/removed
2668
+ if (xhr.status >= 200 && xhr.status < 300) {
2669
+ try {
2670
+ var resp = JSON.parse(xhr.responseText);
2671
+ // /api/media/upload returns {files: [...]} for multipart, or the single result object
2672
+ var result = resp.files && resp.files[0] ? resp.files[0] : resp;
2673
+ att.status = 'done';
2674
+ att.progress = 1;
2675
+ att.url = result.url;
2676
+ att.path = result.path;
2677
+ att.serverSize = result.size;
2678
+ } catch (e) {
2679
+ att.status = 'error';
2680
+ att.error = 'Invalid server response';
2681
+ }
2682
+ } else {
2683
+ att.status = 'error';
2684
+ try {
2685
+ var errResp = JSON.parse(xhr.responseText);
2686
+ att.error = errResp.error || 'HTTP ' + xhr.status;
2687
+ } catch (e) {
2688
+ att.error = 'HTTP ' + xhr.status;
2689
+ }
2690
+ }
2691
+ att.xhr = null;
2692
+ if (onChange) onChange();
2693
+ });
2694
+
2695
+ xhr.addEventListener('error', function () {
2696
+ if (att.status !== 'uploading') return;
2697
+ att.status = 'error';
2698
+ att.error = 'Network error';
2699
+ att.xhr = null;
2700
+ if (onChange) onChange();
2701
+ });
2702
+
2703
+ xhr.addEventListener('abort', function () {
2704
+ // Caller is responsible for removing the chip when it aborts
2705
+ att.xhr = null;
2706
+ });
2707
+
2708
+ xhr.send(file);
2709
+ }
2710
+
2615
2711
  // ─── WebSocket Connection ────────────────────────────────────────────────────
2616
2712
  function createConnection(opts) {
2617
2713
  var wsUrl = opts.wsUrl;
@@ -2852,7 +2948,7 @@
2852
2948
  var fileInput = document.createElement('input');
2853
2949
  fileInput.type = 'file';
2854
2950
  fileInput.multiple = true;
2855
- fileInput.accept = 'image/*,.pdf,.txt,.md,.js,.ts,.py,.json,.csv';
2951
+ // Accept any file type — non-image files upload to media server with progress
2856
2952
  fileInput.style.display = 'none';
2857
2953
  fileInput.setAttribute('data-testid', 'webchat-file-input');
2858
2954
 
@@ -3180,11 +3276,22 @@
3180
3276
  panel.appendChild(dialog);
3181
3277
  }
3182
3278
 
3279
+ function hasUploadingAttachments() {
3280
+ for (var i = 0; i < pendingAttachments.length; i++) {
3281
+ if (pendingAttachments[i].status === 'uploading') return true;
3282
+ }
3283
+ return false;
3284
+ }
3285
+
3183
3286
  function updateSendBtn() {
3184
3287
  if (streaming) {
3185
3288
  sendBtn.textContent = 'Stop';
3186
3289
  sendBtn.classList.add('stop');
3187
3290
  sendBtn.disabled = false;
3291
+ } else if (hasUploadingAttachments()) {
3292
+ sendBtn.textContent = 'Uploading…';
3293
+ sendBtn.classList.remove('stop');
3294
+ sendBtn.disabled = true;
3188
3295
  } else {
3189
3296
  sendBtn.textContent = 'Send';
3190
3297
  sendBtn.classList.remove('stop');
@@ -3222,6 +3329,7 @@
3222
3329
  attachStrip.innerHTML = '';
3223
3330
  if (pendingAttachments.length === 0) {
3224
3331
  attachStrip.style.display = 'none';
3332
+ updateSendBtn();
3225
3333
  return;
3226
3334
  }
3227
3335
  attachStrip.style.display = 'flex';
@@ -3229,6 +3337,8 @@
3229
3337
  var chip = document.createElement('div');
3230
3338
  chip.className = 'cumulus-attach-chip';
3231
3339
  chip.setAttribute('data-testid', 'webchat-attach-chip');
3340
+ chip.setAttribute('data-status', att.status || 'done');
3341
+ if (att.error) chip.setAttribute('title', att.error);
3232
3342
  if (att.isImage) {
3233
3343
  var thumb = document.createElement('img');
3234
3344
  thumb.className = 'cumulus-attach-chip-thumb';
@@ -3245,6 +3355,25 @@
3245
3355
  nameEl.className = 'cumulus-attach-chip-name';
3246
3356
  nameEl.textContent = att.name;
3247
3357
  chip.appendChild(nameEl);
3358
+ if (att.status === 'uploading') {
3359
+ var bar = document.createElement('div');
3360
+ bar.className = 'cumulus-attach-chip-progress';
3361
+ var fill = document.createElement('div');
3362
+ fill.className = 'cumulus-attach-chip-progress-fill';
3363
+ fill.style.width = Math.round((att.progress || 0) * 100) + '%';
3364
+ bar.appendChild(fill);
3365
+ chip.appendChild(bar);
3366
+ } else if (att.status === 'done' && !att.isImage) {
3367
+ var ok = document.createElement('div');
3368
+ ok.className = 'cumulus-attach-chip-status';
3369
+ ok.textContent = '✓';
3370
+ chip.appendChild(ok);
3371
+ } else if (att.status === 'error') {
3372
+ var bad = document.createElement('div');
3373
+ bad.className = 'cumulus-attach-chip-status';
3374
+ bad.textContent = '✗';
3375
+ chip.appendChild(bad);
3376
+ }
3248
3377
  var removeBtn = document.createElement('button');
3249
3378
  removeBtn.className = 'cumulus-attach-chip-remove';
3250
3379
  removeBtn.setAttribute('data-testid', 'webchat-attach-remove');
@@ -3252,6 +3381,14 @@
3252
3381
  removeBtn.setAttribute('title', 'Remove attachment');
3253
3382
  (function (index) {
3254
3383
  removeBtn.addEventListener('click', function () {
3384
+ var removed = pendingAttachments[index];
3385
+ if (removed && removed.xhr) {
3386
+ try {
3387
+ removed.xhr.abort();
3388
+ } catch (e) {
3389
+ /* ignore */
3390
+ }
3391
+ }
3255
3392
  pendingAttachments.splice(index, 1);
3256
3393
  renderAttachStrip();
3257
3394
  });
@@ -3259,23 +3396,44 @@
3259
3396
  chip.appendChild(removeBtn);
3260
3397
  attachStrip.appendChild(chip);
3261
3398
  });
3399
+ updateSendBtn();
3262
3400
  }
3263
3401
 
3264
3402
  async function addFilesToPending(files) {
3265
3403
  for (var i = 0; i < files.length; i++) {
3266
3404
  var file = files[i];
3267
- try {
3268
- var info = await readFileAsBase64(file);
3269
- var dataUrl = await readFileAsDataUrl(file);
3270
- pendingAttachments.push({
3271
- base64: info.base64,
3272
- mimeType: info.mimeType,
3405
+ var isImg = file.type && file.type.startsWith('image/');
3406
+ if (isImg) {
3407
+ try {
3408
+ var info = await readFileAsBase64(file);
3409
+ var dataUrl = await readFileAsDataUrl(file);
3410
+ pendingAttachments.push({
3411
+ base64: info.base64,
3412
+ mimeType: info.mimeType,
3413
+ name: file.name,
3414
+ size: file.size,
3415
+ dataUrl: dataUrl,
3416
+ isImage: true,
3417
+ status: 'done',
3418
+ });
3419
+ } catch (e) {
3420
+ console.error('[Cumulus] Failed to read image:', file.name, e);
3421
+ }
3422
+ } else {
3423
+ var att = {
3273
3424
  name: file.name,
3274
- dataUrl: dataUrl,
3275
- isImage: file.type.startsWith('image/'),
3276
- });
3277
- } catch (e) {
3278
- console.error('[Cumulus] Failed to read file:', file.name, e);
3425
+ mimeType: file.type || 'application/octet-stream',
3426
+ size: file.size,
3427
+ isImage: false,
3428
+ status: 'uploading',
3429
+ progress: 0,
3430
+ url: null,
3431
+ path: null,
3432
+ error: null,
3433
+ xhr: null,
3434
+ };
3435
+ pendingAttachments.push(att);
3436
+ uploadAttachment(att, file, activeApiKey, renderAttachStrip);
3279
3437
  }
3280
3438
  }
3281
3439
  renderAttachStrip();
@@ -3456,7 +3614,13 @@
3456
3614
 
3457
3615
  if (!text && pendingAttachments.length === 0) return;
3458
3616
  if (!connection) return;
3459
- var attachSnapshot = pendingAttachments.slice();
3617
+ if (hasUploadingAttachments()) return; // shouldn't reach here — button disabled
3618
+
3619
+ // Drop attachments that failed to upload — keep only images + done non-images
3620
+ var sendable = pendingAttachments.filter(function (a) {
3621
+ return a.isImage || a.status === 'done';
3622
+ });
3623
+ var attachSnapshot = sendable.slice();
3460
3624
  var displayText = text || '(attachment)';
3461
3625
  messages.push({ role: 'user', content: displayText, attachments: attachSnapshot });
3462
3626
  input.value = '';
@@ -3475,10 +3639,22 @@
3475
3639
  .map(function (a) {
3476
3640
  return { mimeType: a.mimeType, base64: a.base64 };
3477
3641
  });
3642
+ // Build "Attached files:" prefix for non-image uploads so the agent can read_file them
3643
+ var fileAttachments = attachSnapshot.filter(function (a) {
3644
+ return !a.isImage && a.status === 'done' && a.path;
3645
+ });
3646
+ var messageBody = text || '';
3647
+ if (fileAttachments.length > 0) {
3648
+ var lines = fileAttachments.map(function (a) {
3649
+ return '- ' + a.name + ' (' + formatFileSize(a.size || 0) + ') → ' + a.path;
3650
+ });
3651
+ messageBody =
3652
+ 'Attached files:\n' + lines.join('\n') + (messageBody ? '\n\n' + messageBody : '');
3653
+ }
3478
3654
  var payload = {
3479
3655
  type: 'message',
3480
3656
  threadName: sessionId,
3481
- message: text || ' ',
3657
+ message: messageBody || ' ',
3482
3658
  };
3483
3659
  if (imagePayload.length > 0) payload.images = imagePayload;
3484
3660
  connection.send(payload);
@@ -5234,7 +5410,7 @@
5234
5410
  var fileInput = document.createElement('input');
5235
5411
  fileInput.type = 'file';
5236
5412
  fileInput.multiple = true;
5237
- fileInput.accept = 'image/*,.pdf,.txt,.md,.js,.ts,.py,.json,.csv';
5413
+ // Accept any file type — non-image files upload to media server with progress
5238
5414
  fileInput.style.display = 'none';
5239
5415
  fileInput.setAttribute('data-testid', 'webchat-file-input');
5240
5416
 
@@ -5593,8 +5769,16 @@
5593
5769
  panelEl.appendChild(dialog);
5594
5770
  }
5595
5771
 
5772
+ function hasPanelUploading() {
5773
+ for (var i = 0; i < state.pendingAttachments.length; i++) {
5774
+ if (state.pendingAttachments[i].status === 'uploading') return true;
5775
+ }
5776
+ return false;
5777
+ }
5778
+
5596
5779
  function updatePanelSendBtn() {
5597
5780
  var hasInput = inputEl.value.trim().length > 0 || state.pendingAttachments.length > 0;
5781
+ var uploading = hasPanelUploading();
5598
5782
  if (state.streaming && !hasInput) {
5599
5783
  // Streaming with no input — show Stop button
5600
5784
  sendBtn.textContent = 'Stop';
@@ -5602,15 +5786,15 @@
5602
5786
  sendBtn.disabled = false;
5603
5787
  } else if (state.streaming && hasInput) {
5604
5788
  // Streaming with input — show Send (interjection mode)
5605
- sendBtn.textContent = 'Send';
5789
+ sendBtn.textContent = uploading ? 'Uploading…' : 'Send';
5606
5790
  sendBtn.classList.remove('stop');
5607
5791
  sendBtn.classList.add('interject');
5608
- sendBtn.disabled = false;
5792
+ sendBtn.disabled = uploading;
5609
5793
  } else {
5610
- sendBtn.textContent = 'Send';
5794
+ sendBtn.textContent = uploading ? 'Uploading…' : 'Send';
5611
5795
  sendBtn.classList.remove('stop');
5612
5796
  sendBtn.classList.remove('interject');
5613
- sendBtn.disabled = false;
5797
+ sendBtn.disabled = uploading;
5614
5798
  }
5615
5799
  }
5616
5800
 
@@ -5618,6 +5802,7 @@
5618
5802
  attachStrip.innerHTML = '';
5619
5803
  if (state.pendingAttachments.length === 0) {
5620
5804
  attachStrip.style.display = 'none';
5805
+ updatePanelSendBtn();
5621
5806
  return;
5622
5807
  }
5623
5808
  attachStrip.style.display = 'flex';
@@ -5625,6 +5810,8 @@
5625
5810
  var chip = document.createElement('div');
5626
5811
  chip.className = 'cumulus-attach-chip';
5627
5812
  chip.setAttribute('data-testid', 'webchat-attach-chip');
5813
+ chip.setAttribute('data-status', att.status || 'done');
5814
+ if (att.error) chip.setAttribute('title', att.error);
5628
5815
  if (att.isImage) {
5629
5816
  var thumb = document.createElement('img');
5630
5817
  thumb.className = 'cumulus-attach-chip-thumb';
@@ -5641,6 +5828,25 @@
5641
5828
  nameEl.className = 'cumulus-attach-chip-name';
5642
5829
  nameEl.textContent = att.name;
5643
5830
  chip.appendChild(nameEl);
5831
+ if (att.status === 'uploading') {
5832
+ var bar = document.createElement('div');
5833
+ bar.className = 'cumulus-attach-chip-progress';
5834
+ var fill = document.createElement('div');
5835
+ fill.className = 'cumulus-attach-chip-progress-fill';
5836
+ fill.style.width = Math.round((att.progress || 0) * 100) + '%';
5837
+ bar.appendChild(fill);
5838
+ chip.appendChild(bar);
5839
+ } else if (att.status === 'done' && !att.isImage) {
5840
+ var ok = document.createElement('div');
5841
+ ok.className = 'cumulus-attach-chip-status';
5842
+ ok.textContent = '✓';
5843
+ chip.appendChild(ok);
5844
+ } else if (att.status === 'error') {
5845
+ var bad = document.createElement('div');
5846
+ bad.className = 'cumulus-attach-chip-status';
5847
+ bad.textContent = '✗';
5848
+ chip.appendChild(bad);
5849
+ }
5644
5850
  var removeBtn = document.createElement('button');
5645
5851
  removeBtn.className = 'cumulus-attach-chip-remove';
5646
5852
  removeBtn.setAttribute('data-testid', 'webchat-attach-remove');
@@ -5648,6 +5854,14 @@
5648
5854
  removeBtn.setAttribute('title', 'Remove attachment');
5649
5855
  (function (index) {
5650
5856
  removeBtn.addEventListener('click', function () {
5857
+ var removed = state.pendingAttachments[index];
5858
+ if (removed && removed.xhr) {
5859
+ try {
5860
+ removed.xhr.abort();
5861
+ } catch (e) {
5862
+ /* ignore */
5863
+ }
5864
+ }
5651
5865
  state.pendingAttachments.splice(index, 1);
5652
5866
  renderPanelAttachStrip();
5653
5867
  });
@@ -5655,23 +5869,44 @@
5655
5869
  chip.appendChild(removeBtn);
5656
5870
  attachStrip.appendChild(chip);
5657
5871
  });
5872
+ updatePanelSendBtn();
5658
5873
  }
5659
5874
 
5660
5875
  async function addFilesToPending(files) {
5661
5876
  for (var i = 0; i < files.length; i++) {
5662
5877
  var file = files[i];
5663
- try {
5664
- var info = await readFileAsBase64(file);
5665
- var dataUrl = await readFileAsDataUrl(file);
5666
- state.pendingAttachments.push({
5667
- base64: info.base64,
5668
- mimeType: info.mimeType,
5878
+ var isImg = file.type && file.type.startsWith('image/');
5879
+ if (isImg) {
5880
+ try {
5881
+ var info = await readFileAsBase64(file);
5882
+ var dataUrl = await readFileAsDataUrl(file);
5883
+ state.pendingAttachments.push({
5884
+ base64: info.base64,
5885
+ mimeType: info.mimeType,
5886
+ name: file.name,
5887
+ size: file.size,
5888
+ dataUrl: dataUrl,
5889
+ isImage: true,
5890
+ status: 'done',
5891
+ });
5892
+ } catch (e) {
5893
+ console.error('[Cumulus] Failed to read image:', file.name, e);
5894
+ }
5895
+ } else {
5896
+ var att = {
5669
5897
  name: file.name,
5670
- dataUrl: dataUrl,
5671
- isImage: file.type.startsWith('image/'),
5672
- });
5673
- } catch (e) {
5674
- console.error('[Cumulus] Failed to read file:', file.name, e);
5898
+ mimeType: file.type || 'application/octet-stream',
5899
+ size: file.size,
5900
+ isImage: false,
5901
+ status: 'uploading',
5902
+ progress: 0,
5903
+ url: null,
5904
+ path: null,
5905
+ error: null,
5906
+ xhr: null,
5907
+ };
5908
+ state.pendingAttachments.push(att);
5909
+ uploadAttachment(att, file, activeApiKey, renderPanelAttachStrip);
5675
5910
  }
5676
5911
  }
5677
5912
  renderPanelAttachStrip();
@@ -5751,8 +5986,13 @@
5751
5986
 
5752
5987
  if (!text && state.pendingAttachments.length === 0) return;
5753
5988
  if (!connection) return;
5989
+ if (hasPanelUploading()) return; // button is disabled while uploading
5754
5990
 
5755
- var attachSnapshot = state.pendingAttachments.slice();
5991
+ // Drop attachments that failed to upload
5992
+ var sendable = state.pendingAttachments.filter(function (a) {
5993
+ return a.isImage || a.status === 'done';
5994
+ });
5995
+ var attachSnapshot = sendable.slice();
5756
5996
  var displayText = text || '(attachment)';
5757
5997
  state.messages.push({ role: 'user', content: displayText, attachments: attachSnapshot });
5758
5998
  inputEl.value = '';
@@ -5776,10 +6016,23 @@
5776
6016
  return { mimeType: a.mimeType, base64: a.base64 };
5777
6017
  });
5778
6018
 
6019
+ // Build "Attached files:" prefix for non-image uploads
6020
+ var fileAttachments = attachSnapshot.filter(function (a) {
6021
+ return !a.isImage && a.status === 'done' && a.path;
6022
+ });
6023
+ var messageBody = text || '';
6024
+ if (fileAttachments.length > 0) {
6025
+ var lines = fileAttachments.map(function (a) {
6026
+ return '- ' + a.name + ' (' + formatFileSize(a.size || 0) + ') → ' + a.path;
6027
+ });
6028
+ messageBody =
6029
+ 'Attached files:\n' + lines.join('\n') + (messageBody ? '\n\n' + messageBody : '');
6030
+ }
6031
+
5779
6032
  var payload = {
5780
6033
  type: 'message',
5781
6034
  threadName: threadName,
5782
- message: text || ' ',
6035
+ message: messageBody || ' ',
5783
6036
  };
5784
6037
  if (imagePayload.length > 0) payload.images = imagePayload;
5785
6038
  connection.send(payload);
@@ -0,0 +1,103 @@
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
@@ -0,0 +1 @@
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"}