feat(pi): add real-time context window reporting with configurable polling interval#1883
feat(pi): add real-time context window reporting with configurable polling interval#1883theslava wants to merge 5 commits into
Conversation
…lling interval
Add periodic usage polling during active turns via get_session_stats RPC.
New provider params under agents.providers.{pi|omp}.params:
- contextWindowReportingEnabled (boolean, default false) — opt-in feature
- pollingIntervalMs (number, default 5000ms) — customizes poll frequency
Uses milestone-based emission at tool_execution_end and compaction_end
events for zero-overhead granularity between polls. Deduplicates rapid
poll results via buildUsageKey().
Adds 3 integration tests against live OMP binary to verify the full
stack end-to-end.
| contextWindowReportingEnabled: true, | ||
| }); | ||
| // No turn started yet — no interval should exist. | ||
| expect((session as unknown as Record<string, unknown>).usagePollInterval).toBe(null); |
There was a problem hiding this comment.
Tests reach through the module interface into private state
Three tests assert against (session as unknown as Record<string, unknown>).usagePollInterval. This is an implementation detail — callers of PiRpcAgentSession never see this field. The module's observable contract is the stream of AgentStreamEvent values; usagePollInterval being non-null is a side effect of the interval being active, not a behavior the caller cares about.
The relevant behavior is whether a usage_updated event arrives (or doesn't) within a polling window. The "starts polling" test could instead advance fake timers by one interval, drain microtasks, and assert a usage_updated event appears on the subscriber. The "stops on close" test could assert that no further events arrive after close(). Accessing private fields via cast is flagged in the project's test discipline rules as a sign the test is asserting the wrong thing.
Rule Used: # Code Review Pattern Reference: Slop, Tests, Feat... (source)
Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| @@ -1014,7 +1032,9 @@ export class PiRpcAgentSession implements AgentSession { | |||
| this.state.thinkingLevel ?? | |||
| null; | |||
There was a problem hiding this comment.
lastEmittedUsageKey carries over across turns
lastEmittedUsageKey is initialised to null once at construction and never cleared when a new turn starts. Pi's token stats are cumulative, so at the very beginning of turn 2 the stats snapshot will still equal the last snapshot from turn 1. The first poll of the new turn will compare the new key against that stale last-emission key, see no change, and silently drop the first usage_updated event for the turn.
Reset lastEmittedUsageKey = null at the top of startTurn (after setting activeTurnId) so the new turn always emits at least one update.
- Add missing vitest imports (describe, expect, test) to omp-integration.test.ts
causing 'describe is not defined' crash on CI
- Replace conditional assertions with unconditional shape checks in
omp-integration tests per testing discipline rules
- Wrap all three vi.useFakeTimers() tests in try/finally { vi.useRealTimers() }
to prevent fake timer leaks corrupting subsequent tests
Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
Linked issue
Closes # (behavioral improvement — no prior issue)
Type of change
What does this PR do
Adds real-time context window usage reporting for Pi agents during active turns, configurable via provider params.
New provider params under
agents.providers.{pi|omp}.params:contextWindowReportingEnabled(boolean, default false) — opt-in feature gatepollingIntervalMs(number, default 5000ms) — customizes poll frequencyChanges:
tool_execution_endandcompaction_endevents for zero-overhead granularity between pollsbuildUsageKey()Files changed:
packages/server/src/server/agent/providers/pi/agent.ts(+115 lines)packages/server/src/server/agent/providers/pi/agent.test.ts(+187 lines)packages/server/src/server/agent/providers/pi/omp-integration.test.ts(3 new E2E tests against live OMP binary)How did you verify it
npm run build:server— passesChecklist
npm run typecheckpassesnpm run lintpassesnpm run formatran (Biome)