Skip to content

refactor: consolidate extractAgentResponse into shared#423

Merged
ColeMurray merged 3 commits intomainfrom
refactor/shared-extract-agent-response
Mar 28, 2026
Merged

refactor: consolidate extractAgentResponse into shared#423
ColeMurray merged 3 commits intomainfrom
refactor/shared-extract-agent-response

Conversation

@ColeMurray
Copy link
Copy Markdown
Owner

@ColeMurray ColeMurray commented Mar 28, 2026

Summary

  • Moved the duplicated extractAgentResponse module and all its helpers (summarizeToolCall, getArtifactLabel, getArtifactLabelFromArtifact, toEventArtifactInfo, toArtifactType, SUMMARY_TOOL_NAMES) from slack-bot and linear-bot into @open-inspect/shared at packages/shared/src/completion/extractor.ts
  • The shared version accepts an ExtractorDeps interface (fetcher, internalSecret, log) instead of a package-specific Env type, decoupling the logic from Cloudflare Worker bindings
  • Both consumer packages now have thin wrappers that map their Env bindings into ExtractorDeps and delegate to the shared implementation
  • Linear-specific formatAgentResponse remains in the linear-bot package

Test plan

  • npm run build -w @open-inspect/shared passes
  • npm run typecheck passes for all 7 packages (shared, control-plane, web, slack-bot, linear-bot, github-bot)
  • npm test -w @open-inspect/slack-bot — 30/30 tests pass (including extractor tests)
  • npm test -w @open-inspect/linear-bot — 90/90 tests pass
  • npm test -w @open-inspect/shared — 59/59 tests pass
  • npm run lint passes

Summary by CodeRabbit

  • Refactor
    • Centralized agent response extraction and artifact handling into a shared implementation used by all bots, improving consistency, reliability, and logging while preserving public APIs and behavior.
    • Shared utilities and summary constants are now provided and re-exported from the common package, reducing duplication across services.

Move the duplicated extractAgentResponse module and its helpers
(summarizeToolCall, getArtifactLabel, toEventArtifactInfo, etc.)
from slack-bot and linear-bot into the shared package.

The shared version accepts an ExtractorDeps interface (fetcher,
internalSecret, log) instead of a package-specific Env, so both
consumers now delegate to the shared implementation via a thin
adapter that maps their Env bindings.
Copy link
Copy Markdown
Contributor

@greptile-apps greptile-apps Bot left a comment

Choose a reason for hiding this comment

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

Your free trial has ended. If you'd like to continue receiving code reviews, you can add a payment method here.

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 28, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Agent response extraction was moved into a new shared module. Bot-specific extractor files were replaced with thin wrappers that delegate pagination, token aggregation, tool-call summarization, artifact fetching, and logging to the shared extractAgentResponse.

Changes

Cohort / File(s) Summary
Shared Extractor Implementation
packages/shared/src/completion/extractor.ts
New shared extractAgentResponse implementation: builds headers (optional internal HMAC, trace), paginates /sessions/:sessionId/events, derives final text from token events, summarizes tool calls, fetches /sessions/:sessionId/artifacts (with fallback to artifact events), maps artifact types/labels, and logs results. Exposes helpers and types.
Shared Package Exports
packages/shared/src/index.ts
Re-exported the new completion extractor public API via export * from "./completion/extractor";.
Linear Bot Wrapper
packages/linear-bot/src/completion/extractor.ts
Replaced local extraction logic with a wrapper that calls @open-inspect/shared's extractAgentResponse, passing { fetcher: env.CONTROL_PLANE, internalSecret: env.INTERNAL_CALLBACK_SECRET, log }, plus sessionId, messageId, and optional traceId. Removed local helpers and direct control-plane fetches.
Slack Bot Wrapper & Re-exports
packages/slack-bot/src/completion/extractor.ts
Replaced local extraction logic with a delegating wrapper to @open-inspect/shared. Re-exports SUMMARY_TOOL_NAMES and helper functions (summarizeToolCall, getArtifactLabel, getArtifactLabelFromArtifact, toEventArtifactInfo, toArtifactType) from the shared module; removed local implementations and direct fetch/error handling.

Sequence Diagram(s)

mermaid
sequenceDiagram
participant Bot as Bot wrapper
participant Shared as Shared extractor
participant CP as Control‑Plane API
participant ART as Artifacts API
participant Log as Logger

Bot->>Shared: extractAgentResponse(sessionId, messageId, traceId)
Shared->>CP: GET /sessions/:sessionId/events (paginated, trace header)
CP-->>Shared: event pages (tokens, tool_call, artifact, execution_complete)
Shared->>ART: GET /sessions/:sessionId/artifacts (internal auth if provided)
ART-->>Shared: artifacts list (or error → fallback to event artifacts)
Shared->>Log: log timing, counts, success/error
Shared-->>Bot: { textContent, toolCalls, artifacts, success }

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Poem

🐰 I hopped through events, tokens in a line,
Gathered artifacts, stitched outputs fine.
One shared burrow now holds the trail—
Bots whisper answers, swift as a tail. ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 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 primary refactoring: consolidating the extractAgentResponse module and helpers from multiple packages (slack-bot, linear-bot) into the shared package.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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 refactor/shared-extract-agent-response

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

@github-actions
Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

Copy link
Copy Markdown
Contributor

@open-inspect open-inspect Bot left a comment

Choose a reason for hiding this comment

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

Summary

This PR moves the duplicated event/artifact extraction logic into @open-inspect/shared and leaves thin Slack/Linear adapters behind. I reviewed 4 changed files (+352/-421) and the refactor looks behavior-preserving overall, with the dependency injection boundary keeping the shared code appropriately decoupled from Worker-specific Env types.

