Skip to content

feat(workflow): observe Codex and Claude agent runs - #153

Open
Waishnav wants to merge 3 commits into
codex/workflow-navigatorfrom
codex/workflow-provider-observability
Open

feat(workflow): observe Codex and Claude agent runs#153
Waishnav wants to merge 3 commits into
codex/workflow-navigatorfrom
codex/workflow-provider-observability

Conversation

@Waishnav

@Waishnav Waishnav commented Aug 8, 2026

Copy link
Copy Markdown
Owner

This layer adds a provider-neutral observation contract and wires native Codex and Claude SDK events into workflow persistence. Session ids and normalized activity are written immediately; partial token snapshots are throttled to at most once every five seconds and final usage is written immediately. Usage from schema-enforcement retries is accumulated under the same workflow call, and the observer always flushes before a provider run exits.\n\nDepends on #152.

Summary by CodeRabbit

  • New Features
    • Added live session, activity, and token-usage updates during local agent runs.
    • Workflow runs now record agent sessions, tool activity, and cumulative usage.
    • Streamed agent responses are supported with final usage details.
  • Bug Fixes
    • Improved handling of token metrics, including missing or invalid values.
    • Ensured usage updates are saved reliably during retries and run completion.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Local-agent runtimes and adapters now report sessions, activities, and token usage through optional observers. Workflow execution persists these updates, associates them with provider call indexes, and flushes pending usage when runs complete.

Changes

Local-agent observability

Layer / File(s) Summary
Runtime streaming and observer contracts
src/local-agent-runtime.ts, src/local-agent-runtime.test.ts
The runtime defines observer and usage types, processes streamed Codex events, reports sessions and activities, maps token usage, and returns streamed responses. Tests cover these callbacks and results.
Adapter observer forwarding and Claude events
src/local-agent-adapters.ts
Provider wrappers forward observers to Codex and Claude. Claude reports session IDs, tool activity, normalized usage, and final usage.
Workflow observer persistence
src/workflow-agent-observer.ts
The workflow observer records sessions and activities, accumulates usage, throttles interim persistence, and flushes pending updates on close.
Workflow execution wiring and validation
src/workflow-api.ts, src/workflow-worker.ts, src/workflow-store.test.ts
Provider inputs carry call indexes. Workers create and close observers around provider execution. Tests verify session, activity, cumulative usage, and final usage updates.

Estimated code review effort: 4 (Complex) | ~45 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WorkflowWorker
  participant LocalAgentProvider
  participant LocalAgentRuntime
  participant WorkflowAgentObserver
  participant WorkflowStore
  WorkflowWorker->>WorkflowAgentObserver: create observer(runId, callIndex)
  WorkflowWorker->>LocalAgentProvider: run provider with observer
  LocalAgentProvider->>LocalAgentRuntime: run(input, observer)
  LocalAgentRuntime->>WorkflowAgentObserver: session, activity, and usage callbacks
  WorkflowAgentObserver->>WorkflowStore: persist workflow updates
  WorkflowWorker->>WorkflowAgentObserver: close()
  WorkflowAgentObserver->>WorkflowStore: flush pending usage
Loading

Possibly related PRs

Poem

A rabbit watched the tokens flow,
Through tools that ran and streams that glow.
Sessions hopped to workflow ground,
While usage sums were safely bound.
At close, the last notes stayed in sight. 🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: adding observation for Codex and Claude agent runs.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/workflow-provider-observability

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@Waishnav Waishnav changed the title codex/workflow provider observability feat(workflow): observe Codex and Claude agent runs Aug 8, 2026
@greptile-apps

greptile-apps Bot commented Aug 8, 2026

Copy link
Copy Markdown

Greptile Summary

The PR adds live provider observability for workflow agent calls, including sessions, token usage, and tool activity.

  • Streams Codex events into normalized activity and usage snapshots.
  • Extracts equivalent observability data from Claude messages.
  • Persists provider observations through a throttled workflow observer.
  • Threads workflow call indexes through provider execution and adds store/runtime coverage.

Confidence Score: 4/5

The usage accounting defect should be fixed before merging because schema retries currently under-report provider token consumption.

Schema enforcement can invoke the provider multiple times under one call index, while each newly created observer replaces the stored usage with its own final snapshot, dropping tokens consumed by earlier attempts.

Files Needing Attention: src/workflow-worker.ts, src/workflow-agent-observer.ts

