Skip to content

feat: unified internal message bus with platform translators#851

Merged
PaytonWebber merged 7 commits intorefactor/improved-loggingfrom
refactor/message-bus
Feb 10, 2026
Merged

feat: unified internal message bus with platform translators#851
PaytonWebber merged 7 commits intorefactor/improved-loggingfrom
refactor/message-bus

Conversation

@PaytonWebber
Copy link
Copy Markdown
Collaborator

Summary

  • Introduces a platform-agnostic internal message bus that translates webhook payloads from Linear and GitHub into a unified InternalMessage format, enabling handleMessage() in EdgeWorker to process events from all platforms through a single code path
  • Adds the github-event-transport package with webhook endpoint, signature verification, comment service, and message translation
  • Guards all activity-posting methods in AgentSessionManager with externalSessionId checks so GitHub/Slack sessions skip Linear API calls without errors
  • Removes activeWebhookCount double-counting from handleMessage() (legacy handlers already track it)
  • Normalizes guard log messages from warn to debug level since non-Linear sessions hitting these paths is expected behavior

Test plan

  • All 463 edge-worker tests pass (including 5 new GitHub session tests)
  • New GitHubMessageTranslator tests verify translation of all webhook event types
  • New LinearMessageTranslator tests verify translation of all Linear webhook payloads
  • GitHubEventTransport, GitHubCommentService, and github-webhook-utils tests pass
  • pnpm typecheck passes across all packages
  • Verify GitHub webhook end-to-end with a test issue assignment
  • Verify Linear webhook regression (existing flows still work)

🤖 Generated with Claude Code

cyrusagent and others added 7 commits January 30, 2026 12:12
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>
Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
…t to use structured logger

Merge refactor/improved-logging branch into cypack-772 and update all
GitHub-related code to use the new ILogger/createLogger infrastructure
instead of console.log/warn/error calls.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
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>
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>
- 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>
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>
@PaytonWebber PaytonWebber merged commit 203ea4a into refactor/improved-logging Feb 10, 2026
PaytonWebber added a commit that referenced this pull request Feb 13, 2026
* Fix: Disable MCP tools when disallowAllTools is true

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>

* Refactor logging to a dedicated logger

* Add source context to sessions when emitting logs

* Fix issue where single-turn sub-routines would silently fail by exceeding 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.

