Add GitHub session logic, and refactor logging#855
Add GitHub session logic, and refactor logging#855PaytonWebber wants to merge 22 commits intomainfrom
Conversation
Summary subroutines (concise-summary, verbose-summary, question-answer, plan-summary, etc.) have disallowAllTools: true, but MCP tools like Linear's create_comment were still accessible because MCP config was always provided regardless of this setting. This change conditionally disables mcpConfig and mcpConfigPath when disallowAllTools is true, ensuring the agent truly has no tool access during summary subroutines. Closes CYPACK-760 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…YPACK-725) Implements Phase 1 of CYPACK-724 architectural refactor. Creates new GlobalSessionRegistry class that centralizes session storage across all repositories, enabling cross-repository session lookups for orchestrator workflows. Key features: - Session CRUD operations (create, get, update, delete, getAll) - Entry management (add, get, update entries) - Parent-child session mapping for orchestrator workflows - EventEmitter with lifecycle events (sessionCreated, sessionUpdated, sessionCompleted) - Serialization/deserialization (v3.0 format) - Cleanup method for removing old sessions - Comprehensive unit tests (45 tests) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Add changelog entry for GlobalSessionRegistry implementation with PR link. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
Fix TypeScript error in updateEntry method by ensuring type and content fields are never undefined when applying partial updates. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…tation (CYPACK-726) - Create IActivitySink interface for decoupling activity posting - Implement LinearActivitySink wrapping IIssueTrackerService - Add comprehensive unit tests (20 tests, all passing) - Export from edge-worker package Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…726) Add changelog entry documenting the extraction of IActivitySink interface and LinearActivitySink implementation. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
- Add GlobalSessionRegistry instance to EdgeWorker - Replace childToParentAgentSession Map calls with GlobalSessionRegistry methods - Pass GlobalSessionRegistry to AgentSessionManager for future migration - All session lookups for cross-repo parent resume now use GlobalSessionRegistry Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
…(CYPACK-728) - Rename linearAgentActivitySessionId to id for clarity - Add optional externalSessionId for tracker-specific IDs (e.g., Linear's AgentSession ID) - Add optional issueContext object with trackerId, issueId, issueIdentifier - Make issue and issueId optional for standalone sessions - Update PersistenceManager to v3.0 with automatic migration from v2.0 - Update all field references in GlobalSessionRegistry, AgentSessionManager, EdgeWorker - Add 7 new tests for persistence migration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…PACK-728) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Fix missed linearAgentActivitySessionId → id rename in EdgeWorker.ts:1639 (code added in main after the Phase 4 schema changes) - Fix unused variable lint warning in GlobalSessionRegistry.ts:264 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Update getAgentRunnersForIssue, getSessionsByIssueId, and getActiveSessionsByIssueId to resolve issue ID from issueContext first, falling back to deprecated issueId field. This ensures future standalone sessions that only populate issueContext will be found correctly. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…gentSession (CYPACK-724) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…ding max-turns. Now, when the result message of a single-turn sub-routine is an error, the result text from the previous sub-routine is emitted to Linear as a result message.
* feat: implement GitHub webhook endpoint and event transport Add /github-webhook endpoint for receiving forwarded GitHub webhooks from CYHOST. When a valid @cyrusagent mention is received on a PR comment, a new Claude session is created on the PR branch. New package: cyrus-github-event-transport - GitHubEventTransport: EventEmitter-based transport with proxy (Bearer token) and signature (HMAC-SHA256) verification modes - GitHubCommentService: REST API client for posting replies to GitHub PRs - Utility functions for extracting data from GitHub webhook payloads - Handles both issue_comment and pull_request_review_comment events EdgeWorker integration: - registerGitHubEventTransport: registers /github-webhook endpoint - handleGitHubWebhook: full session creation flow (validates PR comment, finds matching repo, creates workspace, starts ClaudeRunner) - postGitHubReply: posts session results back to GitHub Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: update changelogs for GitHub webhook endpoint feature Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: use forwarded GitHub installation token for PR comment replies Updates GitHub webhook handling to extract and use the X-GitHub-Installation-Token header forwarded from CYHOST instead of relying on process.env.GITHUB_TOKEN. Changes: - Extended GitHubWebhookEvent type with optional installationToken field - GitHubEventTransport now extracts X-GitHub-Installation-Token header from incoming webhooks - EdgeWorker.postGitHubReply() prefers forwarded token over process.env.GITHUB_TOKEN - Added comprehensive tests for token extraction behavior (2 new tests) This enables self-hosted Cyrus processes to post PR comment replies using short-lived (1-hour) GitHub App installation tokens. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: use forwarded installation token in fetchPRBranchRef Update EdgeWorker.fetchPRBranchRef() to prefer event.installationToken over process.env.GITHUB_TOKEN for authenticating GitHub API calls to fetch PR branch details. This fixes 404 errors in self-hosted setups where process.env.GITHUB_TOKEN doesn't exist, allowing private repo PR details to be fetched using forwarded GitHub App installation tokens. - Changed line 866 in EdgeWorker.ts from process.env.GITHUB_TOKEN to event.installationToken || process.env.GITHUB_TOKEN - Updated comment to reflect new token preference behavior - Added comprehensive test coverage in EdgeWorker.fetchPRBranchRef.test.ts - Updated CHANGELOG.internal.md to document the change Fixes CYPACK-774 Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * fix: guard activity-posting methods for non-Linear sessions - Add externalSessionId guard to postAnalyzingThought and postProcedureSelectionThought so they skip posting (and don't error) for GitHub/Slack sessions - Remove activeWebhookCount tracking from handleMessage() to avoid double-counting with legacy webhook handlers - Normalize all activity-posting guard clauses from warn to debug level, since non-Linear sessions hitting these paths is expected behavior - Add tests verifying GitHub sessions skip all Linear activity posting Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add unified internal message bus with platform translators Introduce a platform-agnostic message layer that translates webhook payloads from Linear and GitHub into a unified InternalMessage format. This enables handleMessage() in EdgeWorker to process events from all platforms through a single code path. - Add InternalMessage type system in core (SessionStart, UserPrompt, StopSignal, ContentUpdate, Unassign) with type guards and platform refs - Add IMessageTranslator interface for platform-specific translators - Implement LinearMessageTranslator and GitHubMessageTranslator - Wire translators into event transports to emit 'message' alongside legacy 'event' for backward compatibility - Replace old linear-event-transport tests with translator-focused tests Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: agentclear <agentops@ceedar.ai> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
* feat: implement GitHub webhook endpoint and event transport Add /github-webhook endpoint for receiving forwarded GitHub webhooks from CYHOST. When a valid @cyrusagent mention is received on a PR comment, a new Claude session is created on the PR branch. New package: cyrus-github-event-transport - GitHubEventTransport: EventEmitter-based transport with proxy (Bearer token) and signature (HMAC-SHA256) verification modes - GitHubCommentService: REST API client for posting replies to GitHub PRs - Utility functions for extracting data from GitHub webhook payloads - Handles both issue_comment and pull_request_review_comment events EdgeWorker integration: - registerGitHubEventTransport: registers /github-webhook endpoint - handleGitHubWebhook: full session creation flow (validates PR comment, finds matching repo, creates workspace, starts ClaudeRunner) - postGitHubReply: posts session results back to GitHub Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: update changelogs for GitHub webhook endpoint feature Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat: use forwarded GitHub installation token for PR comment replies Updates GitHub webhook handling to extract and use the X-GitHub-Installation-Token header forwarded from CYHOST instead of relying on process.env.GITHUB_TOKEN. Changes: - Extended GitHubWebhookEvent type with optional installationToken field - GitHubEventTransport now extracts X-GitHub-Installation-Token header from incoming webhooks - EdgeWorker.postGitHubReply() prefers forwarded token over process.env.GITHUB_TOKEN - Added comprehensive tests for token extraction behavior (2 new tests) This enables self-hosted Cyrus processes to post PR comment replies using short-lived (1-hour) GitHub App installation tokens. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com> * refactor: consolidate messsage emitting to a single function --------- Co-authored-by: agentclear <agentops@ceedar.ai> Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
When a GitHub webhook arrives for a PR branch that's already checked out in a Linear session's worktree, git worktree add fails. Instead of falling back to an empty directory, detect and reuse the existing worktree path — both proactively (before attempting creation) and as a safety net (parsing the git error message in the catch block). Also adds a concurrency warning log when a GitHub session shares a workspace with an active Linear session. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
| } | ||
|
|
||
| try { | ||
| const body = JSON.stringify(request.body); |
There was a problem hiding this comment.
🔴 HMAC signature verification uses re-serialized JSON instead of raw request body
In signature verification mode, the code computes the HMAC-SHA256 digest over JSON.stringify(request.body) (the re-serialized parsed JSON) instead of the original raw request body bytes. GitHub computes its x-hub-signature-256 over the exact raw bytes sent in the HTTP request. Re-serializing the already-parsed body with JSON.stringify() can produce different output than the original payload (e.g., different whitespace, key ordering, unicode escaping, number formatting), causing the signature comparison to fail.
Root Cause
The route is correctly configured with rawBody: true at GitHubEventTransport.ts:75, which tells Fastify to preserve the raw body as request.rawBody. However, at line 116, the code ignores request.rawBody and instead uses JSON.stringify(request.body):
const body = JSON.stringify(request.body); // ← re-serialized, NOT raw bytes
const isValid = this.verifyGitHubSignature(body, signature, this.config.secret);The verifyGitHubSignature method at line 252 then computes:
const expectedSignature = `sha256=${createHmac("sha256", secret).update(body).digest("hex")}`;Since body is JSON.stringify(request.body) rather than the original raw bytes, any difference between the two (whitespace, key order, etc.) causes timingSafeEqual to return false, rejecting valid webhooks.
Impact: Signature-mode verification (verificationMode: "signature") will reject legitimate GitHub webhooks whenever JSON.stringify(parsed) does not byte-for-byte match the original payload. This effectively breaks the cloud/signature verification mode entirely.
| const body = JSON.stringify(request.body); | |
| const body = (request as any).rawBody ?? JSON.stringify(request.body); |
Was this helpful? React with 👍 or 👎 to provide feedback.
* fix: use rawBody for GitHub webhook HMAC signature verification JSON.stringify(request.body) re-serializes parsed JSON which may differ from the original bytes GitHub signed. Use request.rawBody instead, which preserves the exact payload bytes. The rawBody config was already enabled. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: rename misleading _sessionId param to _runnerSessionId Clarifies that the parameter receives the runner session ID (Claude/Gemini), not the internal session ID. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * refactor: migrate AgentSessionManager from IIssueTrackerService to IActivitySink Decouple AgentSessionManager from Linear-specific IIssueTrackerService by routing all activity posting through the platform-agnostic IActivitySink interface. This enables future support for non-Linear activity sinks. - Expand IActivitySink with ActivitySignal, ActivityPostOptions, ActivityPostResult types - Update LinearActivitySink to map string signals to AgentActivitySignal enum - Replace issueTracker constructor param with activitySink in AgentSessionManager - Rename syncEntryToLinear → syncEntryToActivitySink - Create LinearActivitySink at both EdgeWorker construction sites - Update all test files to use IActivitySink mock pattern Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * feat: add "eyes" emoji reaction to GitHub comments that trigger agent sessions Gives users instant visual feedback that their @mention was received before the potentially long-running session begins. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
This PR introduces GitHub based trigger event support, which allows users to now trigger Cyrus via @ mentions on GitHub issues or PR's. When users post a comment that contains '@cyrusagent', an agent session is triggered in a dedicated Git worktree, with the context of the comment that Cyrus was triggered by.