clementine-agent 1.18.191 → 1.18.193

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.
@@ -13,7 +13,7 @@ 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, SESSION_EXCHANGE_HISTORY_SIZE, SESSION_EXCHANGE_MAX_CHARS, INJECTED_CONTEXT_MAX_CHARS, PROJECTS_META_FILE, CRON_PROGRESS_DIR, CRON_REFLECTIONS_DIR, BUDGET, TASK_BUDGET_TOKENS, currentTimeZone, CLAUDE_CODE_OAUTH_TOKEN, ANTHROPIC_API_KEY as CONFIG_ANTHROPIC_API_KEY, claudeCodeDisableOneMillionForModel, currentOneMillionContextMode, normalizeClaudeModelForOneMillionContext, normalizeClaudeSdkOptionsForOneMillionContext, looksLikeClaudeOneMillionContextError, envSnapshot, } 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, SESSION_EXCHANGE_HISTORY_SIZE, SESSION_EXCHANGE_MAX_CHARS, INJECTED_CONTEXT_MAX_CHARS, PROJECTS_META_FILE, CRON_PROGRESS_DIR, CRON_REFLECTIONS_DIR, BUDGET, TASK_BUDGET_TOKENS, currentTimeZone, CLAUDE_CODE_OAUTH_TOKEN, ANTHROPIC_API_KEY as CONFIG_ANTHROPIC_API_KEY, claudeCodeDisableOneMillionForModel, claudeCodeSystemPrompt, currentOneMillionContextMode, normalizeClaudeModelForOneMillionContext, normalizeClaudeSdkOptionsForOneMillionContext, looksLikeClaudeOneMillionContextError, envSnapshot, } from '../config.js';
17
17
  import { summarizeIntegrationStatus } from '../config/integrations-registry.js';
18
18
  import { loadToolPreferences, computeAvailability, buildPromptInstruction, buildComposioStatusBlock, KNOWN_SERVICES, } from '../integrations/tool-preferences.js';
19
19
  import { loadClaudeIntegrations } from './mcp-bridge.js';
@@ -2841,7 +2841,10 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
2841
2841
  const stream = query({
2842
2842
  prompt: memPrompt,
2843
2843
  options: {
2844
- systemPrompt: 'You are a silent memory extraction agent. Save facts to the vault and exit.',
2844
+ // 1.18.192 preset form so Haiku call uses Claude Code subscription
2845
+ // auth. Without this, every chat exchange's auto-memory extractor
2846
+ // hit "Not logged in" silently in the background.
2847
+ systemPrompt: claudeCodeSystemPrompt('You are a silent memory extraction agent. Save facts to the vault and exit.', { minimal: true }),
2845
2848
  model: AUTO_MEMORY_MODEL,
2846
2849
  permissionMode: 'dontAsk',
2847
2850
  // MCP tool names live in allowedTools, not tools. See note at
@@ -3092,7 +3095,10 @@ You have a cost budget per message — not a hard turn limit. Work until the tas
3092
3095
  const stream = query({
3093
3096
  prompt: reflectionPrompt,
3094
3097
  options: {
3095
- systemPrompt: 'You are a task output verifier. Assess the output quality.',
3098
+ // 1.18.192 preset form so the verifier authenticates via the
3099
+ // Claude Code subscription. Every cron-job reflection was silently
3100
+ // skipping verification before this fix.
3101
+ systemPrompt: claudeCodeSystemPrompt('You are a task output verifier. Assess the output quality.', { minimal: true }),
3096
3102
  model: MODELS.haiku,
3097
3103
  permissionMode: 'dontAsk',
3098
3104
  tools: [],
@@ -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, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext } from '../config.js';
47
+ import { BASE_DIR, MODELS, applyOneMillionContextRecovery, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext } from '../config.js';
48
48
  const logger = pino({ name: 'clementine.bg-planner' });
49
49
  // ── Persistence ──────────────────────────────────────────────────────
50
50
  /**
@@ -268,12 +268,25 @@ function buildPlannerUserPrompt(opts) {
268
268
  async function runPlannerLlm(userPrompt, systemPrompt, model) {
269
269
  const { query } = await import('@anthropic-ai/claude-agent-sdk');
270
270
  let text = '';
271
+ // 1.18.192 — CRITICAL: use the `claude_code` preset for systemPrompt
272
+ // so this query uses Claude Code subscription auth (Max plan etc.).
273
+ // Raw `systemPrompt: string` tells the SDK to use API-key auth, which
274
+ // 99% of installs don't have configured — they're logged into Claude
275
+ // Code, not the Anthropic API. This was the "Not logged in · Please
276
+ // run /login" failure Ross's owner hit on 2026-05-12.
277
+ //
278
+ // The preset injects Claude Code's default system prompt; our planning
279
+ // instructions go in `append` and dominate behavior for the single
280
+ // turn (maxTurns: 1, so no agentic loop where bleed could compound).
271
281
  const stream = query({
272
282
  prompt: userPrompt,
273
283
  options: normalizeClaudeSdkOptionsForOneMillionContext({
274
284
  model,
275
285
  maxTurns: 1, // single shot — emit JSON, done
276
- systemPrompt,
286
+ // Planner uses the full preset (not `minimal`) — the planner benefits
287
+ // from knowing the working directory + git status so it can decompose
288
+ // accurately. See claudeCodeSystemPrompt() in config.ts.
289
+ systemPrompt: claudeCodeSystemPrompt(systemPrompt),
277
290
  }),
278
291
  });
279
292
  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, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
11
+ import { BASE_DIR, CRON_REFLECTIONS_DIR, TASKS_FILE, INBOX_DIR, MODELS, applyOneMillionContextRecovery, 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');
@@ -256,7 +256,9 @@ Rules:
256
256
  options: normalizeClaudeSdkOptionsForOneMillionContext({
257
257
  model: MODELS.haiku,
258
258
  maxTurns: 1,
259
- systemPrompt: 'You are a planning assistant. Analyze the context and produce a prioritized daily plan as JSON. Return only valid JSON, no markdown fencing.',
259
+ // 1.18.192 preset form so SDK uses Claude Code subscription auth
260
+ // (raw string → API-key auth → "Not logged in" failure for Max users).
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 }),
260
262
  }),
261
263
  });
262
264
  for await (const msg of stream) {
@@ -76,6 +76,34 @@ export declare function classifyMessageShape(text: string, opts?: {
76
76
  */
77
77
  export type PlanApprovalSignal = 'approve' | 'revise' | 'cancel' | 'other';
78
78
  export declare function detectPlanApproval(message: string): PlanApprovalSignal;
79
+ /**
80
+ * 1.18.193 — plan-mode opt-in detector.
81
+ *
82
+ * Plan-mode used to auto-trigger when `classifyMessageShape` flagged a
83
+ * message as 'multi-step'. That was too aggressive — Nora's April 28-29
84
+ * work (38 Bash calls in one chat session) would have been routed through
85
+ * the planner unnecessarily. Comparison vs friend's 1.18.62 install showed
86
+ * the auto-route was the main behavior divergence.
87
+ *
88
+ * Now plan-mode is opt-in via explicit owner intent:
89
+ * - Message starts with `/plan` (case-insensitive)
90
+ * - Message contains the `[plan-mode]` token anywhere
91
+ *
92
+ * The chat-overflow recovery path (queueBackgroundTaskAfterContextOverflow)
93
+ * still routes to the planner when the SDK session ACTUALLY overflows —
94
+ * that's a separate escape hatch, not an auto-trigger.
95
+ *
96
+ * Returns `{ requested: true, cleaned }` if the owner asked for plan mode,
97
+ * where `cleaned` is the message with the trigger token stripped.
98
+ * Returns `{ requested: false }` otherwise.
99
+ */
100
+ export type PlanModeRequest = {
101
+ requested: true;
102
+ cleaned: string;
103
+ } | {
104
+ requested: false;
105
+ };
106
+ export declare function detectPlanModeRequest(message: string): PlanModeRequest;
79
107
  /**
80
108
  * Generate a follow-up suggestion prompt suffix based on completed work.
81
109
  *
@@ -332,6 +332,16 @@ export function detectPlanApproval(message) {
332
332
  return 'revise';
333
333
  return 'other';
334
334
  }
335
+ const PLAN_MODE_TRIGGER = /^\s*\/plan\b|\[plan-mode\]/i;
336
+ export function detectPlanModeRequest(message) {
337
+ if (!message || !PLAN_MODE_TRIGGER.test(message))
338
+ return { requested: false };
339
+ const cleaned = message
340
+ .replace(/^\s*\/plan\b\s*/i, '')
341
+ .replace(/\[plan-mode\]/gi, '')
342
+ .trim();
343
+ return { requested: true, cleaned };
344
+ }
335
345
  /**
336
346
  * Generate a follow-up suggestion prompt suffix based on completed work.
337
347
  *
@@ -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, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
13
+ import { BASE_DIR, applyOneMillionContextRecovery, 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');
@@ -451,7 +451,8 @@ export async function probeAvailableTools(force = false) {
451
451
  const stream = query({
452
452
  prompt: 'ok',
453
453
  options: normalizeClaudeSdkOptionsForOneMillionContext({
454
- systemPrompt: 'Reply ok.',
454
+ // 1.18.192 — preset form for Claude Code subscription auth.
455
+ systemPrompt: claudeCodeSystemPrompt('Reply ok.', { minimal: true }),
455
456
  model: 'claude-haiku-4-5',
456
457
  permissionMode: 'dontAsk',
457
458
  mcpServers: externalMcpServers,
@@ -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, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../config.js';
15
+ import { BASE_DIR, GOALS_DIR, MODELS, applyOneMillionContextRecovery, 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');
@@ -26,7 +26,11 @@ async function llmJsonCall(prompt, systemPrompt) {
26
26
  options: normalizeClaudeSdkOptionsForOneMillionContext({
27
27
  model: MODELS.haiku,
28
28
  maxTurns: 1,
29
- systemPrompt,
29
+ // 1.18.192 — preset form so the SDK uses Claude Code subscription auth.
30
+ // Raw `systemPrompt: string` triggers API-key auth and "Not logged in"
31
+ // failures on Max-only installs. Logs confirmed weekly review was
32
+ // silently falling through to the fallback path here since the bug landed.
33
+ systemPrompt: claudeCodeSystemPrompt(systemPrompt, { minimal: true }),
30
34
  }),
31
35
  });
32
36
  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, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../../config.js';
17
+ import { MODELS, applyOneMillionContextRecovery, claudeCodeSystemPrompt, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, } from '../../config.js';
18
18
  export async function* parsePdf(filePath) {
19
19
  let buf;
20
20
  try {
@@ -94,7 +94,10 @@ async function ocrPdfViaClaude(filePath) {
94
94
  options: normalizeClaudeSdkOptionsForOneMillionContext({
95
95
  model: MODELS.haiku,
96
96
  maxTurns: 4, // Read tool call + response (a few turns of thinking is fine)
97
- systemPrompt: '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.',
97
+ // 1.18.192 preset form for Claude Code subscription auth.
98
+ // Without this, every scanned-PDF ingest hit "Not logged in" and
99
+ // silently fell back to empty OCR output.
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 }),
98
101
  // Claude Code's built-in Read tool handles PDFs (text + vision)
99
102
  tools: ['Read'],
100
103
  allowedTools: ['Read'],
@@ -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, currentTimeZone, looksLikeClaudeOneMillionContextError, normalizeClaudeSdkOptionsForOneMillionContext, setEnvOverride, } from '../config.js';
22
+ import { AGENTS_DIR, MEMORY_FILE, MODELS, SESSIONS_FILE, TIMEZONE, applyOneMillionContextRecovery, 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 +
@@ -6399,7 +6399,8 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
6399
6399
  options: normalizeClaudeSdkOptionsForOneMillionContext({
6400
6400
  model: MODELS.haiku,
6401
6401
  maxTurns: 3,
6402
- systemPrompt: '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.',
6402
+ // 1.18.192 preset form for Claude Code subscription auth.
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 }),
6403
6404
  allowedTools: [tool],
6404
6405
  mcpServers,
6405
6406
  permissionMode: 'dontAsk',
@@ -9725,7 +9726,8 @@ If the tool returns nothing or errors, return an empty array \`[]\`.`,
9725
9726
  options: normalizeClaudeSdkOptionsForOneMillionContext({
9726
9727
  model: 'claude-haiku-4-5-20251001',
9727
9728
  maxTurns: 1,
9728
- systemPrompt: 'You are a memory consolidation assistant. Extract only facts directly evidenced by the corpus. Be terse. Output exactly the requested format.',
9729
+ // 1.18.192 preset form for Claude Code subscription auth.
9730
+ 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 }),
9729
9731
  }),
9730
9732
  });
9731
9733
  for await (const msg of stream) {
package/dist/config.d.ts CHANGED
@@ -30,6 +30,39 @@ type ClaudeSdkOptionsLike = {
30
30
  [key: string]: unknown;
31
31
  };
32
32
  export declare function normalizeClaudeSdkOptionsForOneMillionContext<T extends ClaudeSdkOptionsLike>(options: T): T;
33
+ /**
34
+ * 1.18.192 — Build a `systemPrompt` value that uses the `claude_code` preset
35
+ * so the SDK query authenticates via Claude Code subscription (Max plan,
36
+ * CLAUDE_CODE_OAUTH_TOKEN) instead of falling back to ANTHROPIC_API_KEY.
37
+ *
38
+ * Why this matters: passing `systemPrompt` as a raw string tells the SDK
39
+ * "custom prompt, no preset" — which silently routes auth to API-key mode.
40
+ * On an install with no API key (the default for Max subscribers), the
41
+ * query fails with `Error: Not logged in · Please run /login`.
42
+ *
43
+ * Hot paths affected by this bug before 1.18.192: bg-planner, daily-planner,
44
+ * strategic-planner (weekly review), auto-memory extractor, task output
45
+ * verifier, MCP tool-inventory probe, dashboard data-enumerator, dashboard
46
+ * memory-consolidator, PDF OCR, periodic memory consolidation.
47
+ *
48
+ * Use `excludeDynamicSections: true` for lightweight Haiku utility calls
49
+ * where the Claude Code preamble (working-dir, git status, memory paths)
50
+ * is irrelevant noise — saves ~1-2K input tokens per call.
51
+ *
52
+ * @example Full preset (planner, agentic work)
53
+ * systemPrompt: claudeCodeSystemPrompt('You are a planner. Emit JSON.')
54
+ *
55
+ * @example Minimal preset (single-shot Haiku utility)
56
+ * systemPrompt: claudeCodeSystemPrompt('Be terse.', { minimal: true })
57
+ */
58
+ export declare function claudeCodeSystemPrompt(append: string, opts?: {
59
+ minimal?: boolean;
60
+ }): {
61
+ type: 'preset';
62
+ preset: 'claude_code';
63
+ append: string;
64
+ excludeDynamicSections?: boolean;
65
+ };
33
66
  export declare function normalizeClaudeModelForOneMillionContext(model: string, mode?: OneMillionContextMode): string;
34
67
  export declare function usesOneMillionContext(model: string | null | undefined, mode?: OneMillionContextMode, plan?: ClaudePlan): boolean;
35
68
  /**
package/dist/config.js CHANGED
@@ -192,6 +192,39 @@ export function normalizeClaudeSdkOptionsForOneMillionContext(options) {
192
192
  }
193
193
  return next;
194
194
  }
195
+ /**
196
+ * 1.18.192 — Build a `systemPrompt` value that uses the `claude_code` preset
197
+ * so the SDK query authenticates via Claude Code subscription (Max plan,
198
+ * CLAUDE_CODE_OAUTH_TOKEN) instead of falling back to ANTHROPIC_API_KEY.
199
+ *
200
+ * Why this matters: passing `systemPrompt` as a raw string tells the SDK
201
+ * "custom prompt, no preset" — which silently routes auth to API-key mode.
202
+ * On an install with no API key (the default for Max subscribers), the
203
+ * query fails with `Error: Not logged in · Please run /login`.
204
+ *
205
+ * Hot paths affected by this bug before 1.18.192: bg-planner, daily-planner,
206
+ * strategic-planner (weekly review), auto-memory extractor, task output
207
+ * verifier, MCP tool-inventory probe, dashboard data-enumerator, dashboard
208
+ * memory-consolidator, PDF OCR, periodic memory consolidation.
209
+ *
210
+ * Use `excludeDynamicSections: true` for lightweight Haiku utility calls
211
+ * where the Claude Code preamble (working-dir, git status, memory paths)
212
+ * is irrelevant noise — saves ~1-2K input tokens per call.
213
+ *
214
+ * @example Full preset (planner, agentic work)
215
+ * systemPrompt: claudeCodeSystemPrompt('You are a planner. Emit JSON.')
216
+ *
217
+ * @example Minimal preset (single-shot Haiku utility)
218
+ * systemPrompt: claudeCodeSystemPrompt('Be terse.', { minimal: true })
219
+ */
220
+ export function claudeCodeSystemPrompt(append, opts) {
221
+ return {
222
+ type: 'preset',
223
+ preset: 'claude_code',
224
+ append,
225
+ ...(opts?.minimal ? { excludeDynamicSections: true } : {}),
226
+ };
227
+ }
195
228
  export function normalizeClaudeModelForOneMillionContext(model, mode = currentOneMillionContextMode()) {
196
229
  const family = modelFamily(model);
197
230
  if (mode === 'on')
@@ -493,7 +493,7 @@ export class Gateway {
493
493
  */
494
494
  async _maybeHandlePlanMode(opts) {
495
495
  const sess = this.sessions.get(opts.sessionKey);
496
- const { detectPlanApproval } = await import('../agent/intent-classifier.js');
496
+ const { detectPlanApproval, detectPlanModeRequest } = await import('../agent/intent-classifier.js');
497
497
  const { planRequest, savePlan, loadPlan } = await import('../agent/bg-planner.js');
498
498
  const { dispatchChain } = await import('../agent/bg-orchestrator.js');
499
499
  // ── Path A: approval-pending ────────────────────────────────────
@@ -564,11 +564,31 @@ export class Gateway {
564
564
  // pending state. The model will see the user message normally.
565
565
  return { handled: false };
566
566
  }
567
- // ── Path B: multi-step entry ────────────────────────────────────
568
- if (opts.shape === 'multi-step' && sess) {
567
+ // ── Path B: explicit plan-mode entry (1.18.193) ─────────────────
568
+ //
569
+ // Plan mode is now OPT-IN, not auto-triggered by message shape.
570
+ // Default chat behavior matches 1.18.62: the SDK query runs the
571
+ // work in one continuous Sonnet session, like Nora did on April
572
+ // 28-29 (38 Bash calls in one session — no decomposition needed).
573
+ //
574
+ // The planner-orchestrator remains available for genuinely huge
575
+ // jobs, but the owner has to opt in:
576
+ //
577
+ // - Message starts with `/plan` (case-insensitive)
578
+ // - Message contains `[plan-mode]` token anywhere
579
+ //
580
+ // The chat-overflow escape hatch (queueBackgroundTaskAfterContext-
581
+ // Overflow → planner) still works as a separate path when the SDK
582
+ // session ACTUALLY overflows. That's the legitimate "this job is
583
+ // too big for one session" trigger.
584
+ //
585
+ // We keep shape classification (it still gates turn-context
586
+ // density for token savings on 'simple' messages), but shape no
587
+ // longer routes execution.
588
+ const planRequestSignal = detectPlanModeRequest(opts.userMessage);
589
+ if (planRequestSignal.requested && sess) {
590
+ const cleanedRequest = planRequestSignal.cleaned;
569
591
  try {
570
- // Stream a "thinking..." update so the user knows planning is
571
- // happening rather than seeing 30s of silence.
572
592
  if (opts.onText) {
573
593
  try {
574
594
  opts.onText('🤔 Planning the steps...');
@@ -576,7 +596,7 @@ export class Gateway {
576
596
  catch { /* non-fatal */ }
577
597
  }
578
598
  const plan = await planRequest({
579
- userRequest: opts.userMessage,
599
+ userRequest: cleanedRequest || opts.userMessage,
580
600
  originatingSessionKey: opts.sessionKey,
581
601
  ...(opts.activeProject ? { project: opts.activeProject } : {}),
582
602
  });
@@ -592,12 +612,14 @@ export class Gateway {
592
612
  };
593
613
  }
594
614
  catch (err) {
595
- logger.warn({ err, sessionKey: opts.sessionKey }, 'Plan mode: planRequest failed at entry');
615
+ logger.warn({ err, sessionKey: opts.sessionKey }, 'Plan mode: planRequest failed at explicit entry');
596
616
  // Fall through to normal chat. Better than blocking the owner.
597
617
  return { handled: false };
598
618
  }
599
619
  }
600
620
  // Not a plan-mode case — fall through to normal chat.
621
+ // This is the path 99% of messages take. Like 1.18.62 — the SDK
622
+ // query runs the work in one continuous Sonnet session.
601
623
  return { handled: false };
602
624
  }
603
625
  /** Format a plan for owner approval in chat. */
package/dist/index.js CHANGED
@@ -635,7 +635,9 @@ async function asyncMain() {
635
635
  options: config.normalizeClaudeSdkOptionsForOneMillionContext({
636
636
  model: 'claude-haiku-4-5-20251001',
637
637
  maxTurns: 1,
638
- systemPrompt: 'You are a memory consolidation assistant. Be concise.',
638
+ // 1.18.192 preset form so SDK uses Claude Code subscription
639
+ // auth (raw string → API-key path → "Not logged in" for Max users).
640
+ systemPrompt: config.claudeCodeSystemPrompt('You are a memory consolidation assistant. Be concise.', { minimal: true }),
639
641
  }),
640
642
  });
641
643
  for await (const msg of stream) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.18.191",
3
+ "version": "1.18.193",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",