clementine-agent 1.18.176 → 1.18.177
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 +1 -0
- package/dist/agent/run-agent.js +1 -0
- package/dist/gateway/router.d.ts +5 -0
- package/dist/gateway/router.js +59 -4
- package/dist/tools/background-task-tools.js +3 -2
- package/dist/tools/mcp-server.d.ts +1 -1
- package/dist/tools/mcp-server.js +1 -1
- package/dist/tools/shared.d.ts +1 -0
- package/dist/tools/shared.js +2 -0
- package/package.json +1 -1
package/dist/agent/assistant.js
CHANGED
|
@@ -2343,6 +2343,7 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
|
|
|
2343
2343
|
...process.env,
|
|
2344
2344
|
CLEMENTINE_HOME: BASE_DIR,
|
|
2345
2345
|
CLEMENTINE_TEAM_AGENT: profile?.slug ?? 'clementine',
|
|
2346
|
+
...(sessionKey ? { CLEMENTINE_SESSION_KEY: sessionKey } : {}),
|
|
2346
2347
|
CLEMENTINE_INTERACTION_SOURCE: sourceOverride ?? inferInteractionSource(sessionKey),
|
|
2347
2348
|
CLEMENTINE_TOOL_ALLOWLIST: clementineToolAllowlist,
|
|
2348
2349
|
CLEMENTINE_1M_CONTEXT_MODE: oneMillionModeValue,
|
package/dist/agent/run-agent.js
CHANGED
|
@@ -287,6 +287,7 @@ export async function runAgent(prompt, opts) {
|
|
|
287
287
|
...subprocessEnv,
|
|
288
288
|
CLEMENTINE_HOME: BASE_DIR,
|
|
289
289
|
...(opts.profile?.slug ? { CLEMENTINE_TEAM_AGENT: opts.profile.slug } : {}),
|
|
290
|
+
CLEMENTINE_SESSION_KEY: opts.sessionKey,
|
|
290
291
|
CLEMENTINE_INTERACTION_SOURCE: interactionSourceForSession(opts.sessionKey, source),
|
|
291
292
|
CLEMENTINE_TOOL_ALLOWLIST: clementineToolAllowlist,
|
|
292
293
|
},
|
package/dist/gateway/router.d.ts
CHANGED
|
@@ -13,6 +13,11 @@ import type { NotificationDispatcher } from './notifications.js';
|
|
|
13
13
|
import { type ProactiveNotificationInput } from './notification-context.js';
|
|
14
14
|
import { type ToolsetName } from '../agent/toolsets.js';
|
|
15
15
|
export { isLiveUnleashedStatus } from './unleashed-status.js';
|
|
16
|
+
export declare function buildContextOverflowRetryPrompt(opts: {
|
|
17
|
+
chatPrompt: string;
|
|
18
|
+
turnContextPrefix?: string;
|
|
19
|
+
project?: ProjectMeta | null;
|
|
20
|
+
}): string;
|
|
16
21
|
export type ChatErrorKind = 'rate_limit' | 'one_million_context' | 'context_overflow' | 'auth' | 'billing' | 'transient' | 'unknown';
|
|
17
22
|
export declare function classifyChatError(err: unknown): ChatErrorKind;
|
|
18
23
|
/** Detect auth-like errors in response text that the SDK returned as "successful" results. */
|
package/dist/gateway/router.js
CHANGED
|
@@ -49,6 +49,8 @@ const CHAT_TIMEOUT_MS = 10 * 60 * 1000;
|
|
|
49
49
|
* Safety net so no session runs forever, even if active.
|
|
50
50
|
* Primary guardrail is cost budget (maxBudgetUsd), not this timer. */
|
|
51
51
|
const CHAT_MAX_WALL_MS = 30 * 60 * 1000;
|
|
52
|
+
const CHAT_CONTEXT_RETRY_CONTEXT_MAX_CHARS = 6_000;
|
|
53
|
+
const CHAT_CONTEXT_RETRY_SYSTEM_MAX_CHARS = 16_000;
|
|
52
54
|
const BACKGROUND_TASK_ID_RE = /\bbg-[a-z0-9]+-[a-f0-9]{6}\b/i;
|
|
53
55
|
function collectRunToolNames(runId) {
|
|
54
56
|
if (!runId)
|
|
@@ -74,6 +76,26 @@ function compactToolNames(names) {
|
|
|
74
76
|
}
|
|
75
77
|
return out;
|
|
76
78
|
}
|
|
79
|
+
function trimContextRecoveryText(text, maxChars) {
|
|
80
|
+
if (!text || text.length <= maxChars)
|
|
81
|
+
return text;
|
|
82
|
+
return `${text.slice(0, maxChars - 80).trimEnd()}\n\n[context recovery: trimmed oversized carry-over context]`;
|
|
83
|
+
}
|
|
84
|
+
export function buildContextOverflowRetryPrompt(opts) {
|
|
85
|
+
const parts = [
|
|
86
|
+
'[Context recovery: the previous SDK session was too large, so this is a fresh session. Continue with the current user request. Do not ask the user to resend it.]',
|
|
87
|
+
];
|
|
88
|
+
if (opts.project?.path) {
|
|
89
|
+
const description = opts.project.description ? ` (${opts.project.description})` : '';
|
|
90
|
+
parts.push(`[Active project: ${opts.project.path}${description}]`);
|
|
91
|
+
}
|
|
92
|
+
const compactContext = trimContextRecoveryText((opts.turnContextPrefix ?? '').trim(), CHAT_CONTEXT_RETRY_CONTEXT_MAX_CHARS);
|
|
93
|
+
if (compactContext) {
|
|
94
|
+
parts.push(compactContext);
|
|
95
|
+
}
|
|
96
|
+
parts.push(opts.chatPrompt);
|
|
97
|
+
return parts.filter(Boolean).join('\n\n');
|
|
98
|
+
}
|
|
77
99
|
export function classifyChatError(err) {
|
|
78
100
|
const msg = String(err);
|
|
79
101
|
if (isCreditBalanceError(msg))
|
|
@@ -82,7 +104,7 @@ export function classifyChatError(err) {
|
|
|
82
104
|
return 'rate_limit';
|
|
83
105
|
if (looksLikeClaudeOneMillionContextError(msg))
|
|
84
106
|
return 'one_million_context';
|
|
85
|
-
if (/context.?length|token.?limit|maximum.?context|prompt.?too.?long|rapid_refill_breaker|autocompact|context.?refilled/i.test(msg))
|
|
107
|
+
if (/context.?length|token.?limit|maximum.?context|prompt(?:\s+is)?.?too.?long|input.?too.?long|rapid_refill_breaker|autocompact|context.?refilled/i.test(msg))
|
|
86
108
|
return 'context_overflow';
|
|
87
109
|
if (/\b401\b|\b403\b|auth|forbidden|invalid.?api.?key|permission|does not have access|please run \/login/i.test(msg))
|
|
88
110
|
return 'auth';
|
|
@@ -2117,6 +2139,7 @@ export class Gateway {
|
|
|
2117
2139
|
const chatSystemAppend = resolvedSkills && resolvedSkills.promptBlock
|
|
2118
2140
|
? (baseSystemAppend ? `${baseSystemAppend}\n\n${resolvedSkills.promptBlock}` : resolvedSkills.promptBlock)
|
|
2119
2141
|
: baseSystemAppend;
|
|
2142
|
+
const retrySystemAppend = trimContextRecoveryText(chatSystemAppend, CHAT_CONTEXT_RETRY_SYSTEM_MAX_CHARS);
|
|
2120
2143
|
// Per-turn context (recall + persistent learnings + silent
|
|
2121
2144
|
// blocks + security/toolset directives) — real chat only.
|
|
2122
2145
|
// Builder doesn't need recall of unrelated transcripts.
|
|
@@ -2156,7 +2179,7 @@ export class Gateway {
|
|
|
2156
2179
|
skillMatchNames: resolvedSkills?.matches.map(m => m.name) ?? [],
|
|
2157
2180
|
skillHintedMcpServers: resolvedSkills?.hintedMcpServers ?? [],
|
|
2158
2181
|
}, 'Routing chat through runAgent');
|
|
2159
|
-
const
|
|
2182
|
+
const buildRunAgentChatOptions = (opts) => ({
|
|
2160
2183
|
sessionKey: effectiveSessionKey,
|
|
2161
2184
|
source: 'chat',
|
|
2162
2185
|
profile: resolvedProfile,
|
|
@@ -2166,8 +2189,8 @@ export class Gateway {
|
|
|
2166
2189
|
...(maxTurns ? { maxTurns } : {}),
|
|
2167
2190
|
...(chatBudget !== undefined ? { maxBudgetUsd: chatBudget } : {}),
|
|
2168
2191
|
...(builderAllowedTools ? { allowedTools: builderAllowedTools } : {}),
|
|
2169
|
-
...(
|
|
2170
|
-
...(
|
|
2192
|
+
...(opts.systemPromptAppend ? { systemPromptAppend: opts.systemPromptAppend } : {}),
|
|
2193
|
+
...(opts.resumeSessionId ? { resumeSessionId: opts.resumeSessionId } : {}),
|
|
2171
2194
|
...(chatMcp ? { extraMcpServers: chatMcp.servers } : {}),
|
|
2172
2195
|
onText: wrappedOnText,
|
|
2173
2196
|
onToolActivity: ({ tool, input }) => {
|
|
@@ -2178,6 +2201,38 @@ export class Gateway {
|
|
|
2178
2201
|
},
|
|
2179
2202
|
abortSignal: chatAc.signal,
|
|
2180
2203
|
});
|
|
2204
|
+
let runAgentResult;
|
|
2205
|
+
try {
|
|
2206
|
+
runAgentResult = await runAgent(finalPrompt, buildRunAgentChatOptions({
|
|
2207
|
+
...(priorSdkSessionId ? { resumeSessionId: priorSdkSessionId } : {}),
|
|
2208
|
+
...(chatSystemAppend ? { systemPromptAppend: chatSystemAppend } : {}),
|
|
2209
|
+
}));
|
|
2210
|
+
}
|
|
2211
|
+
catch (err) {
|
|
2212
|
+
if (chatAc.signal.aborted || classifyChatError(err) !== 'context_overflow') {
|
|
2213
|
+
throw err;
|
|
2214
|
+
}
|
|
2215
|
+
const retryPrompt = buildContextOverflowRetryPrompt({
|
|
2216
|
+
chatPrompt,
|
|
2217
|
+
turnContextPrefix,
|
|
2218
|
+
project: sess?.project ?? null,
|
|
2219
|
+
});
|
|
2220
|
+
logger.info({
|
|
2221
|
+
sessionKey: effectiveSessionKey,
|
|
2222
|
+
hadResume: !!priorSdkSessionId,
|
|
2223
|
+
promptChars: finalPrompt.length,
|
|
2224
|
+
retryPromptChars: retryPrompt.length,
|
|
2225
|
+
systemAppendChars: chatSystemAppend.length,
|
|
2226
|
+
retrySystemAppendChars: retrySystemAppend.length,
|
|
2227
|
+
}, 'Context overflow — retrying current message in fresh SDK session');
|
|
2228
|
+
if (onProgress) {
|
|
2229
|
+
await onProgress('refreshing conversation context...').catch(() => { });
|
|
2230
|
+
}
|
|
2231
|
+
this.assistant.clearSession(effectiveSessionKey);
|
|
2232
|
+
runAgentResult = await runAgent(retryPrompt, buildRunAgentChatOptions({
|
|
2233
|
+
...(retrySystemAppend ? { systemPromptAppend: retrySystemAppend } : {}),
|
|
2234
|
+
}));
|
|
2235
|
+
}
|
|
2181
2236
|
if (ledgerRunMetadata) {
|
|
2182
2237
|
ledgerRunMetadata.runId = runAgentResult.runId;
|
|
2183
2238
|
ledgerRunMetadata.executionMode = ledgerRunMetadata.executionMode ?? 'inline';
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { z } from 'zod';
|
|
15
15
|
import { createBackgroundTask, listBackgroundTasks, loadBackgroundTask, } from '../agent/background-tasks.js';
|
|
16
|
-
import { ACTIVE_AGENT_SLUG, logger, textResult } from './shared.js';
|
|
16
|
+
import { ACTIVE_AGENT_SLUG, ACTIVE_SESSION_KEY, logger, textResult } from './shared.js';
|
|
17
17
|
const DEFAULT_MAX_MINUTES = 30;
|
|
18
18
|
export function registerBackgroundTaskTools(server) {
|
|
19
19
|
server.tool('start_background_task', 'Kick off a long-running autonomous task in the background. Use when the work would burn the chat context (deep research, multi-page extraction, batch processing) or take longer than a chat turn. Returns a task id immediately. The daemon picks the task up within seconds, runs it with your profile + tools, and posts the deliverable to your Discord channel when done.', {
|
|
@@ -30,8 +30,9 @@ export function registerBackgroundTaskTools(server) {
|
|
|
30
30
|
fromAgent,
|
|
31
31
|
prompt: trimmed,
|
|
32
32
|
maxMinutes: cap,
|
|
33
|
+
...(ACTIVE_SESSION_KEY ? { sessionKey: ACTIVE_SESSION_KEY } : {}),
|
|
33
34
|
});
|
|
34
|
-
logger.info({ id: task.id, fromAgent, maxMinutes: task.maxMinutes }, 'Background task queued');
|
|
35
|
+
logger.info({ id: task.id, fromAgent, sessionKey: task.sessionKey, maxMinutes: task.maxMinutes }, 'Background task queued');
|
|
35
36
|
return textResult(`Queued **${task.id}** (max ${task.maxMinutes} min). The daemon will pick it up within a few seconds and run it in the background. You'll get a notification in your channel when the deliverable lands. Use \`get_background_task\` to check status.`);
|
|
36
37
|
});
|
|
37
38
|
server.tool('get_background_task', 'Check the status of a background task. Returns its lifecycle state (pending|running|done|failed|aborted|interrupted), how long it has been running, and the result/error if terminal.', {
|
|
@@ -8,6 +8,6 @@
|
|
|
8
8
|
* Usage:
|
|
9
9
|
* npx tsx src/tools/mcp-server.ts
|
|
10
10
|
*/
|
|
11
|
-
export { getStore, textResult, externalResult, incrementalSync, ACTIVE_AGENT_SLUG } from './shared.js';
|
|
11
|
+
export { getStore, textResult, externalResult, incrementalSync, ACTIVE_AGENT_SLUG, ACTIVE_SESSION_KEY } from './shared.js';
|
|
12
12
|
export type { MemoryStoreType } from './shared.js';
|
|
13
13
|
//# sourceMappingURL=mcp-server.d.ts.map
|
package/dist/tools/mcp-server.js
CHANGED
|
@@ -15,7 +15,7 @@ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js'
|
|
|
15
15
|
import { z } from 'zod';
|
|
16
16
|
import { BASE_DIR, VAULT_DIR, env, getStore, getStoreSync, logger, textResult, externalResult, } from './shared.js';
|
|
17
17
|
// Re-export for any code that imports from mcp-server.ts directly
|
|
18
|
-
export { getStore, textResult, externalResult, incrementalSync, ACTIVE_AGENT_SLUG } from './shared.js';
|
|
18
|
+
export { getStore, textResult, externalResult, incrementalSync, ACTIVE_AGENT_SLUG, ACTIVE_SESSION_KEY } from './shared.js';
|
|
19
19
|
// ── Tool modules ────────────────────────────────────────────────────────
|
|
20
20
|
import { registerMemoryTools } from './memory-tools.js';
|
|
21
21
|
import { registerVaultTools } from './vault-tools.js';
|
package/dist/tools/shared.d.ts
CHANGED
|
@@ -671,6 +671,7 @@ export type MemoryStoreType = {
|
|
|
671
671
|
export declare function getStore(): Promise<MemoryStoreType>;
|
|
672
672
|
export declare function getStoreSync(): MemoryStoreType | null;
|
|
673
673
|
export declare const ACTIVE_AGENT_SLUG: string | null;
|
|
674
|
+
export declare const ACTIVE_SESSION_KEY: string | null;
|
|
674
675
|
export declare const GOALS_DIR: string;
|
|
675
676
|
export declare function agentTasksFile(slug: string | null): string;
|
|
676
677
|
export declare function agentWorkingMemoryFile(slug: string | null): string;
|
package/dist/tools/shared.js
CHANGED
|
@@ -59,6 +59,8 @@ export function getStoreSync() {
|
|
|
59
59
|
// ── Active Agent Slug ──────────────────────────────────────────────────
|
|
60
60
|
const _rawAgentSlug = process.env.CLEMENTINE_TEAM_AGENT || null;
|
|
61
61
|
export const ACTIVE_AGENT_SLUG = _rawAgentSlug === 'clementine' ? null : _rawAgentSlug;
|
|
62
|
+
const _rawSessionKey = process.env.CLEMENTINE_SESSION_KEY || null;
|
|
63
|
+
export const ACTIVE_SESSION_KEY = _rawSessionKey?.trim() || null;
|
|
62
64
|
// ── Agent-aware path helpers ───────────────────────────────────────────
|
|
63
65
|
// GOALS_DIR is defined in config.ts but not in shared.ts — define it here
|
|
64
66
|
export const GOALS_DIR = path.join(BASE_DIR, 'goals');
|