Skip to content

feat: add Claude Code adapter#1

Merged
Shahfarzane merged 74 commits intomainfrom
feat/claude-code-adapter
Mar 7, 2026
Merged

feat: add Claude Code adapter#1
Shahfarzane merged 74 commits intomainfrom
feat/claude-code-adapter

Conversation

@Shahfarzane
Copy link
Owner

Summary

Merges the Claude Code adapter (from upstream PR pingdotgg#179) into main.

Source

juliusmarminge and others added 30 commits March 5, 2026 18:12
- Add per-provider model options, defaults, and slug aliases
- Add provider-aware model normalization/resolution helpers
- Preserve Codex-only constants/functions for backward compatibility
- Extend tests to cover Claude aliases and provider-specific fallback behavior
- introduce `ClaudeCodeAdapter` service and live layer wiring
- map runtime/session/request failures into provider adapter error types
- add coverage for validation, session-not-found mapping, lifecycle forwarding, and event passthrough
Add provider-service routing coverage for explicit claudeCode sessions.

Co-authored-by: codex <codex@users.noreply.github.com>
Add restart semantics when requested provider changes and cover with tests.

Co-authored-by: codex <codex@users.noreply.github.com>
Add provider-aware model options and include provider in turn-start dispatch.

Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
- add `@anthropic-ai/claude-agent-sdk` dependency for `apps/server`
- replace placeholder Claude adapter with live session/query/event handling
- add comprehensive adapter tests for runtime events, approvals, resume, rollback, and model overrides
- Replace manual prompt async iterator with `Stream.fromQueue(...).toAsyncIterable`
- Use `Ref` for shared session context in tool approval callbacks
- Simplify timestamp/ID generation and close sessions via queue shutdown
- Stop synthesizing `resume` from generated thread IDs
- Persist resume session ID from query messages when available
- Validate resume/sessionId values as UUIDs before reuse
- Add tests for valid UUID resume passthrough and no synthesized resume
Co-authored-by: codex <codex@users.noreply.github.com>
- Do not pass `resumeCursor` when restarting a session after changing providers
- Treat synthetic Claude thread IDs (`claude-thread-*`) as unscoped during runtime ingestion
- Add tests covering provider-switch restart behavior and Claude turn lifecycle acceptance
Co-authored-by: codex <codex@users.noreply.github.com>
- Preserve active turn state when `session.started`/`thread.started` arrive mid-turn
- Emit `message.delta` from assistant text when stream deltas are missing, then complete the message
- Add Claude native SDK NDJSON observability logging and wire its log path in server layers
- Expand ingestion/adapter tests to cover mid-turn lifecycle and delta fallback behavior
- Default Claude sessions to bypass permissions when approval policy is `never`, while preserving explicit `permissionMode`
- Hide/send reasoning effort only for providers that support it in ChatView
- Add coverage for Claude permission-mode derivation and precedence, and update runtime event model docs
- add per-session `sessionSequence` on provider runtime events and persist activity `sequence`
- migrate `projection_thread_activities` with nullable `sequence` column + index
- sort server/web activity projections by sequence fallback to timestamp/id
- allow provider sessions/turns to start before a real threadId is emitted
- define `CursorAdapter` service contract and Cursor stream-json schema types
- add `CursorCliStreamEvent` decoding tests for system/thinking/tool/result/retry events
- add implementation plan for Cursor provider integration

Co-authored-by: codex <codex@users.noreply.github.com>
- Accept `cursor` as a first-class provider in orchestration, persistence, and session directory flows
- Update model/provider inference and normalization to handle Cursor model aliases
- Revamp chat provider/model picker with Cursor-specific trait controls and add coverage in tests
- add `scripts/cursor-acp-probe.mjs` to run ACP protocol probing scenarios
- update `package.json` scripts for probe execution
- include generated probe summaries/transcripts under `.tmp/acp-probe/`
juliusmarminge and others added 26 commits March 5, 2026 18:13
- add structured lifecycle logs for Codex thread open/resume and provider session restarts
- introduce persistent server file logging layer and tighten WebSocket server startup/finalizer ordering
- preserve env-based boolean overrides in dev runner and stop forcing WS event logging in dev
- enrich provider session listing from persisted bindings and fix Claude rate-limit/task-id event mapping
- update orchestration and dev-runner tests for runtime-mode restart and env-flag behavior
- Replace provider session-id centric plumbing with thread-id based runtime handling across server orchestration and provider layers
- Update persistence repositories/migrations and contracts for threadless runtime semantics
- Refresh integration/unit tests and web session/store types to match the new runtime model
- Wire Codex plan mode through turn start, including collaboration settings
- Track and answer pending `requestUserInput` prompts instead of auto-empty responses
- Persist/project thread `interactionMode` (schema, migration, queries) and update tests/contracts
- Add `thread.proposed-plan-upserted` orchestration flow from ingestion through decider/projector
- Store proposed plans in a dedicated projection table and include them in snapshot queries
- Update provider runtime/web handling and tests to use proposed plans instead of assistant message wrappers
- Stop exporting internal-only schema constants/types across contracts modules
- Remove unused provider checkpoint list/revert/diff RPC contract schemas
- Keep public contract surface focused on externally consumed types
Co-authored-by: codex <codex@users.noreply.github.com>
- Replace ad-hoc `effort` handling with provider `modelOptions` across decider/reactor/adapters
- Map Codex `reasoningEffort` and `fastMode` to turn/session params (`effort`, `serviceTier`)
- Persist runtime/interaction/model selections from composer and sync them before sending turns
- Move model normalization/helpers into `@t3tools/shared/model` with updated contracts/tests
- include `gpt-5.4` in Codex app model option coverage
- only keep truly custom Cursor models in the custom model option list
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
- Keep Claude Code and Cursor in provider list as disabled options
- Remove unused Claude agent SDK dependency from server
- Delete stale plan-mode deep-dive doc
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Co-authored-by: codex <codex@users.noreply.github.com>
Copilot AI review requested due to automatic review settings March 7, 2026 05:08
@Shahfarzane Shahfarzane merged commit 5db2208 into main Mar 7, 2026
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Merges upstream orchestration + multi-provider work to introduce a Claude Code provider adapter and expand runtime/session orchestration to be thread-centric with richer projections.

Changes:

  • Adds ClaudeCodeAdapter service tag + registers claudeCode provider in the adapter registry.
  • Refactors provider/session routing to use canonical threadId (instead of provider session IDs) and adds adapter capability reporting + structured user-input responses.
  • Extends persistence + projections for runtimeMode, interactionMode, activity sequencing, and proposed plans (incl. migrations and projector updates).

Reviewed changes

Copilot reviewed 70 out of 124 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
apps/server/src/provider/Services/ProviderService.ts Extends provider service contract (capabilities + user-input responses) and shifts rollback to threadId.
apps/server/src/provider/Services/ProviderAdapter.ts Adds adapter capabilities and replaces session-id routing with threadId/turnId.
apps/server/src/provider/Services/ClaudeCodeAdapter.ts Introduces Claude Code adapter service tag and shape.
apps/server/src/provider/Layers/ProviderSessionDirectory.ts Refactors directory persistence to be keyed by threadId and adds cursor provider decoding.
apps/server/src/provider/Layers/ProviderSessionDirectory.test.ts Updates tests for thread-keyed directory behavior and adds cursor binding coverage.
apps/server/src/provider/Layers/ProviderService.ts Refactors orchestration routing around threadId, adds respondToUserInput, getCapabilities, and changes recovery/listing behavior.
apps/server/src/provider/Layers/ProviderAdapterRegistry.ts Registers Claude adapter by default in the registry.
apps/server/src/provider/Layers/ProviderAdapterRegistry.test.ts Expands registry tests to cover multiple providers and new adapter surface (capabilities, respondToUserInput).
apps/server/src/provider/Errors.ts Renames error payloads/messages from sessionId to threadId where applicable.
apps/server/src/persistence/Services/ProviderSessions.ts Removes legacy provider session repository service.
apps/server/src/persistence/Services/ProviderSessionRuntime.ts Updates runtime persistence contract to be keyed by threadId and track runtimeMode.
apps/server/src/persistence/Services/ProjectionThreads.ts Adds runtimeMode + interactionMode to thread projection model.
apps/server/src/persistence/Services/ProjectionThreadSessions.ts Simplifies session projection to persist runtimeMode (removes approval/sandbox columns).
apps/server/src/persistence/Services/ProjectionThreadProposedPlans.ts Adds repository contract for persisted proposed plans per thread.
apps/server/src/persistence/Services/ProjectionThreadActivities.ts Adds optional sequence support and updates ordering semantics docs.
apps/server/src/persistence/Migrations/013_ProjectionThreadProposedPlans.ts Creates proposed plans projection table + index.
apps/server/src/persistence/Migrations/012_ProjectionThreadsInteractionMode.ts Adds interaction_mode column to projection_threads.
apps/server/src/persistence/Migrations/011_OrchestrationThreadCreatedRuntimeMode.ts Backfills runtimeMode into thread.created events where missing.
apps/server/src/persistence/Migrations/010_ProjectionThreadsRuntimeMode.ts Adds/backfills runtime_mode column for projection_threads.
apps/server/src/persistence/Migrations/009_ProviderSessionRuntimeMode.ts Adds a migration placeholder for provider session runtime mode (currently no-op).
apps/server/src/persistence/Migrations/008_ProjectionThreadActivitySequence.ts Adds sequence column + index for thread activities.
apps/server/src/persistence/Migrations/006_ProjectionThreadSessionRuntimeModeColumns.ts Replaces approval/sandbox columns with runtime_mode on thread sessions projection.
apps/server/src/persistence/Migrations/004_ProviderSessionRuntime.ts Changes provider runtime table definition to be thread-keyed and include runtime_mode.
apps/server/src/persistence/Migrations.ts Adds new migrations and wraps execution with logging.
apps/server/src/persistence/Layers/ProviderSessions.ts Removes legacy provider session repository layer.
apps/server/src/persistence/Layers/ProviderSessionRuntime.ts Updates repository implementation to read/write by thread_id and persist runtime_mode.
apps/server/src/persistence/Layers/ProjectionThreads.ts Updates SQL upsert/selects for new thread projection columns.
apps/server/src/persistence/Layers/ProjectionThreadSessions.ts Updates SQL upsert/selects for new runtime_mode session column.
apps/server/src/persistence/Layers/ProjectionThreadProposedPlans.ts Implements proposed plan repository (upsert/list/delete).
apps/server/src/persistence/Layers/ProjectionThreadActivities.ts Persists/loads sequence and orders activities with sequence-aware SQL.
apps/server/src/persistence/Layers/OrchestrationEventStore.ts Adjusts actor-kind inference by removing provider session/thread metadata triggers.
apps/server/src/orchestration/projector.ts Adds runtime/interaction mode updates, proposed plan handling, and sequence-aware activity sorting.
apps/server/src/orchestration/projector.test.ts Updates thread model expectations and adds runtime-mode-set test coverage.
apps/server/src/orchestration/decider.ts Adds commands/events for runtime/interaction mode and user-input responses; updates turn start payload.
apps/server/src/orchestration/decider.projectScripts.test.ts Updates scripted command expectations for provider/model options + new mode fields; adds mode-set event tests.
apps/server/src/orchestration/commandInvariants.test.ts Updates invariant fixtures for new thread/session shape fields.
apps/server/src/orchestration/Schemas.ts Re-exports new event payload schemas for projector decoding.
apps/server/src/orchestration/Layers/ProviderCommandReactor.ts Updates session lifecycle logic for provider/model/mode switching and adds user-input response forwarding.
apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.ts Includes proposed plans + activity sequence in snapshot assembly; updates thread/session fields.
apps/server/src/orchestration/Layers/ProjectionSnapshotQuery.test.ts Updates snapshot expectations for new fields and runtime_mode column.
apps/server/src/orchestration/Layers/ProjectionPipeline.ts Adds projector for proposed plans; updates activity sequencing and thread mode projections.
apps/server/src/orchestration/Layers/ProjectionPipeline.test.ts Updates fixtures for runtimeMode changes and removes provider session/thread metadata dependence.
apps/server/src/orchestration/Layers/OrchestrationEngine.test.ts Updates engine tests for new thread command inputs and mode fields.
apps/server/src/orchestration/Layers/CheckpointReactor.ts Refactors checkpoint ingestion to route by canonical threadId and updates runtime event field usage.
apps/server/src/main.ts Adds server logger layer, refactors boolean flag resolution, and sanitizes startup log payload.
apps/server/src/main.test.ts Renames CLI test description.
apps/server/src/codexAppServerManager.test.ts Updates Codex manager tests for thread-keyed API, user-input answers, and interaction mode.
apps/server/src/checkpointing/Layers/CheckpointDiffQuery.test.ts Updates snapshot fixture for new thread fields.
apps/server/scripts/logger-scope-repro.ts Adds a script to reproduce logger scope behavior with provider NDJSON logging.
apps/server/package.json Adds @anthropic-ai/claude-agent-sdk dependency.
apps/server/integration/providerService.integration.test.ts Updates provider service integration tests to use threadId routing.
apps/server/integration/fixtures/providerRuntime.ts Updates runtime fixture events to new canonical shapes (content/request/item payload model).
apps/server/integration/TestProviderAdapter.integration.ts Refactors test adapter harness to be thread-keyed and normalize legacy fixture events.
apps/server/integration/OrchestrationEngineHarness.integration.ts Adds options for real Codex vs fake adapter; updates lifecycle/disposal patterns.
SPEC.md Removes legacy orchestration spec document.
AGENTS.md Renames doc (from CLAUDE.md) and adds testing guidance.
.test-favicon.mjs Removes unused favicon probe script.
.plans/18-cursor-agent-provider.md Adds Cursor ACP integration plan doc.
.plans/17-claude-code.md Updates Claude plan doc to reflect post-Stage 1 architecture and updated contracts.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +9 to 16
thread_id TEXT PRIMARY KEY,
provider_name TEXT NOT NULL,
adapter_key TEXT NOT NULL,
provider_thread_id TEXT,
runtime_mode TEXT NOT NULL DEFAULT 'full-access',
status TEXT NOT NULL,
last_seen_at TEXT NOT NULL,
resume_cursor_json TEXT,
runtime_payload_json TEXT
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

This PR modifies an existing migration (004). If any environments already ran the original 004 migration, they will not pick up these schema changes (SQLite migrations won't re-run), leaving provider_session_runtime with the old shape while the new code assumes thread_id primary key and runtime_mode. Move this change into a new forward migration that migrates/rewrites the existing table (copy data into a new table, drop/rename, recreate indexes), and keep 004 immutable.

Copilot uses AI. Check for mistakes.
import * as Effect from "effect/Effect";

export default Effect.gen(function* () {
yield* SqlClient.SqlClient;
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

Migration 009 is a no-op but will still be recorded as executed, which can permanently block a future real migration with the same intent/name. Either remove it from the loader (and delete the file) or implement the intended schema/data changes here.

Suggested change
yield* SqlClient.SqlClient;
const client = yield* SqlClient.SqlClient;
// Execute a harmless query so this migration is not a no-op and is safely idempotent.
yield* client.execute("SELECT 1");

Copilot uses AI. Check for mistakes.
Comment on lines +74 to +75
yield* run({ loader });
yield* Effect.log("Migrations ran successfully");
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

runMigrations used to return the migrator result (per the doc comment: 'returns ... executed migrations'), but this generator currently discards run({ loader })'s return value. Return/yield the migrator result (and then log), or update the JSDoc + callers to reflect that it now returns void.

Suggested change
yield* run({ loader });
yield* Effect.log("Migrations ran successfully");
const migrations = yield* run({ loader });
yield* Effect.log("Migrations ran successfully");
return migrations;

Copilot uses AI. Check for mistakes.
thread_id = excluded.thread_id,
turn_id = excluded.turn_id,
plan_markdown = excluded.plan_markdown,
created_at = excluded.created_at,
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

On upsert conflict, this overwrites created_at. For an append-only/immutable creation timestamp, created_at should generally remain the original value and only updated_at should change. Consider removing created_at = excluded.created_at from the conflict update clause (or only set it on insert).

Suggested change
created_at = excluded.created_at,

Copilot uses AI. Check for mistakes.
(orchestrationThreadId) =>
canonicalEventLogger.write(canonicalEvent, orchestrationThreadId),
)
? canonicalEventLogger.write(canonicalEvent, null)
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

The canonical event logger is always invoked with a null thread id. Since runtime events already carry a threadId, this drops useful correlation/context for logs. Pass the canonical thread id through (e.g., canonicalEvent.threadId) so logs can be attributed to the correct orchestration thread.

Suggested change
? canonicalEventLogger.write(canonicalEvent, null)
? canonicalEventLogger.write(canonicalEvent, canonicalEvent.threadId)

Copilot uses AI. Check for mistakes.
* ProviderAdapterRegistryLive - In-memory provider adapter lookup layer.
*
* Binds provider kinds (codex/claudeCode/...) to concrete adapter services.
* Binds provider kinds (codex/cursor/...) to concrete adapter services.
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

The comment lists (codex/cursor/...), but this layer now also binds claudeCode by default. Update the comment to include claudeCode (or use a more general phrasing like 'codex/claudeCode/cursor/...').

Suggested change
* Binds provider kinds (codex/cursor/...) to concrete adapter services.
* Binds provider kinds (codex/claudeCode/cursor/...) to concrete adapter services.

Copilot uses AI. Check for mistakes.
import { Command, Flag } from "effect/unstable/cli";
import { NetService } from "@t3tools/shared/Net";

// Dummy comment.
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

Remove this placeholder comment before merging; it doesn't convey intent and will become noise in the entrypoint.

Suggested change
// Dummy comment.

Copilot uses AI. Check for mistakes.
Comment on lines +118 to 132
const getProvider: ProviderSessionDirectoryShape["getProvider"] = (threadId) =>
getBinding(threadId).pipe(
Effect.flatMap((binding) =>
Option.match(binding, {
onSome: (value) => Effect.succeed(value.provider),
onNone: () => Effect.fail(new ProviderSessionNotFoundError({ sessionId })),
}),
),
);

const getBindingBySessionId: ProviderSessionDirectoryShape["getBinding"] = (sessionId) =>
getBinding(sessionId);

const getThreadId: ProviderSessionDirectoryShape["getThreadId"] = (sessionId) =>
getBinding(sessionId).pipe(
Effect.flatMap((binding) =>
Option.match(binding, {
onSome: (value) => Effect.succeed(Option.fromNullishOr(value.threadId)),
onNone: () => Effect.fail(new ProviderSessionNotFoundError({ sessionId })),
onNone: () =>
Effect.fail(
new ProviderSessionDirectoryPersistenceError({
operation: "ProviderSessionDirectory.getProvider",
detail: `No persisted provider binding found for thread '${threadId}'.`,
}),
),
}),
),
);
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

A missing binding is modeled as ProviderSessionDirectoryPersistenceError, which reads like a storage failure rather than a not-found condition. Consider restoring/adding a dedicated not-found error (e.g., ProviderSessionNotFoundError with threadId) so callers can distinguish 'no binding exists' from 'persistence operation failed'.

Copilot uses AI. Check for mistakes.
Comment on lines +646 to +661
const applyThreadProposedPlansProjection: ProjectorDefinition["apply"] = (
event,
_attachmentSideEffects,
) =>
Effect.gen(function* () {
switch (event.type) {
case "thread.proposed-plan-upserted":
yield* projectionThreadProposedPlanRepository.upsert({
planId: event.payload.proposedPlan.id,
threadId: event.payload.threadId,
turnId: event.payload.proposedPlan.turnId,
planMarkdown: event.payload.proposedPlan.planMarkdown,
createdAt: event.payload.proposedPlan.createdAt,
updatedAt: event.payload.proposedPlan.updatedAt,
});
return;
Copy link

Copilot AI Mar 7, 2026

Choose a reason for hiding this comment

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

A new projector (threadProposedPlans) is introduced with non-trivial behavior (upsert + retention on revert), but ProjectionPipeline.test.ts doesn't appear to assert proposed-plan projection or revert retention. Add coverage that: (1) thread.proposed-plan-upserted creates/updates rows, and (2) thread.reverted removes proposed plans tied to reverted turns while keeping turnId: null entries.

Copilot uses AI. Check for mistakes.
@Aflous
Copy link

Aflous commented Mar 7, 2026

Summary

Merges the Claude Code adapter (from upstream PR pingdotgg#179) into main.

Source

do you know if this works with Claude code via aws bedrock?

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.

4 participants