Skip to content

Add Ditto harness provider integration - #1

Merged
Peyton-Spencer merged 1 commit into
mainfrom
ditto-harness-provider
Aug 30, 2026
Merged

Add Ditto harness provider integration#1
Peyton-Spencer merged 1 commit into
mainfrom
ditto-harness-provider

Conversation

@nicktallen

@nicktallen nicktallen commented Jun 15, 2026

Copy link
Copy Markdown

Summary

  • add the local Ditto Harness contracts, settings, service, and WebSocket RPC surface
  • register Ditto as a built-in provider with adapter, status probing, text generation, and prompt-memory context
  • expose Ditto provider configuration through the existing generic provider settings UI
  • rebase the original contribution onto current main without restoring removed runtime or local-API abstractions

Behavior

  • remains local-first and does not require a Ditto account
  • keeps the existing Codex model selection as the application default; the optional native Ditto harness cannot break startup when unavailable
  • reports harness unavailable/disabled states through typed status rather than crashing the server

Validation

  • vp check (passes; repository has pre-existing warnings)
  • vp run typecheck
  • vp test run apps/server/src/dittoHarness/DittoHarnessRuntime.test.ts packages/contracts/src/settings.test.ts (60 tests)

Stack

Base layer of stack pingdotgg#4. Merge this before the universal-chat contract and adapter layers.

@coderabbitai

coderabbitai Bot commented Jun 15, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Introduces Ditto as a new local AI provider backed by a native Node.js binding ("Ditto Harness"). The change adds Effect-based schemas, an RPC surface, a native runtime loader, an Effect service layer, a full provider driver/adapter stack, text generation, WebSocket RPC handlers, server layer wiring, and a web settings UI for configuration and status.

Changes

Ditto Local AI Provider Integration

Layer / File(s) Summary
Public contracts: schemas, RPC, IPC, model constants, settings
packages/contracts/src/dittoHarness.ts, packages/contracts/src/index.ts, packages/contracts/src/model.ts, packages/contracts/src/rpc.ts, packages/contracts/src/ipc.ts, packages/contracts/src/settings.ts, packages/contracts/src/settings.test.ts
Defines DittoHarnessError, status/request/result schemas, five new WS_METHODS and Rpc.make handlers, LocalApi.server extensions, DITTO_DRIVER_KIND model defaults, DittoSettings/DittoHarnessSettings schemas with patch support, and a new "select" ProviderSettingsFormControl variant.
Native harness runtime and Effect service
apps/server/src/dittoHarness/DittoHarnessRuntime.ts, apps/server/src/dittoHarness/DittoHarnessService.ts, apps/server/src/dittoHarness/DittoHarnessRuntime.test.ts
DittoHarnessRuntime.ts loads the native .node binding from candidate paths and exports a memoized makeDittoHarnessOpener. DittoHarnessService.ts wraps it in an Effect Context service with DittoHarnessServiceDisabled and DittoHarnessServiceLive layers covering status, memory, dream, and prompt-context operations.
Ditto provider adapter, status probe, and driver
apps/server/src/provider/Layers/DittoAdapter.ts, apps/server/src/provider/Layers/DittoProvider.ts, apps/server/src/provider/Drivers/DittoDriver.ts, apps/server/src/provider/builtInDrivers.ts, apps/server/src/provider/Layers/ProviderRegistry.test.ts
DittoAdapter manages in-memory sessions and emits ordered runtime events per turn. DittoProvider probes harness status via a chat call and builds ServerProviderDraft objects. DittoDriver composes opener + adapter + text generation into a ProviderInstance with a snapshot refresh stream. Registered in BUILT_IN_DRIVERS.
Text generation and prompt context injection
apps/server/src/textGeneration/DittoTextGeneration.ts, apps/server/src/textGeneration/TextGeneration.ts, apps/server/src/orchestration/Layers/ProviderCommandReactor.ts, apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
DittoTextGeneration implements TextGenerationShape using single-turn JSON harness calls with schema-validated output. ProviderCommandReactor optionally calls buildPromptContext and prepends context to the provider input before forwarding each turn.
WebSocket RPC handlers, client methods, and server layer composition
apps/server/src/ws.ts, packages/client-runtime/src/wsRpcClient.ts, apps/web/src/localApi.ts, apps/server/src/server.ts
ws.ts registers authorization scopes and five dittoHarness* RPC handlers. WsRpcClient gains five matching client methods. localApi.ts adds browser-side passthroughs. server.ts inserts DittoHarnessServiceLive into RuntimeCoreDependenciesLive.
Web UI: settings, provider form select fields, icon, and registration
apps/web/src/components/settings/SettingsPanels.tsx, apps/web/src/components/settings/ProviderSettingsForm.tsx, apps/web/src/components/settings/ProviderSettingsForm.test.ts, apps/web/src/components/settings/providerDriverMeta.ts, apps/web/src/components/Icons.tsx, apps/web/src/components/chat/providerIconUtils.ts, apps/web/src/components/settings/ProviderModelsSection.tsx, apps/web/src/session-logic.ts, apps/web/src/modelSelection.ts
Adds DittoHarnessSettingsSection with status refresh and full harness configuration controls. Extends ProviderSettingsForm with a select control type. Registers Ditto icon, provider metadata, and sets Ditto as the default text generation provider.
Test harness and mock stub updates
apps/server/integration/OrchestrationEngineHarness.integration.ts, apps/server/src/server.test.ts, apps/web/src/environments/runtime/..., apps/web/src/localApi.test.ts, apps/web/src/components/KeybindingsToast.browser.tsx
Wires DittoHarnessServiceDisabled into all integration and server test layer stacks; extends all WsRpcClient test doubles with the five new Ditto harness RPC stubs returning disabled/empty payloads.

