feat(planning): full chat history + auto-advance phases

Three fixes that together restore the planning UX:

- Dashboard reopen showed only a 500-char truncated excerpt of the last
  assistant message. Backend now reads the Claude session JSONL directly
  and returns full per-turn history; frontend iterates and renders all
  bubbles, falling back to last_text_excerpt when the JSONL is missing.
- Phases never advanced because the agent ran /plan-* skills inline as
  tool calls and the marker protocol was loose. Tightened the planning
  prompt (mandatory PHASE_STATUS marker on the last line of every turn,
  ban on inline phase invocation), and the frontend now auto-calls
  /plan/advance when phase_ready=true.
- The phase strip never showed visual state because data-phase values
  ("office-hours") didn't match orchestrator phase names ("/office-hours").
  Added normalizePhase + cleanup of PHASE_STATUS markers from rendered
  bubbles.

Also bumps eco.py session-content truncation from 2k to 20k so /eco
session views aren't cut mid-response either.

Bumps last_text_excerpt fallback in planning_session.py from 500 to
50_000 so even when the JSONL is unavailable, the bubble isn't sliced
mid-word.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-05 07:47:10 +00:00
parent d0faeed181
commit 8432fe3150
5 changed files with 197 additions and 22 deletions

View File

@@ -155,7 +155,7 @@ class EcoHandlers:
if isinstance(content, str):
text = content.replace('[EXTERNAL CONTENT]\n', '').replace('\n[END EXTERNAL CONTENT]', '').strip()
if text:
messages.append({'role': 'user', 'text': text[:2000]})
messages.append({'role': 'user', 'text': text[:20000]})
elif t == 'assistant':
msg = d.get('message', {})
content = msg.get('content', '')
@@ -163,9 +163,9 @@ class EcoHandlers:
parts = [block['text'] for block in content if block.get('type') == 'text']
text = '\n'.join(parts).strip()
if text:
messages.append({'role': 'assistant', 'text': text[:2000]})
messages.append({'role': 'assistant', 'text': text[:20000]})
elif isinstance(content, str) and content.strip():
messages.append({'role': 'assistant', 'text': content[:2000]})
messages.append({'role': 'assistant', 'text': content[:20000]})
self.send_json({'messages': messages})
except Exception as e: