clementine-agent 1.18.193 → 1.18.194
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/bg-planner.js +5 -1
- package/dist/agent/daily-planner.js +3 -1
- package/dist/agent/mcp-bridge.js +3 -1
- package/dist/agent/run-agent-context.js +8 -1
- package/dist/agent/strategic-planner.js +3 -1
- package/dist/brain/adapters/pdf.js +3 -1
- package/dist/cli/dashboard.js +5 -1
- package/dist/config.d.ts +25 -0
- package/dist/config.js +45 -0
- package/dist/gateway/router.d.ts +0 -1
- package/dist/gateway/router.js +47 -111
- package/dist/index.js +2 -0
- package/package.json +1 -1
package/dist/agent/bg-planner.js
CHANGED
|
@@ -44,7 +44,7 @@ import fs from 'node:fs';
|
|
|
44
44
|
import path from 'node:path';
|
|
45
45
|
import { randomUUID } from 'node:crypto';
|
|
46
46
|
import pino from 'pino';
|
|
47
|
-
import { BASE_DIR, MODELS, applyOneMillionContextRecovery, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext } from '../config.js';
|
|
47
|
+
import { BASE_DIR, MODELS, applyOneMillionContextRecovery, claudeCodeSubprocessEnv, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext } from '../config.js';
|
|
48
48
|
const logger = pino({ name: 'clementine.bg-planner' });
|
|
49
49
|
// ── Persistence ──────────────────────────────────────────────────────
|
|
50
50
|
/**
|
|
@@ -287,6 +287,10 @@ async function runPlannerLlm(userPrompt, systemPrompt, model) {
|
|
|
287
287
|
// from knowing the working directory + git status so it can decompose
|
|
288
288
|
// accurately. See claudeCodeSystemPrompt() in config.ts.
|
|
289
289
|
systemPrompt: claudeCodeSystemPrompt(systemPrompt),
|
|
290
|
+
// 1.18.194 — pass OAuth token to the SDK subprocess. Without this,
|
|
291
|
+
// process.env doesn't have CLAUDE_CODE_OAUTH_TOKEN (config.ts keeps
|
|
292
|
+
// secrets out of process.env) and the SDK fails with "Not logged in".
|
|
293
|
+
env: claudeCodeSubprocessEnv(),
|
|
290
294
|
}),
|
|
291
295
|
});
|
|
292
296
|
for await (const msg of stream) {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
9
9
|
import path from 'node:path';
|
|
10
10
|
import pino from 'pino';
|
|
11
|
-
import { BASE_DIR, CRON_REFLECTIONS_DIR, TASKS_FILE, INBOX_DIR, MODELS, applyOneMillionContextRecovery, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
|
|
11
|
+
import { BASE_DIR, CRON_REFLECTIONS_DIR, TASKS_FILE, INBOX_DIR, MODELS, applyOneMillionContextRecovery, claudeCodeSubprocessEnv, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
|
|
12
12
|
import { listAllGoals } from '../tools/shared.js';
|
|
13
13
|
const logger = pino({ name: 'clementine.daily-planner' });
|
|
14
14
|
const PLANS_DIR = path.join(BASE_DIR, 'plans', 'daily');
|
|
@@ -259,6 +259,8 @@ Rules:
|
|
|
259
259
|
// 1.18.192 — preset form so SDK uses Claude Code subscription auth
|
|
260
260
|
// (raw string → API-key auth → "Not logged in" failure for Max users).
|
|
261
261
|
systemPrompt: claudeCodeSystemPrompt('You are a planning assistant. Analyze the context and produce a prioritized daily plan as JSON. Return only valid JSON, no markdown fencing.', { minimal: true }),
|
|
262
|
+
// 1.18.194 — propagate OAuth token to SDK subprocess.
|
|
263
|
+
env: claudeCodeSubprocessEnv(),
|
|
262
264
|
}),
|
|
263
265
|
});
|
|
264
266
|
for await (const msg of stream) {
|
package/dist/agent/mcp-bridge.js
CHANGED
|
@@ -10,7 +10,7 @@ import { existsSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
|
10
10
|
import os from 'node:os';
|
|
11
11
|
import path from 'node:path';
|
|
12
12
|
import pino from 'pino';
|
|
13
|
-
import { BASE_DIR, applyOneMillionContextRecovery, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
|
|
13
|
+
import { BASE_DIR, applyOneMillionContextRecovery, claudeCodeSubprocessEnv, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
|
|
14
14
|
const logger = pino({ name: 'clementine.mcp-bridge' });
|
|
15
15
|
const MCP_SERVERS_FILE = path.join(BASE_DIR, 'mcp-servers.json');
|
|
16
16
|
const INTEGRATIONS_FILE = path.join(BASE_DIR, 'claude-integrations.json');
|
|
@@ -453,6 +453,8 @@ export async function probeAvailableTools(force = false) {
|
|
|
453
453
|
options: normalizeClaudeSdkOptionsForOneMillionContext({
|
|
454
454
|
// 1.18.192 — preset form for Claude Code subscription auth.
|
|
455
455
|
systemPrompt: claudeCodeSystemPrompt('Reply ok.', { minimal: true }),
|
|
456
|
+
// 1.18.194 — propagate OAuth token to SDK subprocess.
|
|
457
|
+
env: claudeCodeSubprocessEnv(),
|
|
456
458
|
model: 'claude-haiku-4-5',
|
|
457
459
|
permissionMode: 'dontAsk',
|
|
458
460
|
mcpServers: externalMcpServers,
|
|
@@ -123,7 +123,14 @@ const BEHAVIORAL_POSTURE = `## How you operate
|
|
|
123
123
|
|
|
124
124
|
**Discovering new projects.** If the owner mentions a project by name that isn't in your registry, don't free-float — call \`project_discover\` with the name. It searches common locations (~/Downloads, ~/Desktop, ~/Projects, ~/Documents) and returns ranked candidates. Confirm the right one with the owner, then call \`project_link\` to register it. Future turns will then resolve it automatically.
|
|
125
125
|
|
|
126
|
-
**Verification posture for disputed claims.** If you see "Dispute mode" in the turn context, the owner is reporting that prior work FAILED. Past \`done\` claims in memory are NOT authoritative — your recall is biased. Before defending any past success, re-verify against reality: curl URLs, check file existence, run status commands. Saying "but my memory says it's live" without re-checking is a hallucination, not a defense
|
|
126
|
+
**Verification posture for disputed claims.** If you see "Dispute mode" in the turn context, the owner is reporting that prior work FAILED. Past \`done\` claims in memory are NOT authoritative — your recall is biased. Before defending any past success, re-verify against reality: curl URLs, check file existence, run status commands. Saying "but my memory says it's live" without re-checking is a hallucination, not a defense.
|
|
127
|
+
|
|
128
|
+
**Fan-out posture (1.18.194).** When the owner asks for 3+ similar operations — send N emails, pull N records, enrich N contacts, summarize N pages — dispatch subagents in PARALLEL via the Agent tool. One subagent per item. Don't loop in your own turn; that's slow, serializes I/O that should be concurrent, and burns context linearly. Available subagents (see Agent tool descriptions for the canonical list):
|
|
129
|
+
- \`researcher\` (Haiku, parallel, read-only) — per-item investigation
|
|
130
|
+
- \`planner\` (Opus, 1-turn, no tools) — decomposition before write/send batches
|
|
131
|
+
- Hired agents (Ross, Nora, etc.) — cross-delegation when relevant
|
|
132
|
+
|
|
133
|
+
A 25-contact enrichment that fans out to 25 \`researcher\` calls finishes in ~30s. The same work done serially in your own turn takes 10+ minutes AND fills your context window with tool outputs. Default to fan-out for batch work.`;
|
|
127
134
|
/**
|
|
128
135
|
* Read the long-term memory block for an autonomous run (cron, team-task).
|
|
129
136
|
* Returns the agent-specific MEMORY.md when a hired agent is active, the
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
import { existsSync, mkdirSync, readFileSync, readdirSync, writeFileSync } from 'node:fs';
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import pino from 'pino';
|
|
15
|
-
import { BASE_DIR, GOALS_DIR, MODELS, applyOneMillionContextRecovery, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
|
|
15
|
+
import { BASE_DIR, GOALS_DIR, MODELS, applyOneMillionContextRecovery, claudeCodeSubprocessEnv, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
|
|
16
16
|
import { listAllGoals } from '../tools/shared.js';
|
|
17
17
|
const logger = pino({ name: 'clementine.strategic-planner' });
|
|
18
18
|
const DAILY_PLANS_DIR = path.join(BASE_DIR, 'plans', 'daily');
|
|
@@ -31,6 +31,8 @@ async function llmJsonCall(prompt, systemPrompt) {
|
|
|
31
31
|
// failures on Max-only installs. Logs confirmed weekly review was
|
|
32
32
|
// silently falling through to the fallback path here since the bug landed.
|
|
33
33
|
systemPrompt: claudeCodeSystemPrompt(systemPrompt, { minimal: true }),
|
|
34
|
+
// 1.18.194 — propagate OAuth token to SDK subprocess.
|
|
35
|
+
env: claudeCodeSubprocessEnv(),
|
|
34
36
|
}),
|
|
35
37
|
});
|
|
36
38
|
for await (const msg of stream) {
|
|
@@ -14,7 +14,7 @@ import { readFileSync } from 'node:fs';
|
|
|
14
14
|
import path from 'node:path';
|
|
15
15
|
import pdfParse from 'pdf-parse';
|
|
16
16
|
import { contentHash } from './common.js';
|
|
17
|
-
import { MODELS, applyOneMillionContextRecovery, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../../config.js';
|
|
17
|
+
import { MODELS, applyOneMillionContextRecovery, claudeCodeSubprocessEnv, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../../config.js';
|
|
18
18
|
export async function* parsePdf(filePath) {
|
|
19
19
|
let buf;
|
|
20
20
|
try {
|
|
@@ -98,6 +98,8 @@ async function ocrPdfViaClaude(filePath) {
|
|
|
98
98
|
// Without this, every scanned-PDF ingest hit "Not logged in" and
|
|
99
99
|
// silently fell back to empty OCR output.
|
|
100
100
|
systemPrompt: claudeCodeSystemPrompt('You are a faithful OCR transcriber. Copy text exactly as written. When the PDF has images or scans, read the text from them using vision. Never invent content.', { minimal: true }),
|
|
101
|
+
// 1.18.194 — propagate OAuth token to SDK subprocess.
|
|
102
|
+
env: claudeCodeSubprocessEnv(),
|
|
101
103
|
// Claude Code's built-in Read tool handles PDFs (text + vision)
|
|
102
104
|
tools: ['Read'],
|
|
103
105
|
allowedTools: ['Read'],
|
package/dist/cli/dashboard.js
CHANGED
|
@@ -19,7 +19,7 @@ import { TunnelManager } from './tunnel.js';
|
|
|
19
19
|
import { AgentManager } from '../agent/agent-manager.js';
|
|
20
20
|
import { discoverMcpServers, getClaudeIntegrations, KNOWN_MCP_DESCRIPTIONS } from '../agent/mcp-bridge.js';
|
|
21
21
|
import { buildBuilderEnrichedMessage, builderSessionKey } from '../dashboard/builder/prompt.js';
|
|
22
|
-
import { AGENTS_DIR, MEMORY_FILE, MODELS, SESSIONS_FILE, TIMEZONE, applyOneMillionContextRecovery, claudeCodeSystemPrompt, currentTimeZone, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, setEnvOverride, } from '../config.js';
|
|
22
|
+
import { AGENTS_DIR, MEMORY_FILE, MODELS, SESSIONS_FILE, TIMEZONE, applyOneMillionContextRecovery, claudeCodeSubprocessEnv, claudeCodeSystemPrompt, currentTimeZone, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, setEnvOverride, } from '../config.js';
|
|
23
23
|
import { parseTasks } from '../tools/shared.js';
|
|
24
24
|
// 1.18.160 — also pull parseCronJobs + parseAgentCronJobs so getCronJobs()
|
|
25
25
|
// returns the same merged set the runtime fires (CRON.md + agent CRON +
|
|
@@ -6401,6 +6401,8 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
|
|
|
6401
6401
|
maxTurns: 3,
|
|
6402
6402
|
// 1.18.192 — preset form for Claude Code subscription auth.
|
|
6403
6403
|
systemPrompt: claudeCodeSystemPrompt('You are a data enumerator. You call the given tool once, extract the items from its response, and emit a strict JSON array. No commentary.', { minimal: true }),
|
|
6404
|
+
// 1.18.194 — propagate OAuth token to SDK subprocess.
|
|
6405
|
+
env: claudeCodeSubprocessEnv(),
|
|
6404
6406
|
allowedTools: [tool],
|
|
6405
6407
|
mcpServers,
|
|
6406
6408
|
permissionMode: 'dontAsk',
|
|
@@ -9728,6 +9730,8 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
|
|
|
9728
9730
|
maxTurns: 1,
|
|
9729
9731
|
// 1.18.192 — preset form for Claude Code subscription auth.
|
|
9730
9732
|
systemPrompt: claudeCodeSystemPrompt('You are a memory consolidation assistant. Extract only facts directly evidenced by the corpus. Be terse. Output exactly the requested format.', { minimal: true }),
|
|
9733
|
+
// 1.18.194 — propagate OAuth token to SDK subprocess.
|
|
9734
|
+
env: claudeCodeSubprocessEnv(),
|
|
9731
9735
|
}),
|
|
9732
9736
|
});
|
|
9733
9737
|
for await (const msg of stream) {
|
package/dist/config.d.ts
CHANGED
|
@@ -63,6 +63,31 @@ export declare function claudeCodeSystemPrompt(append: string, opts?: {
|
|
|
63
63
|
append: string;
|
|
64
64
|
excludeDynamicSections?: boolean;
|
|
65
65
|
};
|
|
66
|
+
/**
|
|
67
|
+
* 1.18.194 — Build the env Record for a direct SDK `query()` call so the
|
|
68
|
+
* Claude Code OAuth token reaches the SDK subprocess.
|
|
69
|
+
*
|
|
70
|
+
* Why this matters: `getSecret('CLAUDE_CODE_OAUTH_TOKEN')` reads
|
|
71
|
+
* ~/.clementine/.env and stores the value in the in-memory constant
|
|
72
|
+
* `CLAUDE_CODE_OAUTH_TOKEN` — it intentionally does NOT write to
|
|
73
|
+
* process.env (config.ts:478 keeps secrets out of process.env to prevent
|
|
74
|
+
* leakage). When a direct `query()` call omits `env:`, the SDK defaults
|
|
75
|
+
* to `process.env` — which doesn't have the token — and authentication
|
|
76
|
+
* silently falls back to API-key mode and fails with "Not logged in".
|
|
77
|
+
*
|
|
78
|
+
* 1.18.192 fixed the systemPrompt shape (preset vs raw string) but missed
|
|
79
|
+
* this env-propagation half of the same auth bug. Daily-planner, weekly
|
|
80
|
+
* review, bg-planner, and several other Haiku utility paths were still
|
|
81
|
+
* silently failing AFTER 1.18.192 because env: wasn't being passed.
|
|
82
|
+
*
|
|
83
|
+
* Use this alongside `claudeCodeSystemPrompt()` for every direct `query()`
|
|
84
|
+
* call. The runAgent path already builds its own env via buildRunAgentEnv;
|
|
85
|
+
* the assistant.ts auto-memory + verifier already use SAFE_ENV (same
|
|
86
|
+
* pattern). This helper exists for the lightweight utility callers.
|
|
87
|
+
*
|
|
88
|
+
* Priority order matches buildRunAgentEnv: OAuth > ANTHROPIC_AUTH_TOKEN > API key.
|
|
89
|
+
*/
|
|
90
|
+
export declare function claudeCodeSubprocessEnv(): Record<string, string>;
|
|
66
91
|
export declare function normalizeClaudeModelForOneMillionContext(model: string, mode?: OneMillionContextMode): string;
|
|
67
92
|
export declare function usesOneMillionContext(model: string | null | undefined, mode?: OneMillionContextMode, plan?: ClaudePlan): boolean;
|
|
68
93
|
/**
|
package/dist/config.js
CHANGED
|
@@ -225,6 +225,51 @@ export function claudeCodeSystemPrompt(append, opts) {
|
|
|
225
225
|
...(opts?.minimal ? { excludeDynamicSections: true } : {}),
|
|
226
226
|
};
|
|
227
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* 1.18.194 — Build the env Record for a direct SDK `query()` call so the
|
|
230
|
+
* Claude Code OAuth token reaches the SDK subprocess.
|
|
231
|
+
*
|
|
232
|
+
* Why this matters: `getSecret('CLAUDE_CODE_OAUTH_TOKEN')` reads
|
|
233
|
+
* ~/.clementine/.env and stores the value in the in-memory constant
|
|
234
|
+
* `CLAUDE_CODE_OAUTH_TOKEN` — it intentionally does NOT write to
|
|
235
|
+
* process.env (config.ts:478 keeps secrets out of process.env to prevent
|
|
236
|
+
* leakage). When a direct `query()` call omits `env:`, the SDK defaults
|
|
237
|
+
* to `process.env` — which doesn't have the token — and authentication
|
|
238
|
+
* silently falls back to API-key mode and fails with "Not logged in".
|
|
239
|
+
*
|
|
240
|
+
* 1.18.192 fixed the systemPrompt shape (preset vs raw string) but missed
|
|
241
|
+
* this env-propagation half of the same auth bug. Daily-planner, weekly
|
|
242
|
+
* review, bg-planner, and several other Haiku utility paths were still
|
|
243
|
+
* silently failing AFTER 1.18.192 because env: wasn't being passed.
|
|
244
|
+
*
|
|
245
|
+
* Use this alongside `claudeCodeSystemPrompt()` for every direct `query()`
|
|
246
|
+
* call. The runAgent path already builds its own env via buildRunAgentEnv;
|
|
247
|
+
* the assistant.ts auto-memory + verifier already use SAFE_ENV (same
|
|
248
|
+
* pattern). This helper exists for the lightweight utility callers.
|
|
249
|
+
*
|
|
250
|
+
* Priority order matches buildRunAgentEnv: OAuth > ANTHROPIC_AUTH_TOKEN > API key.
|
|
251
|
+
*/
|
|
252
|
+
export function claudeCodeSubprocessEnv() {
|
|
253
|
+
const env = {
|
|
254
|
+
PATH: process.env.PATH ?? '',
|
|
255
|
+
HOME: process.env.HOME ?? '',
|
|
256
|
+
LANG: process.env.LANG ?? 'en_US.UTF-8',
|
|
257
|
+
TERM: process.env.TERM ?? 'xterm-256color',
|
|
258
|
+
USER: process.env.USER ?? '',
|
|
259
|
+
SHELL: process.env.SHELL ?? '',
|
|
260
|
+
CLEMENTINE_HOME: BASE_DIR,
|
|
261
|
+
};
|
|
262
|
+
const oauthTok = CLAUDE_CODE_OAUTH_TOKEN || process.env.CLAUDE_CODE_OAUTH_TOKEN;
|
|
263
|
+
const authTok = process.env.ANTHROPIC_AUTH_TOKEN;
|
|
264
|
+
const apiKey = ANTHROPIC_API_KEY || process.env.ANTHROPIC_API_KEY;
|
|
265
|
+
if (oauthTok)
|
|
266
|
+
env.CLAUDE_CODE_OAUTH_TOKEN = oauthTok;
|
|
267
|
+
else if (authTok)
|
|
268
|
+
env.ANTHROPIC_AUTH_TOKEN = authTok;
|
|
269
|
+
else if (apiKey)
|
|
270
|
+
env.ANTHROPIC_API_KEY = apiKey;
|
|
271
|
+
return env;
|
|
272
|
+
}
|
|
228
273
|
export function normalizeClaudeModelForOneMillionContext(model, mode = currentOneMillionContextMode()) {
|
|
229
274
|
const family = modelFamily(model);
|
|
230
275
|
if (mode === 'on')
|
package/dist/gateway/router.d.ts
CHANGED
package/dist/gateway/router.js
CHANGED
|
@@ -49,15 +49,13 @@ 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
|
-
// 1.18.189 — tightened
|
|
53
|
-
//
|
|
54
|
-
//
|
|
55
|
-
//
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
// the model actually needs it.
|
|
52
|
+
// 1.18.189 — tightened cap on retry-recovery context. 1.18.194 — only
|
|
53
|
+
// CHAT_CONTEXT_RETRY_CONTEXT_MAX_CHARS still has a caller (the legacy
|
|
54
|
+
// buildContextOverflowRetryPrompt, exported for an existing unit test
|
|
55
|
+
// but no longer invoked from the chat path). The system-prompt-cap was
|
|
56
|
+
// removed when we deleted the in-line retry loop in favor of trusting
|
|
57
|
+
// the SDK's own autocompact.
|
|
59
58
|
const CHAT_CONTEXT_RETRY_CONTEXT_MAX_CHARS = 3_000;
|
|
60
|
-
const CHAT_CONTEXT_RETRY_SYSTEM_MAX_CHARS = 8_000;
|
|
61
59
|
const BACKGROUND_TASK_ID_RE = /\bbg-[a-z0-9]+-[a-f0-9]{6}\b/i;
|
|
62
60
|
function collectRunToolNames(runId) {
|
|
63
61
|
if (!runId)
|
|
@@ -429,46 +427,12 @@ export class Gateway {
|
|
|
429
427
|
`Use \`status ${task.id}\` or check the dashboard Background Tasks panel for progress.`,
|
|
430
428
|
].join('\n');
|
|
431
429
|
}
|
|
432
|
-
queueBackgroundTaskAfterContextOverflow
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
// decomposes the user's request into 3-7 PlanSteps; the
|
|
439
|
-
// orchestrator then dispatches one step at a time, each with
|
|
440
|
-
// its own fresh 200K worker window. See agent/bg-planner.ts +
|
|
441
|
-
// agent/bg-orchestrator.ts for the full pattern.
|
|
442
|
-
//
|
|
443
|
-
// The legacy `detectComplexTaskForBackground` heuristic is no
|
|
444
|
-
// longer used here — the planner itself decides how to decompose,
|
|
445
|
-
// and per-step maxMinutes is governed by orchestrator settings.
|
|
446
|
-
// Planner tasks get a tight 5-minute cap; total chain wall-clock
|
|
447
|
-
// is the sum of each step's own maxMinutes.
|
|
448
|
-
const task = createBackgroundTask({
|
|
449
|
-
fromAgent: this.backgroundAgentForSession(sessionKey),
|
|
450
|
-
prompt,
|
|
451
|
-
maxMinutes: 5, // planner needs minutes, not hours
|
|
452
|
-
sessionKey,
|
|
453
|
-
kind: 'planner',
|
|
454
|
-
});
|
|
455
|
-
logger.warn({
|
|
456
|
-
taskId: task.id,
|
|
457
|
-
sessionKey,
|
|
458
|
-
fromAgent: task.fromAgent,
|
|
459
|
-
kind: 'planner',
|
|
460
|
-
}, 'Queued planner task after repeated chat context overflow');
|
|
461
|
-
return {
|
|
462
|
-
task,
|
|
463
|
-
response: [
|
|
464
|
-
`The live chat context hit the limit, so I'm decomposing your request into chained steps via background task **${task.id}**.`,
|
|
465
|
-
'',
|
|
466
|
-
`Step 1: a Sonnet planner reads the request and emits a plan (~30 seconds).`,
|
|
467
|
-
`Then each step runs as its own fresh task — you'll see step-by-step updates rather than one big "done" at the end.`,
|
|
468
|
-
`Use \`status ${task.id}\` or the dashboard Background Tasks panel for progress.`,
|
|
469
|
-
].join('\n'),
|
|
470
|
-
};
|
|
471
|
-
}
|
|
430
|
+
// 1.18.194 — `queueBackgroundTaskAfterContextOverflow` removed.
|
|
431
|
+
// The chat-overflow path no longer auto-fires the planner. Instead
|
|
432
|
+
// the chat-error handler surfaces a clean "rephrase or `/plan`"
|
|
433
|
+
// message and trusts the SDK's own autocompact. The planner +
|
|
434
|
+
// orchestrator stay available as the implementation behind explicit
|
|
435
|
+
// `/plan` (see `_maybeHandlePlanMode`).
|
|
472
436
|
/**
|
|
473
437
|
* 1.18.191 — chat-side plan mode state machine.
|
|
474
438
|
*
|
|
@@ -2350,7 +2314,6 @@ export class Gateway {
|
|
|
2350
2314
|
// Interrupt flag was set but no useful partial text — just clear it.
|
|
2351
2315
|
delete sessState.pendingInterrupt;
|
|
2352
2316
|
}
|
|
2353
|
-
let contextOverflowRecoveryPrompt = '';
|
|
2354
2317
|
try {
|
|
2355
2318
|
// ── Canonical SDK chat path (Phase 5) ────────────────────────
|
|
2356
2319
|
// runAgent() owns chat. No legacy fallback — errors propagate
|
|
@@ -2493,7 +2456,6 @@ export class Gateway {
|
|
|
2493
2456
|
const chatSystemAppend = resolvedSkills && resolvedSkills.promptBlock
|
|
2494
2457
|
? (baseSystemAppend ? `${baseSystemAppend}\n\n${resolvedSkills.promptBlock}` : resolvedSkills.promptBlock)
|
|
2495
2458
|
: baseSystemAppend;
|
|
2496
|
-
const retrySystemAppend = trimContextRecoveryText(chatSystemAppend, CHAT_CONTEXT_RETRY_SYSTEM_MAX_CHARS);
|
|
2497
2459
|
// Per-turn context (recall + persistent learnings + silent
|
|
2498
2460
|
// blocks + security/toolset directives) — real chat only.
|
|
2499
2461
|
// Builder doesn't need recall of unrelated transcripts.
|
|
@@ -2638,34 +2600,12 @@ export class Gateway {
|
|
|
2638
2600
|
},
|
|
2639
2601
|
abortSignal: chatAc.signal,
|
|
2640
2602
|
});
|
|
2641
|
-
|
|
2642
|
-
|
|
2643
|
-
|
|
2644
|
-
|
|
2645
|
-
|
|
2646
|
-
|
|
2647
|
-
const retryPrompt = buildContextOverflowRetryPrompt({
|
|
2648
|
-
chatPrompt,
|
|
2649
|
-
turnContextPrefix,
|
|
2650
|
-
project: sess?.project ?? null,
|
|
2651
|
-
});
|
|
2652
|
-
contextOverflowRecoveryPrompt = retryPrompt;
|
|
2653
|
-
logger.info({
|
|
2654
|
-
sessionKey: effectiveSessionKey,
|
|
2655
|
-
hadResume: !!priorSdkSessionId,
|
|
2656
|
-
promptChars: finalPrompt.length,
|
|
2657
|
-
retryPromptChars: retryPrompt.length,
|
|
2658
|
-
systemAppendChars: chatSystemAppend.length,
|
|
2659
|
-
retrySystemAppendChars: retrySystemAppend.length,
|
|
2660
|
-
}, 'Context overflow — retrying current message in fresh SDK session');
|
|
2661
|
-
if (onProgress) {
|
|
2662
|
-
await onProgress('refreshing conversation context...').catch(() => { });
|
|
2663
|
-
}
|
|
2664
|
-
this.assistant.clearSession(effectiveSessionKey);
|
|
2665
|
-
return runAgent(retryPrompt, buildRunAgentChatOptions({
|
|
2666
|
-
...(retrySystemAppend ? { systemPromptAppend: retrySystemAppend } : {}),
|
|
2667
|
-
}));
|
|
2668
|
-
};
|
|
2603
|
+
// 1.18.194 — single SDK call. The SDK does its own autocompact
|
|
2604
|
+
// internally; we don't layer our own compress-and-retry on top.
|
|
2605
|
+
// If the SDK returns context_overflow (either thrown or as a
|
|
2606
|
+
// result with terminalReason), we surface a clean "rephrase or
|
|
2607
|
+
// /plan" message via the `case 'context_overflow':` handler
|
|
2608
|
+
// below. No more "Planning failed" half-finished chains.
|
|
2669
2609
|
let runAgentResult;
|
|
2670
2610
|
try {
|
|
2671
2611
|
runAgentResult = await runAgent(finalPrompt, buildRunAgentChatOptions({
|
|
@@ -2677,34 +2617,18 @@ export class Gateway {
|
|
|
2677
2617
|
if (chatAc.signal.aborted || classifyChatError(err) !== 'context_overflow') {
|
|
2678
2618
|
throw err;
|
|
2679
2619
|
}
|
|
2680
|
-
|
|
2620
|
+
// Re-throw so the outer catch's classifyChatError gets it
|
|
2621
|
+
// and routes to the 'context_overflow' case.
|
|
2622
|
+
throw err;
|
|
2681
2623
|
}
|
|
2682
2624
|
if (!chatAc.signal.aborted && runAgentResultIndicatesContextOverflow(runAgentResult)) {
|
|
2683
|
-
if (didContextOverflowRetry) {
|
|
2684
|
-
logger.info({
|
|
2685
|
-
sessionKey: effectiveSessionKey,
|
|
2686
|
-
subtype: runAgentResult.subtype,
|
|
2687
|
-
terminalReason: runAgentResult.terminalReason,
|
|
2688
|
-
textPreview: runAgentResult.text?.slice(0, 240),
|
|
2689
|
-
}, 'Context overflow result after retry — queueing background task');
|
|
2690
|
-
throw contextOverflowAfterRetryError();
|
|
2691
|
-
}
|
|
2692
2625
|
logger.info({
|
|
2693
2626
|
sessionKey: effectiveSessionKey,
|
|
2694
2627
|
subtype: runAgentResult.subtype,
|
|
2695
2628
|
terminalReason: runAgentResult.terminalReason,
|
|
2696
2629
|
textPreview: runAgentResult.text?.slice(0, 240),
|
|
2697
|
-
}, 'Context overflow result —
|
|
2698
|
-
|
|
2699
|
-
if (runAgentResultIndicatesContextOverflow(runAgentResult)) {
|
|
2700
|
-
logger.info({
|
|
2701
|
-
sessionKey: effectiveSessionKey,
|
|
2702
|
-
subtype: runAgentResult.subtype,
|
|
2703
|
-
terminalReason: runAgentResult.terminalReason,
|
|
2704
|
-
textPreview: runAgentResult.text?.slice(0, 240),
|
|
2705
|
-
}, 'Context overflow result after retry — queueing background task');
|
|
2706
|
-
throw contextOverflowAfterRetryError();
|
|
2707
|
-
}
|
|
2630
|
+
}, 'Context overflow result — autocompact ceiling reached, surfacing recovery message');
|
|
2631
|
+
throw new Error('context_overflow_after_autocompact');
|
|
2708
2632
|
}
|
|
2709
2633
|
if (ledgerRunMetadata) {
|
|
2710
2634
|
ledgerRunMetadata.runId = runAgentResult.runId;
|
|
@@ -2789,19 +2713,31 @@ export class Gateway {
|
|
|
2789
2713
|
applyOneMillionContextRecovery();
|
|
2790
2714
|
this.clearSession(effectiveSessionKey);
|
|
2791
2715
|
return oneMillionContextRecoveryMessage();
|
|
2792
|
-
case 'context_overflow':
|
|
2793
|
-
|
|
2716
|
+
case 'context_overflow': {
|
|
2717
|
+
// 1.18.194 — trust the SDK. By the time we see context_overflow
|
|
2718
|
+
// here, the SDK has ALREADY tried autocompact (it's built-in).
|
|
2719
|
+
// Our previous behavior was to compress + retry + queue a
|
|
2720
|
+
// separate planner background task. That layered our own
|
|
2721
|
+
// retry on top of the SDK's, and when any step in the planner
|
|
2722
|
+
// pipeline failed (auth, planning, chain dispatch), users saw
|
|
2723
|
+
// a confusing "Planning failed" message — that's what bit
|
|
2724
|
+
// Zach. SDK best practice: when autocompact + retry have both
|
|
2725
|
+
// failed, that's a real context ceiling. Surface a clean
|
|
2726
|
+
// message that gives the owner two recovery options, clear
|
|
2727
|
+
// the session pointer, and trust them to resend smaller or
|
|
2728
|
+
// opt in to explicit plan mode.
|
|
2729
|
+
logger.info({ sessionKey }, 'Context overflow — autocompact ceiling reached, resetting');
|
|
2794
2730
|
this.assistant.clearSession(effectiveSessionKey);
|
|
2795
|
-
|
|
2796
|
-
|
|
2797
|
-
|
|
2798
|
-
|
|
2799
|
-
|
|
2800
|
-
|
|
2801
|
-
|
|
2802
|
-
|
|
2803
|
-
|
|
2804
|
-
|
|
2731
|
+
const response = [
|
|
2732
|
+
"That work pushed past the context limit even with autocompact.",
|
|
2733
|
+
"I've reset our conversation. Two ways forward:",
|
|
2734
|
+
"",
|
|
2735
|
+
"1. Rephrase the task in smaller scope (e.g. 'just the first 10' instead of 'all 100')",
|
|
2736
|
+
"2. Use `/plan` to have me decompose it into chained workers before running",
|
|
2737
|
+
].join('\n');
|
|
2738
|
+
this.mirrorChatExchange(sessionKey, originalText, response, { model: 'chat-control' });
|
|
2739
|
+
return response;
|
|
2740
|
+
}
|
|
2805
2741
|
case 'auth':
|
|
2806
2742
|
this.recordAuthFailure();
|
|
2807
2743
|
return "I'm temporarily offline due to an authentication issue. The owner needs to re-authenticate — I'll recover automatically once it's resolved.";
|
package/dist/index.js
CHANGED
|
@@ -638,6 +638,8 @@ async function asyncMain() {
|
|
|
638
638
|
// 1.18.192 — preset form so SDK uses Claude Code subscription
|
|
639
639
|
// auth (raw string → API-key path → "Not logged in" for Max users).
|
|
640
640
|
systemPrompt: config.claudeCodeSystemPrompt('You are a memory consolidation assistant. Be concise.', { minimal: true }),
|
|
641
|
+
// 1.18.194 — propagate OAuth token to SDK subprocess.
|
|
642
|
+
env: config.claudeCodeSubprocessEnv(),
|
|
641
643
|
}),
|
|
642
644
|
});
|
|
643
645
|
for await (const msg of stream) {
|