clementine-agent 1.18.153 → 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.
@@ -175,6 +175,18 @@ export interface RunAgentCronOptions {
175
175
  * prior progress. The fix for fire-time memory drift. Undefined =
176
176
  * legacy behavior (inject everything). */
177
177
  predictable?: boolean;
178
+ /** Lean mode — strictest possible context envelope, used by meta-jobs
179
+ * (insight-check, outcome-grader, route-classifier, failure-diagnostics,
180
+ * __heartbeat__) that ALSO have to stay under Haiku's prompt cap.
181
+ * Implies predictable + drops progress/goal/criteria/skill-context blocks
182
+ * + prunes MCP catalog to just the explicit allowed servers (no Composio
183
+ * auto-discovery, no claude.ai connector inventory). When unset, behaves
184
+ * like predictable. The 1.18.148 fix patched outcome-grader / route-
185
+ * classifier / failure-diagnostics directly; this 1.18.154 flag covers
186
+ * insight-check (which still failed 65/70 because predictable alone
187
+ * wasn't strict enough) and is the canonical hook for any future
188
+ * meta-job that needs a tiny prompt. */
189
+ lean?: boolean;
178
190
  /** Extra read+execute scope for the agent's Read/Bash/Glob tools. Maps
179
191
  * directly to the CronJobDefinition.addDirs YAML field. Combined with
180
192
  * every pinned-skill folder so `Bash python3 scripts/render.py` works
@@ -548,13 +548,32 @@ export async function buildCronExecutionPlan(opts) {
548
548
  // fire time. Legacy tricks (predictable === undefined) preserve existing
549
549
  // behavior so we don't surprise anyone.
550
550
  const predictable = opts.predictable === true;
551
- const memoryContext = predictable ? '' : buildAutonomousMemoryContext(opts.profile);
552
- const progressContext = buildProgressContext(opts.jobName); // opt-in via cron_progress writes
553
- const goalContext = buildGoalContext(opts.jobName); // explicit links; not auto-inferred
554
- const delegationContext = predictable ? '' : buildDelegationContext(agentSlug);
555
- const teamContext = predictable ? '' : buildTeamContext(agentSlug);
556
- const criteriaContext = buildCriteriaContext(opts.successCriteria);
557
- const skillResult = await buildSkillContext(opts.jobName, opts.jobPrompt, agentSlug, opts.pinnedSkills, opts.memoryStore, { skipAutoMatch: predictable, projectWorkDir: opts.workDir });
551
+ // 1.18.154 lean mode goes one tier stricter for meta-jobs (insight-check,
552
+ // outcome-grader, route-classifier, failure-diagnostics, __heartbeat__).
553
+ // Drops progress / goal / criteria / skill-context blocks too, leaving
554
+ // the agent with [jobName] + [jobPrompt] + [howToRespond] only. Implies
555
+ // predictable. Anything that ALSO needs a tiny MCP catalog uses the
556
+ // pruning logic below.
557
+ //
558
+ // Auto-apply for known meta-jobs even if the CRON.md/registry entry
559
+ // doesn't carry `lean: true` — these names are hard-coded singletons in
560
+ // heartbeat-scheduler.ts and we know they need the strict envelope. Lets
561
+ // existing user installs benefit from the fix without requiring them to
562
+ // hand-edit CRON.md.
563
+ const KNOWN_META_JOBS = new Set([
564
+ 'insight-check', 'outcome-grader', 'route-classifier',
565
+ 'failure-diagnostics', '__heartbeat__',
566
+ ]);
567
+ const lean = opts.lean === true || KNOWN_META_JOBS.has(opts.jobName);
568
+ const memoryContext = predictable || lean ? '' : buildAutonomousMemoryContext(opts.profile);
569
+ const progressContext = lean ? '' : buildProgressContext(opts.jobName); // opt-in via cron_progress writes
570
+ const goalContext = lean ? '' : buildGoalContext(opts.jobName); // explicit links; not auto-inferred
571
+ const delegationContext = predictable || lean ? '' : buildDelegationContext(agentSlug);
572
+ const teamContext = predictable || lean ? '' : buildTeamContext(agentSlug);
573
+ const criteriaContext = lean ? '' : buildCriteriaContext(opts.successCriteria);
574
+ const skillResult = lean
575
+ ? { text: '', applied: [], missing: [], pinnedBodies: [], pinnedToolsRequested: [] }
576
+ : await buildSkillContext(opts.jobName, opts.jobPrompt, agentSlug, opts.pinnedSkills, opts.memoryStore, { skipAutoMatch: predictable, projectWorkDir: opts.workDir });
558
577
  const skillContext = skillResult.text;
559
578
  const howToRespond = `## How to respond\n` +
560
579
  `You're sending this directly to ${ownerName} as a DM. ` +
@@ -577,15 +596,24 @@ export async function buildCronExecutionPlan(opts) {
577
596
  criteriaContext +
578
597
  `${opts.jobPrompt}\n\n` +
579
598
  howToRespond;
580
- const mcp = await buildExtraMcpForRunAgent({
581
- scopeText: [
582
- opts.jobName,
583
- opts.jobPrompt,
584
- opts.profile?.description,
585
- opts.profile?.systemPromptBody,
586
- ].filter(Boolean).join('\n\n'),
587
- profile: opts.profile,
588
- });
599
+ // 1.18.154 lean mode skips Composio + claude.ai MCP discovery entirely.
600
+ // Meta-jobs (insight-check etc.) only need the always-on Clementine MCP
601
+ // server (which is added in run-agent.ts and has cron_list, discord_*,
602
+ // etc.). Loading 200+ extra MCP tool schemas just to call cron_list is
603
+ // what was tipping the prompt over Haiku's cap. If a lean job DOES need
604
+ // extras, declare them explicitly via `allowedMcpServers` and the helper
605
+ // narrows discovery to those names.
606
+ const mcp = lean && (!opts.allowedMcpServers || opts.allowedMcpServers.length === 0)
607
+ ? { servers: {}, composioConnected: [], externalConnected: [] }
608
+ : await buildExtraMcpForRunAgent({
609
+ scopeText: [
610
+ opts.jobName,
611
+ opts.jobPrompt,
612
+ opts.profile?.description,
613
+ opts.profile?.systemPromptBody,
614
+ ].filter(Boolean).join('\n\n'),
615
+ profile: opts.profile,
616
+ });
589
617
  // 1.18.125 — pinned-skill scope widening (SDK alignment).
590
618
  // A pinned skill that declares `clementine.tools.allow` or references
591
619
  // `mcp__server__tool` in its body needs those tools/servers to actually
@@ -701,19 +729,28 @@ export async function buildCronExecutionPlan(opts) {
701
729
  export async function runAgentCron(opts) {
702
730
  const plan = await buildCronExecutionPlan(opts);
703
731
  const { builtPrompt, agentSlug, effort, maxBudgetUsd: maxBudget, effectiveAllowedTools, mcpServerMap, composioConnected, externalConnected, mcpServersApplied, additionalDirectories, } = plan;
704
- logger.info({
732
+ // 1.18.154 — surface lean + promptChars for meta-job diagnostics. The
733
+ // self-improve / insight-check failure mode is "small prompt, fat context"
734
+ // which only shows in promptChars; plain log level was previously info but
735
+ // anything > 50KB is on the cusp of Haiku's cap and worth a warn.
736
+ const promptBytes = Buffer.byteLength(builtPrompt, 'utf8');
737
+ const promptOversized = promptBytes > 50_000;
738
+ logger[promptOversized ? 'warn' : 'info']({
705
739
  job: opts.jobName,
706
740
  tier: plan.tier,
707
741
  profile: agentSlug,
742
+ lean: opts.lean === true || ['insight-check', 'outcome-grader', 'route-classifier', 'failure-diagnostics', '__heartbeat__'].includes(opts.jobName),
708
743
  composioConnected,
709
744
  externalConnected,
710
745
  promptChars: builtPrompt.length,
746
+ promptBytes,
711
747
  pinnedSkills: opts.pinnedSkills?.length ?? 0,
712
748
  skillsApplied: plan.skillsApplied.length,
713
749
  skillsMissing: plan.skillsMissing.length,
714
750
  trickAllowedTools: effectiveAllowedTools?.length,
715
751
  trickAllowedMcp: opts.allowedMcpServers?.length,
716
752
  widenedFromSkills: plan.widenedFromSkills,
753
+ ...(promptOversized ? { warning: 'prompt > 50KB; risk of "Prompt is too long" failure' } : {}),
717
754
  }, 'runAgentCron: dispatching to runAgent');
718
755
  const startedAt = Date.now();
719
756
  const result = await runAgent(builtPrompt, {
@@ -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
@@ -169,6 +169,8 @@ function parseJobYaml(job) {
169
169
  ? categoryRaw.trim().slice(0, 64)
170
170
  : undefined;
171
171
  const predictable = typeof job.predictable === 'boolean' ? job.predictable : undefined;
172
+ // 1.18.154 — strictest envelope for meta-jobs (insight-check, heartbeat).
173
+ const lean = typeof job.lean === 'boolean' ? job.lean : undefined;
172
174
  const description = typeof job.description === 'string' && job.description.trim()
173
175
  ? job.description.trim().slice(0, 500)
174
176
  : undefined;
@@ -177,7 +179,7 @@ function parseJobYaml(job) {
177
179
  maxHours, maxRetries, after, successCriteria, successCriteriaText, successSchema, addDirs,
178
180
  alwaysDeliver, context, preCheck, attachments, requiresConfirmation, confirmationTimeoutMin,
179
181
  agentSlug,
180
- skills, allowedTools, allowedMcpServers, tags, category, predictable,
182
+ skills, allowedTools, allowedMcpServers, tags, category, predictable, lean,
181
183
  };
182
184
  }
183
185
  /**
@@ -1224,12 +1226,12 @@ export class CronScheduler {
1224
1226
  const startedAt = new Date();
1225
1227
  try {
1226
1228
  // Standard cron jobs get a timeout via SDK AbortController (advisor may override)
1227
- let response = await this.gateway.handleCronJob(job.name, jobPrompt, job.tier, job.maxTurns, job.model, job.workDir, job.mode, job.maxHours, effectiveTimeoutMs, job.successCriteria, job.agentSlug, job.skills, job.allowedTools, job.allowedMcpServers, job.predictable, job.addDirs);
1229
+ let response = await this.gateway.handleCronJob(job.name, jobPrompt, job.tier, job.maxTurns, job.model, job.workDir, job.mode, job.maxHours, effectiveTimeoutMs, job.successCriteria, job.agentSlug, job.skills, job.allowedTools, job.allowedMcpServers, job.predictable, job.lean, job.addDirs);
1228
1230
  // alwaysDeliver: retry once if the response is empty/noise
1229
1231
  if (job.alwaysDeliver && (!response || CronScheduler.isCronNoise(response))) {
1230
1232
  logger.info({ job: job.name }, 'alwaysDeliver: empty/noise response — retrying once');
1231
1233
  try {
1232
- const retryResponse = await this.gateway.handleCronJob(job.name, jobPrompt + '\n\nYou MUST produce a brief status update. Do NOT return __NOTHING__.', job.tier, job.maxTurns, job.model, job.workDir, job.mode, job.maxHours, effectiveTimeoutMs, job.successCriteria, job.agentSlug, job.skills, job.allowedTools, job.allowedMcpServers, job.predictable, job.addDirs);
1234
+ const retryResponse = await this.gateway.handleCronJob(job.name, jobPrompt + '\n\nYou MUST produce a brief status update. Do NOT return __NOTHING__.', job.tier, job.maxTurns, job.model, job.workDir, job.mode, job.maxHours, effectiveTimeoutMs, job.successCriteria, job.agentSlug, job.skills, job.allowedTools, job.allowedMcpServers, job.predictable, job.lean, job.addDirs);
1233
1235
  if (retryResponse && !CronScheduler.isCronNoise(retryResponse)) {
1234
1236
  response = retryResponse;
1235
1237
  }
@@ -183,6 +183,9 @@ export declare class Gateway {
183
183
  _mode?: 'standard' | 'unleashed', maxHours?: number, timeoutMs?: number, successCriteria?: string[], agentSlug?: string, pinnedSkills?: string[], allowedTools?: string[], allowedMcpServers?: string[],
184
184
  /** Predictable (contract) mode — runner skips memory/team/auto-skills. */
185
185
  predictable?: boolean,
186
+ /** Lean (meta-job) mode — strictest envelope, drops every auto-injected
187
+ * context block. For insight-check, the four meta-routers, heartbeat. */
188
+ lean?: boolean,
186
189
  /** Extra read+exec scope for the SDK's Read/Bash/Glob tools. From the
187
190
  * CronJobDefinition.addDirs YAML field. Combined inside runAgentCron
188
191
  * with each pinned-skill folder. (1.18.121) */
@@ -1977,6 +1977,9 @@ export class Gateway {
1977
1977
  pinnedSkills, allowedTools, allowedMcpServers,
1978
1978
  /** Predictable (contract) mode — runner skips memory/team/auto-skills. */
1979
1979
  predictable,
1980
+ /** Lean (meta-job) mode — strictest envelope, drops every auto-injected
1981
+ * context block. For insight-check, the four meta-routers, heartbeat. */
1982
+ lean,
1980
1983
  /** Extra read+exec scope for the SDK's Read/Bash/Glob tools. From the
1981
1984
  * CronJobDefinition.addDirs YAML field. Combined inside runAgentCron
1982
1985
  * with each pinned-skill folder. (1.18.121) */
@@ -2026,6 +2029,7 @@ export class Gateway {
2026
2029
  allowedTools,
2027
2030
  allowedMcpServers,
2028
2031
  predictable,
2032
+ lean,
2029
2033
  addDirs,
2030
2034
  });
2031
2035
  scanner.refreshIntegrity();
package/dist/types.d.ts CHANGED
@@ -550,6 +550,15 @@ export interface CronJobDefinition {
550
550
  * Existing tricks (no field set) keep current behavior — backward compat.
551
551
  */
552
552
  predictable?: boolean;
553
+ /** 1.18.154 — strictest possible context envelope, for meta-jobs that
554
+ * need to stay under Haiku's prompt cap (insight-check, the four
555
+ * meta-routers, __heartbeat__). Implies predictable + drops progress/
556
+ * goal/criteria/skill blocks entirely. Use only for jobs whose prompt
557
+ * is self-contained and whose tools are explicitly allow-listed. The
558
+ * fix for the 1.18.148 regression where insight-check kept failing
559
+ * with "Prompt is too long" because predictable alone wasn't strict
560
+ * enough. */
561
+ lean?: boolean;
553
562
  /** 1.18.129 — where this cron job came from. 'cron-md' is the legacy
554
563
  * fat-cron format in vault/00-System/CRON.md (or per-agent CRON.md).
555
564
  * 'scheduled-skill' is the Anthropic-pure registry — a thin entry in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.18.153",
3
+ "version": "1.18.155",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",