Critical Issues

  • None.

Suggestions

  • [Testing] packages/shared/src/completion/extractor.ts - Consider adding shared-package unit tests for the extracted helpers and extractAgentResponse itself. The existing Slack tests still cover core behavior, but now that this logic lives in @open-inspect/shared, package-local tests would better protect future refactors without relying on consumer test suites.

Nitpicks

  • None.

Positive Feedback

  • The ExtractorDeps interface is a clean abstraction that removes direct Env coupling without over-engineering the API.
  • Preserving the Linear-specific formatter in linear-bot keeps the shared module focused on extraction only.
  • Re-exporting Slack compatibility helpers minimizes downstream churn while still consolidating the implementation.

Questions

  • None.

Verdict

Approve: Ready to merge, no blocking issues.

@github-actions
Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/shared/src/completion/extractor.ts (1)

42-45: ExtractorDeps.fetcher interface overclaims support for generic HTTP clients.

The interface documents fetcher as accepting "any HTTP-capable client", but both event and artifact fetches use hard-coded https://internal URLs. This hostname works only with Cloudflare Workers service bindings; a generic fetch-compatible client would fail attempting to resolve "internal" as a real hostname. Either update the interface documentation to clarify it requires service bindings, or parameterize the base URL to support both service bindings and generic fetch clients.

Appears at lines 94–95 and 189–190.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@packages/shared/src/completion/extractor.ts` around lines 42 - 45, The
ExtractorDeps.fetcher doc overclaims generic HTTP-client support while code uses
hard-coded "https://internal" host (used in event/artifact fetches), so either
clarify that ControlPlaneFetcher must be a service-binding capable of resolving
"internal" or make the base host configurable; update the ExtractorDeps
interface by adding a baseUrl (or controlPlaneBase) string field and update all
uses in the artifact/event fetching logic (references: ExtractorDeps.fetcher,
ControlPlaneFetcher and the event/artifact fetch call sites) to build requests
from that base instead of the hard-coded "https://internal", or alternatively
change the JSDoc on ExtractorDeps.fetcher to explicitly state it requires a
service binding that can resolve "internal".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@packages/shared/src/completion/extractor.ts`:
- Around line 181-203: fetchSessionArtifacts is returning all session artifacts
(from GET /sessions/:sessionId/artifacts) and can attach artifacts from prior
messages to the wrong update; modify the flow to include message scoping: either
update the server endpoint to accept a messageId query param and return only
artifacts for that message, or extend the ArtifactResponse/ArtifactInfo to
include messageId and add client-side filtering inside fetchSessionArtifacts
(use the messageId from base or an added parameter) so only artifacts matching
the current messageId are returned; locate fetchSessionArtifacts and the
ListArtifactsResponse/ArtifactResponse types to implement the chosen change and
update the call sites that build the request headers/base accordingly.

---

Nitpick comments:
In `@packages/shared/src/completion/extractor.ts`:
- Around line 42-45: The ExtractorDeps.fetcher doc overclaims generic
HTTP-client support while code uses hard-coded "https://internal" host (used in
event/artifact fetches), so either clarify that ControlPlaneFetcher must be a
service-binding capable of resolving "internal" or make the base host
configurable; update the ExtractorDeps interface by adding a baseUrl (or
controlPlaneBase) string field and update all uses in the artifact/event
fetching logic (references: ExtractorDeps.fetcher, ControlPlaneFetcher and the
event/artifact fetch call sites) to build requests from that base instead of the
hard-coded "https://internal", or alternatively change the JSDoc on
ExtractorDeps.fetcher to explicitly state it requires a service binding that can
resolve "internal".
🪄 Autofix (Beta)

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: defaults

Review profile: CHILL

Plan: Pro

Run ID: c2ae6f62-713a-42b7-ab70-b08ad5a85406

📥 Commits

Reviewing files that changed from the base of the PR and between f2b74ab and 6513095.

📒 Files selected for processing (4)
  • packages/linear-bot/src/completion/extractor.ts
  • packages/shared/src/completion/extractor.ts
  • packages/shared/src/index.ts
  • packages/slack-bot/src/completion/extractor.ts

Comment thread packages/shared/src/completion/extractor.ts
@github-actions
Copy link
Copy Markdown

Terraform Validation Results

Step Status
Format
Init
Validate

Note: Terraform plan was skipped because secrets are not configured. This is expected for external contributors. See docs/GETTING_STARTED.md for setup instructions.

Pushed by: @ColeMurray, Action: pull_request

@ColeMurray
Copy link
Copy Markdown
Owner Author

Addressing the review feedback:

JSDoc overclaims (nitpick): Fair point. Tightened the JSDoc to clarify fetcher requires a Cloudflare Workers service binding that resolves https://internal URLs, rather than a generic HTTP client. Fixed in 13d53a3.

Shared-package unit tests (suggestion from open-inspect bot): Good suggestion. The existing slack-bot extractor tests still exercise the shared logic end-to-end. Adding shared-package-local unit tests is worth doing as a follow-up but out of scope for this refactor PR.

@ColeMurray ColeMurray merged commit 89319ac into main Mar 28, 2026
14 of 15 checks passed
@ColeMurray ColeMurray deleted the refactor/shared-extract-agent-response branch March 28, 2026 06:14
ColeMurray added a commit that referenced this pull request Mar 28, 2026
Take thin wrapper extractors from main (PR #423), update shared
extractor to use buildInternalAuthHeaders instead of inline
generateInternalToken.
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