Sequence Diagram(s)

sequenceDiagram
  participant WebApp as Web App
  participant LocalApi as localApi.ts
  participant WsRpcClient as WsRpcClient
  participant WsHandler as ws.ts
  participant Service as DittoHarnessService
  participant Runtime as DittoHarnessRuntime
  participant Native as NativeHarness

  rect rgba(30, 100, 160, 0.5)
    note over WebApp,Native: Status check
    WebApp->>LocalApi: getDittoHarnessStatus()
    LocalApi->>WsRpcClient: server.getDittoHarnessStatus()
    WsRpcClient->>WsHandler: transport.request(dittoHarnessStatus, {})
    WsHandler->>Service: status
    Service->>Runtime: opener(settings)
    Runtime->>Native: harness.open(dbPath)
    Native-->>Runtime: OpenHarness
    Runtime-->>Service: status result
    Service-->>WsHandler: DittoHarnessStatus
    WsHandler-->>WebApp: DittoHarnessStatus
  end

  rect rgba(160, 80, 30, 0.5)
    note over WebApp,Native: Turn with prompt context
    WebApp->>WsHandler: sendTurn(prompt)
    WsHandler->>Service: buildPromptContext(input)
    Service->>Native: searchMemories(query)
    Native-->>Service: memories[]
    Service-->>WsHandler: contextString
    WsHandler->>Native: harness.chat(contextString + prompt)
    Native-->>WsHandler: DittoHarnessChatTurnResult
    WsHandler-->>WebApp: runtime events (content delta, turn complete)
  end
Loading

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~120 minutes

Poem

🐇 Hop hop, a harness in hand,
A local Ditto model — how grand!
Native bindings found with care,
Memory, dreams, and prompts to share.
The rabbit wired it layer by layer,
Now Ditto runs — a local slayer! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add Ditto harness provider integration' clearly and specifically describes the main change: integrating Ditto Harness as a provider with contracts, settings, service, adapters, and UI controls.
Description check ✅ Passed The PR description includes a clear summary of the main changes (Ditto Harness integration across contracts, settings, server, RPC, and web UI), validation steps performed (vp check, vp run typecheck), and covers the substantive aspects of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ditto-harness-provider

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

❤️ Share

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

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

Inline comments:
In `@apps/server/src/dittoHarness/DittoHarnessRuntime.ts`:
- Around line 177-187: The cacheKey function is missing the chat and action
model settings from its serialized key, causing cache hits even when these
settings change. Add the chat* and action* settings fields from input.settings
to the JSON object being stringified in the cacheKey function to ensure that
different model configurations produce different cache keys and prevent stale
settings from being reused.

In `@apps/server/src/provider/Layers/DittoAdapter.ts`:
- Around line 343-345: The turnId generation in DittoAdapter.ts at lines 343-345
is based on state.turnCount, which gets reset during rollback operations,
causing turn IDs to be reused. Since the interruptedTurnIds set persists across
rollbacks, reused turn IDs can be falsely marked as interrupted in later turns.
Replace the turnId generation mechanism to use a monotonically increasing
counter that does not get reset during rollbacks (rather than relying on
state.turnCount which is reset to snapshot length). This same fix must be
applied at the two other affected locations in DittoAdapter.ts at lines 460 and
545 where turn IDs are similarly generated using state.turnCount.
- Around line 354-381: The issue is that if options.opener(settings) fails after
turn.started and item.started events have been emitted, the code exits without
properly finalizing the turn state, leaving the session in a running state with
an active turn and no terminal events. To fix this, add error handling after the
options.opener(settings) call that catches the ProviderAdapterRequestError and
ensures failTurn is invoked before the error propagates, so the turn lifecycle
is properly closed and session state is correctly finalized when the opener
fails.

In `@apps/server/src/provider/Layers/DittoProvider.ts`:
- Around line 88-93: The modelOptions function checks if
trimToUndefined(settings.chatModel) and trimToUndefined(settings.chatBaseUrl)
exist as a presence check, but then passes the raw untrimmed values from
settings.chatModel and settings.chatBaseUrl to the returned object. This causes
leading/trailing whitespace to survive and potentially break URL or model
resolution. Fix this by using the trimmed values returned from trimToUndefined()
directly in the model and baseUrl properties instead of the raw settings values,
so that whitespace is properly removed before being passed to the harness.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: fdb1caca-f72d-48bd-9df0-96ca98ee6af7

📥 Commits

