clementine-agent 1.18.154 → 1.18.155

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.
@@ -25418,8 +25418,18 @@ function renderScheduledTaskCard(task) {
25418
25418
  } else {
25419
25419
  badges += '<span class="badge badge-gray" title="Legacy CRON.md job. Carries its own prompt/tools/MCP. Convert to a scheduled skill when you can.">LEGACY CRON</span>';
25420
25420
  }
25421
- if (task.predictable === true) badges += '<span class="badge badge-green" title="Contract mode runs with only the prompt + pinned skills/tools. No MEMORY.md, no auto-matched skills, no team comms injection at fire-time.">🔒 predictable</span>';
25422
- else if (task.predictable === false) badges += '<span class="badge badge-yellow" title="Dynamic mode — fire-time injects MEMORY.md, recent team activity, and auto-matched skills. Can drift from chat-time intent.">🔄 reads memory</span>';
25421
+ // 1.18.155 replace the "🔒 predictable" lock icon (which appeared on
25422
+ // ~every row and added pure noise) with a single "Strict" pill that ONLY
25423
+ // appears when behavior diverges from the default. predictable=true is
25424
+ // the default for new tasks since 1.18.68, so suppress that badge — only
25425
+ // show one when the user has explicitly opted into dynamic mode (the
25426
+ // less safe path, worth flagging) or when lean mode is in effect for a
25427
+ // meta-job (worth showing because it explains why the prompt is small).
25428
+ if (task.lean === true) {
25429
+ badges += '<span class="badge badge-purple" title="Lean envelope — drops every auto-injected context block (memory, progress, goal, criteria, skills) and prunes the MCP catalog. Used for meta-jobs that must stay under Haiku\'s prompt cap.">Lean</span>';
25430
+ } else if (task.predictable === false) {
25431
+ badges += '<span class="badge badge-yellow" title="Dynamic mode — fire-time injects MEMORY.md, recent team activity, and auto-matched skills. Can drift from chat-time intent.">Reads memory</span>';
25432
+ }
25423
25433
  if (task.mode === 'unleashed') badges += '<span class="badge badge-purple">long-running</span>';
25424
25434
  if (task.after) badges += '<span class="badge badge-yellow" title="Triggered after ' + esc(task.after) + '">after ' + esc(task.after) + '</span>';
25425
25435
  if (task.maxRetries != null) badges += '<span class="badge badge-gray">' + esc(task.maxRetries) + ' retries</span>';
@@ -25670,7 +25680,13 @@ function renderRecentHistoryList(runs) {
25670
25680
  var msg = String(entry.error).slice(0, 120);
25671
25681
  errorPreview = '<div style="font-size:11px;color:var(--red);margin-top:2px;word-break:break-word">' + esc(msg) + '</div>';
25672
25682
  } else if (entry.outputPreview) {
25673
- var preview = String(entry.outputPreview).slice(0, 140);
25683
+ // 1.18.155 — strip the __NOTHING__ sentinel agents emit when they
25684
+ // intentionally stay silent (it's the "no signal" contract from
25685
+ // run-agent-cron.ts howToRespond block, not output the user should see).
25686
+ var rawPreview = String(entry.outputPreview);
25687
+ var preview = (rawPreview === '__NOTHING__' || rawPreview.trim() === '__NOTHING__')
25688
+ ? '(no output — agent reported nothing worth saying)'
25689
+ : rawPreview.slice(0, 140);
25674
25690
  errorPreview = '<div style="font-size:11px;color:var(--text-muted);margin-top:2px;word-break:break-word">' + esc(preview) + '</div>';
25675
25691
  }
25676
25692
  // PRD Phase 1.1: goal cell. Empty cell when no goal configured (status='skipped'
@@ -25775,16 +25791,31 @@ async function refreshHealthStrip() {
25775
25791
  var ops = await apiFetch('/api/build/operations?hours=1&limit=10').then(function(rr) { return rr.json(); });
25776
25792
  activeRuns = ((ops && ops.runningNow) || []).length;
25777
25793
  } catch (e) { /* fall back to 0 */ }
25778
- // Top failure category for the day.
25794
+ // Top failure category for the day. 1.18.155 — when no failureCategory
25795
+ // is set on the failed runs (which is common for raw SDK errors like
25796
+ // "Prompt is too long"), fall back to grouping by jobName so the user
25797
+ // sees WHICH job is failing instead of a useless dash. Categories still
25798
+ // win when present (more semantic than a job name).
25779
25799
  var catCounts = {};
25800
+ var jobCounts = {};
25780
25801
  for (var i = 0; i < last24.length; i++) {
25781
25802
  var c = last24[i].failureCategory;
25782
25803
  if (c) catCounts[c] = (catCounts[c] || 0) + 1;
25804
+ if (last24[i].status === 'error' || last24[i].status === 'failed') {
25805
+ var jn = last24[i].jobName || last24[i].name;
25806
+ if (jn) jobCounts[jn] = (jobCounts[jn] || 0) + 1;
25807
+ }
25783
25808
  }
25784
25809
  var topCat = null, topCount = 0;
25785
25810
  Object.keys(catCounts).forEach(function(k) {
25786
25811
  if (catCounts[k] > topCount) { topCat = k; topCount = catCounts[k]; }
25787
25812
  });
25813
+ var topJob = null, topJobCount = 0;
25814
+ Object.keys(jobCounts).forEach(function(k) {
25815
+ if (jobCounts[k] > topJobCount) { topJob = k; topJobCount = jobCounts[k]; }
25816
+ });
25817
+ // Prefer the categorized one (more semantic), fall back to job name.
25818
+ if (!topCat && topJob) { topCat = topJob; topCount = topJobCount; }
25788
25819
  // Render six tiles.
25789
25820
  function tile(label, value, sub, color) {
25790
25821
  return '<div class="health-tile">'
@@ -26450,7 +26481,12 @@ function renderRunListBody(allRuns) {
26450
26481
  if (status === 'error' && entry.error) {
26451
26482
  preview = '<div style="font-size:11px;color:var(--red);margin-top:2px;word-break:break-word">' + esc(String(entry.error).slice(0, 140)) + '</div>';
26452
26483
  } else if (entry.outputPreview) {
26453
- preview = '<div style="font-size:11px;color:var(--text-muted);margin-top:2px;word-break:break-word">' + esc(String(entry.outputPreview).slice(0, 120)) + '</div>';
26484
+ // 1.18.155 see renderRecentHistoryList for the same __NOTHING__ strip.
26485
+ var rawOp = String(entry.outputPreview);
26486
+ var opText = (rawOp === '__NOTHING__' || rawOp.trim() === '__NOTHING__')
26487
+ ? '(no output — agent reported nothing worth saying)'
26488
+ : rawOp.slice(0, 120);
26489
+ preview = '<div style="font-size:11px;color:var(--text-muted);margin-top:2px;word-break:break-word">' + esc(opText) + '</div>';
26454
26490
  }
26455
26491
  // 1.18.89: cost label. Showing 4 decimals for sub-penny costs (Haiku
26456
26492
  // runs land in fractions of a cent), 2 decimals when ≥ $0.01.
@@ -27079,7 +27115,11 @@ async function refreshCron() {
27079
27115
  var bannerHtml = '';
27080
27116
  var dismissed = localStorage.getItem('clem-skill-migrate-banner-dismissed') === '1';
27081
27117
  if (legacyCount > 0 && !dismissed) {
27082
- bannerHtml = '<div style="background:rgba(124,58,237,0.08);border:1px solid var(--purple);border-radius:8px;padding:12px 14px;margin-bottom:14px;display:flex;align-items:center;gap:12px;flex-wrap:wrap">'
27118
+ // 1.18.155 data-banner-kind tags this as the legacy-cron soft-
27119
+ // deprecation banner so refreshCronMigrateBanner can suppress its
27120
+ // secondary "clean up preambles" banner when this one is showing
27121
+ // (full migration is a superset; showing both is confusing noise).
27122
+ bannerHtml = '<div data-banner-kind="legacy-cron-soft-deprecation" style="background:rgba(124,58,237,0.08);border:1px solid var(--purple);border-radius:8px;padding:12px 14px;margin-bottom:14px;display:flex;align-items:center;gap:12px;flex-wrap:wrap">'
27083
27123
  + '<span style="font-size:18px">⚡</span>'
27084
27124
  + '<div style="flex:1;min-width:200px">'
27085
27125
  + '<div style="font-size:13px;font-weight:500;color:var(--text-primary)">' + legacyCount + ' legacy cron task' + (legacyCount === 1 ? '' : 's') + ' can become scheduled skills</div>'
@@ -29049,6 +29089,19 @@ async function refreshCronMigrateBanner() {
29049
29089
  return;
29050
29090
  }
29051
29091
  _cronMigratePreview = d;
29092
+ // 1.18.155 — suppress this banner when the upstream "X legacy crons
29093
+ // can become scheduled skills" banner is already showing. The full
29094
+ // migration there is a superset of this cleanup (the destination skill
29095
+ // gets a clean prompt automatically), so showing both at once is just
29096
+ // confusing noise. If the upstream banner is dismissed via localStorage
29097
+ // OR there are no legacy crons to migrate, this orange banner shows up
29098
+ // as the secondary action.
29099
+ var dismissed = localStorage.getItem('clem-skill-migrate-banner-dismissed') === '1';
29100
+ var hasLegacyCronBanner = !dismissed && document.querySelector('[data-banner-kind="legacy-cron-soft-deprecation"]');
29101
+ if (hasLegacyCronBanner) {
29102
+ host.innerHTML = '';
29103
+ return;
29104
+ }
29052
29105
  var n = d.eligible.length;
29053
29106
  host.innerHTML =
29054
29107
  '<div style="margin:0 0 14px;padding:12px 16px;border:1px solid var(--accent);background:rgba(255,141,0,0.06);border-radius:8px;display:flex;align-items:center;gap:14px;flex-wrap:wrap">'
@@ -101,6 +101,10 @@ export interface ScheduledTaskCard {
101
101
  * team comms / auto-matched skills. The visibility-on-card flag for
102
102
  * "this trick will run with only what you see here." */
103
103
  predictable?: boolean;
104
+ /** 1.18.154/155 — Lean envelope (meta-jobs). Surfaced in the row badge
105
+ * so the user understands why insight-check / outcome-grader / etc.
106
+ * show a tiny prompt + restricted MCP catalog. */
107
+ lean?: boolean;
104
108
  }
105
109
  export interface ScheduledWorkflowCard {
106
110
  type: 'scheduled_workflow';
@@ -216,6 +216,7 @@ export function buildOperationsSnapshot(input) {
216
216
  tags: asStringArray(job.tags),
217
217
  category: typeof job.category === 'string' && job.category.trim() ? job.category.trim() : undefined,
218
218
  predictable: typeof job.predictable === 'boolean' ? job.predictable : undefined,
219
+ lean: typeof job.lean === 'boolean' ? job.lean : undefined,
219
220
  };
220
221
  }).sort((a, b) => a.owner.localeCompare(b.owner) || a.displayName.localeCompare(b.displayName));
221
222
  const scheduledWorkflows = input.workflowSummaries
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.18.154",
3
+ "version": "1.18.155",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",