clementine-agent 1.18.2 → 1.18.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.
- package/dist/agent/assistant.js +5 -6
- package/dist/config/effective-config.js +2 -0
- package/dist/config.d.ts +1 -0
- package/dist/config.js +7 -0
- package/dist/gateway/router.js +35 -4
- package/package.json +1 -1
package/dist/agent/assistant.js
CHANGED
|
@@ -13,7 +13,11 @@ import fs from 'node:fs';
|
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { query as rawQuery, listSubagents, getSubagentMessages, SYSTEM_PROMPT_DYNAMIC_BOUNDARY, } from '@anthropic-ai/claude-agent-sdk';
|
|
15
15
|
import pino from 'pino';
|
|
16
|
-
import { BASE_DIR, PKG_DIR, VAULT_DIR, DAILY_NOTES_DIR, SOUL_FILE, AGENTS_FILE, MEMORY_FILE, AGENTS_DIR, ASSISTANT_NAME, OWNER_NAME, MODEL, MODELS, HEARTBEAT_MAX_TURNS, SEARCH_CONTEXT_LIMIT, SEARCH_RECENCY_LIMIT, SYSTEM_PROMPT_MAX_CONTEXT_CHARS, SESSION_EXCHANGE_HISTORY_SIZE, SESSION_EXCHANGE_MAX_CHARS, INJECTED_CONTEXT_MAX_CHARS, UNLEASHED_PHASE_TURNS, UNLEASHED_DEFAULT_MAX_HOURS, UNLEASHED_MAX_PHASES, PROJECTS_META_FILE, CRON_PROGRESS_DIR, CRON_REFLECTIONS_DIR, HANDOFFS_DIR, BUDGET, TASK_BUDGET_TOKENS, IDENTITY_FILE, CLAUDE_CODE_OAUTH_TOKEN, ANTHROPIC_API_KEY as CONFIG_ANTHROPIC_API_KEY, } from '../config.js';
|
|
16
|
+
import { BASE_DIR, PKG_DIR, VAULT_DIR, DAILY_NOTES_DIR, SOUL_FILE, AGENTS_FILE, MEMORY_FILE, AGENTS_DIR, ASSISTANT_NAME, OWNER_NAME, MODEL, MODELS, HEARTBEAT_MAX_TURNS, SEARCH_CONTEXT_LIMIT, SEARCH_RECENCY_LIMIT, SYSTEM_PROMPT_MAX_CONTEXT_CHARS, SESSION_EXCHANGE_HISTORY_SIZE, SESSION_EXCHANGE_MAX_CHARS, INJECTED_CONTEXT_MAX_CHARS, UNLEASHED_PHASE_TURNS, UNLEASHED_DEFAULT_MAX_HOURS, UNLEASHED_MAX_PHASES, PROJECTS_META_FILE, CRON_PROGRESS_DIR, CRON_REFLECTIONS_DIR, HANDOFFS_DIR, BUDGET, TASK_BUDGET_TOKENS, IDENTITY_FILE, CLAUDE_CODE_OAUTH_TOKEN, ANTHROPIC_API_KEY as CONFIG_ANTHROPIC_API_KEY, envSnapshot, } from '../config.js';
|
|
17
|
+
import { summarizeIntegrationStatus } from '../config/integrations-registry.js';
|
|
18
|
+
import { loadToolPreferences, computeAvailability, buildPromptInstruction, buildComposioStatusBlock, } from '../integrations/tool-preferences.js';
|
|
19
|
+
import { loadClaudeIntegrations } from './mcp-bridge.js';
|
|
20
|
+
import { detectFrustrationSignals, detectRepeatedTopics } from './insight-engine.js';
|
|
17
21
|
import { DEFAULT_CHANNEL_CAPABILITIES } from '../types.js';
|
|
18
22
|
import { enforceToolPermissions, getSecurityPrompt, getHeartbeatSecurityPrompt, getCronSecurityPrompt, getHeartbeatDisallowedTools, logToolUse, setProfileTier, setProfileAllowedTools, setAgentDir, setSendPolicy, setInteractionSource, logAuditJsonl, } from './hooks.js';
|
|
19
23
|
import { scanner } from '../security/scanner.js';
|
|
@@ -1558,8 +1562,6 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
|
|
|
1558
1562
|
// Integration status — changes as owner adds credentials.
|
|
1559
1563
|
if (!isAutonomous) {
|
|
1560
1564
|
try {
|
|
1561
|
-
const { summarizeIntegrationStatus } = require('../config/integrations-registry.js');
|
|
1562
|
-
const { envSnapshot } = require('../config.js');
|
|
1563
1565
|
const summary = summarizeIntegrationStatus(envSnapshot());
|
|
1564
1566
|
if (summary)
|
|
1565
1567
|
volatileParts.push(`## Integration Status\n\n${summary}\n\nCall \`integration_status\`, \`list_integrations\`, or \`setup_integration\` for details.`);
|
|
@@ -1578,8 +1580,6 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
|
|
|
1578
1580
|
// every turn regardless.
|
|
1579
1581
|
if (!isAutonomous) {
|
|
1580
1582
|
try {
|
|
1581
|
-
const { loadToolPreferences, computeAvailability, buildPromptInstruction, buildComposioStatusBlock } = require('../integrations/tool-preferences.js');
|
|
1582
|
-
const { loadClaudeIntegrations } = require('./mcp-bridge.js');
|
|
1583
1583
|
const composioSet = new Set(composioConnectedSlugs);
|
|
1584
1584
|
const cdIntegrations = loadClaudeIntegrations();
|
|
1585
1585
|
const cdActive = new Set(Object.values(cdIntegrations).filter(i => i.connected).map(i => i.name));
|
|
@@ -1605,7 +1605,6 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
|
|
|
1605
1605
|
// one signal fires — keeps the prompt clean during normal sessions.
|
|
1606
1606
|
if (!isAutonomous) {
|
|
1607
1607
|
try {
|
|
1608
|
-
const { detectFrustrationSignals, detectRepeatedTopics } = require('./insight-engine.js');
|
|
1609
1608
|
const since24h = new Date(Date.now() - 24 * 60 * 60 * 1000).toISOString();
|
|
1610
1609
|
const since7d = new Date(Date.now() - 7 * 24 * 60 * 60 * 1000).toISOString();
|
|
1611
1610
|
let recent = this.getRecentActivity(since24h, 50);
|
|
@@ -25,6 +25,8 @@ const SPECS = [
|
|
|
25
25
|
{ key: 'HAIKU_MODEL', group: 'models', jsonPath: 'models.haiku', default: 'claude-haiku-4-5-20251001' },
|
|
26
26
|
{ key: 'SONNET_MODEL', group: 'models', jsonPath: 'models.sonnet', default: 'claude-sonnet-4-6' },
|
|
27
27
|
{ key: 'OPUS_MODEL', group: 'models', jsonPath: 'models.opus', default: 'claude-opus-4-7' },
|
|
28
|
+
// Team routing
|
|
29
|
+
{ key: 'AUTO_DELEGATE_ENABLED', group: 'team', default: false },
|
|
28
30
|
// Budgets
|
|
29
31
|
{ key: 'BUDGET_HEARTBEAT_USD', group: 'budgets', jsonPath: 'budgets.heartbeat', default: 0.50 },
|
|
30
32
|
{ key: 'BUDGET_CRON_T1_USD', group: 'budgets', jsonPath: 'budgets.cronT1', default: 2.00 },
|
package/dist/config.d.ts
CHANGED
|
@@ -84,6 +84,7 @@ export declare const TASK_BUDGET_TOKENS: {
|
|
|
84
84
|
};
|
|
85
85
|
export declare const DEFAULT_MODEL_TIER: keyof Models;
|
|
86
86
|
export declare const MODEL: string;
|
|
87
|
+
export declare const AUTO_DELEGATE_ENABLED: boolean;
|
|
87
88
|
export declare const DISCORD_TOKEN: string;
|
|
88
89
|
export declare const DISCORD_OWNER_ID: string;
|
|
89
90
|
export declare const DISCORD_WATCHED_CHANNELS: string[];
|
package/dist/config.js
CHANGED
|
@@ -236,6 +236,13 @@ export const TASK_BUDGET_TOKENS = {
|
|
|
236
236
|
};
|
|
237
237
|
export const DEFAULT_MODEL_TIER = (getEnvOrJson('DEFAULT_MODEL_TIER', json.models?.default, 'sonnet'));
|
|
238
238
|
export const MODEL = MODELS[DEFAULT_MODEL_TIER] ?? MODELS.sonnet;
|
|
239
|
+
// ── Team routing ─────────────────────────────────────────────────────
|
|
240
|
+
// When false (default), the gateway never auto-delegates a Clementine-
|
|
241
|
+
// owned message to a sub-agent without explicit user opt-in. High-
|
|
242
|
+
// confidence routing is demoted to a soft-suggest so the user can
|
|
243
|
+
// confirm ("send to Ross") or override before any handoff happens.
|
|
244
|
+
// Flip via dashboard or `clementine config set AUTO_DELEGATE_ENABLED true`.
|
|
245
|
+
export const AUTO_DELEGATE_ENABLED = getEnv('AUTO_DELEGATE_ENABLED', 'false').toLowerCase() === 'true';
|
|
239
246
|
// ── Discord ──────────────────────────────────────────────────────────
|
|
240
247
|
export const DISCORD_TOKEN = getSecret('DISCORD_TOKEN');
|
|
241
248
|
export const DISCORD_OWNER_ID = getEnv('DISCORD_OWNER_ID', '0');
|
package/dist/gateway/router.js
CHANGED
|
@@ -10,7 +10,7 @@ import pino from 'pino';
|
|
|
10
10
|
import { PersonalAssistant } from '../agent/assistant.js';
|
|
11
11
|
import { runWithTrace, logAuditJsonl } from '../agent/hooks.js';
|
|
12
12
|
import { SelfImproveLoop } from '../agent/self-improve.js';
|
|
13
|
-
import { MODELS, AGENTS_DIR, TEAM_COMMS_LOG, BASE_DIR, SEEN_CHANNELS_FILE } from '../config.js';
|
|
13
|
+
import { MODELS, AGENTS_DIR, TEAM_COMMS_LOG, BASE_DIR, SEEN_CHANNELS_FILE, AUTO_DELEGATE_ENABLED } from '../config.js';
|
|
14
14
|
import { scanner } from '../security/scanner.js';
|
|
15
15
|
import { lanes } from './lanes.js';
|
|
16
16
|
import { AgentManager } from '../agent/agent-manager.js';
|
|
@@ -255,8 +255,10 @@ export class Gateway {
|
|
|
255
255
|
const targetProfile = agents.find(a => a.slug === decision.targetAgent);
|
|
256
256
|
if (!targetProfile)
|
|
257
257
|
return null;
|
|
258
|
-
// Auto-delegate at high confidence
|
|
259
|
-
|
|
258
|
+
// Auto-delegate at high confidence — only when the user has explicitly
|
|
259
|
+
// opted in via AUTO_DELEGATE_ENABLED. Default behavior is to demote
|
|
260
|
+
// every routing to a soft-suggest so the user controls every handoff.
|
|
261
|
+
if (decision.confidence >= 0.8 && AUTO_DELEGATE_ENABLED) {
|
|
260
262
|
// Fire the team task in the background; ack immediately.
|
|
261
263
|
const ackMessage = `Routing this to **${targetProfile.name}** (${decision.reasoning.toLowerCase()}). I'll post their response back here when done.`;
|
|
262
264
|
onText?.(ackMessage).catch(() => { });
|
|
@@ -266,7 +268,32 @@ export class Gateway {
|
|
|
266
268
|
if (!sess.teamTaskControllers)
|
|
267
269
|
sess.teamTaskControllers = new Set();
|
|
268
270
|
sess.teamTaskControllers.add(teamAbortController);
|
|
269
|
-
this
|
|
271
|
+
// Progress visibility — without this the channel goes dark from
|
|
272
|
+
// ack to final result, which can be many minutes on research-style
|
|
273
|
+
// tasks. We batch the delegated agent's tool announcements and
|
|
274
|
+
// flush every PROGRESS_INTERVAL_MS so the user sees what's
|
|
275
|
+
// happening without a token firehose.
|
|
276
|
+
const recentTools = [];
|
|
277
|
+
let progressTimer;
|
|
278
|
+
const PROGRESS_INTERVAL_MS = 30_000;
|
|
279
|
+
const dispatcher = this._dispatcher;
|
|
280
|
+
const flushProgress = () => {
|
|
281
|
+
progressTimer = undefined;
|
|
282
|
+
if (recentTools.length === 0)
|
|
283
|
+
return;
|
|
284
|
+
const tools = recentTools.slice(-5).join(', ');
|
|
285
|
+
recentTools.length = 0;
|
|
286
|
+
void dispatcher?.send(`_${targetProfile.name} is working — recent actions: ${tools}_`, { sessionKey, agentSlug: targetProfile.slug });
|
|
287
|
+
};
|
|
288
|
+
const onTeamProgress = (chunk) => {
|
|
289
|
+
const m = chunk.match(/\[using ([^\]]+?)\.\.\.\]/);
|
|
290
|
+
if (m && m[1])
|
|
291
|
+
recentTools.push(m[1]);
|
|
292
|
+
if (!progressTimer && recentTools.length > 0) {
|
|
293
|
+
progressTimer = setTimeout(flushProgress, PROGRESS_INTERVAL_MS);
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
this.handleTeamTask('Clementine', 'clementine', text, targetProfile, onTeamProgress, teamAbortController)
|
|
270
297
|
.then(response => {
|
|
271
298
|
if (!response)
|
|
272
299
|
return;
|
|
@@ -282,6 +309,10 @@ export class Gateway {
|
|
|
282
309
|
void this._dispatcher?.send(`**${targetProfile.name}** hit an error handling that: ${String(err).slice(0, 200)}`, { sessionKey, agentSlug: targetProfile.slug });
|
|
283
310
|
})
|
|
284
311
|
.finally(() => {
|
|
312
|
+
if (progressTimer) {
|
|
313
|
+
clearTimeout(progressTimer);
|
|
314
|
+
progressTimer = undefined;
|
|
315
|
+
}
|
|
285
316
|
const s = this.sessions.get(sessionKey);
|
|
286
317
|
s?.teamTaskControllers?.delete(teamAbortController);
|
|
287
318
|
});
|