TracingConfig interface, initTracing(), traceRouteDecision().
routeInference() emits Langfuse traces when LANGFUSE_PUBLIC_KEY is set.
Uses same @langfuse/otel + @langfuse/tracing pattern as system-bus.
Pi session resolves (not throws) on rate limit errors — errorMessage is
set on the message object in the message_end event, but the throw-based
fallback path (onPromptError) never sees it.
Now: message_end checks event.message.errorMessage for rate_limit (429)
and overloaded (529) patterns, calls onPromptError to trigger fallback
controller. Other API errors are logged and dropped (not routed to user).
Canonical TelemetryEmitter interface + createGatewayEmitter factory.
~120 call sites migrated from gateway/observability.ts.
model-fallback and message-store now import TelemetryEmitter from here.
Old observability.ts deleted. 3 tests.
All 4 consumer channels now implement Channel interface:
- TelegramChannel, SlackChannel, DiscordChannel, IMessageChannel
Redis remains separate as EventBridge port.
resolveModel() and providerForModel() now use catalog lookup from
@joelclaw/inference-router instead of hardcoded MODEL_PROVIDERS map.
Logs catalog resolution for observability. Falls back to env vars
and 'anthropic' default when catalog misses.
Both channel files now export classes implementing the Channel port interface.
Backward-compat named exports preserved — daemon.ts unchanged.
No behavioral changes.
795-line message-store.ts extracted to standalone package with:
- TelemetryEmitter DI (same pattern as model-fallback)
- ioredis as peer dep
- 6 tests passing
- Gateway consumers updated to import from package
- Old file deleted
Three fixes for gateway stability:
1. Proactive compaction now blocks the drain loop (awaited, not fire-and-forget).
Pauses fallback timeout watch during compaction and resumes after with a
fresh timeout window. Prevents false fallback triggers when compaction
takes >90s.
2. iMessage connectAndRun() now rejects (not resolves) when socket closes
before connecting. This preserves the exponential backoff counter —
previously every ENOENT resolved the promise, resetting backoff to 0
and causing 5s spam forever.
3. ModelFallbackController gains pauseTimeoutWatch()/resumeTimeoutWatch()
for known slow operations (compaction, session migration) where no
streaming tokens are expected.
- New @joelclaw/model-fallback with TelemetryEmitter port (DI)
- Gateway imports from package, passes emitGatewayOtel adapter
- FallbackConfig type owned by model-fallback package
- 6/6 unit tests passing (fake timers, telemetry, recovery)
- Removed gateway/src/model-fallback.ts
- New @joelclaw/markdown-formatter package with FormatConverter interface
- TelegramConverter as first implementation (Telegram HTML from AST)
- Shared parser, escape, chunker modules for future platform converters
- Removed gateway-local ast-telegram.ts (moved to shared package)
- 9/9 tests passing
LLM responses containing valid Telegram HTML tags (<b>, <code>, etc.) were
being escaped to <b> by escapeHtml() before markdown conversion.
Now protects valid Telegram tags with placeholders before escaping, same
pattern used for code blocks and links.
Also: added shipped + all ADR status variants to site status-config.
- 16 unit tests for inference-router (catalog + router)
- Gateway model-fallback.ts wired to inference-router catalog
- Langfuse traces every inference call (success + failure)
- Strict/permissive mode based on NODE_ENV
- All TS errors fixed across system-bus and gateway
- ADR status page: added shipped + all status variants
Proactive context health (ADR-0141):
- After each turn_end, estimate context token usage via session entries
- At 70%: log elevated warning + OTEL event
- At 85%: force proactive compaction BEFORE the next prompt blows up
- Context health exposed in gateway status payload (entries, tokens, percent, health)
iMessage exponential backoff:
- Replace fixed 5s reconnect with exponential backoff (5s → 5min max)
- Only log first failure and every 10th attempt to reduce log spam
- Reset backoff on successful connection
Outbound router:
- Log when no channel handler found (was silently returning)
Logs chatId, textLength, source, and success/failure for every
outbound Telegram send. Missing observability here made it impossible
to distinguish 'response generated but not sent' from 'response sent
but Telegram didn't deliver'.
- self-healing-router: major expansion (+520 lines)
- self-healing-investigator: enhanced diagnostics
- self-healing-gateway-bridge: new function for gateway recovery
- check-system-health: expanded health checks
- Observability triage and restart-worker updates
- New @joelclaw/inference-router package
- Refactored inference.ts with expanded capabilities
- Updated all system-bus functions to use new inference lib
- CLI and gateway package updates for inference-router imports
When the API returns 'prompt is too long':
1. First attempt: aggressive compaction + retry
2. If still overflowing: build compression summary from recent session
entries (last 10 user messages, last 3 assistant responses, channel info)
3. Force new session, inject summary as first message
4. Replay the failed message into the fresh session
5. Alert Joel via Telegram
The compression summary is built locally from the JSONL entries without
an LLM call (since the whole problem is we can't reach the API with
the current context). It captures enough recent activity to maintain
conversational continuity.
Addresses the silent failure where gateway processed inbound messages
(acked from stream) but every API call 400'd for hours with zero
recovery action.
The relative import ../../gateway/src/commands/config.ts caused CLI to
transitively resolve @joelclaw/inference-router from gateway's node_modules
context, which Bun couldn't find. Import GATEWAY_ALLOWED_MODELS directly
from the workspace package.