* Respect agent guidance for draft PRs and use --track for worktrees (#834)

- Update gh-pr subroutine to conditionally gate `gh pr ready` based on
  agent guidance (respects --draft flag in workspace guidance)
- Update changelog-update subroutine to include --base flag in PR creation
  targeting the configured base branch from context
- Add --track flag to git worktree add commands to properly set upstream
  tracking branch

Fixes issues where PRs were auto-converted from draft to ready and
worktrees weren't tracking the correct upstream branch.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Prepare release v0.2.20 (#835)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools (#837)

* Update @anthropic-ai/claude-agent-sdk to v0.2.34 and @anthropic-ai/sdk to v0.73.0

- Updated @anthropic-ai/claude-agent-sdk from ^0.2.7 to ^0.2.34
- Updated @anthropic-ai/sdk from ^0.71.2 to ^0.73.0
- Fixed type compatibility with new BetaUsage fields (inference_geo, iterations, stop_reason)
- Updated gemini-runner adapters to handle new SDK types

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Update changelog for SDK updates

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Enable Claude Code experimental features in claude-runner

- Set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1
- Set CLAUDE_CODE_ENABLE_TASKS=true
- Set CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
- Updated test expectations to include new env configuration

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools

- Added formatTaskParameter() method to IMessageFormatter interface
- Implemented Task tool formatting (TaskCreate, TaskUpdate, TaskList, TaskGet) in ClaudeMessageFormatter and GeminiMessageFormatter
- Updated AgentSessionManager to handle Task tools as thought activities
- Added comprehensive tests for Task tool formatting (20 new tests)
- Marked TodoWrite as deprecated while maintaining backward compatibility
- Fixed linting warnings in switch case block scope

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix spawn node ENOENT by passing process.env to Claude SDK

The env option only contained 3 hardcoded vars, causing PATH to be
missing from the spawned Claude Code process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Cherry-pick d5d9e5a: Fix single-turn subroutine silent failures (#843)

* Cherry-pick d5d9e5a: Fix single-turn subroutine silent failures

Cherry-picks commit d5d9e5a from refactor/improved-logging branch.
Resolves merge conflicts adapting logging refactor patterns
(sessionId/log.info) to current branch patterns
(linearAgentActivitySessionId/console.log).

When a single-turn subroutine exits with an error (e.g. error_max_turns),
AgentSessionManager now recovers by using the last completed subroutine's
result via ProcedureAnalyzer.getLastSubroutineResult(), allowing the
procedure to continue to completion instead of failing silently.

Also adds tools config pass-through to ClaudeRunner and EdgeWorker
so disallowAllTools subroutines properly disable built-in tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update changelogs for CYPACK-792 (#843)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ClaudeRunner test env assertions after process.env spread

Tests expected env to contain only 3 explicit keys, but commit
6e8d873 added ...process.env to prevent spawn ENOENT errors.
Updated assertions to use expect.objectContaining() so tests
verify required keys without failing on additional process.env
entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix TaskCreate/TaskGet formatting, add ToolSearch/TaskOutput formatting, include task subject in TaskUpdate/TaskGet (#846)

* Fix TaskCreate/TaskGet formatting for parallel execution, add ToolSearch and TaskOutput formatting

TaskCreate now renders as concise checklist items (⏳ **subject**) instead of
verbose multi-line format, better suited for parallel async execution. TaskGet
shows subject with ID when enriched. ToolSearch renders as a thought with
descriptive formatting. TaskOutput shows task status with blocking/non-blocking
context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-795

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify TaskUpdate/TaskGet to use task number, show ToolSearch query directly

TaskUpdate and TaskGet now always display the task number (e.g. "Task #123")
instead of the subject description for consistency with parallel execution.
ToolSearch now shows the query directly as the parameter value, matching
the pattern of other tools like Bash (command) and Read (file_path).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert Gemini runner formatting changes (not applicable to these tools)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Include task subject in TaskUpdate and TaskGet formatting

TaskUpdate and TaskGet now display the task's short description
alongside the task number (e.g., "✅ Task #3 — Fix login bug")
when a subject is available, falling back to number-only when not.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Enrich TaskUpdate/TaskGet activities with task subject (CYPACK-797) (#847)

* Enrich TaskUpdate/TaskGet activities with task subject from cache and result parsing

TaskUpdate and TaskGet tool inputs don't include the task subject, causing
Linear activities to show only "Task #3" without context. This change defers
their activity posting from tool_use time to tool_result time, where subject
can be sourced from: (1) a cache populated by TaskCreate results, or
(2) parsing the Subject field from TaskGet result content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-797

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Delete packages/edge-worker/test/AgentSessionManager.task-subject-enrichment.test.ts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>

* Release v0.2.21 (#848)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat: unified internal message bus with platform translators (#851)

* 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>

* Consolidate scattered message posting logic to a helper function (#854)

* 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>

* feat: reuse existing worktree when branch is already checked out

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>

* Feature/add GitHub comment reaction (#860)

* 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>

---------

Co-authored-by: agentclear <agentops@ceedar.ai>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Connoropolous added a commit that referenced this pull request Feb 19, 2026
…r service extraction, Slack transport (CYPACK-724) (#773)

* feat: Create GlobalSessionRegistry for centralized session storage (CYPACK-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>

* docs: Update internal changelog for GlobalSessionRegistry (CYPACK-725)

Add changelog entry for GlobalSessionRegistry implementation with PR link.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Ensure updateEntry preserves required fields (CYPACK-725)

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>

* feat: Extract IActivitySink interface and LinearActivitySink implementation (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>

* docs: Update internal changelog for IActivitySink extraction (CYPACK-726)

Add changelog entry documenting the extraction of IActivitySink interface
and LinearActivitySink implementation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: Integrate GlobalSessionRegistry with EdgeWorker (CYPACK-727)

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

* docs: Update internal changelog for GlobalSessionRegistry integration (CYPACK-727)

* feat: Update CyrusAgentSession schema and persistence format to v3.0 (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>

* docs: Update internal changelog for CyrusAgentSession v3.0 schema (CYPACK-728)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: Resolve CI failures after rebase on main (CYPACK-724)

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

* fix: Use issueContext.issueId in session lookup methods (CYPACK-724)

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>

* fix: Add defensive result.success check in LinearActivitySink.createAgentSession (CYPACK-724)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor logging to a dedicated logger (#818)

* Fix: Disable MCP tools when disallowAllTools is true

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>

* Refactor logging to a dedicated logger

* Add source context to sessions when emitting logs

* Fix issue where single-turn sub-routines would silently fail by exceeding 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.

* Respect agent guidance for draft PRs and use --track for worktrees (#834)

- Update gh-pr subroutine to conditionally gate `gh pr ready` based on
  agent guidance (respects --draft flag in workspace guidance)
- Update changelog-update subroutine to include --base flag in PR creation
  targeting the configured base branch from context
- Add --track flag to git worktree add commands to properly set upstream
  tracking branch

Fixes issues where PRs were auto-converted from draft to ready and
worktrees weren't tracking the correct upstream branch.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Prepare release v0.2.20 (#835)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools (#837)

* Update @anthropic-ai/claude-agent-sdk to v0.2.34 and @anthropic-ai/sdk to v0.73.0

- Updated @anthropic-ai/claude-agent-sdk from ^0.2.7 to ^0.2.34
- Updated @anthropic-ai/sdk from ^0.71.2 to ^0.73.0
- Fixed type compatibility with new BetaUsage fields (inference_geo, iterations, stop_reason)
- Updated gemini-runner adapters to handle new SDK types

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Update changelog for SDK updates

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Enable Claude Code experimental features in claude-runner

- Set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1
- Set CLAUDE_CODE_ENABLE_TASKS=true
- Set CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
- Updated test expectations to include new env configuration

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools

- Added formatTaskParameter() method to IMessageFormatter interface
- Implemented Task tool formatting (TaskCreate, TaskUpdate, TaskList, TaskGet) in ClaudeMessageFormatter and GeminiMessageFormatter
- Updated AgentSessionManager to handle Task tools as thought activities
- Added comprehensive tests for Task tool formatting (20 new tests)
- Marked TodoWrite as deprecated while maintaining backward compatibility
- Fixed linting warnings in switch case block scope

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix spawn node ENOENT by passing process.env to Claude SDK

The env option only contained 3 hardcoded vars, causing PATH to be
missing from the spawned Claude Code process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures (#843)

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures

Cherry-picks commit d5d9e5a0 from refactor/improved-logging branch.
Resolves merge conflicts adapting logging refactor patterns
(sessionId/log.info) to current branch patterns
(linearAgentActivitySessionId/console.log).

When a single-turn subroutine exits with an error (e.g. error_max_turns),
AgentSessionManager now recovers by using the last completed subroutine's
result via ProcedureAnalyzer.getLastSubroutineResult(), allowing the
procedure to continue to completion instead of failing silently.

Also adds tools config pass-through to ClaudeRunner and EdgeWorker
so disallowAllTools subroutines properly disable built-in tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update changelogs for CYPACK-792 (#843)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ClaudeRunner test env assertions after process.env spread

Tests expected env to contain only 3 explicit keys, but commit
6e8d873d added ...process.env to prevent spawn ENOENT errors.
Updated assertions to use expect.objectContaining() so tests
verify required keys without failing on additional process.env
entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix TaskCreate/TaskGet formatting, add ToolSearch/TaskOutput formatting, include task subject in TaskUpdate/TaskGet (#846)

* Fix TaskCreate/TaskGet formatting for parallel execution, add ToolSearch and TaskOutput formatting

TaskCreate now renders as concise checklist items (⏳ **subject**) instead of
verbose multi-line format, better suited for parallel async execution. TaskGet
shows subject with ID when enriched. ToolSearch renders as a thought with
descriptive formatting. TaskOutput shows task status with blocking/non-blocking
context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-795

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify TaskUpdate/TaskGet to use task number, show ToolSearch query directly

TaskUpdate and TaskGet now always display the task number (e.g. "Task #123")
instead of the subject description for consistency with parallel execution.
ToolSearch now shows the query directly as the parameter value, matching
the pattern of other tools like Bash (command) and Read (file_path).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert Gemini runner formatting changes (not applicable to these tools)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Include task subject in TaskUpdate and TaskGet formatting

TaskUpdate and TaskGet now display the task's short description
alongside the task number (e.g., "✅ Task #3 — Fix login bug")
when a subject is available, falling back to number-only when not.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Enrich TaskUpdate/TaskGet activities with task subject (CYPACK-797) (#847)

* Enrich TaskUpdate/TaskGet activities with task subject from cache and result parsing

TaskUpdate and TaskGet tool inputs don't include the task subject, causing
Linear activities to show only "Task #3" without context. This change defers
their activity posting from tool_use time to tool_result time, where subject
can be sourced from: (1) a cache populated by TaskCreate results, or
(2) parsing the Subject field from TaskGet result content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-797

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Delete packages/edge-worker/test/AgentSessionManager.task-subject-enrichment.test.ts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>

* Release v0.2.21 (#848)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat: unified internal message bus with platform translators (#851)

* 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>

* Consolidate scattered message posting logic to a helper function (#854)

* 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>

* feat: reuse existing worktree when branch is already checked out

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>

* Feature/add GitHub comment reaction (#860)

* 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>

---------

Co-authored-by: agentclear <agentops@ceedar.ai>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat: Add Slack event transport for @mention webhooks (CYPACK-807) (#861)

* feat: Add Slack event transport for @mention webhooks (CYPACK-807)

Implement cyrus-slack-event-transport package to receive Slack app_mention
webhooks forwarded from CYHOST. Adds SlackEventTransport (Fastify endpoint
with Bearer token auth), SlackMessageTranslator (translates to unified
SessionStartMessage/UserPromptMessage), thread-aware session keys, and
Slack platform data types in cyrus-core.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Handle Slack @mention webhooks as agent sessions with thread reply (CYPACK-807)

Slack @mentions now trigger full agent sessions using the first configured
repository, with results posted back as threaded replies in Slack.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Slack sessions use empty working directories, not git worktrees (CYPACK-815) (#868)

* feat: Slack sessions use empty working directories, not git worktrees (CYPACK-815)

Decouple Slack agent sessions from repository configuration so they run
in transient empty directories rather than git worktrees. Subsequent
@mentions in the same Slack thread now share the same session context
via resume or streaming input injection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: Update changelogs for CYPACK-815 (#868)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use plain systemPrompt for Slack sessions to avoid empty text block error

The claude_code preset constructs context blocks (CLAUDE.md, git status)
that are empty in Slack's non-git empty workspace directories, causing
"cache_control cannot be set for empty text blocks" API errors. Using a
direct systemPrompt string bypasses the preset entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Handle empty Slack @mention by providing default prompt

Reverts 166d88be which switched from appendSystemPrompt to plain
systemPrompt — we need the claude_code preset. The actual root cause
was an empty user prompt: when a Slack message is just "@cyrus" with
no other text, stripMention returns "", which the SDK sends as an
empty text block with cache_control, rejected by the API. Now falls
back to "Ask the user for more context" when the stripped text is empty.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Skip procedure routing for Slack sessions on completion

Slack sessions are created without a ProcedureAnalyzer since they are
simple conversational sessions, not multi-subroutine workflows. When
the session completed, completeSession called handleProcedureCompletion
which threw "ProcedureAnalyzer not available". Now gracefully returns
early when no ProcedureAnalyzer is configured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Add Slack thread context to agent sessions

When a user @mentions Cyrus in a Slack thread, fetch the preceding
conversation via conversations.replies and prepend it as XML context
to the user prompt. This gives the agent visibility into what users
were discussing before invoking it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: Extract Slack integration via ChatPlatformAdapter pattern

Introduce ChatPlatformAdapter<TEvent> interface and ChatSessionHandler<TEvent>
class to decouple chat platform session lifecycle from EdgeWorker. Slack becomes
the first adapter implementation (SlackChatAdapter), removing ~450 lines and 9
private methods from EdgeWorker.ts. Adding a new chat platform now requires only
an adapter (~80 lines) instead of duplicating session management logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: Add createChatSession for issue-free sessions, fix appendSystemPrompt

Addresses PR review feedback:
- Add AgentSessionManager.createChatSession() for chat platforms that
  don't have issue context, avoiding synthetic IssueMinimal objects
- ChatSessionHandler now uses createChatSession instead of
  createLinearAgentSession with fake issue data
- Fix ChatSessionHandler.buildRunnerConfig using appendSystemPrompt
  instead of systemPrompt (re-introduced the cache_control bug)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: Use appendSystemPrompt for chat sessions (root cause was empty prompt)

The cache_control error was caused by an empty prompt string after
stripping the @mention (e.g. just "@cyrus" with no text), not by the
claude_code preset. Revert back to appendSystemPrompt so chat sessions
benefit from the full Claude Code system prompt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use streaming mode for chat sessions to enable follow-up message injection

Chat sessions (Slack threads) were started with runner.start() (string mode),
so follow-up messages arriving mid-session would crash with "Cannot add stream
message when not in streaming mode". Switch to startStreaming() and add an
isStreaming() guard before calling addStreamMessage().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <paytonwebber@gmail.com>

* Merge main into cypack-807 branch (CYPACK-821) (#873)

* Respect agent guidance for draft PRs and use --track for worktrees (#834)

- Update gh-pr subroutine to conditionally gate `gh pr ready` based on
  agent guidance (respects --draft flag in workspace guidance)
- Update changelog-update subroutine to include --base flag in PR creation
  targeting the configured base branch from context
- Add --track flag to git worktree add commands to properly set upstream
  tracking branch

Fixes issues where PRs were auto-converted from draft to ready and
worktrees weren't tracking the correct upstream branch.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Prepare release v0.2.20 (#835)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools (#837)

* Update @anthropic-ai/claude-agent-sdk to v0.2.34 and @anthropic-ai/sdk to v0.73.0

- Updated @anthropic-ai/claude-agent-sdk from ^0.2.7 to ^0.2.34
- Updated @anthropic-ai/sdk from ^0.71.2 to ^0.73.0
- Fixed type compatibility with new BetaUsage fields (inference_geo, iterations, stop_reason)
- Updated gemini-runner adapters to handle new SDK types

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Update changelog for SDK updates

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Enable Claude Code experimental features in claude-runner

- Set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1
- Set CLAUDE_CODE_ENABLE_TASKS=true
- Set CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
- Updated test expectations to include new env configuration

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools

- Added formatTaskParameter() method to IMessageFormatter interface
- Implemented Task tool formatting (TaskCreate, TaskUpdate, TaskList, TaskGet) in ClaudeMessageFormatter and GeminiMessageFormatter
- Updated AgentSessionManager to handle Task tools as thought activities
- Added comprehensive tests for Task tool formatting (20 new tests)
- Marked TodoWrite as deprecated while maintaining backward compatibility
- Fixed linting warnings in switch case block scope

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix spawn node ENOENT by passing process.env to Claude SDK

The env option only contained 3 hardcoded vars, causing PATH to be
missing from the spawned Claude Code process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures (#843)

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures

Cherry-picks commit d5d9e5a0 from refactor/improved-logging branch.
Resolves merge conflicts adapting logging refactor patterns
(sessionId/log.info) to current branch patterns
(linearAgentActivitySessionId/console.log).

When a single-turn subroutine exits with an error (e.g. error_max_turns),
AgentSessionManager now recovers by using the last completed subroutine's
result via ProcedureAnalyzer.getLastSubroutineResult(), allowing the
procedure to continue to completion instead of failing silently.

Also adds tools config pass-through to ClaudeRunner and EdgeWorker
so disallowAllTools subroutines properly disable built-in tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update changelogs for CYPACK-792 (#843)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ClaudeRunner test env assertions after process.env spread

Tests expected env to contain only 3 explicit keys, but commit
6e8d873d added ...process.env to prevent spawn ENOENT errors.
Updated assertions to use expect.objectContaining() so tests
verify required keys without failing on additional process.env
entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix TaskCreate/TaskGet formatting, add ToolSearch/TaskOutput formatting, include task subject in TaskUpdate/TaskGet (#846)

* Fix TaskCreate/TaskGet formatting for parallel execution, add ToolSearch and TaskOutput formatting

TaskCreate now renders as concise checklist items (⏳ **subject**) instead of
verbose multi-line format, better suited for parallel async execution. TaskGet
shows subject with ID when enriched. ToolSearch renders as a thought with
descriptive formatting. TaskOutput shows task status with blocking/non-blocking
context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-795

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify TaskUpdate/TaskGet to use task number, show ToolSearch query directly

TaskUpdate and TaskGet now always display the task number (e.g. "Task #123")
instead of the subject description for consistency with parallel execution.
ToolSearch now shows the query directly as the parameter value, matching
the pattern of other tools like Bash (command) and Read (file_path).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert Gemini runner formatting changes (not applicable to these tools)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Include task subject in TaskUpdate and TaskGet formatting

TaskUpdate and TaskGet now display the task's short description
alongside the task number (e.g., "✅ Task #3 — Fix login bug")
when a subject is available, falling back to number-only when not.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Enrich TaskUpdate/TaskGet activities with task subject (CYPACK-797) (#847)

* Enrich TaskUpdate/TaskGet activities with task subject from cache and result parsing

TaskUpdate and TaskGet tool inputs don't include the task subject, causing
Linear activities to show only "Task #3" without context. This change defers
their activity posting from tool_use time to tool_result time, where subject
can be sourced from: (1) a cache populated by TaskCreate results, or
(2) parsing the Subject field from TaskGet result content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-797

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Delete packages/edge-worker/test/AgentSessionManager.task-subject-enrichment.test.ts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>

* Release v0.2.21 (#848)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage' (#815)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage'

Update orchestrator system prompts to explicitly require setting state to
"To Do" when creating issues with mcp__linear__create_issue. Previously,
issues were being created with default "Triage" status.

Changes to both orchestrator.md (v2.5.0) and graphite-orchestrator.md (v1.3.0):
- Added state requirement to Required Tools section
- Added Status bullet point in Decompose section
- Added status checklist item in Sub-Issue Creation Checklist
- Fixed outdated tool names in orchestrator.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(changelog): add entry for CYPACK-761

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>

* Codex SDK migration + split agent/model selectors (#850)

* chore: checkpoint codex sdk integration WIP

* Switch codex runner to SDK and add agent/model selectors

* chore: checkpoint codex sdk integration WIP

* Switch codex runner to SDK and add agent/model selectors

* Resolve rebase conflicts, migrate model defaults, and fix codex session init

* Fix Codex/Gemini usage typing and add Codex formatter replay tests

* Emit Codex tool lifecycle events for Linear activities

* Format Codex todos as markdown checklists

* Extract F1 test-drive workflow into shared skill

* Add user-facing changelog entry for agent/model selectors

* Add PR link to changelog entry for codex selector work

* update sandbox settings

* add .git folder for worktrees to allowed directories list

* Cursor harness MCP enable + permission mapping hardening (#858)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage' (#815)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage'

Update orchestrator system prompts to explicitly require setting state to
"To Do" when creating issues with mcp__linear__create_issue. Previously,
issues were being created with default "Triage" status.

Changes to both orchestrator.md (v2.5.0) and graphite-orchestrator.md (v1.3.0):
- Added state requirement to Required Tools section
- Added Status bullet point in Decompose section
- Added status checklist item in Sub-Issue Creation Checklist
- Fixed outdated tool names in orchestrator.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(changelog): add entry for CYPACK-761

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>

* feat(runners): add codex/cursor harness support and F1 validation

* fix(cursor-runner): support non-mock cursor F1 session output

* Fix cursor harness verification regressions

* fix(ci): resolve build type errors in config-updater and core session types

* fix(edge-worker): wire cursor runner selection and prompt assets

* docs(changelog): frame cursor harness as major feature

* docs(changelog): use user-facing framing for cursor entry

* docs(changelog): clarify cursor selector usage

* fix(build): remove baseUrl/paths from tsconfig to fix dist output structure

The baseUrl pointing to monorepo root caused TypeScript to expand rootDir,
nesting compiled output under dist/edge-worker/src/ instead of dist/.
This broke module resolution since package.json declares main as dist/index.js.
Also adds missing cyrus-cursor-runner dependency to edge-worker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cursor/edge-worker): stop-session handling and cursor tool activity mapping

* chore(gitignore): ignore nested node_modules directories

* fix(edge-worker): resume cursor sessions for prompted continuation

* fix(cursor-runner): sync project permissions for cursor CLI sessions

* fix(cursor-runner): map MCP tool permissions to Cursor CLI config

* docs(claude): add cursor permission translation gotcha

* fix(codex): default runner model to gpt-5.3-codex

* fix(cursor-runner): pre-enable MCP servers before cursor sessions

* docs(cursor): clarify .cursor/mcp.json source-of-truth guidance

* fix(cursor-runner): scope wildcard file permissions to workspace

* fix(codex): post actual error message to Linear for usage limit errors

When Codex hits usage limits or turn.failed errors, the full error message
is now posted to Linear agent activity instead of a generic message.
- AgentSessionManager: use errors[] for content when result is empty
- CodexRunner: fallback to standalone error event when turn.failed has no message

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): enable sandbox by default, fix cli.json formatting

- Default Cursor sandbox to enabled for tool execution isolation
- Write .cursor/cli.json with tabs for Biome compatibility
- Exclude .cursor from Biome checks (generated config)
- Update CHANGELOG with sandbox default

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): validate cursor-agent version before run, post error to Linear on mismatch

- Run cursor-agent --version before spawn and compare to tested version
- Post error_during_execution to Linear via agent activity when version mismatches
- Add cursorAgentVersion config and CYRUS_CURSOR_AGENT_VERSION env override
- Add CursorRunner.version-check.test.ts with 3 tests
- Update CHANGELOG with version validation behavior

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): render TODO_STATUS_COMPLETED todos as checked in Linear

- Map Cursor API TODO_STATUS_COMPLETED to [x] marker in formatter and summarizeTodoList
- Map TODO_STATUS_IN_PROGRESS for (in progress) suffix
- Add formatter test for TODO_STATUS_* values
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): log assembled cursor-agent CLI args to console and session logs

- Log full spawn command (path + args) before cursor-agent execution
- Write to both console and session log file for debugging
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): remove --force and --api-key from CLI args

- Remove --force option from cursor-agent invocations
- Pass API key via CURSOR_API_KEY env only (no longer in args)
- API key no longer appears in spawn logs or terminal output
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* add --trust

* move trust line

* Delete .cursor/cli.json

* fix(cursor-runner): backup and restore .cursor/cli.json instead of overwriting

Temporarily rename existing .cursor/cli.json before writing Cyrus permissions,
then restore original when session ends. Preserves user's config.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): add deny rules to scope Read/Write to workspace only

- When broad Read or Write allowed, add deny rules for /etc, /usr, etc.
- Add deny rules for workspace sibling directories
- Prevents cursor-agent from accessing /etc/hosts and system paths
- Add test: only mutates project .cursor/cli.json, leaves home config unchanged
- Update wildcard permissions test to expect system-path denies

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(changelog): add Cursor .cursor/cli.json backup/restore entry for CYPACK-804

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: agentclear <agentops@ceedar.ai>
Co-authored-by: Cyrus <208047790+cyrusagent@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Update README with agent capabilities and notes (#869)

* Bump fastify from 5.6.1 to 5.7.3 (#828)

Bumps [fastify](https://github.com/fastify/fastify) from 5.6.1 to 5.7.3.
- [Release notes](https://github.com/fastify/fastify/releases)
- [Commits](https://github.com/fastify/fastify/compare/v5.6.1...v5.7.3)

---
updated-dependencies:
- dependency-name: fastify
  dependency-version: 5.7.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* docs: Update internal changelog for main merge (CYPACK-821)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <paytonwebber@gmail.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* refactor: Extract 5 service modules from EdgeWorker.ts (CYPACK-822) (#874)

* feat: Add Slack event transport for @mention webhooks (CYPACK-807)

Implement cyrus-slack-event-transport package to receive Slack app_mention
webhooks forwarded from CYHOST. Adds SlackEventTransport (Fastify endpoint
with Bearer token auth), SlackMessageTranslator (translates to unified
SessionStartMessage/UserPromptMessage), thread-aware session keys, and
Slack platform data types in cyrus-core.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Handle Slack @mention webhooks as agent sessions with thread reply (CYPACK-807)

Slack @mentions now trigger full agent sessions using the first configured
repository, with results posted back as threaded replies in Slack.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Slack sessions use empty working directories, not git worktrees (CYPACK-815) (#868)

* feat: Slack sessions use empty working directories, not git worktrees (CYPACK-815)

Decouple Slack agent sessions from repository configuration so they run
in transient empty directories rather than git worktrees. Subsequent
@mentions in the same Slack thread now share the same session context
via resume or streaming input injection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: Update changelogs for CYPACK-815 (#868)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use plain systemPrompt for Slack sessions to avoid empty text block error

The claude_code preset constructs context blocks (CLAUDE.md, git status)
that are empty in Slack's non-git empty workspace directories, causing
"cache_control cannot be set for empty text blocks" API errors. Using a
direct systemPrompt string bypasses the preset entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Handle empty Slack @mention by providing default prompt

Reverts 166d88be which switched from appendSystemPrompt to plain
systemPrompt — we need the claude_code preset. The actual root cause
was an empty user prompt: when a Slack message is just "@cyrus" with
no other text, stripMention returns "", which the SDK sends as an
empty text block with cache_control, rejected by the API. Now falls
back to "Ask the user for more context" when the stripped text is empty.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Skip procedure routing for Slack sessions on completion

Slack sessions are created without a ProcedureAnalyzer since they are
simple conversational sessions, not multi-subroutine workflows. When
the session completed, completeSession called handleProcedureCompletion
which threw "ProcedureAnalyzer not available". Now gracefully returns
early when no ProcedureAnalyzer is configured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Add Slack thread context to agent sessions

When a user @mentions Cyrus in a Slack thread, fetch the preceding
conversation via conversations.replies and prepend it as XML context
to the user prompt. This gives the agent visibility into what users
were discussing before invoking it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: Extract Slack integration via ChatPlatformAdapter pattern

Introduce ChatPlatformAdapter<TEvent> interface and ChatSessionHandler<TEvent>
class to decouple chat platform session lifecycle from EdgeWorker. Slack becomes
the first adapter implementation (SlackChatAdapter), removing ~450 lines and 9
private methods from EdgeWorker.ts. Adding a new chat platform now requires only
an adapter (~80 lines) instead of duplicating session management logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: Add createChatSession for issue-free sessions, fix appendSystemPrompt

Addresses PR review feedback:
- Add AgentSessionManager.createChatSession() for chat platforms that
  don't have issue context, avoiding synthetic IssueMinimal objects
- ChatSessionHandler now uses createChatSession instead of
  createLinearAgentSession with fake issue data
- Fix ChatSessionHandler.buildRunnerConfig using appendSystemPrompt
  instead of systemPrompt (re-introduced the cache_control bug)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: Use appendSystemPrompt for chat sessions (root cause was empty prompt)

The cache_control error was caused by an empty prompt string after
stripping the @mention (e.g. just "@cyrus" with no text), not by the
claude_code preset. Revert back to appendSystemPrompt so chat sessions
benefit from the full Claude Code system prompt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use streaming mode for chat sessions to enable follow-up message injection

Chat sessions (Slack threads) were started with runner.start() (string mode),
so follow-up messages arriving mid-session would crash with "Cannot add stream
message when not in streaming mode". Switch to startStreaming() and add an
isStreaming() guard before calling addStreamMessage().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <paytonwebber@gmail.com>

* Merge main into cypack-807 branch (CYPACK-821) (#873)

* Respect agent guidance for draft PRs and use --track for worktrees (#834)

- Update gh-pr subroutine to conditionally gate `gh pr ready` based on
  agent guidance (respects --draft flag in workspace guidance)
- Update changelog-update subroutine to include --base flag in PR creation
  targeting the configured base branch from context
- Add --track flag to git worktree add commands to properly set upstream
  tracking branch

Fixes issues where PRs were auto-converted from draft to ready and
worktrees weren't tracking the correct upstream branch.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Prepare release v0.2.20 (#835)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools (#837)

* Update @anthropic-ai/claude-agent-sdk to v0.2.34 and @anthropic-ai/sdk to v0.73.0

- Updated @anthropic-ai/claude-agent-sdk from ^0.2.7 to ^0.2.34
- Updated @anthropic-ai/sdk from ^0.71.2 to ^0.73.0
- Fixed type compatibility with new BetaUsage fields (inference_geo, iterations, stop_reason)
- Updated gemini-runner adapters to handle new SDK types

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Update changelog for SDK updates

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Enable Claude Code experimental features in claude-runner

- Set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1
- Set CLAUDE_CODE_ENABLE_TASKS=true
- Set CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
- Updated test expectations to include new env configuration

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools

- Added formatTaskParameter() method to IMessageFormatter interface
- Implemented Task tool formatting (TaskCreate, TaskUpdate, TaskList, TaskGet) in ClaudeMessageFormatter and GeminiMessageFormatter
- Updated AgentSessionManager to handle Task tools as thought activities
- Added comprehensive tests for Task tool formatting (20 new tests)
- Marked TodoWrite as deprecated while maintaining backward compatibility
- Fixed linting warnings in switch case block scope

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix spawn node ENOENT by passing process.env to Claude SDK

The env option only contained 3 hardcoded vars, causing PATH to be
missing from the spawned Claude Code process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures (#843)

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures

Cherry-picks commit d5d9e5a0 from refactor/improved-logging branch.
Resolves merge conflicts adapting logging refactor patterns
(sessionId/log.info) to current branch patterns
(linearAgentActivitySessionId/console.log).

When a single-turn subroutine exits with an error (e.g. error_max_turns),
AgentSessionManager now recovers by using the last completed subroutine's
result via ProcedureAnalyzer.getLastSubroutineResult(), allowing the
procedure to continue to completion instead of failing silently.

Also adds tools config pass-through to ClaudeRunner and EdgeWorker
so disallowAllTools subroutines properly disable built-in tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update changelogs for CYPACK-792 (#843)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ClaudeRunner test env assertions after process.env spread

Tests expected env to contain only 3 explicit keys, but commit
6e8d873d added ...process.env to prevent spawn ENOENT errors.
Updated assertions to use expect.objectContaining() so tests
verify required keys without failing on additional process.env
entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix TaskCreate/TaskGet formatting, add ToolSearch/TaskOutput formatting, include task subject in TaskUpdate/TaskGet (#846)

* Fix TaskCreate/TaskGet formatting for parallel execution, add ToolSearch and TaskOutput formatting

TaskCreate now renders as concise checklist items (⏳ **subject**) instead of
verbose multi-line format, better suited for parallel async execution. TaskGet
shows subject with ID when enriched. ToolSearch renders as a thought with
descriptive formatting. TaskOutput shows task status with blocking/non-blocking
context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-795

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify TaskUpdate/TaskGet to use task number, show ToolSearch query directly

TaskUpdate and TaskGet now always display the task number (e.g. "Task #123")
instead of the subject description for consistency with parallel execution.
ToolSearch now shows the query directly as the parameter value, matching
the pattern of other tools like Bash (command) and Read (file_path).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert Gemini runner formatting changes (not applicable to these tools)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Include task subject in TaskUpdate and TaskGet formatting

TaskUpdate and TaskGet now display the task's short description
alongside the task number (e.g., "✅ Task #3 — Fix login bug")
when a subject is available, falling back to number-only when not.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Enrich TaskUpdate/TaskGet activities with task subject (CYPACK-797) (#847)

* Enrich TaskUpdate/TaskGet activities with task subject from cache and result parsing

TaskUpdate and TaskGet tool inputs don't include the task subject, causing
Linear activities to show only "Task #3" without context. This change defers
their activity posting from tool_use time to tool_result time, where subject
can be sourced from: (1) a cache populated by TaskCreate results, or
(2) parsing the Subject field from TaskGet result content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-797

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Delete packages/edge-worker/test/AgentSessionManager.task-subject-enrichment.test.ts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>

* Release v0.2.21 (#848)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage' (#815)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage'

Update orchestrator system prompts to explicitly require setting state to
"To Do" when creating issues with mcp__linear__create_issue. Previously,
issues were being created with default "Triage" status.

Changes to both orchestrator.md (v2.5.0) and graphite-orchestrator.md (v1.3.0):
- Added state requirement to Required Tools section
- Added Status bullet point in Decompose section
- Added status checklist item in Sub-Issue Creation Checklist
- Fixed outdated tool names in orchestrator.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(changelog): add entry for CYPACK-761

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>

* Codex SDK migration + split agent/model selectors (#850)

* chore: checkpoint codex sdk integration WIP

* Switch codex runner to SDK and add agent/model selectors

* chore: checkpoint codex sdk integration WIP

* Switch codex runner to SDK and add agent/model selectors

* Resolve rebase conflicts, migrate model defaults, and fix codex session init

* Fix Codex/Gemini usage typing and add Codex formatter replay tests

* Emit Codex tool lifecycle events for Linear activities

* Format Codex todos as markdown checklists

* Extract F1 test-drive workflow into shared skill

* Add user-facing changelog entry for agent/model selectors

* Add PR link to changelog entry for codex selector work

* update sandbox settings

* add .git folder for worktrees to allowed directories list

* Cursor harness MCP enable + permission mapping hardening (#858)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage' (#815)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage'

Update orchestrator system prompts to explicitly require setting state to
"To Do" when creating issues with mcp__linear__create_issue. Previously,
issues were being created with default "Triage" status.

Changes to both orchestrator.md (v2.5.0) and graphite-orchestrator.md (v1.3.0):
- Added state requirement to Required Tools section
- Added Status bullet point in Decompose section
- Added status checklist item in Sub-Issue Creation Checklist
- Fixed outdated tool names in orchestrator.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(changelog): add entry for CYPACK-761

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>

* feat(runners): add codex/cursor harness support and F1 validation

* fix(cursor-runner): support non-mock cursor F1 session output

* Fix cursor harness verification regressions

* fix(ci): resolve build type errors in config-updater and core session types

* fix(edge-worker): wire cursor runner selection and prompt assets

* docs(changelog): frame cursor harness as major feature

* docs(changelog): use user-facing framing for cursor entry

* docs(changelog): clarify cursor selector usage

* fix(build): remove baseUrl/paths from tsconfig to fix dist output structure

The baseUrl pointing to monorepo root caused TypeScript to expand rootDir,
nesting compiled output under dist/edge-worker/src/ instead of dist/.
This broke module resolution since package.json declares main as dist/index.js.
Also adds missing cyrus-cursor-runner dependency to edge-worker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cursor/edge-worker): stop-session handling and cursor tool activity mapping

* chore(gitignore): ignore nested node_modules directories

* fix(edge-worker): resume cursor sessions for prompted continuation

* fix(cursor-runner): sync project permissions for cursor CLI sessions

* fix(cursor-runner): map MCP tool permissions to Cursor CLI config

* docs(claude): add cursor permission translation gotcha

* fix(codex): default runner model to gpt-5.3-codex

* fix(cursor-runner): pre-enable MCP servers before cursor sessions

* docs(cursor): clarify .cursor/mcp.json source-of-truth guidance

* fix(cursor-runner): scope wildcard file permissions to workspace

* fix(codex): post actual error message to Linear for usage limit errors

When Codex hits usage limits or turn.failed errors, the full error message
is now posted to Linear agent activity instead of a generic message.
- AgentSessionManager: use errors[] for content when result is empty
- CodexRunner: fallback to standalone error event when turn.failed has no message

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): enable sandbox by default, fix cli.json formatting

- Default Cursor sandbox to enabled for tool execution isolation
- Write .cursor/cli.json with tabs for Biome compatibility
- Exclude .cursor from Biome checks (generated config)
- Update CHANGELOG with sandbox default

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): validate cursor-agent version before run, post error to Linear on mismatch

- Run cursor-agent --version before spawn and compare to tested version
- Post error_during_execution to Linear via agent activity when version mismatches
- Add cursorAgentVersion config and CYRUS_CURSOR_AGENT_VERSION env override
- Add CursorRunner.version-check.test.ts with 3 tests
- Update CHANGELOG with version validation behavior

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): render TODO_STATUS_COMPLETED todos as checked in Linear

- Map Cursor API TODO_STATUS_COMPLETED to [x] marker in formatter and summarizeTodoList
- Map TODO_STATUS_IN_PROGRESS for (in progress) suffix
- Add formatter test for TODO_STATUS_* values
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): log assembled cursor-agent CLI args to console and session logs

- Log full spawn command (path + args) before cursor-agent execution
- Write to both console and session log file for debugging
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): remove --force and --api-key from CLI args

- Remove --force option from cursor-agent invocations
- Pass API key via CURSOR_API_KEY env only (no longer in args)
- API key no longer appears in spawn logs or terminal output
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* add --trust

* move trust line

* Delete .cursor/cli.json

* fix(cursor-runner): backup and restore .cursor/cli.json instead of overwriting

Temporarily rename existing .cursor/cli.json before writing Cyrus permissions,
then restore original when session ends. Preserves user's config.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): add deny rules to scope Read/Write to workspace only

- When broad Read or Write allowed, add deny rules for /etc, /usr, etc.
- Add deny rules for workspace sibling directories
- Prevents cursor-agent from accessing /etc/hosts and system paths
- Add test: only mutates project .cursor/cli.json, leaves home config unchanged
- Update wildcard permissions test to expect system-path denies

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(changelog): add Cursor .cursor/cli.json backup/restore entry for CYPACK-804

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: agentclear <agentops@ceedar.ai>
Co-authored-by: Cyrus <208047790+cyrusagent@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Update README with agent capabilities and notes (#869)

* Bump fastify from 5.6.1 to 5.7.3 (#828)

Bumps [fastify](https://github.com/fastify/fastify) from 5.6.1 to 5.7.3.
- [Release notes](https://github.com/fastify/fastify/releases)
- [Commits](https://github.com/fastify/fastify/compare/v5.6.1...v5.7.3)

---
updated-dependencies:
- dependency-name: fastify
  dependency-version: 5.7.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* docs: Update internal changelog for main merge (CYPACK-821)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* refactor: Extract 5 service modules from EdgeWorker.ts (CYPACK-822)

Extract logically grouped functionality into dedicated modules to improve
readability and maintainability of the 7,687-line EdgeWorker.ts file:

- ActivityPoster: Linear activity posting (289 lines)
- AttachmentService: Attachment download and manifest generation (488 lines)
- ConfigManager: Config file watching, reload, and change detection (320 lines)
- PromptBuilder: Prompt assembly, system prompts, and issue context (1,270 lines)
- RunnerSelectionService: Runner/model selection and tool configuration (498 lines)

EdgeWorker.ts reduced from 7,687 to 5,466 lines (29% reduction). All 522
tests passing, typecheck clean, lint clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <paytonwebber@gmail.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat: Read Slack bot token from SLACK_BOT_TOKEN env var only (CYPACK-824) (#876)

* feat: Read Slack bot token from SLACK_BOT_TOKEN env var with header fallback

Read the Slack bot token from the SLACK_BOT_TOKEN environment variable as
the primary source, falling back to the X-Slack-Bot-Token header for
backwards compatibility. Logs a warning when using the header fallback to
help identify when infrastructure hasn't been updated yet.

CYPACK-824

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: Update changelogs for CYPACK-824

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: secure cyrus-tools MCP auth and codex MCP header mapping (CYPACK-817) (#870)

* chore: initialize draft PR for CYPACK-817

* feat(edge-worker): serve cyrus tools via Fastify MCP (CYPACK-817)

* refactor: move cyrus tools server and tests to mcp-tools

* fix(edge-worker): resolve cyrus-mcp-tools as package for builds

* feat(runners): pass MCP configs to gemini cursor codex

* feat(edge-worker): enforce CYRUS_API_KEY auth for cyrus-tools MCP

* fix(codex-runner): map MCP auth headers for HTTP servers

* refactor: Remove X-Slack-Bot-Token header fallback, use env var only

Remove the backwards-compatibility fallback that read the Slack bot
token from the X-Slack-Bot-Token request header when the SLACK_BOT_TOKEN
env var was not set. The previous implementation was in beta, so no
transition period is needed. The token should only exist as an
environment variable.

- Simplify getSlackBotToken() to only read process.env.SLACK_BOT_TOKEN
- Remove header fallback logic and warning log
- Remove 3 fallback-related tests, add env-var-missing test
- Update SlackChatAdapter warning to reference env var instead of header
- Update changelogs to reflect no-fallback behavior

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Add cyrus-tools MCP to Slack ag…
Connoropolous added a commit that referenced this pull request Feb 19, 2026
…-826) (#878)

* feat: Create GlobalSessionRegistry for centralized session storage (CYPACK-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>

* docs: Update internal changelog for GlobalSessionRegistry (CYPACK-725)

Add changelog entry for GlobalSessionRegistry implementation with PR link.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* fix: Ensure updateEntry preserves required fields (CYPACK-725)

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>

* feat: Extract IActivitySink interface and LinearActivitySink implementation (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>

* docs: Update internal changelog for IActivitySink extraction (CYPACK-726)

Add changelog entry documenting the extraction of IActivitySink interface
and LinearActivitySink implementation.

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* feat: Integrate GlobalSessionRegistry with EdgeWorker (CYPACK-727)

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

* docs: Update internal changelog for GlobalSessionRegistry integration (CYPACK-727)

* feat: Update CyrusAgentSession schema and persistence format to v3.0 (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>

* docs: Update internal changelog for CyrusAgentSession v3.0 schema (CYPACK-728)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* fix: Resolve CI failures after rebase on main (CYPACK-724)

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

* fix: Use issueContext.issueId in session lookup methods (CYPACK-724)

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>

* fix: Add defensive result.success check in LinearActivitySink.createAgentSession (CYPACK-724)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor logging to a dedicated logger (#818)

* Fix: Disable MCP tools when disallowAllTools is true

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>

* Refactor logging to a dedicated logger

* Add source context to sessions when emitting logs

* Fix issue where single-turn sub-routines would silently fail by exceeding 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.

* Respect agent guidance for draft PRs and use --track for worktrees (#834)

- Update gh-pr subroutine to conditionally gate `gh pr ready` based on
  agent guidance (respects --draft flag in workspace guidance)
- Update changelog-update subroutine to include --base flag in PR creation
  targeting the configured base branch from context
- Add --track flag to git worktree add commands to properly set upstream
  tracking branch

Fixes issues where PRs were auto-converted from draft to ready and
worktrees weren't tracking the correct upstream branch.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Prepare release v0.2.20 (#835)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools (#837)

* Update @anthropic-ai/claude-agent-sdk to v0.2.34 and @anthropic-ai/sdk to v0.73.0

- Updated @anthropic-ai/claude-agent-sdk from ^0.2.7 to ^0.2.34
- Updated @anthropic-ai/sdk from ^0.71.2 to ^0.73.0
- Fixed type compatibility with new BetaUsage fields (inference_geo, iterations, stop_reason)
- Updated gemini-runner adapters to handle new SDK types

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Update changelog for SDK updates

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Enable Claude Code experimental features in claude-runner

- Set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1
- Set CLAUDE_CODE_ENABLE_TASKS=true
- Set CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
- Updated test expectations to include new env configuration

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools

- Added formatTaskParameter() method to IMessageFormatter interface
- Implemented Task tool formatting (TaskCreate, TaskUpdate, TaskList, TaskGet) in ClaudeMessageFormatter and GeminiMessageFormatter
- Updated AgentSessionManager to handle Task tools as thought activities
- Added comprehensive tests for Task tool formatting (20 new tests)
- Marked TodoWrite as deprecated while maintaining backward compatibility
- Fixed linting warnings in switch case block scope

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix spawn node ENOENT by passing process.env to Claude SDK

The env option only contained 3 hardcoded vars, causing PATH to be
missing from the spawned Claude Code process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures (#843)

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures

Cherry-picks commit d5d9e5a0 from refactor/improved-logging branch.
Resolves merge conflicts adapting logging refactor patterns
(sessionId/log.info) to current branch patterns
(linearAgentActivitySessionId/console.log).

When a single-turn subroutine exits with an error (e.g. error_max_turns),
AgentSessionManager now recovers by using the last completed subroutine's
result via ProcedureAnalyzer.getLastSubroutineResult(), allowing the
procedure to continue to completion instead of failing silently.

Also adds tools config pass-through to ClaudeRunner and EdgeWorker
so disallowAllTools subroutines properly disable built-in tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update changelogs for CYPACK-792 (#843)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ClaudeRunner test env assertions after process.env spread

Tests expected env to contain only 3 explicit keys, but commit
6e8d873d added ...process.env to prevent spawn ENOENT errors.
Updated assertions to use expect.objectContaining() so tests
verify required keys without failing on additional process.env
entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix TaskCreate/TaskGet formatting, add ToolSearch/TaskOutput formatting, include task subject in TaskUpdate/TaskGet (#846)

* Fix TaskCreate/TaskGet formatting for parallel execution, add ToolSearch and TaskOutput formatting

TaskCreate now renders as concise checklist items (⏳ **subject**) instead of
verbose multi-line format, better suited for parallel async execution. TaskGet
shows subject with ID when enriched. ToolSearch renders as a thought with
descriptive formatting. TaskOutput shows task status with blocking/non-blocking
context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-795

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify TaskUpdate/TaskGet to use task number, show ToolSearch query directly

TaskUpdate and TaskGet now always display the task number (e.g. "Task #123")
instead of the subject description for consistency with parallel execution.
ToolSearch now shows the query directly as the parameter value, matching
the pattern of other tools like Bash (command) and Read (file_path).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert Gemini runner formatting changes (not applicable to these tools)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Include task subject in TaskUpdate and TaskGet formatting

TaskUpdate and TaskGet now display the task's short description
alongside the task number (e.g., "✅ Task #3 — Fix login bug")
when a subject is available, falling back to number-only when not.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Enrich TaskUpdate/TaskGet activities with task subject (CYPACK-797) (#847)

* Enrich TaskUpdate/TaskGet activities with task subject from cache and result parsing

TaskUpdate and TaskGet tool inputs don't include the task subject, causing
Linear activities to show only "Task #3" without context. This change defers
their activity posting from tool_use time to tool_result time, where subject
can be sourced from: (1) a cache populated by TaskCreate results, or
(2) parsing the Subject field from TaskGet result content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-797

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Delete packages/edge-worker/test/AgentSessionManager.task-subject-enrichment.test.ts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>

* Release v0.2.21 (#848)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* feat: unified internal message bus with platform translators (#851)

* 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>

* Consolidate scattered message posting logic to a helper function (#854)

* 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>

* feat: reuse existing worktree when branch is already checked out

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>

* Feature/add GitHub comment reaction (#860)

* 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>

---------

Co-authored-by: agentclear <agentops@ceedar.ai>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* feat: Add Slack event transport for @mention webhooks (CYPACK-807) (#861)

* feat: Add Slack event transport for @mention webhooks (CYPACK-807)

Implement cyrus-slack-event-transport package to receive Slack app_mention
webhooks forwarded from CYHOST. Adds SlackEventTransport (Fastify endpoint
with Bearer token auth), SlackMessageTranslator (translates to unified
SessionStartMessage/UserPromptMessage), thread-aware session keys, and
Slack platform data types in cyrus-core.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Handle Slack @mention webhooks as agent sessions with thread reply (CYPACK-807)

Slack @mentions now trigger full agent sessions using the first configured
repository, with results posted back as threaded replies in Slack.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Slack sessions use empty working directories, not git worktrees (CYPACK-815) (#868)

* feat: Slack sessions use empty working directories, not git worktrees (CYPACK-815)

Decouple Slack agent sessions from repository configuration so they run
in transient empty directories rather than git worktrees. Subsequent
@mentions in the same Slack thread now share the same session context
via resume or streaming input injection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: Update changelogs for CYPACK-815 (#868)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use plain systemPrompt for Slack sessions to avoid empty text block error

The claude_code preset constructs context blocks (CLAUDE.md, git status)
that are empty in Slack's non-git empty workspace directories, causing
"cache_control cannot be set for empty text blocks" API errors. Using a
direct systemPrompt string bypasses the preset entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Handle empty Slack @mention by providing default prompt

Reverts 166d88be which switched from appendSystemPrompt to plain
systemPrompt — we need the claude_code preset. The actual root cause
was an empty user prompt: when a Slack message is just "@cyrus" with
no other text, stripMention returns "", which the SDK sends as an
empty text block with cache_control, rejected by the API. Now falls
back to "Ask the user for more context" when the stripped text is empty.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Skip procedure routing for Slack sessions on completion

Slack sessions are created without a ProcedureAnalyzer since they are
simple conversational sessions, not multi-subroutine workflows. When
the session completed, completeSession called handleProcedureCompletion
which threw "ProcedureAnalyzer not available". Now gracefully returns
early when no ProcedureAnalyzer is configured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Add Slack thread context to agent sessions

When a user @mentions Cyrus in a Slack thread, fetch the preceding
conversation via conversations.replies and prepend it as XML context
to the user prompt. This gives the agent visibility into what users
were discussing before invoking it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: Extract Slack integration via ChatPlatformAdapter pattern

Introduce ChatPlatformAdapter<TEvent> interface and ChatSessionHandler<TEvent>
class to decouple chat platform session lifecycle from EdgeWorker. Slack becomes
the first adapter implementation (SlackChatAdapter), removing ~450 lines and 9
private methods from EdgeWorker.ts. Adding a new chat platform now requires only
an adapter (~80 lines) instead of duplicating session management logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: Add createChatSession for issue-free sessions, fix appendSystemPrompt

Addresses PR review feedback:
- Add AgentSessionManager.createChatSession() for chat platforms that
  don't have issue context, avoiding synthetic IssueMinimal objects
- ChatSessionHandler now uses createChatSession instead of
  createLinearAgentSession with fake issue data
- Fix ChatSessionHandler.buildRunnerConfig using appendSystemPrompt
  instead of systemPrompt (re-introduced the cache_control bug)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: Use appendSystemPrompt for chat sessions (root cause was empty prompt)

The cache_control error was caused by an empty prompt string after
stripping the @mention (e.g. just "@cyrus" with no text), not by the
claude_code preset. Revert back to appendSystemPrompt so chat sessions
benefit from the full Claude Code system prompt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use streaming mode for chat sessions to enable follow-up message injection

Chat sessions (Slack threads) were started with runner.start() (string mode),
so follow-up messages arriving mid-session would crash with "Cannot add stream
message when not in streaming mode". Switch to startStreaming() and add an
isStreaming() guard before calling addStreamMessage().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <paytonwebber@gmail.com>

* Merge main into cypack-807 branch (CYPACK-821) (#873)

* Respect agent guidance for draft PRs and use --track for worktrees (#834)

- Update gh-pr subroutine to conditionally gate `gh pr ready` based on
  agent guidance (respects --draft flag in workspace guidance)
- Update changelog-update subroutine to include --base flag in PR creation
  targeting the configured base branch from context
- Add --track flag to git worktree add commands to properly set upstream
  tracking branch

Fixes issues where PRs were auto-converted from draft to ready and
worktrees weren't tracking the correct upstream branch.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Prepare release v0.2.20 (#835)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools (#837)

* Update @anthropic-ai/claude-agent-sdk to v0.2.34 and @anthropic-ai/sdk to v0.73.0

- Updated @anthropic-ai/claude-agent-sdk from ^0.2.7 to ^0.2.34
- Updated @anthropic-ai/sdk from ^0.71.2 to ^0.73.0
- Fixed type compatibility with new BetaUsage fields (inference_geo, iterations, stop_reason)
- Updated gemini-runner adapters to handle new SDK types

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Update changelog for SDK updates

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Enable Claude Code experimental features in claude-runner

- Set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1
- Set CLAUDE_CODE_ENABLE_TASKS=true
- Set CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
- Updated test expectations to include new env configuration

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools

- Added formatTaskParameter() method to IMessageFormatter interface
- Implemented Task tool formatting (TaskCreate, TaskUpdate, TaskList, TaskGet) in ClaudeMessageFormatter and GeminiMessageFormatter
- Updated AgentSessionManager to handle Task tools as thought activities
- Added comprehensive tests for Task tool formatting (20 new tests)
- Marked TodoWrite as deprecated while maintaining backward compatibility
- Fixed linting warnings in switch case block scope

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix spawn node ENOENT by passing process.env to Claude SDK

The env option only contained 3 hardcoded vars, causing PATH to be
missing from the spawned Claude Code process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures (#843)

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures

Cherry-picks commit d5d9e5a0 from refactor/improved-logging branch.
Resolves merge conflicts adapting logging refactor patterns
(sessionId/log.info) to current branch patterns
(linearAgentActivitySessionId/console.log).

When a single-turn subroutine exits with an error (e.g. error_max_turns),
AgentSessionManager now recovers by using the last completed subroutine's
result via ProcedureAnalyzer.getLastSubroutineResult(), allowing the
procedure to continue to completion instead of failing silently.

Also adds tools config pass-through to ClaudeRunner and EdgeWorker
so disallowAllTools subroutines properly disable built-in tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update changelogs for CYPACK-792 (#843)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ClaudeRunner test env assertions after process.env spread

Tests expected env to contain only 3 explicit keys, but commit
6e8d873d added ...process.env to prevent spawn ENOENT errors.
Updated assertions to use expect.objectContaining() so tests
verify required keys without failing on additional process.env
entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix TaskCreate/TaskGet formatting, add ToolSearch/TaskOutput formatting, include task subject in TaskUpdate/TaskGet (#846)

* Fix TaskCreate/TaskGet formatting for parallel execution, add ToolSearch and TaskOutput formatting

TaskCreate now renders as concise checklist items (⏳ **subject**) instead of
verbose multi-line format, better suited for parallel async execution. TaskGet
shows subject with ID when enriched. ToolSearch renders as a thought with
descriptive formatting. TaskOutput shows task status with blocking/non-blocking
context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-795

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify TaskUpdate/TaskGet to use task number, show ToolSearch query directly

TaskUpdate and TaskGet now always display the task number (e.g. "Task #123")
instead of the subject description for consistency with parallel execution.
ToolSearch now shows the query directly as the parameter value, matching
the pattern of other tools like Bash (command) and Read (file_path).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert Gemini runner formatting changes (not applicable to these tools)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Include task subject in TaskUpdate and TaskGet formatting

TaskUpdate and TaskGet now display the task's short description
alongside the task number (e.g., "✅ Task #3 — Fix login bug")
when a subject is available, falling back to number-only when not.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Enrich TaskUpdate/TaskGet activities with task subject (CYPACK-797) (#847)

* Enrich TaskUpdate/TaskGet activities with task subject from cache and result parsing

TaskUpdate and TaskGet tool inputs don't include the task subject, causing
Linear activities to show only "Task #3" without context. This change defers
their activity posting from tool_use time to tool_result time, where subject
can be sourced from: (1) a cache populated by TaskCreate results, or
(2) parsing the Subject field from TaskGet result content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-797

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Delete packages/edge-worker/test/AgentSessionManager.task-subject-enrichment.test.ts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>

* Release v0.2.21 (#848)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage' (#815)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage'

Update orchestrator system prompts to explicitly require setting state to
"To Do" when creating issues with mcp__linear__create_issue. Previously,
issues were being created with default "Triage" status.

Changes to both orchestrator.md (v2.5.0) and graphite-orchestrator.md (v1.3.0):
- Added state requirement to Required Tools section
- Added Status bullet point in Decompose section
- Added status checklist item in Sub-Issue Creation Checklist
- Fixed outdated tool names in orchestrator.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(changelog): add entry for CYPACK-761

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>

* Codex SDK migration + split agent/model selectors (#850)

* chore: checkpoint codex sdk integration WIP

* Switch codex runner to SDK and add agent/model selectors

* chore: checkpoint codex sdk integration WIP

* Switch codex runner to SDK and add agent/model selectors

* Resolve rebase conflicts, migrate model defaults, and fix codex session init

* Fix Codex/Gemini usage typing and add Codex formatter replay tests

* Emit Codex tool lifecycle events for Linear activities

* Format Codex todos as markdown checklists

* Extract F1 test-drive workflow into shared skill

* Add user-facing changelog entry for agent/model selectors

* Add PR link to changelog entry for codex selector work

* update sandbox settings

* add .git folder for worktrees to allowed directories list

* Cursor harness MCP enable + permission mapping hardening (#858)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage' (#815)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage'

Update orchestrator system prompts to explicitly require setting state to
"To Do" when creating issues with mcp__linear__create_issue. Previously,
issues were being created with default "Triage" status.

Changes to both orchestrator.md (v2.5.0) and graphite-orchestrator.md (v1.3.0):
- Added state requirement to Required Tools section
- Added Status bullet point in Decompose section
- Added status checklist item in Sub-Issue Creation Checklist
- Fixed outdated tool names in orchestrator.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(changelog): add entry for CYPACK-761

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>

* feat(runners): add codex/cursor harness support and F1 validation

* fix(cursor-runner): support non-mock cursor F1 session output

* Fix cursor harness verification regressions

* fix(ci): resolve build type errors in config-updater and core session types

* fix(edge-worker): wire cursor runner selection and prompt assets

* docs(changelog): frame cursor harness as major feature

* docs(changelog): use user-facing framing for cursor entry

* docs(changelog): clarify cursor selector usage

* fix(build): remove baseUrl/paths from tsconfig to fix dist output structure

The baseUrl pointing to monorepo root caused TypeScript to expand rootDir,
nesting compiled output under dist/edge-worker/src/ instead of dist/.
This broke module resolution since package.json declares main as dist/index.js.
Also adds missing cyrus-cursor-runner dependency to edge-worker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cursor/edge-worker): stop-session handling and cursor tool activity mapping

* chore(gitignore): ignore nested node_modules directories

* fix(edge-worker): resume cursor sessions for prompted continuation

* fix(cursor-runner): sync project permissions for cursor CLI sessions

* fix(cursor-runner): map MCP tool permissions to Cursor CLI config

* docs(claude): add cursor permission translation gotcha

* fix(codex): default runner model to gpt-5.3-codex

* fix(cursor-runner): pre-enable MCP servers before cursor sessions

* docs(cursor): clarify .cursor/mcp.json source-of-truth guidance

* fix(cursor-runner): scope wildcard file permissions to workspace

* fix(codex): post actual error message to Linear for usage limit errors

When Codex hits usage limits or turn.failed errors, the full error message
is now posted to Linear agent activity instead of a generic message.
- AgentSessionManager: use errors[] for content when result is empty
- CodexRunner: fallback to standalone error event when turn.failed has no message

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): enable sandbox by default, fix cli.json formatting

- Default Cursor sandbox to enabled for tool execution isolation
- Write .cursor/cli.json with tabs for Biome compatibility
- Exclude .cursor from Biome checks (generated config)
- Update CHANGELOG with sandbox default

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): validate cursor-agent version before run, post error to Linear on mismatch

- Run cursor-agent --version before spawn and compare to tested version
- Post error_during_execution to Linear via agent activity when version mismatches
- Add cursorAgentVersion config and CYRUS_CURSOR_AGENT_VERSION env override
- Add CursorRunner.version-check.test.ts with 3 tests
- Update CHANGELOG with version validation behavior

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): render TODO_STATUS_COMPLETED todos as checked in Linear

- Map Cursor API TODO_STATUS_COMPLETED to [x] marker in formatter and summarizeTodoList
- Map TODO_STATUS_IN_PROGRESS for (in progress) suffix
- Add formatter test for TODO_STATUS_* values
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): log assembled cursor-agent CLI args to console and session logs

- Log full spawn command (path + args) before cursor-agent execution
- Write to both console and session log file for debugging
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): remove --force and --api-key from CLI args

- Remove --force option from cursor-agent invocations
- Pass API key via CURSOR_API_KEY env only (no longer in args)
- API key no longer appears in spawn logs or terminal output
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* add --trust

* move trust line

* Delete .cursor/cli.json

* fix(cursor-runner): backup and restore .cursor/cli.json instead of overwriting

Temporarily rename existing .cursor/cli.json before writing Cyrus permissions,
then restore original when session ends. Preserves user's config.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): add deny rules to scope Read/Write to workspace only

- When broad Read or Write allowed, add deny rules for /etc, /usr, etc.
- Add deny rules for workspace sibling directories
- Prevents cursor-agent from accessing /etc/hosts and system paths
- Add test: only mutates project .cursor/cli.json, leaves home config unchanged
- Update wildcard permissions test to expect system-path denies

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(changelog): add Cursor .cursor/cli.json backup/restore entry for CYPACK-804

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: agentclear <agentops@ceedar.ai>
Co-authored-by: Cyrus <208047790+cyrusagent@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Update README with agent capabilities and notes (#869)

* Bump fastify from 5.6.1 to 5.7.3 (#828)

Bumps [fastify](https://github.com/fastify/fastify) from 5.6.1 to 5.7.3.
- [Release notes](https://github.com/fastify/fastify/releases)
- [Commits](https://github.com/fastify/fastify/compare/v5.6.1...v5.7.3)

---
updated-dependencies:
- dependency-name: fastify
  dependency-version: 5.7.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* docs: Update internal changelog for main merge (CYPACK-821)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <paytonwebber@gmail.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* refactor: Extract 5 service modules from EdgeWorker.ts (CYPACK-822) (#874)

* feat: Add Slack event transport for @mention webhooks (CYPACK-807)

Implement cyrus-slack-event-transport package to receive Slack app_mention
webhooks forwarded from CYHOST. Adds SlackEventTransport (Fastify endpoint
with Bearer token auth), SlackMessageTranslator (translates to unified
SessionStartMessage/UserPromptMessage), thread-aware session keys, and
Slack platform data types in cyrus-core.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Handle Slack @mention webhooks as agent sessions with thread reply (CYPACK-807)

Slack @mentions now trigger full agent sessions using the first configured
repository, with results posted back as threaded replies in Slack.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Slack sessions use empty working directories, not git worktrees (CYPACK-815) (#868)

* feat: Slack sessions use empty working directories, not git worktrees (CYPACK-815)

Decouple Slack agent sessions from repository configuration so they run
in transient empty directories rather than git worktrees. Subsequent
@mentions in the same Slack thread now share the same session context
via resume or streaming input injection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: Update changelogs for CYPACK-815 (#868)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use plain systemPrompt for Slack sessions to avoid empty text block error

The claude_code preset constructs context blocks (CLAUDE.md, git status)
that are empty in Slack's non-git empty workspace directories, causing
"cache_control cannot be set for empty text blocks" API errors. Using a
direct systemPrompt string bypasses the preset entirely.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Handle empty Slack @mention by providing default prompt

Reverts 166d88be which switched from appendSystemPrompt to plain
systemPrompt — we need the claude_code preset. The actual root cause
was an empty user prompt: when a Slack message is just "@cyrus" with
no other text, stripMention returns "", which the SDK sends as an
empty text block with cache_control, rejected by the API. Now falls
back to "Ask the user for more context" when the stripped text is empty.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Skip procedure routing for Slack sessions on completion

Slack sessions are created without a ProcedureAnalyzer since they are
simple conversational sessions, not multi-subroutine workflows. When
the session completed, completeSession called handleProcedureCompletion
which threw "ProcedureAnalyzer not available". Now gracefully returns
early when no ProcedureAnalyzer is configured.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* feat: Add Slack thread context to agent sessions

When a user @mentions Cyrus in a Slack thread, fetch the preceding
conversation via conversations.replies and prepend it as XML context
to the user prompt. This gives the agent visibility into what users
were discussing before invoking it.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: Extract Slack integration via ChatPlatformAdapter pattern

Introduce ChatPlatformAdapter<TEvent> interface and ChatSessionHandler<TEvent>
class to decouple chat platform session lifecycle from EdgeWorker. Slack becomes
the first adapter implementation (SlackChatAdapter), removing ~450 lines and 9
private methods from EdgeWorker.ts. Adding a new chat platform now requires only
an adapter (~80 lines) instead of duplicating session management logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* refactor: Add createChatSession for issue-free sessions, fix appendSystemPrompt

Addresses PR review feedback:
- Add AgentSessionManager.createChatSession() for chat platforms that
  don't have issue context, avoiding synthetic IssueMinimal objects
- ChatSessionHandler now uses createChatSession instead of
  createLinearAgentSession with fake issue data
- Fix ChatSessionHandler.buildRunnerConfig using appendSystemPrompt
  instead of systemPrompt (re-introduced the cache_control bug)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* revert: Use appendSystemPrompt for chat sessions (root cause was empty prompt)

The cache_control error was caused by an empty prompt string after
stripping the @mention (e.g. just "@cyrus" with no text), not by the
claude_code preset. Revert back to appendSystemPrompt so chat sessions
benefit from the full Claude Code system prompt.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Use streaming mode for chat sessions to enable follow-up message injection

Chat sessions (Slack threads) were started with runner.start() (string mode),
so follow-up messages arriving mid-session would crash with "Cannot add stream
message when not in streaming mode". Switch to startStreaming() and add an
isStreaming() guard before calling addStreamMessage().

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <paytonwebber@gmail.com>

* Merge main into cypack-807 branch (CYPACK-821) (#873)

* Respect agent guidance for draft PRs and use --track for worktrees (#834)

- Update gh-pr subroutine to conditionally gate `gh pr ready` based on
  agent guidance (respects --draft flag in workspace guidance)
- Update changelog-update subroutine to include --base flag in PR creation
  targeting the configured base branch from context
- Add --track flag to git worktree add commands to properly set upstream
  tracking branch

Fixes issues where PRs were auto-converted from draft to ready and
worktrees weren't tracking the correct upstream branch.

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Prepare release v0.2.20 (#835)

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools (#837)

* Update @anthropic-ai/claude-agent-sdk to v0.2.34 and @anthropic-ai/sdk to v0.73.0

- Updated @anthropic-ai/claude-agent-sdk from ^0.2.7 to ^0.2.34
- Updated @anthropic-ai/sdk from ^0.71.2 to ^0.73.0
- Fixed type compatibility with new BetaUsage fields (inference_geo, iterations, stop_reason)
- Updated gemini-runner adapters to handle new SDK types

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Update changelog for SDK updates

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Enable Claude Code experimental features in claude-runner

- Set CLAUDE_CODE_ADDITIONAL_DIRECTORIES_CLAUDE_MD=1
- Set CLAUDE_CODE_ENABLE_TASKS=true
- Set CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1
- Updated test expectations to include new env configuration

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Refactor TodoWrite formatting to support Task tools

- Added formatTaskParameter() method to IMessageFormatter interface
- Implemented Task tool formatting (TaskCreate, TaskUpdate, TaskList, TaskGet) in ClaudeMessageFormatter and GeminiMessageFormatter
- Updated AgentSessionManager to handle Task tools as thought activities
- Added comprehensive tests for Task tool formatting (20 new tests)
- Marked TodoWrite as deprecated while maintaining backward compatibility
- Fixed linting warnings in switch case block scope

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix spawn node ENOENT by passing process.env to Claude SDK

The env option only contained 3 hardcoded vars, causing PATH to be
missing from the spawned Claude Code process.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures (#843)

* Cherry-pick d5d9e5a0: Fix single-turn subroutine silent failures

Cherry-picks commit d5d9e5a0 from refactor/improved-logging branch.
Resolves merge conflicts adapting logging refactor patterns
(sessionId/log.info) to current branch patterns
(linearAgentActivitySessionId/console.log).

When a single-turn subroutine exits with an error (e.g. error_max_turns),
AgentSessionManager now recovers by using the last completed subroutine's
result via ProcedureAnalyzer.getLastSubroutineResult(), allowing the
procedure to continue to completion instead of failing silently.

Also adds tools config pass-through to ClaudeRunner and EdgeWorker
so disallowAllTools subroutines properly disable built-in tools.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Update changelogs for CYPACK-792 (#843)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Fix ClaudeRunner test env assertions after process.env spread

Tests expected env to contain only 3 explicit keys, but commit
6e8d873d added ...process.env to prevent spawn ENOENT errors.
Updated assertions to use expect.objectContaining() so tests
verify required keys without failing on additional process.env
entries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>

* Fix TaskCreate/TaskGet formatting, add ToolSearch/TaskOutput formatting, include task subject in TaskUpdate/TaskGet (#846)

* Fix TaskCreate/TaskGet formatting for parallel execution, add ToolSearch and TaskOutput formatting

TaskCreate now renders as concise checklist items (⏳ **subject**) instead of
verbose multi-line format, better suited for parallel async execution. TaskGet
shows subject with ID when enriched. ToolSearch renders as a thought with
descriptive formatting. TaskOutput shows task status with blocking/non-blocking
context.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-795

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Simplify TaskUpdate/TaskGet to use task number, show ToolSearch query directly

TaskUpdate and TaskGet now always display the task number (e.g. "Task #123")
instead of the subject description for consistency with parallel execution.
ToolSearch now shows the query directly as the parameter value, matching
the pattern of other tools like Bash (command) and Read (file_path).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Revert Gemini runner formatting changes (not applicable to these tools)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Include task subject in TaskUpdate and TaskGet formatting

TaskUpdate and TaskGet now display the task's short description
alongside the task number (e.g., "✅ Task #3 — Fix login bug")
when a subject is available, falling back to number-only when not.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* Enrich TaskUpdate/TaskGet activities with task subject (CYPACK-797) (#847)

* Enrich TaskUpdate/TaskGet activities with task subject from cache and result parsing

TaskUpdate and TaskGet tool inputs don't include the task subject, causing
Linear activities to show only "Task #3" without context. This change defers
their activity posting from tool_use time to tool_result time, where subject
can be sourced from: (1) a cache populated by TaskCreate results, or
(2) parsing the Subject field from TaskGet result content.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Add changelog entries for CYPACK-797

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* Delete packages/edge-worker/test/AgentSessionManager.task-subject-enrichment.test.ts

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>

* Release v0.2.21 (#848)

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage' (#815)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage'

Update orchestrator system prompts to explicitly require setting state to
"To Do" when creating issues with mcp__linear__create_issue. Previously,
issues were being created with default "Triage" status.

Changes to both orchestrator.md (v2.5.0) and graphite-orchestrator.md (v1.3.0):
- Added state requirement to Required Tools section
- Added Status bullet point in Decompose section
- Added status checklist item in Sub-Issue Creation Checklist
- Fixed outdated tool names in orchestrator.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(changelog): add entry for CYPACK-761

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>

* Codex SDK migration + split agent/model selectors (#850)

* chore: checkpoint codex sdk integration WIP

* Switch codex runner to SDK and add agent/model selectors

* chore: checkpoint codex sdk integration WIP

* Switch codex runner to SDK and add agent/model selectors

* Resolve rebase conflicts, migrate model defaults, and fix codex session init

* Fix Codex/Gemini usage typing and add Codex formatter replay tests

* Emit Codex tool lifecycle events for Linear activities

* Format Codex todos as markdown checklists

* Extract F1 test-drive workflow into shared skill

* Add user-facing changelog entry for agent/model selectors

* Add PR link to changelog entry for codex selector work

* update sandbox settings

* add .git folder for worktrees to allowed directories list

* Cursor harness MCP enable + permission mapping hardening (#858)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage' (#815)

* fix(orchestrator): ensure issues are created with 'To Do' status instead of 'Triage'

Update orchestrator system prompts to explicitly require setting state to
"To Do" when creating issues with mcp__linear__create_issue. Previously,
issues were being created with default "Triage" status.

Changes to both orchestrator.md (v2.5.0) and graphite-orchestrator.md (v1.3.0):
- Added state requirement to Required Tools section
- Added Status bullet point in Decompose section
- Added status checklist item in Sub-Issue Creation Checklist
- Fixed outdated tool names in orchestrator.md

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

* docs(changelog): add entry for CYPACK-761

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>

* feat(runners): add codex/cursor harness support and F1 validation

* fix(cursor-runner): support non-mock cursor F1 session output

* Fix cursor harness verification regressions

* fix(ci): resolve build type errors in config-updater and core session types

* fix(edge-worker): wire cursor runner selection and prompt assets

* docs(changelog): frame cursor harness as major feature

* docs(changelog): use user-facing framing for cursor entry

* docs(changelog): clarify cursor selector usage

* fix(build): remove baseUrl/paths from tsconfig to fix dist output structure

The baseUrl pointing to monorepo root caused TypeScript to expand rootDir,
nesting compiled output under dist/edge-worker/src/ instead of dist/.
This broke module resolution since package.json declares main as dist/index.js.
Also adds missing cyrus-cursor-runner dependency to edge-worker.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix(cursor/edge-worker): stop-session handling and cursor tool activity mapping

* chore(gitignore): ignore nested node_modules directories

* fix(edge-worker): resume cursor sessions for prompted continuation

* fix(cursor-runner): sync project permissions for cursor CLI sessions

* fix(cursor-runner): map MCP tool permissions to Cursor CLI config

* docs(claude): add cursor permission translation gotcha

* fix(codex): default runner model to gpt-5.3-codex

* fix(cursor-runner): pre-enable MCP servers before cursor sessions

* docs(cursor): clarify .cursor/mcp.json source-of-truth guidance

* fix(cursor-runner): scope wildcard file permissions to workspace

* fix(codex): post actual error message to Linear for usage limit errors

When Codex hits usage limits or turn.failed errors, the full error message
is now posted to Linear agent activity instead of a generic message.
- AgentSessionManager: use errors[] for content when result is empty
- CodexRunner: fallback to standalone error event when turn.failed has no message

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): enable sandbox by default, fix cli.json formatting

- Default Cursor sandbox to enabled for tool execution isolation
- Write .cursor/cli.json with tabs for Biome compatibility
- Exclude .cursor from Biome checks (generated config)
- Update CHANGELOG with sandbox default

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): validate cursor-agent version before run, post error to Linear on mismatch

- Run cursor-agent --version before spawn and compare to tested version
- Post error_during_execution to Linear via agent activity when version mismatches
- Add cursorAgentVersion config and CYRUS_CURSOR_AGENT_VERSION env override
- Add CursorRunner.version-check.test.ts with 3 tests
- Update CHANGELOG with version validation behavior

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): render TODO_STATUS_COMPLETED todos as checked in Linear

- Map Cursor API TODO_STATUS_COMPLETED to [x] marker in formatter and summarizeTodoList
- Map TODO_STATUS_IN_PROGRESS for (in progress) suffix
- Add formatter test for TODO_STATUS_* values
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* feat(cursor): log assembled cursor-agent CLI args to console and session logs

- Log full spawn command (path + args) before cursor-agent execution
- Write to both console and session log file for debugging
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): remove --force and --api-key from CLI args

- Remove --force option from cursor-agent invocations
- Pass API key via CURSOR_API_KEY env only (no longer in args)
- API key no longer appears in spawn logs or terminal output
- Update CHANGELOG

Co-authored-by: Cursor <cursoragent@cursor.com>

* add --trust

* move trust line

* Delete .cursor/cli.json

* fix(cursor-runner): backup and restore .cursor/cli.json instead of overwriting

Temporarily rename existing .cursor/cli.json before writing Cyrus permissions,
then restore original when session ends. Preserves user's config.

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix(cursor): add deny rules to scope Read/Write to workspace only

- When broad Read or Write allowed, add deny rules for /etc, /usr, etc.
- Add deny rules for workspace sibling directories
- Prevents cursor-agent from accessing /etc/hosts and system paths
- Add test: only mutates project .cursor/cli.json, leaves home config unchanged
- Update wildcard permissions test to expect system-path denies

Co-authored-by: Cursor <cursoragent@cursor.com>

* docs(changelog): add Cursor .cursor/cli.json backup/restore entry for CYPACK-804

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: agentclear <agentops@ceedar.ai>
Co-authored-by: Cyrus <208047790+cyrusagent@users.noreply.github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Cursor <cursoragent@cursor.com>

* Update README with agent capabilities and notes (#869)

* Bump fastify from 5.6.1 to 5.7.3 (#828)

Bumps [fastify](https://github.com/fastify/fastify) from 5.6.1 to 5.7.3.
- [Release notes](https://github.com/fastify/fastify/releases)
- [Commits](https://github.com/fastify/fastify/compare/v5.6.1...v5.7.3)

---
updated-dependencies:
- dependency-name: fastify
  dependency-version: 5.7.3
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* docs: Update internal changelog for main merge (CYPACK-821)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* refactor: Extract 5 service modules from EdgeWorker.ts (CYPACK-822)

Extract logically grouped functionality into dedicated modules to improve
readability and maintainability of the 7,687-line EdgeWorker.ts file:

- ActivityPoster: Linear activity posting (289 lines)
- AttachmentService: Attachment download and manifest generation (488 lines)
- ConfigManager: Config file watching, reload, and change detection (320 lines)
- PromptBuilder: Prompt assembly, system prompts, and issue context (1,270 lines)
- RunnerSelectionService: Runner/model selection and tool configuration (498 lines)

EdgeWorker.ts reduced from 7,687 to 5,466 lines (29% reduction). All 522
tests passing, typecheck clean, lint clean.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: Payton Webber <paytonwebber@gmail.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat: Configurable default runner with API key auto-detection (CYPACK-826)

Add `defaultRunner` config field and auto-detect logic so the default
harness is no longer hardcoded to Claude. When exactly one API key is
set, that runner is used automatically; otherwise falls back to the
explicit config value or "claude".

- Add `defaultRunner` to EdgeConfigSchema (updateable via config endpoint)
- Add `RunnerSelectionService.getDefaultRunner()` with priority:
  config > single-key auto-detect > "claude" fallback
- Implement SimpleCodexRunner and SimpleCursorRunner
- Update ProcedureAnalyzer to support all 4 runner types
- Pin zod to 4.3.6 via pnpm overrides to fix dual-version type conflicts
- Delete obsolete codex-runner-shim.d.ts
- Change SDKMessage imports in simple-agent-runner to use cyrus-core

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* docs: Add candidate values for defaultRunner in changelog

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: Align ProcedureAnalyzer default to "claude" and remove changelog duplication

ProcedureAnalyzer SimpleRunner fallback was "gemini" — aligned it to "claude"
to match RunnerSelectionService. Removed duplicate ### Fixed section in
CHANGELOG.md introduced during merge conflict resolution.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
Co-authored-by: Payton Webber <53197664+PaytonWebber@users.noreply.github.com>
Co-authored-by: Payton Webber <paytonwebber@gmail.com>
Co-authored-by: Connor Turland <1409121+Connoropolous@users.noreply.github.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
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.

2 participants