compactWithReason runs a session compaction with the supplied reason and emits a TokenUsageEvent so the UI immediately reflects the new context pressure. reason is reported to BeforeCompaction / AfterCompaction hooks as CompactionReason. Use [compactionReasonThreshold] for proactive 90%-of-context
(ctx context.Context, sess *session.Session, additionalPrompt, reason string, events EventSink)
| 1846 | // hooks then fire inside [LocalRuntime.doCompact] with [Input.CompactionReason] |
| 1847 | // set to the canonical reason; they may veto or supply a custom summary. |
| 1848 | func (r *LocalRuntime) compactWithReason(ctx context.Context, sess *session.Session, additionalPrompt, reason string, events EventSink) { |
| 1849 | // Stamp the session ID on ctx so the compaction LLM call carries |
| 1850 | // `X-Cagent-Session-Id` to the gateway. Manual compaction |
| 1851 | // (via `Summarize` from the App) bypasses `runStreamLoop`'s seed; |
| 1852 | // internal callers (proactive threshold, overflow recovery) already |
| 1853 | // run with a stamped ctx, but re-stamping is idempotent. |
| 1854 | ctx = httpclient.ContextWithSessionID(ctx, sess.ID) |
| 1855 | a := r.resolveSessionAgent(sess) |
| 1856 | |
| 1857 | source := preCompactSourceFor(reason) |
| 1858 | skip, msg, extraPrompt := r.executePreCompactHooks(ctx, sess, a, source, events) |
| 1859 | if skip { |
| 1860 | slog.WarnContext(ctx, "pre_compact hook signalled skip", |
| 1861 | "agent", a.Name(), "session_id", sess.ID, "source", source, "reason", msg) |
| 1862 | if msg != "" { |
| 1863 | events.Emit(Warning(msg, a.Name())) |
| 1864 | } |
| 1865 | return |
| 1866 | } |
| 1867 | additionalPrompt = joinPrompts(additionalPrompt, extraPrompt) |
| 1868 | |
| 1869 | r.doCompact(ctx, sess, a, additionalPrompt, reason, events) |
| 1870 | |
| 1871 | // Emit a TokenUsageEvent so the sidebar immediately reflects the |
| 1872 | // compaction: tokens drop to the summary size, context % drops, and |
| 1873 | // cost increases by the summary generation cost. |
| 1874 | modelID := r.getEffectiveModelID(ctx, a) |
| 1875 | contextLimit := r.effectiveContextLimit(ctx, a, r.resolveContextLimit(ctx, a.Model(ctx), modelID)) |
| 1876 | events.Emit(NewTokenUsageEvent(sess.ID, a.Name(), SessionUsage(sess, contextLimit))) |
| 1877 | } |
| 1878 | |
| 1879 | // preCompactSourceFor maps the canonical compaction reason |
| 1880 | // ([compactionReasonThreshold] / [compactionReasonOverflow] / |