clementine-agent 1.18.192 → 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.
@@ -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
  *
@@ -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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clementine-agent",
3
- "version": "1.18.192",
3
+ "version": "1.18.193",
4
4
  "description": "Clementine — Personal AI Assistant (TypeScript)",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",