clementine-agent 1.13.2 → 1.13.4

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.
@@ -247,7 +247,9 @@ export declare class PersonalAssistant {
247
247
  };
248
248
  delegateProfile?: AgentProfile;
249
249
  }): Promise<string>;
250
- runCronJob(jobName: string, jobPrompt: string, tier?: number, maxTurns?: number, model?: string, workDir?: string, timeoutMs?: number, successCriteria?: string[], agentSlug?: string): Promise<string>;
250
+ runCronJob(jobName: string, jobPrompt: string, tier?: number, maxTurns?: number, model?: string, workDir?: string, timeoutMs?: number, successCriteria?: string[], agentSlug?: string, opts?: {
251
+ disableAllTools?: boolean;
252
+ }): Promise<string>;
251
253
  /**
252
254
  * Goal-backward verification pass using Haiku after cron job execution.
253
255
  * Instead of vague quality ratings, verifies actual outcomes:
@@ -3897,9 +3897,14 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
3897
3897
  // ── Heartbeat / Cron ──────────────────────────────────────────────
3898
3898
  async heartbeat(standingInstructions, changesSummary = '', timeContext = '', dedupContext = '', profile) {
3899
3899
  setInteractionSource('autonomous');
3900
+ // Heartbeat speaks text only — the prompt below explicitly forbids tool
3901
+ // calls. Skipping MCP server load + tool inventory cuts the prompt by
3902
+ // hundreds of thousands of tokens on installs with many integrations,
3903
+ // which is what kept Haiku exceeding its 200K context window.
3900
3904
  const sdkOptions = await this.buildOptions({
3901
3905
  isHeartbeat: true,
3902
3906
  enableTeams: false,
3907
+ disableAllTools: true,
3903
3908
  model: MODELS.haiku,
3904
3909
  profile: profile ?? undefined,
3905
3910
  });
@@ -4006,7 +4011,7 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
4006
4011
  return extractDeliverable(trace) ||
4007
4012
  trace.filter(t => t.type === 'text').map(t => t.content).join('').trim();
4008
4013
  }
4009
- async runCronJob(jobName, jobPrompt, tier = 1, maxTurns, model, workDir, timeoutMs, successCriteria, agentSlug) {
4014
+ async runCronJob(jobName, jobPrompt, tier = 1, maxTurns, model, workDir, timeoutMs, successCriteria, agentSlug, opts) {
4010
4015
  setInteractionSource('autonomous');
4011
4016
  // Tag every tool_use audit event with the cron job name + agent so
4012
4017
  // analytics tool-usage can show "Bash×893 driven by market-leader-followup"
@@ -4040,6 +4045,7 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
4040
4045
  enableTeams: true,
4041
4046
  stallGuard: cronGuard,
4042
4047
  profile: cronProfile,
4048
+ disableAllTools: opts?.disableAllTools ?? false,
4043
4049
  });
4044
4050
  // Override cwd if a project workDir is specified
4045
4051
  if (workDir) {
@@ -318,7 +318,14 @@ export async function classifyRoute(userMessage, agents, gateway) {
318
318
  try {
319
319
  raw = await gateway.handleCronJob('route-classify', prompt, 1, // tier 1
320
320
  3, // maxTurns — classifier doesn't need tools
321
- 'haiku');
321
+ 'haiku', // cheap
322
+ undefined, // workDir
323
+ 'standard', // mode
324
+ undefined, // maxHours
325
+ undefined, // timeoutMs
326
+ undefined, // successCriteria
327
+ undefined, // agentSlug
328
+ { disableAllTools: true });
322
329
  }
323
330
  catch (err) {
324
331
  logger.warn({ err }, 'Route classifier call failed');
@@ -135,7 +135,13 @@ export async function gradeRun(entry, gateway, jobPrompt) {
135
135
  try {
136
136
  raw = await gateway.handleCronJob(`grade:${entry.jobName}`, prompt, 1, // tier 1
137
137
  3, // maxTurns — tight
138
- 'haiku');
138
+ 'haiku', undefined, // workDir
139
+ 'standard', // mode
140
+ undefined, // maxHours
141
+ undefined, // timeoutMs
142
+ undefined, // successCriteria
143
+ undefined, // agentSlug
144
+ { disableAllTools: true });
139
145
  }
140
146
  catch (err) {
141
147
  logger.warn({ err, jobName: entry.jobName }, 'Outcome grader LLM call failed');
@@ -152,7 +152,9 @@ export declare class Gateway {
152
152
  handleMessage(sessionKey: string, text: string, onText?: OnTextCallback, model?: string, maxTurns?: number, onToolActivity?: OnToolActivityCallback, onProgress?: OnProgressCallback): Promise<string>;
153
153
  private _handleMessageInner;
154
154
  handleHeartbeat(standingInstructions: string, changesSummary?: string, timeContext?: string, dedupContext?: string, profile?: import('../types.js').AgentProfile | null): Promise<string>;
155
- handleCronJob(jobName: string, jobPrompt: string, tier?: number, maxTurns?: number, model?: string, workDir?: string, mode?: 'standard' | 'unleashed', maxHours?: number, timeoutMs?: number, successCriteria?: string[], agentSlug?: string): Promise<string>;
155
+ handleCronJob(jobName: string, jobPrompt: string, tier?: number, maxTurns?: number, model?: string, workDir?: string, mode?: 'standard' | 'unleashed', maxHours?: number, timeoutMs?: number, successCriteria?: string[], agentSlug?: string, opts?: {
156
+ disableAllTools?: boolean;
157
+ }): Promise<string>;
156
158
  /**
157
159
  * Process a team message as an autonomous task — same multi-phase execution
158
160
  * as cron unleashed jobs, so agents can work until done instead of being
@@ -1345,7 +1345,7 @@ export class Gateway {
1345
1345
  releaseLane();
1346
1346
  }
1347
1347
  }
1348
- async handleCronJob(jobName, jobPrompt, tier = 1, maxTurns, model, workDir, mode = 'standard', maxHours, timeoutMs, successCriteria, agentSlug) {
1348
+ async handleCronJob(jobName, jobPrompt, tier = 1, maxTurns, model, workDir, mode = 'standard', maxHours, timeoutMs, successCriteria, agentSlug, opts) {
1349
1349
  const releaseLane = await lanes.acquire('cron');
1350
1350
  try {
1351
1351
  logger.info(`Running cron job: ${jobName}${workDir ? ` in ${workDir}` : ''}${mode === 'unleashed' ? ' (unleashed)' : ''}${agentSlug && agentSlug !== 'clementine' ? ` as ${agentSlug}` : ''}`);
@@ -1357,7 +1357,7 @@ export class Gateway {
1357
1357
  response = await this.assistant.runUnleashedTask(jobName, jobPrompt, tier, maxTurns, model, workDir, maxHours, agentSlug);
1358
1358
  }
1359
1359
  else {
1360
- response = await this.assistant.runCronJob(jobName, jobPrompt, tier, maxTurns, model, workDir, timeoutMs, successCriteria, agentSlug);
1360
+ response = await this.assistant.runCronJob(jobName, jobPrompt, tier, maxTurns, model, workDir, timeoutMs, successCriteria, agentSlug, opts);
1361
1361
  }
1362
1362
  // Re-baseline integrity checksums after cron job (may write to vault)
1363
1363
  scanner.refreshIntegrity();
@@ -54,10 +54,15 @@ async function buildOne(composio, slug, _connected) {
54
54
  // mcp__outlook__OUTLOOK_LIST_MESSAGES. The alternative, composio.create()
55
55
  // + session.tools(), uses Composio's tool-router pattern and only returns
56
56
  // 5 meta-tools (COMPOSIO_SEARCH_TOOLS, COMPOSIO_MULTI_EXECUTE_TOOL, …),
57
- // which doesn't match what the agent calls. Verified empirically:
58
- // tools.get returns 50+ actual Outlook tools.
57
+ // which doesn't match what the agent calls.
58
+ //
59
+ // Limit MUST be high enough to include every alphabetically-late tool.
60
+ // Outlook has ~400+ tools; capping at 200 silently dropped the message-
61
+ // reading tools (OUTLOOK_LIST_MESSAGES, OUTLOOK_GET_MESSAGES, etc.) which
62
+ // alphabetically come after OUTLOOK_LIST_CALENDAR_GROUP_*. GitHub has
63
+ // 800+. Set 1000 — comfortable headroom for any single toolkit.
59
64
  const userId = await getPreferredUserId();
60
- const toolsRaw = await composio.tools.get(userId, { toolkits: [slug], limit: 200 });
65
+ const toolsRaw = await composio.tools.get(userId, { toolkits: [slug], limit: 1000 });
61
66
  // tools.get can return an array OR an object depending on provider; normalise.
62
67
  const toolsArr = Array.isArray(toolsRaw) ? toolsRaw : Object.values(toolsRaw);
63
68
  const tools = toolsArr.filter((t) => t && typeof t.name === 'string' && typeof t.handler === 'function');
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.13.2",
3
+ "version": "1.13.4",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",