clementine-agent 1.18.152 → 1.18.154

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, {
@@ -39485,9 +39485,15 @@ async function runWorkflow(name) {
39485
39485
  // the list so the migrated workflow disappears (its .md is renamed to
39486
39486
  // .md.migrated server-side and parseAllWorkflows stops picking it up).
39487
39487
  async function migrateWorkflowToSkill(name) {
39488
- if (!confirm('Migrate "' + name + '" into a skill folder?\n\n' +
39489
- '• A new skill will be created at vault/00-System/skills/<slug>/SKILL.md\n' +
39490
- '• The original workflow .md will be renamed to .md.migrated (kept for rollback)\n' +
39488
+ // 1.18.153 escape \\n through the outer template literal. Without the
39489
+ // double-backslash the served HTML gets literal newlines INSIDE a single-
39490
+ // quoted JS string, which the browser parser rejects as
39491
+ // "Uncaught SyntaxError: Invalid or unexpected token". That kills the
39492
+ // entire inline script block, so no API calls fire, no data renders,
39493
+ // dashboard appears completely broken even though the server is healthy.
39494
+ if (!confirm('Migrate "' + name + '" into a skill folder?\\n\\n' +
39495
+ '• A new skill will be created at vault/00-System/skills/<slug>/SKILL.md\\n' +
39496
+ '• The original workflow .md will be renamed to .md.migrated (kept for rollback)\\n' +
39491
39497
  '• The workflow stops firing; the skill is ready to schedule from the Skills tab')) return;
39492
39498
  try {
39493
39499
  var r = await apiFetch('/api/workflows/' + encodeURIComponent(name) + '/migrate-to-skill', {
@@ -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.152",
3
+ "version": "1.18.154",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",