clementine-agent 1.18.3 → 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/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
|
@@ -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
|
});
|