Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
9d7beca
feat(harness): add unavailable devin_cloud_agent harness kind
devin-ai-integration[bot] Sep 9, 2026
5e5a207
fix(devin): harden devin harness boundary from review findings
devin-ai-integration[bot] Sep 9, 2026
d1ead0b
feat(devin): rebuild devin harness on the official API v3 contract
devin-ai-integration[bot] Sep 9, 2026
b20fee6
feat(devin): make the devin cloud harness a live, available route
devin-ai-integration[bot] Sep 9, 2026
7e3d478
fix(devin): make the devin route safe to cancel, recover, and dry-run
devin-ai-integration[bot] Sep 9, 2026
edc4c16
fix(devin): make the remote session lifecycle restart-safe
devin-ai-integration[bot] Sep 9, 2026
67605ea
fix(devin): keep detached sessions safe across launch, recovery, cleanup
devin-ai-integration[bot] Sep 9, 2026
b620a43
fix(config): carry central devin settings into the resolved workflow
devin-ai-integration[bot] Sep 10, 2026
2e3d270
Merge main and harden Devin cloud session lifecycle
kumanday Sep 26, 2026
95274bf
fix(devin): durable cleanup fence, verified tenancy, safe evidence wr…
devin-ai-integration[bot] Oct 7, 2026
0bb08ba
fix(devin): stop exited sessions safely, surface message PR URLs
devin-ai-integration[bot] Oct 7, 2026
cf7ae3c
feat(devin): render remote repository instructions into Devin prompts
devin-ai-integration[bot] Oct 7, 2026
d836648
fix(devin): publish heartbeats so a busy session is not aborted as a …
devin-ai-integration[bot] Oct 7, 2026
2d355e6
fix(devin): let terminated route tasks settle before the worker is ab…
devin-ai-integration[bot] Oct 7, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 53 additions & 2 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,8 @@ The runtime client must:
### Keep harness capability discovery public

- Public harness metadata belongs in `opensymphony-gateway-schema::capability::HarnessCapability` and the `/api/v1/capabilities` response.
- Use stable harness kind strings such as `openhands_agent_server`, `codex_app_server`, `acp`, and `rust_native`; do not expose private adapter type names to clients.
- Concrete harness adapters should implement the domain `HarnessAdapter` capability boundary and keep OpenHands, Codex, or future in-process protocol details inside their adapter modules.
- Use stable harness kind strings such as `openhands_agent_server`, `codex_app_server`, `devin_cloud_agent`, `acp`, and `rust_native`; do not expose private adapter type names to clients.
- Concrete harness adapters should implement the domain `HarnessAdapter` capability boundary and keep OpenHands, Codex, Devin, ACP, or future in-process protocol details inside their adapter modules.
- Future or experimental harnesses may be advertised as unavailable capability entries, but their feature gaps must be explicit.
- When changing harness capability discovery, update gateway schema round-trip tests, the gateway capabilities endpoint test, adapter-boundary tests, and `docs/harness-adapter-compatibility.md`.

Expand All @@ -179,6 +179,57 @@ capability must stay available in normal builds. Do not advertise hosted worker
pools, remote routing, or loopback WebSocket as production-ready until those
paths have their own hardening evidence.

The remote Devin cloud harness uses the `opensymphony_devin` internal module
boundary and is advertised as an available HTTPS capability against the Devin
API v3 contract (`devin-api-v3`), with explicit feature gaps for the properties
the hosted API does not provide: cursor-paginated polling instead of a push
stream, cancellation as session termination instead of a mid-run interrupt, no
pause/resume or approval flow, and model selection fixed at session creation.
Devin owns its own remote execution workspace: never treat a local issue
workspace path as Devin's working directory. The scheduler-bound issue workspace
holds run manifests, journals, and imported evidence only.

Devin hardening rules that must hold for any change to this harness:

- Every client binds to exactly one organization through `GET /v3/self` before
any session call, and session, listing, and message payloads owned by another
organization are rejected rather than journaled.
- Credentials stay environment-variable references in workflow config,
manifests, and logs; the token is never serialized or logged.
- Secrets are injected by organization-scoped Devin `secret_ids` resolved from
the bound organization; raw secret values never enter OpenSymphony.
- Transport is HTTPS-only with TLS 1.2+, no redirects, and no credentials,
query, or fragment in the configured base URL. Certificate pinning is not
implemented; do not claim it.
- Evidence downloads are restricted to the authenticated API origin, bounded by
size and count limits, and written under sanitized names inside the issue
workspace.
- Every abandoned path (poll timeout, transport failure, scheduler interrupt,
worker abort, daemon shutdown) must terminate and archive the remote session
and await the acknowledgement; an orphaned Devin session keeps consuming
ACUs. `Drop` is never the only safeguard: the persisted session binding and
route identity (`devin-route.json`) are the durable fence, and a bound
session is stopped through *its own* persisted origin/organization before
any replacement is created.
- Scheduler abort and daemon shutdown terminate the session first and then
give the route task a bounded window to observe the archived session,
import its evidence, and finish `run.json`; the task is aborted only if it
does not settle in time. Aborting first leaves a `running` manifest with no
evidence for a run the tracker already moved on from.
- Organization-scoped requests require verified tenancy (`bind_tenant`), not
just a configured organization id; a failed `create_session` is reconciled
through the correlation tag before it is retried.
- Parent issues with sub-issues are not routed to remote-only harnesses.
- A quiet, executing session is not a stalled one: the poll loop publishes a
`session.heartbeat` liveness event inside the scheduler stall window so a
long Devin step is never aborted and replaced as a stall. Heartbeats are
orchestrator observations and stay out of the evidence journal.

Changes to the route or the client must keep the opt-in live suite
(`OPENSYMPHONY_DEVIN_LIVE=1 cargo test --test devin_cloud_agent_live --
--ignored`) passing; it is the only evidence that the hosted contract still
holds.

### Preserve forward compatibility

OpenHands event schemas can evolve. Implement:
Expand Down
1 change: 1 addition & 0 deletions crates/opensymphony-cli/src/debug_session.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2309,6 +2309,7 @@ mod tests {
updated_at: Utc::now(),
last_seen_tracker_refresh_at: None,
repository_binding: None,
evidence_only: false,
}
}

Expand Down
3 changes: 3 additions & 0 deletions crates/opensymphony-cli/src/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10254,6 +10254,7 @@ mod tests {
updated_at: now,
last_seen_tracker_refresh_at: None,
repository_binding: None,
evidence_only: false,
})
.expect("workspace manifest json"),
)
Expand Down Expand Up @@ -10464,6 +10465,7 @@ mod tests {
updated_at: now,
last_seen_tracker_refresh_at: None,
repository_binding: None,
evidence_only: false,
})
.expect("manifest json"),
)
Expand Down Expand Up @@ -15098,6 +15100,7 @@ Public memory concept.
.expect("runtime envelope");
let now = Utc::now();
let run = RunManifest {
terminal_worker_outcome: None,
run_id: "run-551".to_string(),
issue_id: "issue-551".to_string(),
identifier: "COE-551".to_string(),
Expand Down
Loading
Loading