Important Files Changed

Filename Overview
src/local-agent-runtime.ts Adds Codex streamed-event collection and normalized session, activity, and token-usage reporting.
src/local-agent-adapters.ts Propagates observers through local-agent adapters and extracts Claude session, activity, and usage events.
src/workflow-agent-observer.ts Introduces throttled persistence for workflow-agent observations, but usage snapshots replace rather than aggregate schema-retry attempts.
src/workflow-worker.ts Creates an observer around every provider invocation, causing retry attempts sharing a call index to overwrite prior usage.
src/workflow-api.ts Adds the stable workflow call index to provider-run inputs.
src/local-agent-runtime.test.ts Covers the normalized Codex streaming happy path for sessions, activities, final responses, and usage.
src/workflow-store.test.ts Covers persistence of observed sessions, activity, and final usage snapshots.

Sequence Diagram

sequenceDiagram
  participant W as Workflow API
  participant P as Provider runner
  participant O as Workflow observer
  participant S as Workflow store
  W->>P: Run agent call (callIndex)
  P->>O: Session/activity/usage callbacks
  O->>S: Persist observations
  alt Schema validation fails
    W->>P: Retry with same callIndex
    P->>O: Create new observer
    O->>S: Replace usage snapshot
  end
  P-->>W: Final provider result
Loading

Reviews (1): Last reviewed commit: "feat(workflow): persist live provider ob..." | Re-trigger Greptile

Comment thread src/workflow-worker.ts
@Waishnav
Waishnav force-pushed the codex/workflow-provider-observability branch from 63e67b8 to 84fc2ee Compare August 8, 2026 17:30
@Waishnav

Waishnav commented Aug 9, 2026

Copy link
Copy Markdown
Owner Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 9, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/local-agent-adapters.ts`:
- Around line 188-198: Update the token aggregate in the usage return block to
include cacheReadInputTokens and cacheCreationInputTokens alongside inputTokens
and outputTokens when calculating totalTokens. Preserve the existing
non-negative normalization and undefined handling in the surrounding token
fields.
- Around line 127-128: Update the Claude record-processing flow around
claudeUsage and observer.onUsage so usage is parsed for every received record,
emitting a partial snapshot for non-result records and retaining final for the
result record. Add coverage for an intermediate usage record verifying the
observer receives the partial update.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 49413a83-95bf-4d43-9a58-8aa4c182193c

📥 Commits

Reviewing files that changed from the base of the PR and between 3aadb3c and 84fc2ee.

📒 Files selected for processing (7)
  • src/local-agent-adapters.ts
  • src/local-agent-runtime.test.ts
  • src/local-agent-runtime.ts
  • src/workflow-agent-observer.ts
  • src/workflow-api.ts
  • src/workflow-store.test.ts
  • src/workflow-worker.ts

Comment on lines +127 to +128
const usage = claudeUsage(record.usage, "final");
if (usage) observer?.onUsage?.(usage);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Emit partial Claude usage updates.

Lines 127-128 call onUsage only for a result record. Line 119 skips this block for every non-result record. The observer therefore never receives a state: "partial" snapshot.

Parse usage on each received record. Emit partial for non-result records. Keep the result record as final. Add a test with an intermediate usage record.

Proposed fix
         notifyClaudeActivity(record, observer);
+        const usage = claudeUsage(record.usage, record.type === "result" ? "final" : "partial");
+        if (usage) observer?.onUsage?.(usage);
         if (record.type !== "result") continue;
         const resultError = claudeResultError(record);
         if (resultError) throw new Error(resultError);
@@
-        const usage = claudeUsage(record.usage, "final");
-        if (usage) observer?.onUsage?.(usage);
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const usage = claudeUsage(record.usage, "final");
if (usage) observer?.onUsage?.(usage);
notifyClaudeActivity(record, observer);
const usage = claudeUsage(record.usage, record.type === "result" ? "final" : "partial");
if (usage) observer?.onUsage?.(usage);
if (record.type !== "result") continue;
🧰 Tools
🪛 ast-grep (0.45.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-adapters.ts` around lines 127 - 128, Update the Claude
record-processing flow around claudeUsage and observer.onUsage so usage is
parsed for every received record, emitting a partial snapshot for non-result
records and retaining final for the result record. Add coverage for an
intermediate usage record verifying the observer receives the partial update.

Comment on lines +188 to +198
const inputTokens = nonNegativeInteger(usage.input_tokens);
const outputTokens = nonNegativeInteger(usage.output_tokens);
if (inputTokens === undefined && outputTokens === undefined) return undefined;
return {
inputTokens,
cachedInputTokens: nonNegativeInteger(usage.cache_read_input_tokens),
cacheCreationInputTokens: nonNegativeInteger(usage.cache_creation_input_tokens),
outputTokens,
totalTokens: (inputTokens ?? 0) + (outputTokens ?? 0),
state,
};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf 'Files matching local-agent-adapters.ts:\n'
fd -a 'local-agent-adapters\.ts$' . || true

printf '\nRelevant file excerpt:\n'
file="$(fd 'local-agent-adapters\.ts$' . | head -n 1 || true)"
if [ -n "${file:-}" ]; then
  wc -l "$file"
  sed -n '150,220p' "$file" | nl -ba -v150
fi

printf '\nSearch usage of function/totalTokens/cached tokens:\n'
rg -n "totalTokens|cache_read_input_tokens|cache_creation_input_tokens|inputTokens|cachedInputTokens|cacheCreationInputTokens|nonNegativeInteger" .

Repository: Waishnav/devspace

Length of output: 341


🌐 Web query:

Claude Prompt Caching total input tokens uncached input cache read cache creation documentation

💡 Result:

Claude prompt caching allows you to store and reuse portions of your prompt (the "prefix") across multiple API requests, significantly reducing latency and cost [1][2]. The total input tokens for a request using prompt caching are calculated as the sum of three distinct categories provided in the usage statistics [3][4]: total_input_tokens = cache_read_input_tokens + cache_creation_input_tokens + input_tokens Here is the breakdown of these terms: 1. cache_read_input_tokens: The number of tokens retrieved from an existing cache entry [3][5]. These are billed at 10% of the standard input token price [3][1]. 2. cache_creation_input_tokens: The number of tokens being written to a new cache entry (or a refresh) [3][5]. These are billed at a premium (1.25x the base input price for a 5-minute TTL, or 2x for a 1-hour TTL) [1][6]. 3. input_tokens: The number of "uncached" tokens, representing only the content that appears after the last cache breakpoint in your request [3][4]. These are billed at the standard input token rate [3][1]. Key operational details: - Prefix Matching: Caching is based on exact "prefix matching" [7][8]. The API caches content from the start of the request up to designated cache breakpoints [5][7]. If any part of the prompt before a breakpoint changes, the cache is invalidated for that breakpoint and all subsequent ones [9][4][8]. - Diagnostics: If you observe cache_read_input_tokens dropping to zero unexpectedly, you can use cache diagnostics to determine if the prefix has changed or if the cache entry has expired [9]. - Ordering Matters: Because the cache is a byte-for-byte prefix match, the order of content—such as tool definitions, system prompts, and message history—is critical [7][8]. Organizing your prompt to maximize stable, shared prefixes is the most effective way to utilize this feature [7].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file="$(fd 'local-agent-adapters\.ts$' . | head -n 1)"
printf 'File: %s\n' "$file"

printf '\nLines 180-205:\n'
awk 'NR>=180 && NR<=205 {printf "%6d  %s\n", NR, $0}' "$file"

printf '\nLines 220-290:\n'
awk 'NR>=220 && NR<=290 {printf "%6d  %s\n", NR, $0}' "$file"

printf '\nSearch relevant usages:\n'
rg -n "totalTokens|cache_read_input_tokens|cache_creation_input_tokens|inputTokens|cachedInputTokens|cacheCreationInputTokens|nonNegativeInteger|persist|observer" src || true

Repository: Waishnav/devspace

Length of output: 16327


Include Claude cache tokens in totalTokens.

totalTokens currently sums only inputTokens and outputTokens, then persists the value through the workflow agent observer. Claude prompt-cached requests define total input tokens as input_tokens + cache_read_input_tokens + cache_creation_input_tokens, so adjust the aggregate to include the cached token fields before storing it.

🧰 Tools
🪛 ast-grep (0.45.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from "node:child_process";
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/local-agent-adapters.ts` around lines 188 - 198, Update the token
aggregate in the usage return block to include cacheReadInputTokens and
cacheCreationInputTokens alongside inputTokens and outputTokens when calculating
totalTokens. Preserve the existing non-negative normalization and undefined
handling in the surrounding token fields.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant