clementine-agent 1.18.121 → 1.18.123
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/cli/dashboard.js +39 -36
- package/dist/gateway/cron-scheduler.js +95 -132
- package/dist/types.d.ts +19 -78
- package/package.json +1 -1
package/dist/cli/dashboard.js
CHANGED
|
@@ -14419,6 +14419,9 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
14419
14419
|
}
|
|
14420
14420
|
.toast.success { border-left: 3px solid var(--green); }
|
|
14421
14421
|
.toast.error { border-left: 3px solid var(--red); }
|
|
14422
|
+
/* 1.18.122 — variants used by callsites that lacked CSS support */
|
|
14423
|
+
.toast.warn { border-left: 3px solid var(--yellow); }
|
|
14424
|
+
.toast.info { border-left: 3px solid var(--accent); }
|
|
14422
14425
|
@keyframes toastIn {
|
|
14423
14426
|
from { transform: translateX(40px); opacity: 0; }
|
|
14424
14427
|
to { transform: translateX(0); opacity: 1; }
|
|
@@ -19466,11 +19469,11 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19466
19469
|
clearInterval(ticker);
|
|
19467
19470
|
|
|
19468
19471
|
if (errorMsg) {
|
|
19469
|
-
manifestEl.innerHTML = '<div style="color
|
|
19472
|
+
manifestEl.innerHTML = '<div style="color:var(--red)">Error: ' + escapeHtml(errorMsg) + '</div>';
|
|
19470
19473
|
return;
|
|
19471
19474
|
}
|
|
19472
19475
|
if (!manifestData || !finalData) {
|
|
19473
|
-
manifestEl.innerHTML = '<div style="color
|
|
19476
|
+
manifestEl.innerHTML = '<div style="color:var(--red)">Preview ended without a result. Check dashboard logs.</div>';
|
|
19474
19477
|
return;
|
|
19475
19478
|
}
|
|
19476
19479
|
|
|
@@ -19480,13 +19483,13 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19480
19483
|
const manifestRows = Object.entries(manifest.formats || {})
|
|
19481
19484
|
.map(([fmt, n]) => '<tr><td>' + escapeHtml(fmt) + '</td><td>' + n + '</td></tr>').join('');
|
|
19482
19485
|
const warnBlock = errorsList.length
|
|
19483
|
-
? '<div style="margin-top:10px;padding:10px;background
|
|
19486
|
+
? '<div style="margin-top:10px;padding:10px;background:rgba(245,158,11,0.10);border:1px solid rgba(245,158,11,0.32);border-radius:6px;color:var(--text-primary);font-size:13px">' +
|
|
19484
19487
|
'<div style="font-weight:600;margin-bottom:4px">' + errorsList.length + ' file(s) could not be ingested</div>' +
|
|
19485
19488
|
errorsList.map((e) => '<div style="font-family:monospace;font-size:12px">• ' + escapeHtml(e.error) + '</div>').join('') +
|
|
19486
19489
|
'</div>'
|
|
19487
19490
|
: '';
|
|
19488
19491
|
const emptyNote = (preview.length === 0 && !errorsList.length)
|
|
19489
|
-
? '<div style="margin-top:10px;padding:10px;background
|
|
19492
|
+
? '<div style="margin-top:10px;padding:10px;background:rgba(245,158,11,0.10);border:1px solid rgba(245,158,11,0.32);border-radius:6px;color:var(--text-primary);font-size:13px">No records extracted. The file may be empty or in an unsupported format.</div>'
|
|
19490
19493
|
: '';
|
|
19491
19494
|
manifestEl.innerHTML =
|
|
19492
19495
|
'<div class="card" style="padding:12px"><div style="font-weight:600;margin-bottom:8px">Manifest</div>' +
|
|
@@ -19501,7 +19504,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19501
19504
|
'<div style="font-weight:600">#' + (i + 1) + ' ' + escapeHtml(p.title || '(untitled)') + '</div>' +
|
|
19502
19505
|
'<div style="color:var(--muted);font-size:12px;margin:4px 0">' + escapeHtml(p.targetRelPath || '') + '</div>' +
|
|
19503
19506
|
'<div style="font-size:13px">' + escapeHtml((p.body || '').slice(0, 400)) + '</div>' +
|
|
19504
|
-
(p.tags && p.tags.length ? '<div style="margin-top:6px;font-size:12px;color
|
|
19507
|
+
(p.tags && p.tags.length ? '<div style="margin-top:6px;font-size:12px;color:var(--text-muted)">tags: ' + p.tags.map(escapeHtml).join(', ') + '</div>' : '') +
|
|
19505
19508
|
'</div>').join('');
|
|
19506
19509
|
previewEl.innerHTML =
|
|
19507
19510
|
'<div style="font-weight:600;margin-bottom:8px">Preview (first ' + Math.min(preview.length, 10) + ' records, dry-run)</div>' + previewHtml;
|
|
@@ -19549,11 +19552,11 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19549
19552
|
}
|
|
19550
19553
|
|
|
19551
19554
|
if (errorMsg) {
|
|
19552
|
-
progEl.innerHTML = '<div style="color
|
|
19555
|
+
progEl.innerHTML = '<div style="color:var(--red)">Error: ' + escapeHtml(errorMsg) + '</div>';
|
|
19553
19556
|
return;
|
|
19554
19557
|
}
|
|
19555
19558
|
if (!finalData) {
|
|
19556
|
-
progEl.innerHTML = '<div style="color
|
|
19559
|
+
progEl.innerHTML = '<div style="color:var(--red)">Ingestion ended without a result. Check dashboard logs.</div>';
|
|
19557
19560
|
return;
|
|
19558
19561
|
}
|
|
19559
19562
|
const elapsed = Math.floor((Date.now() - progress.startedAt) / 1000);
|
|
@@ -19564,7 +19567,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19564
19567
|
? 'Ingestion complete'
|
|
19565
19568
|
: 'Ingestion finished, but nothing was written';
|
|
19566
19569
|
const errBlock = errList.length
|
|
19567
|
-
? '<div style="margin-top:10px;padding:10px;background
|
|
19570
|
+
? '<div style="margin-top:10px;padding:10px;background:rgba(245,158,11,0.10);border:1px solid rgba(245,158,11,0.32);border-radius:6px;color:var(--text-primary);font-size:13px">' +
|
|
19568
19571
|
'<div style="font-weight:600;margin-bottom:4px">' + errList.length + ' error(s)</div>' +
|
|
19569
19572
|
errList.map((e) => '<div style="font-family:monospace;font-size:12px">• ' + escapeHtml(e.error) + '</div>').join('') +
|
|
19570
19573
|
'</div>'
|
|
@@ -19655,8 +19658,8 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19655
19658
|
}
|
|
19656
19659
|
el.innerHTML = data.integrations.map(function(i) {
|
|
19657
19660
|
const ok = i.connected && i.hasFeedReadyTools;
|
|
19658
|
-
const color = ok ? '
|
|
19659
|
-
const bg = ok ? '
|
|
19661
|
+
const color = ok ? 'var(--green)' : 'var(--text-primary)';
|
|
19662
|
+
const bg = ok ? 'rgba(34,197,94,0.10)' : 'rgba(245,158,11,0.10)';
|
|
19660
19663
|
const dot = ok ? '✓' : '⚠';
|
|
19661
19664
|
const source = i.kind === 'composio' ? 'Composio' : (i.kind === 'claude-desktop' ? 'Claude Desktop' : 'MCP');
|
|
19662
19665
|
const label = ok ? i.label + ' · ' + source : i.label + ' (incomplete in ' + source + ')';
|
|
@@ -19685,7 +19688,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19685
19688
|
: '<span style="color:var(--muted)">no fields</span>';
|
|
19686
19689
|
return '<div class="card" style="padding:10px 12px;margin-bottom:8px;display:flex;align-items:center;gap:12px">' +
|
|
19687
19690
|
'<div style="flex:1">' +
|
|
19688
|
-
'<div style="font-weight:600">' + escapeHtml(f.name) + (f.enabled ? '' : ' <span style="color
|
|
19691
|
+
'<div style="font-weight:600">' + escapeHtml(f.name) + (f.enabled ? '' : ' <span style="color:var(--red);font-weight:normal">(disabled)</span>') + '</div>' +
|
|
19689
19692
|
'<div style="font-size:12px;color:var(--muted)">' +
|
|
19690
19693
|
'Recipe: <code>' + escapeHtml(f.recipeId) + '</code> · Schedule: <code>' + escapeHtml(f.schedule) + '</code> · Target: <code>' + escapeHtml(f.targetFolder) + '</code>' +
|
|
19691
19694
|
'</div>' +
|
|
@@ -19732,10 +19735,10 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19732
19735
|
if (!brainFeedWizardState) return;
|
|
19733
19736
|
const s = brainFeedWizardState;
|
|
19734
19737
|
if (s.step === 0) {
|
|
19735
|
-
if (!s.pick) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19738
|
+
if (!s.pick) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Pick a connector.</span>'; return; }
|
|
19736
19739
|
s.step = 1;
|
|
19737
19740
|
} else if (s.step === 1) {
|
|
19738
|
-
if (!s.recipe) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19741
|
+
if (!s.recipe) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Pick a recipe.</span>'; return; }
|
|
19739
19742
|
s.values = {};
|
|
19740
19743
|
for (const f of (s.recipe.fields || [])) {
|
|
19741
19744
|
if (f.defaultValue) s.values[f.key] = f.defaultValue;
|
|
@@ -19752,11 +19755,11 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19752
19755
|
const inputs = document.querySelectorAll('#brain-feed-wizard-step [data-field]');
|
|
19753
19756
|
inputs.forEach(function(inp) { s.values[inp.dataset.field] = inp.value; });
|
|
19754
19757
|
const missing = (s.recipe.fields || []).filter(function(f) { return f.required && !(s.values[f.key] || '').trim(); });
|
|
19755
|
-
if (missing.length) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19758
|
+
if (missing.length) { document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Required: ' + missing.map(function(f) { return f.label; }).join(', ') + '</span>'; return; }
|
|
19756
19759
|
if (s.recipe && s.recipe.id === 'tool-backed-memory-seed') {
|
|
19757
19760
|
const toolName = String(s.values.toolName || '').trim();
|
|
19758
19761
|
if (!/^mcp__.+__.+$/.test(toolName)) {
|
|
19759
|
-
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19762
|
+
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Pick an exact tool before continuing.</span>';
|
|
19760
19763
|
return;
|
|
19761
19764
|
}
|
|
19762
19765
|
const rawVariables = String(s.values.variablesJson || '').trim();
|
|
@@ -19764,12 +19767,12 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19764
19767
|
try {
|
|
19765
19768
|
const parsedVariables = JSON.parse(rawVariables);
|
|
19766
19769
|
if (!parsedVariables || typeof parsedVariables !== 'object' || Array.isArray(parsedVariables)) {
|
|
19767
|
-
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19770
|
+
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Tool variables must be a JSON object, for example {}.</span>';
|
|
19768
19771
|
return;
|
|
19769
19772
|
}
|
|
19770
19773
|
} catch (err) {
|
|
19771
19774
|
void err;
|
|
19772
|
-
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
19775
|
+
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">Tool variables must be valid JSON, for example {}.</span>';
|
|
19773
19776
|
return;
|
|
19774
19777
|
}
|
|
19775
19778
|
}
|
|
@@ -19836,7 +19839,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19836
19839
|
// can still set the value manually.
|
|
19837
19840
|
container.innerHTML =
|
|
19838
19841
|
'<input type="text" value="' + escapeHtml(currentVal) + '" placeholder="(type a value)" style="width:100%" oninput="(document.querySelector(\\'input[type=hidden][data-field=' + field.key + ']\\')||{}).value=this.value">' +
|
|
19839
|
-
'<div style="color
|
|
19842
|
+
'<div style="color:var(--text-primary);font-size:11px;margin-top:4px">Nothing returned from the probe — type a value manually' + (data.rawPreview ? ' (probe output: ' + escapeHtml(data.rawPreview.slice(0, 120)) + '…)' : '') + '</div>';
|
|
19840
19843
|
return;
|
|
19841
19844
|
}
|
|
19842
19845
|
|
|
@@ -19862,7 +19865,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19862
19865
|
} catch (err) {
|
|
19863
19866
|
container.innerHTML =
|
|
19864
19867
|
'<input type="text" value="' + escapeHtml(values[field.key] || '') + '" placeholder="(probe failed — type manually)" style="width:100%" oninput="(document.querySelector(\\'input[type=hidden][data-field=' + field.key + ']\\')||{}).value=this.value">' +
|
|
19865
|
-
'<div style="color
|
|
19868
|
+
'<div style="color:var(--red);font-size:11px;margin-top:4px">Picker failed: ' + escapeHtml(String(err && err.message ? err.message : err)) + ' — type a value manually.</div>';
|
|
19866
19869
|
}
|
|
19867
19870
|
}
|
|
19868
19871
|
|
|
@@ -19888,7 +19891,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19888
19891
|
const rawHint = trimmed && !isEmptyArray
|
|
19889
19892
|
? ' <span style="color:var(--muted)">Tool said: ' + escapeHtml(trimmed.slice(0, 140)) + '</span>'
|
|
19890
19893
|
: '';
|
|
19891
|
-
resultsEl.innerHTML = '<div style="color
|
|
19894
|
+
resultsEl.innerHTML = '<div style="color:var(--text-primary);font-size:12px;padding:6px">No matches for "' + escapeHtml(query) + '". The tool may be limited by macOS permissions or not support this query — use <a href="#" onclick="brainFieldPickerToggleCustom(\\'' + field.key + '\\', \\'\\');return false">type a value directly</a>.' + rawHint + '</div>';
|
|
19892
19895
|
return;
|
|
19893
19896
|
}
|
|
19894
19897
|
resultsEl.innerHTML = items.map(function(it) {
|
|
@@ -19897,7 +19900,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19897
19900
|
}).join('') +
|
|
19898
19901
|
'<div style="font-size:11px;color:var(--muted);margin-top:4px">' + items.length + ' result' + (items.length === 1 ? '' : 's') + (data.cached ? ' (cached)' : '') + '</div>';
|
|
19899
19902
|
} catch (err) {
|
|
19900
|
-
resultsEl.innerHTML = '<div style="color
|
|
19903
|
+
resultsEl.innerHTML = '<div style="color:var(--red);font-size:12px;padding:6px">' + escapeHtml(String(err && err.message ? err.message : err)) + '</div>';
|
|
19901
19904
|
}
|
|
19902
19905
|
}
|
|
19903
19906
|
|
|
@@ -19963,7 +19966,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
19963
19966
|
let html = '';
|
|
19964
19967
|
if (s.step === 0) {
|
|
19965
19968
|
if (!s.connected.length) {
|
|
19966
|
-
html = '<div style="color
|
|
19969
|
+
html = '<div style="color:var(--text-primary)">No connectors have feed-ready tools. Connect Composio or open Claude Desktop → Connectors and sign into Google Drive, Outlook, Gmail, or Slack first.</div>';
|
|
19967
19970
|
} else {
|
|
19968
19971
|
html = '<div style="display:grid;grid-template-columns:repeat(auto-fill,minmax(180px,1fr));gap:8px">' +
|
|
19969
19972
|
s.connected.map(function(i) {
|
|
@@ -20026,7 +20029,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20026
20029
|
} else {
|
|
20027
20030
|
control = '<input type="text" data-field="' + f.key + '" value="' + escapeHtml(val) + '" placeholder="' + escapeHtml(f.placeholder || '') + '" style="width:100%">';
|
|
20028
20031
|
}
|
|
20029
|
-
return '<label style="font-weight:500;padding-top:6px">' + escapeHtml(f.label) + (f.required ? ' <span style="color
|
|
20032
|
+
return '<label style="font-weight:500;padding-top:6px">' + escapeHtml(f.label) + (f.required ? ' <span style="color:var(--red)">*</span>' : '') + '</label>' +
|
|
20030
20033
|
'<div>' + control + help + '</div>';
|
|
20031
20034
|
}).join('') + '</div>';
|
|
20032
20035
|
|
|
@@ -20091,13 +20094,13 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20091
20094
|
});
|
|
20092
20095
|
const data = await resp.json();
|
|
20093
20096
|
if (!resp.ok) {
|
|
20094
|
-
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
20097
|
+
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">' + escapeHtml(data.error || 'save failed') + '</span>';
|
|
20095
20098
|
return;
|
|
20096
20099
|
}
|
|
20097
20100
|
brainCloseFeedWizard();
|
|
20098
20101
|
brainLoadFeeds();
|
|
20099
20102
|
} catch (err) {
|
|
20100
|
-
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color
|
|
20103
|
+
document.getElementById('brain-feed-wizard-status').innerHTML = '<span style="color:var(--red)">' + escapeHtml(String(err)) + '</span>';
|
|
20101
20104
|
}
|
|
20102
20105
|
}
|
|
20103
20106
|
|
|
@@ -20210,8 +20213,8 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20210
20213
|
const slug = document.getElementById('brain-webhook-slug').value.trim();
|
|
20211
20214
|
const folder = document.getElementById('brain-webhook-folder').value.trim() || ('04-Ingest/' + slug);
|
|
20212
20215
|
const statusEl = document.getElementById('brain-webhook-status');
|
|
20213
|
-
if (!slug) { statusEl.innerHTML = '<span style="color
|
|
20214
|
-
if (!/^[a-z][a-z0-9_-]*$/.test(slug)) { statusEl.innerHTML = '<span style="color
|
|
20216
|
+
if (!slug) { statusEl.innerHTML = '<span style="color:var(--red)">slug required</span>'; return; }
|
|
20217
|
+
if (!/^[a-z][a-z0-9_-]*$/.test(slug)) { statusEl.innerHTML = '<span style="color:var(--red)">slug must be lowercase alphanumeric</span>'; return; }
|
|
20215
20218
|
|
|
20216
20219
|
// 1) Generate a random 32-byte secret and save it under ref "webhook_<slug>"
|
|
20217
20220
|
const bytes = new Uint8Array(32);
|
|
@@ -20226,7 +20229,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20226
20229
|
});
|
|
20227
20230
|
if (!credResp.ok) {
|
|
20228
20231
|
const e = await credResp.json();
|
|
20229
|
-
statusEl.innerHTML = '<span style="color
|
|
20232
|
+
statusEl.innerHTML = '<span style="color:var(--red)">Secret save failed: ' + escapeHtml(e.error || 'unknown') + '</span>';
|
|
20230
20233
|
return;
|
|
20231
20234
|
}
|
|
20232
20235
|
|
|
@@ -20246,7 +20249,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20246
20249
|
});
|
|
20247
20250
|
if (!resp.ok) {
|
|
20248
20251
|
const e = await resp.json();
|
|
20249
|
-
statusEl.innerHTML = '<span style="color
|
|
20252
|
+
statusEl.innerHTML = '<span style="color:var(--red)">' + escapeHtml(e.error || 'save failed') + '</span>';
|
|
20250
20253
|
return;
|
|
20251
20254
|
}
|
|
20252
20255
|
|
|
@@ -20271,7 +20274,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20271
20274
|
const cronExpr = document.getElementById('brain-poll-cron').value.trim();
|
|
20272
20275
|
const folder = document.getElementById('brain-poll-folder').value.trim();
|
|
20273
20276
|
const statusEl = document.getElementById('brain-poll-status');
|
|
20274
|
-
if (!slug || !url) { statusEl.innerHTML = '<span style="color
|
|
20277
|
+
if (!slug || !url) { statusEl.innerHTML = '<span style="color:var(--red)">slug and URL required</span>'; return; }
|
|
20275
20278
|
|
|
20276
20279
|
const headers = brainCollectKv('headers');
|
|
20277
20280
|
const params = brainCollectKv('params');
|
|
@@ -20293,7 +20296,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20293
20296
|
}),
|
|
20294
20297
|
});
|
|
20295
20298
|
const data = await resp.json();
|
|
20296
|
-
if (!resp.ok) { statusEl.innerHTML = '<span style="color
|
|
20299
|
+
if (!resp.ok) { statusEl.innerHTML = '<span style="color:var(--red)">' + escapeHtml(data.error || 'save failed') + '</span>'; return; }
|
|
20297
20300
|
statusEl.innerHTML = '<span style="color:#4ade80">✓ Saved</span>';
|
|
20298
20301
|
|
|
20299
20302
|
if (runNow) {
|
|
@@ -20302,7 +20305,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20302
20305
|
const runData = await runResp.json();
|
|
20303
20306
|
statusEl.innerHTML = runResp.ok
|
|
20304
20307
|
? '<span style="color:#4ade80">✓ Saved + run: in=' + runData.recordsIn + ' written=' + runData.recordsWritten + '</span>'
|
|
20305
|
-
: '<span style="color
|
|
20308
|
+
: '<span style="color:var(--red)">Saved but run failed: ' + escapeHtml(runData.error || 'unknown') + '</span>';
|
|
20306
20309
|
}
|
|
20307
20310
|
brainLoadSources();
|
|
20308
20311
|
}
|
|
@@ -20323,7 +20326,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20323
20326
|
const ref = document.getElementById('brain-cred-ref').value.trim();
|
|
20324
20327
|
const value = document.getElementById('brain-cred-val').value;
|
|
20325
20328
|
const statusEl = document.getElementById('brain-cred-status');
|
|
20326
|
-
if (!ref || !value) { statusEl.innerHTML = '<span style="color
|
|
20329
|
+
if (!ref || !value) { statusEl.innerHTML = '<span style="color:var(--red)">ref and value required</span>'; return; }
|
|
20327
20330
|
const resp = await apiFetch('/api/brain/credentials', {
|
|
20328
20331
|
method: 'POST', headers: { 'content-type': 'application/json' },
|
|
20329
20332
|
body: JSON.stringify({ ref, value }),
|
|
@@ -20331,7 +20334,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20331
20334
|
const data = await resp.json();
|
|
20332
20335
|
statusEl.innerHTML = resp.ok
|
|
20333
20336
|
? '<span style="color:#4ade80">✓ Saved ' + escapeHtml(ref) + '</span>'
|
|
20334
|
-
: '<span style="color
|
|
20337
|
+
: '<span style="color:var(--red)">' + escapeHtml(data.error || 'save failed') + '</span>';
|
|
20335
20338
|
document.getElementById('brain-cred-ref').value = '';
|
|
20336
20339
|
document.getElementById('brain-cred-val').value = '';
|
|
20337
20340
|
brainShowCredsForm(); // refresh list
|
|
@@ -20425,7 +20428,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20425
20428
|
});
|
|
20426
20429
|
const data = await resp.json();
|
|
20427
20430
|
if (!resp.ok) {
|
|
20428
|
-
statusEl.innerHTML = '<span style="color
|
|
20431
|
+
statusEl.innerHTML = '<span style="color:var(--red)">Upload failed: ' + escapeHtml(data.error || 'unknown') + '</span>';
|
|
20429
20432
|
return;
|
|
20430
20433
|
}
|
|
20431
20434
|
statusEl.innerHTML = '<span style="color:#4ade80">✓ Uploaded ' + data.count + ' file(s)</span>';
|
|
@@ -20433,7 +20436,7 @@ if('serviceWorker' in navigator){navigator.serviceWorker.getRegistrations().then
|
|
|
20433
20436
|
// Kick off preview immediately
|
|
20434
20437
|
await brainPreviewSeed();
|
|
20435
20438
|
} catch (err) {
|
|
20436
|
-
statusEl.innerHTML = '<span style="color
|
|
20439
|
+
statusEl.innerHTML = '<span style="color:var(--red)">Upload error: ' + escapeHtml(String(err)) + '</span>';
|
|
20437
20440
|
}
|
|
20438
20441
|
}
|
|
20439
20442
|
|
|
@@ -79,6 +79,87 @@ function normalizeStringArray(v) {
|
|
|
79
79
|
const out = Array.from(new Set(v.map(x => typeof x === 'string' ? x.trim() : '').filter(Boolean)));
|
|
80
80
|
return out.length > 0 ? out : undefined;
|
|
81
81
|
}
|
|
82
|
+
/**
|
|
83
|
+
* Single-source-of-truth YAML → CronJobDefinition parser.
|
|
84
|
+
*
|
|
85
|
+
* Used by parseCronJobs (global CRON.md) AND parseAgentCronJobs (per-agent).
|
|
86
|
+
* Caller is responsible for handling the name prefix (agent jobs are
|
|
87
|
+
* prefixed with `<slug>:`) and for tagging agentSlug.
|
|
88
|
+
*
|
|
89
|
+
* Returns null when the row is malformed (missing name/schedule/prompt) —
|
|
90
|
+
* the caller logs + skips. Accepts both snake_case and camelCase YAML keys
|
|
91
|
+
* since users hand-edit CRON.md and we want to be forgiving.
|
|
92
|
+
*
|
|
93
|
+
* Centralizing here closes the drift the previous audit flagged: the agent
|
|
94
|
+
* variant was missing alwaysDeliver/attachments/requiresConfirmation/
|
|
95
|
+
* confirmationTimeoutMin and the description field. After this refactor
|
|
96
|
+
* both surfaces have the same field set.
|
|
97
|
+
*/
|
|
98
|
+
function parseJobYaml(job) {
|
|
99
|
+
const name = String(job.name ?? '');
|
|
100
|
+
const schedule = String(job.schedule ?? '');
|
|
101
|
+
const prompt = String(job.prompt ?? '');
|
|
102
|
+
if (!name || !schedule || !prompt)
|
|
103
|
+
return null;
|
|
104
|
+
const enabled = job.enabled !== false;
|
|
105
|
+
const tier = Number(job.tier ?? 1);
|
|
106
|
+
const maxTurns = job.max_turns != null ? Number(job.max_turns) : undefined;
|
|
107
|
+
const model = job.model != null ? String(job.model) : undefined;
|
|
108
|
+
const workDir = job.work_dir != null ? String(job.work_dir) : undefined;
|
|
109
|
+
const mode = job.mode === 'unleashed' ? 'unleashed' : 'standard';
|
|
110
|
+
const maxHours = job.max_hours != null ? Number(job.max_hours) : undefined;
|
|
111
|
+
const maxRetries = job.max_retries != null ? Number(job.max_retries) : undefined;
|
|
112
|
+
const after = job.after != null ? String(job.after) : undefined;
|
|
113
|
+
const successCriteria = Array.isArray(job.success_criteria)
|
|
114
|
+
? job.success_criteria.map(c => String(c))
|
|
115
|
+
: undefined;
|
|
116
|
+
// Prefer free-form successCriteriaText; fall back to joining the legacy
|
|
117
|
+
// string[] so legacy YAML keeps rendering. Writes go to the new field.
|
|
118
|
+
let successCriteriaText = typeof job.success_criteria_text === 'string'
|
|
119
|
+
? String(job.success_criteria_text)
|
|
120
|
+
: (typeof job.successCriteriaText === 'string' ? String(job.successCriteriaText) : undefined);
|
|
121
|
+
if (!successCriteriaText && Array.isArray(successCriteria) && successCriteria.length > 0) {
|
|
122
|
+
successCriteriaText = successCriteria.join('\n');
|
|
123
|
+
}
|
|
124
|
+
const successSchemaRaw = job.success_schema ?? job.successSchema;
|
|
125
|
+
const successSchema = (successSchemaRaw && typeof successSchemaRaw === 'object' && !Array.isArray(successSchemaRaw))
|
|
126
|
+
? successSchemaRaw
|
|
127
|
+
: undefined;
|
|
128
|
+
const addDirs = normalizeStringArray(job.add_dirs ?? job.addDirs);
|
|
129
|
+
const alwaysDeliver = job.always_deliver === true ? true : undefined;
|
|
130
|
+
const context = job.context != null ? String(job.context) : undefined;
|
|
131
|
+
const preCheck = job.pre_check != null ? String(job.pre_check) : undefined;
|
|
132
|
+
const attachments = normalizeStringArray(job.attachments);
|
|
133
|
+
const requiresConfirmation = job.requires_confirmation === true || job.requiresConfirmation === true ? true : undefined;
|
|
134
|
+
const confirmationTimeoutMin = job.confirmation_timeout_min != null
|
|
135
|
+
? Number(job.confirmation_timeout_min)
|
|
136
|
+
: (job.confirmationTimeoutMin != null ? Number(job.confirmationTimeoutMin) : undefined);
|
|
137
|
+
// Per-job agent scoping (a global cron can be scoped to a specific
|
|
138
|
+
// agent's profile). Accept both casings.
|
|
139
|
+
const agentSlugRaw = job.agentSlug ?? job.agent_slug;
|
|
140
|
+
const agentSlug = typeof agentSlugRaw === 'string' && /^[a-z0-9-]+$/i.test(agentSlugRaw)
|
|
141
|
+
? agentSlugRaw
|
|
142
|
+
: undefined;
|
|
143
|
+
const skills = normalizeStringArray(job.skills);
|
|
144
|
+
const allowedTools = normalizeStringArray(job.allowed_tools ?? job.allowedTools);
|
|
145
|
+
const allowedMcpServers = normalizeStringArray(job.allowed_mcp_servers ?? job.allowedMcpServers);
|
|
146
|
+
const tags = normalizeStringArray(job.tags);
|
|
147
|
+
const categoryRaw = job.category;
|
|
148
|
+
const category = typeof categoryRaw === 'string' && categoryRaw.trim()
|
|
149
|
+
? categoryRaw.trim().slice(0, 64)
|
|
150
|
+
: undefined;
|
|
151
|
+
const predictable = typeof job.predictable === 'boolean' ? job.predictable : undefined;
|
|
152
|
+
const description = typeof job.description === 'string' && job.description.trim()
|
|
153
|
+
? job.description.trim().slice(0, 500)
|
|
154
|
+
: undefined;
|
|
155
|
+
return {
|
|
156
|
+
name, schedule, prompt, enabled, tier, description, maxTurns, model, workDir, mode,
|
|
157
|
+
maxHours, maxRetries, after, successCriteria, successCriteriaText, successSchema, addDirs,
|
|
158
|
+
alwaysDeliver, context, preCheck, attachments, requiresConfirmation, confirmationTimeoutMin,
|
|
159
|
+
agentSlug,
|
|
160
|
+
skills, allowedTools, allowedMcpServers, tags, category, predictable,
|
|
161
|
+
};
|
|
162
|
+
}
|
|
82
163
|
/**
|
|
83
164
|
* Parse cron job definitions from vault/00-System/CRON.md frontmatter.
|
|
84
165
|
* Used by both the in-process CronScheduler and the standalone CLI runner.
|
|
@@ -86,10 +167,9 @@ function normalizeStringArray(v) {
|
|
|
86
167
|
export function parseCronJobs() {
|
|
87
168
|
if (!existsSync(CRON_FILE))
|
|
88
169
|
return [];
|
|
89
|
-
const raw = readFileSync(CRON_FILE, 'utf-8');
|
|
90
170
|
let parsed;
|
|
91
171
|
try {
|
|
92
|
-
parsed = matter(
|
|
172
|
+
parsed = matter(readFileSync(CRON_FILE, 'utf-8'));
|
|
93
173
|
}
|
|
94
174
|
catch (err) {
|
|
95
175
|
logger.error({ err }, 'CRON.md YAML parse error — keeping previous jobs. Fix the file manually.');
|
|
@@ -98,76 +178,11 @@ export function parseCronJobs() {
|
|
|
98
178
|
const jobDefs = (parsed.data.jobs ?? []);
|
|
99
179
|
const jobs = [];
|
|
100
180
|
for (const job of jobDefs) {
|
|
101
|
-
const
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
const tier = Number(job.tier ?? 1);
|
|
106
|
-
const maxTurns = job.max_turns != null ? Number(job.max_turns) : undefined;
|
|
107
|
-
const model = job.model != null ? String(job.model) : undefined;
|
|
108
|
-
const workDir = job.work_dir != null ? String(job.work_dir) : undefined;
|
|
109
|
-
const mode = job.mode === 'unleashed' ? 'unleashed' : 'standard';
|
|
110
|
-
const maxHours = job.max_hours != null ? Number(job.max_hours) : undefined;
|
|
111
|
-
const maxRetries = job.max_retries != null ? Number(job.max_retries) : undefined;
|
|
112
|
-
const after = job.after != null ? String(job.after) : undefined;
|
|
113
|
-
const successCriteria = Array.isArray(job.success_criteria)
|
|
114
|
-
? job.success_criteria.map(c => String(c))
|
|
115
|
-
: undefined;
|
|
116
|
-
// PRD Phase 1: prefer success_criteria_text (free-form). On read, fall
|
|
117
|
-
// back to joining the legacy success_criteria string[] so legacy YAML
|
|
118
|
-
// keeps rendering in the new editor surface. Writes go to the new field.
|
|
119
|
-
let successCriteriaText = typeof job.success_criteria_text === 'string'
|
|
120
|
-
? String(job.success_criteria_text)
|
|
121
|
-
: (typeof job.successCriteriaText === 'string' ? String(job.successCriteriaText) : undefined);
|
|
122
|
-
if (!successCriteriaText && Array.isArray(successCriteria) && successCriteria.length > 0) {
|
|
123
|
-
successCriteriaText = successCriteria.join('\n');
|
|
124
|
-
}
|
|
125
|
-
// PRD Phase 1: JSON Schema validated against ResultMessage.structured_output.
|
|
126
|
-
// Accept either snake_case (success_schema) or camelCase from API. Stored
|
|
127
|
-
// as a plain object; ajv is loaded lazily at validation time.
|
|
128
|
-
const successSchemaRaw = job.success_schema ?? job.successSchema;
|
|
129
|
-
const successSchema = (successSchemaRaw && typeof successSchemaRaw === 'object' && !Array.isArray(successSchemaRaw))
|
|
130
|
-
? successSchemaRaw
|
|
131
|
-
: undefined;
|
|
132
|
-
// PRD Phase 1: read scope beyond cwd. Accept either casing.
|
|
133
|
-
const addDirs = normalizeStringArray(job.add_dirs ?? job.addDirs);
|
|
134
|
-
const alwaysDeliver = job.always_deliver === true ? true : undefined;
|
|
135
|
-
const context = job.context != null ? String(job.context) : undefined;
|
|
136
|
-
const preCheck = job.pre_check != null ? String(job.pre_check) : undefined;
|
|
137
|
-
// Optional: scope a global job to a specific agent's profile (loads
|
|
138
|
-
// the agent's allowedTools whitelist, system prompt, etc.). Accept
|
|
139
|
-
// both camelCase and snake_case to be forgiving of user-written YAML.
|
|
140
|
-
const agentSlugRaw = job.agentSlug ?? job.agent_slug;
|
|
141
|
-
const agentSlug = typeof agentSlugRaw === 'string' && /^[a-z0-9-]+$/i.test(agentSlugRaw)
|
|
142
|
-
? agentSlugRaw
|
|
143
|
-
: undefined;
|
|
144
|
-
// ── Trick capabilities — accept both camelCase and snake_case ─────
|
|
145
|
-
const skills = normalizeStringArray(job.skills);
|
|
146
|
-
const allowedTools = normalizeStringArray(job.allowed_tools ?? job.allowedTools);
|
|
147
|
-
const allowedMcpServers = normalizeStringArray(job.allowed_mcp_servers ?? job.allowedMcpServers);
|
|
148
|
-
const tags = normalizeStringArray(job.tags);
|
|
149
|
-
const categoryRaw = job.category;
|
|
150
|
-
const category = typeof categoryRaw === 'string' && categoryRaw.trim()
|
|
151
|
-
? categoryRaw.trim().slice(0, 64)
|
|
152
|
-
: undefined;
|
|
153
|
-
// Predictable (contract) mode — undefined means legacy behavior.
|
|
154
|
-
const predictable = typeof job.predictable === 'boolean' ? job.predictable : undefined;
|
|
155
|
-
// 1.18.119 — human-readable description (used by the task card preview
|
|
156
|
-
// and by the cron-clean migrator to surface what each job does without
|
|
157
|
-
// showing raw prompt boilerplate).
|
|
158
|
-
const description = typeof job.description === 'string' && job.description.trim()
|
|
159
|
-
? job.description.trim().slice(0, 500)
|
|
160
|
-
: undefined;
|
|
161
|
-
if (!name || !schedule || !prompt) {
|
|
181
|
+
const def = parseJobYaml(job);
|
|
182
|
+
if (def)
|
|
183
|
+
jobs.push(def);
|
|
184
|
+
else
|
|
162
185
|
logger.warn({ job }, 'Skipping malformed cron job');
|
|
163
|
-
continue;
|
|
164
|
-
}
|
|
165
|
-
jobs.push({
|
|
166
|
-
name, schedule, prompt, enabled, tier, description, maxTurns, model, workDir, mode,
|
|
167
|
-
maxHours, maxRetries, after, successCriteria, successCriteriaText, successSchema, addDirs,
|
|
168
|
-
alwaysDeliver, context, preCheck, agentSlug,
|
|
169
|
-
skills, allowedTools, allowedMcpServers, tags, category, predictable,
|
|
170
|
-
});
|
|
171
186
|
}
|
|
172
187
|
return jobs;
|
|
173
188
|
}
|
|
@@ -193,72 +208,20 @@ export function parseAgentCronJobs(agentsDir) {
|
|
|
193
208
|
if (!existsSync(cronFile))
|
|
194
209
|
continue;
|
|
195
210
|
try {
|
|
196
|
-
const
|
|
197
|
-
const parsed = matter(raw);
|
|
211
|
+
const parsed = matter(readFileSync(cronFile, 'utf-8'));
|
|
198
212
|
const jobDefs = (parsed.data.jobs ?? []);
|
|
199
213
|
for (const job of jobDefs) {
|
|
200
|
-
const
|
|
201
|
-
|
|
202
|
-
const prompt = String(job.prompt ?? '');
|
|
203
|
-
const enabled = job.enabled !== false;
|
|
204
|
-
const tier = Number(job.tier ?? 1);
|
|
205
|
-
const maxTurns = job.max_turns != null ? Number(job.max_turns) : undefined;
|
|
206
|
-
const model = job.model != null ? String(job.model) : undefined;
|
|
207
|
-
const workDir = job.work_dir != null ? String(job.work_dir) : undefined;
|
|
208
|
-
const mode = job.mode === 'unleashed' ? 'unleashed' : 'standard';
|
|
209
|
-
const maxHours = job.max_hours != null ? Number(job.max_hours) : undefined;
|
|
210
|
-
const maxRetries = job.max_retries != null ? Number(job.max_retries) : undefined;
|
|
211
|
-
const after = job.after != null ? String(job.after) : undefined;
|
|
212
|
-
const successCriteria = Array.isArray(job.success_criteria)
|
|
213
|
-
? job.success_criteria.map(c => String(c))
|
|
214
|
-
: undefined;
|
|
215
|
-
// PRD Phase 1 fields — symmetric with global parser above.
|
|
216
|
-
let successCriteriaText = typeof job.success_criteria_text === 'string'
|
|
217
|
-
? String(job.success_criteria_text)
|
|
218
|
-
: (typeof job.successCriteriaText === 'string' ? String(job.successCriteriaText) : undefined);
|
|
219
|
-
if (!successCriteriaText && Array.isArray(successCriteria) && successCriteria.length > 0) {
|
|
220
|
-
successCriteriaText = successCriteria.join('\n');
|
|
221
|
-
}
|
|
222
|
-
const successSchemaRaw = job.success_schema ?? job.successSchema;
|
|
223
|
-
const successSchema = (successSchemaRaw && typeof successSchemaRaw === 'object' && !Array.isArray(successSchemaRaw))
|
|
224
|
-
? successSchemaRaw
|
|
225
|
-
: undefined;
|
|
226
|
-
const addDirs = normalizeStringArray(job.add_dirs ?? job.addDirs);
|
|
227
|
-
const context = job.context != null ? String(job.context) : undefined;
|
|
228
|
-
const preCheck = job.pre_check != null ? String(job.pre_check) : undefined;
|
|
229
|
-
// ── Trick capabilities — symmetric with global parser ─────────
|
|
230
|
-
// (NB: this parser still lacks alwaysDeliver/attachments/
|
|
231
|
-
// requiresConfirmation/confirmationTimeoutMin from the global
|
|
232
|
-
// parser — pre-existing drift, fix in a separate change.)
|
|
233
|
-
const skills = normalizeStringArray(job.skills);
|
|
234
|
-
const allowedTools = normalizeStringArray(job.allowed_tools ?? job.allowedTools);
|
|
235
|
-
const allowedMcpServers = normalizeStringArray(job.allowed_mcp_servers ?? job.allowedMcpServers);
|
|
236
|
-
const tags = normalizeStringArray(job.tags);
|
|
237
|
-
const categoryRaw = job.category;
|
|
238
|
-
const category = typeof categoryRaw === 'string' && categoryRaw.trim()
|
|
239
|
-
? categoryRaw.trim().slice(0, 64)
|
|
240
|
-
: undefined;
|
|
241
|
-
const predictable = typeof job.predictable === 'boolean' ? job.predictable : undefined;
|
|
242
|
-
// 1.18.119 — symmetric with the global parseCronJobs description
|
|
243
|
-
// field. Without this, agent jobs always look "missing description"
|
|
244
|
-
// to the cron-clean migrator and stay flagged as eligible forever.
|
|
245
|
-
const description = typeof job.description === 'string' && job.description.trim()
|
|
246
|
-
? job.description.trim().slice(0, 500)
|
|
247
|
-
: undefined;
|
|
248
|
-
if (!name || !schedule || !prompt) {
|
|
214
|
+
const def = parseJobYaml(job);
|
|
215
|
+
if (!def) {
|
|
249
216
|
logger.warn({ job, agent: slug }, 'Skipping malformed agent cron job');
|
|
250
217
|
continue;
|
|
251
218
|
}
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
context, preCheck,
|
|
259
|
-
agentSlug: slug,
|
|
260
|
-
skills, allowedTools, allowedMcpServers, tags, category, predictable,
|
|
261
|
-
});
|
|
219
|
+
// Agent CRON.md stores BARE job names; we prefix with the slug at
|
|
220
|
+
// read time so the runtime can route by `<slug>:<job>` and the
|
|
221
|
+
// dashboard can disambiguate same-named jobs across agents.
|
|
222
|
+
// agentSlug is stamped from the folder location, overriding any
|
|
223
|
+
// value in the YAML — single source of truth.
|
|
224
|
+
allJobs.push({ ...def, name: `${slug}:${def.name}`, agentSlug: slug });
|
|
262
225
|
}
|
|
263
226
|
}
|
|
264
227
|
catch (err) {
|
package/dist/types.d.ts
CHANGED
|
@@ -767,23 +767,21 @@ export interface Feedback {
|
|
|
767
767
|
comment?: string;
|
|
768
768
|
createdAt?: string;
|
|
769
769
|
}
|
|
770
|
-
export interface BehavioralCorrection {
|
|
771
|
-
correction: string;
|
|
772
|
-
category: 'verbosity' | 'tone' | 'workflow' | 'format' | 'accuracy' | 'proactivity' | 'scope';
|
|
773
|
-
strength: 'explicit' | 'implicit';
|
|
774
|
-
}
|
|
775
|
-
export interface PreferenceLearned {
|
|
776
|
-
preference: string;
|
|
777
|
-
confidence: 'high' | 'medium' | 'low';
|
|
778
|
-
}
|
|
779
770
|
export interface SessionReflection {
|
|
780
771
|
id?: number;
|
|
781
772
|
sessionKey: string;
|
|
782
773
|
exchangeCount: number;
|
|
783
774
|
frictionSignals: string[];
|
|
784
775
|
qualityScore: number;
|
|
785
|
-
behavioralCorrections:
|
|
786
|
-
|
|
776
|
+
behavioralCorrections: Array<{
|
|
777
|
+
correction: string;
|
|
778
|
+
category: 'verbosity' | 'tone' | 'workflow' | 'format' | 'accuracy' | 'proactivity' | 'scope';
|
|
779
|
+
strength: 'explicit' | 'implicit';
|
|
780
|
+
}>;
|
|
781
|
+
preferencesLearned: Array<{
|
|
782
|
+
preference: string;
|
|
783
|
+
confidence: 'high' | 'medium' | 'low';
|
|
784
|
+
}>;
|
|
787
785
|
agentSlug?: string;
|
|
788
786
|
createdAt?: string;
|
|
789
787
|
}
|
|
@@ -835,13 +833,6 @@ export interface ExecutionPlan {
|
|
|
835
833
|
steps: PlanStep[];
|
|
836
834
|
synthesisPrompt: string;
|
|
837
835
|
}
|
|
838
|
-
export interface PlanProgressUpdate {
|
|
839
|
-
stepId: string;
|
|
840
|
-
status: 'waiting' | 'running' | 'done' | 'failed';
|
|
841
|
-
description: string;
|
|
842
|
-
durationMs?: number;
|
|
843
|
-
resultPreview?: string;
|
|
844
|
-
}
|
|
845
836
|
export interface WorkflowInput {
|
|
846
837
|
type: 'string' | 'number';
|
|
847
838
|
default?: string;
|
|
@@ -853,13 +844,6 @@ export interface WorkflowStepMcpConfig {
|
|
|
853
844
|
tool: string;
|
|
854
845
|
inputs?: Record<string, unknown>;
|
|
855
846
|
}
|
|
856
|
-
export interface WorkflowStepCliConfig {
|
|
857
|
-
cmd: string;
|
|
858
|
-
args?: string[];
|
|
859
|
-
workDir?: string;
|
|
860
|
-
timeoutMs?: number;
|
|
861
|
-
captureStderr?: boolean;
|
|
862
|
-
}
|
|
863
847
|
export interface WorkflowStepChannelConfig {
|
|
864
848
|
channel: 'discord' | 'slack' | 'telegram' | 'whatsapp' | 'email' | 'webhook';
|
|
865
849
|
target: string;
|
|
@@ -891,7 +875,16 @@ export interface WorkflowStep {
|
|
|
891
875
|
workDir?: string;
|
|
892
876
|
kind?: WorkflowStepKind;
|
|
893
877
|
mcp?: WorkflowStepMcpConfig;
|
|
894
|
-
|
|
878
|
+
/** CLI step config — inline shape (was the standalone WorkflowStepCliConfig
|
|
879
|
+
* type that had zero external references; the field stays but the
|
|
880
|
+
* named alias was dropped in 1.18.122). */
|
|
881
|
+
cli?: {
|
|
882
|
+
cmd: string;
|
|
883
|
+
args?: string[];
|
|
884
|
+
workDir?: string;
|
|
885
|
+
timeoutMs?: number;
|
|
886
|
+
captureStderr?: boolean;
|
|
887
|
+
};
|
|
895
888
|
channel?: WorkflowStepChannelConfig;
|
|
896
889
|
transform?: WorkflowStepTransformConfig;
|
|
897
890
|
conditional?: WorkflowStepConditionalConfig;
|
|
@@ -1180,14 +1173,6 @@ export interface SessionRecord {
|
|
|
1180
1173
|
lastUsedAt: number;
|
|
1181
1174
|
userAgent?: string;
|
|
1182
1175
|
}
|
|
1183
|
-
export interface ConfigRevision {
|
|
1184
|
-
id?: number;
|
|
1185
|
-
agentSlug: string;
|
|
1186
|
-
fileName: string;
|
|
1187
|
-
content: string;
|
|
1188
|
-
changedBy?: string;
|
|
1189
|
-
createdAt?: string;
|
|
1190
|
-
}
|
|
1191
1176
|
export interface Lead {
|
|
1192
1177
|
id?: number;
|
|
1193
1178
|
agentSlug: string;
|
|
@@ -1202,16 +1187,6 @@ export interface Lead {
|
|
|
1202
1187
|
createdAt?: string;
|
|
1203
1188
|
updatedAt?: string;
|
|
1204
1189
|
}
|
|
1205
|
-
export interface SequenceEnrollment {
|
|
1206
|
-
id?: number;
|
|
1207
|
-
leadId: number;
|
|
1208
|
-
sequenceName: string;
|
|
1209
|
-
currentStep: number;
|
|
1210
|
-
status: 'active' | 'paused' | 'replied' | 'completed' | 'opted_out';
|
|
1211
|
-
nextStepDueAt?: string;
|
|
1212
|
-
startedAt?: string;
|
|
1213
|
-
updatedAt?: string;
|
|
1214
|
-
}
|
|
1215
1190
|
export interface Activity {
|
|
1216
1191
|
id?: number;
|
|
1217
1192
|
leadId?: number;
|
|
@@ -1222,40 +1197,6 @@ export interface Activity {
|
|
|
1222
1197
|
templateUsed?: string;
|
|
1223
1198
|
performedAt?: string;
|
|
1224
1199
|
}
|
|
1225
|
-
export interface SuppressionEntry {
|
|
1226
|
-
id?: number;
|
|
1227
|
-
email: string;
|
|
1228
|
-
reason: 'unsubscribe' | 'bounce' | 'manual' | 'complaint';
|
|
1229
|
-
addedAt?: string;
|
|
1230
|
-
addedBy?: string;
|
|
1231
|
-
}
|
|
1232
|
-
export interface ApprovalRequest {
|
|
1233
|
-
id?: number;
|
|
1234
|
-
agentSlug: string;
|
|
1235
|
-
actionType: 'email_send' | 'sequence_start' | 'escalation';
|
|
1236
|
-
summary: string;
|
|
1237
|
-
detail?: Record<string, unknown>;
|
|
1238
|
-
status: 'pending' | 'approved' | 'rejected';
|
|
1239
|
-
requestedAt?: string;
|
|
1240
|
-
resolvedAt?: string;
|
|
1241
|
-
resolvedBy?: string;
|
|
1242
|
-
}
|
|
1243
|
-
export interface SfSyncRecord {
|
|
1244
|
-
id?: number;
|
|
1245
|
-
localTable: 'leads' | 'activities';
|
|
1246
|
-
localId: number;
|
|
1247
|
-
sfObjectType: 'Lead' | 'Contact' | 'Opportunity' | 'Task' | 'Event';
|
|
1248
|
-
sfId: string;
|
|
1249
|
-
syncDirection: 'push' | 'pull';
|
|
1250
|
-
syncedAt?: string;
|
|
1251
|
-
syncStatus: 'success' | 'error' | 'conflict';
|
|
1252
|
-
errorMessage?: string;
|
|
1253
|
-
}
|
|
1254
|
-
export interface SfFieldMapping {
|
|
1255
|
-
localField: string;
|
|
1256
|
-
sfField: string;
|
|
1257
|
-
direction: 'bidirectional' | 'push-only' | 'pull-only';
|
|
1258
|
-
}
|
|
1259
1200
|
/** Supported v1 ingest formats detected by format-detector. */
|
|
1260
1201
|
export type DetectedFormat = 'csv' | 'json' | 'jsonl' | 'markdown' | 'pdf' | 'email' | 'docx' | 'unknown';
|
|
1261
1202
|
/** Operational mode for a source. */
|