Reviewing files that changed from the base of the PR and between d25090c and 0b36fde.

📒 Files selected for processing (40)
  • apps/server/integration/OrchestrationEngineHarness.integration.ts
  • apps/server/src/dittoHarness/DittoHarnessRuntime.test.ts
  • apps/server/src/dittoHarness/DittoHarnessRuntime.ts
  • apps/server/src/dittoHarness/DittoHarnessService.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.test.ts
  • apps/server/src/orchestration/Layers/ProviderCommandReactor.ts
  • apps/server/src/provider/Drivers/DittoDriver.ts
  • apps/server/src/provider/Layers/DittoAdapter.ts
  • apps/server/src/provider/Layers/DittoProvider.ts
  • apps/server/src/provider/Layers/ProviderRegistry.test.ts
  • apps/server/src/provider/builtInDrivers.ts
  • apps/server/src/server.test.ts
  • apps/server/src/server.ts
  • apps/server/src/textGeneration/DittoTextGeneration.ts
  • apps/server/src/textGeneration/TextGeneration.ts
  • apps/server/src/ws.ts
  • apps/web/src/components/Icons.tsx
  • apps/web/src/components/KeybindingsToast.browser.tsx
  • apps/web/src/components/chat/providerIconUtils.ts
  • apps/web/src/components/settings/ProviderModelsSection.tsx
  • apps/web/src/components/settings/ProviderSettingsForm.test.ts
  • apps/web/src/components/settings/ProviderSettingsForm.tsx
  • apps/web/src/components/settings/SettingsPanels.tsx
  • apps/web/src/components/settings/providerDriverMeta.ts
  • apps/web/src/environments/runtime/connection.test.ts
  • apps/web/src/environments/runtime/service.addSavedEnvironment.test.ts
  • apps/web/src/environments/runtime/service.savedEnvironments.test.ts
  • apps/web/src/environments/runtime/service.threadSubscriptions.test.ts
  • apps/web/src/localApi.test.ts
  • apps/web/src/localApi.ts
  • apps/web/src/modelSelection.ts
  • apps/web/src/session-logic.ts
  • packages/client-runtime/src/wsRpcClient.ts
  • packages/contracts/src/dittoHarness.ts
  • packages/contracts/src/index.ts
  • packages/contracts/src/ipc.ts
  • packages/contracts/src/model.ts
  • packages/contracts/src/rpc.ts
  • packages/contracts/src/settings.test.ts
  • packages/contracts/src/settings.ts

Comment thread apps/server/src/dittoHarness/DittoHarnessRuntime.ts
Comment thread apps/server/src/provider/Layers/DittoAdapter.ts Outdated
Comment thread apps/server/src/provider/Layers/DittoAdapter.ts
Comment thread apps/server/src/provider/Layers/DittoProvider.ts
@github-actions github-actions Bot added the vouch:trusted PR author is trusted by repo permissions or the VOUCHED list. label Aug 28, 2026
@Peyton-Spencer
Peyton-Spencer force-pushed the ditto-harness-provider branch from 0b36fde to 2f57981 Compare August 28, 2026 21:47
@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Thread transfer impact

✅ Thread transfer remains within every enforced ceiling.

Provider Metric Main baseline This PR Impact PR ceiling
Codex Total thread wire 13.3 KiB 13.3 KiB +3 B (+0.0%) 15.1 KiB
Codex Thread snapshot wire 6.9 KiB 6.9 KiB 0 B (0.0%) 7.3 KiB
Codex Live turn WebSocket wire 6.4 KiB 6.4 KiB +3 B (+0.0%) 7.8 KiB
Codex Live turn WebSocket decoded 55.6 KiB 55.6 KiB 0 B (0.0%) 66.4 KiB
Codex Live turn messages 11 11 0 (0.0%) 21
Claude Total thread wire 13.3 KiB 13.3 KiB −3 B (−0.0%) 15.1 KiB
Claude Thread snapshot wire 6.9 KiB 6.9 KiB +3 B (+0.0%) 7.3 KiB
Claude Live turn WebSocket wire 6.4 KiB 6.4 KiB −6 B (−0.1%) 7.8 KiB
Claude Live turn WebSocket decoded 56.4 KiB 56.4 KiB 0 B (0.0%) 66.4 KiB
Claude Live turn messages 9 9 0 (0.0%) 21

Baseline: ff17610 · PR result: 20c8881 · Source CI: success

Scenario and decoded snapshot size

10 historical turns, 5 command tools per turn, 878.9 KiB retained MCP result per historical turn, and a 1.05 MiB retained result in the measured turn.

  • Codex decoded thread snapshot: 109.4 KiB
  • Claude decoded thread snapshot: 110.1 KiB

Updated in place by a trusted workflow. PR artifacts are strictly validated and never executed.

@Peyton-Spencer
Peyton-Spencer force-pushed the ditto-harness-provider branch from d6d1a9c to 20c8881 Compare August 28, 2026 22:21
@Peyton-Spencer
Peyton-Spencer merged commit 1b52659 into main Aug 30, 2026
21 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

size:XXL vouch:trusted PR author is trusted by repo permissions or the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants