English | 한국어
An agent framework that does not write its own agent loop.
please runs an existing coding-agent harness — Claude Code, OpenCode, Pi — as its runtime, and
supplies the things a harness has no opinion about: where it executes, how it is reached, and how
work is sequenced so it survives a crash.
Frameworks in this space (eve, flue) drive a model directly and rebuild the surrounding agent: the tool set, the permission prompts, the session and resume story, the compaction. That is the part of a coding agent that is already good, already maintained upstream, and already familiar to the people who would use this. A reimplementation is a worse copy carrying a permanent obligation to chase the original.
Two things follow from reusing the harness instead:
- Much of its ecosystem comes along. The built-in tools, the native conversation state and compaction, the session and resume story, and durable workflow stepping are the harness's, not ours to re-earn.
- It decides the bill. Driving Claude Code as a harness runs on a Claude Code subscription. A hand-rolled loop against the Messages API bills per token for the same work.
What does not come along is on the record too, and it keeps shrinking. The adapter's settings
carry no hooks, no skills, no subagents and only three permission modes — but the settings are not
the way in. The runtime reads all of it from a .claude/ directory in the session's own working
directory, so an existing Claude Code project carries over by being placed there; the adapter's
inline skills option is a wrapper that writes those same files. Live probes measured two of the
consequences — a seeded hook runs, and a seeded deny rule overrides the adapter's permission
mode, including the mode that asks the runtime to skip permission checks altogether — and the
Agent SDK's own documentation covers the rest. See
docs/project-layout.md.
The harness boundary itself is not ours either — it is the AI SDK's
harness agent and
harness adapters contract, which
already normalizes sessions, streamed events, tools, usage and lifecycle across runtimes. please
is what you build around a HarnessAgent, not a competitor to it.
This is the scope, not an API. Nothing below is implemented and no signature is settled — see Status.
| Axis | Planned |
|---|---|
| Harnesses | Claude Code, OpenCode (both sandbox-bridged), Pi (host process) — via @ai-sdk/harness |
| Deploy targets | Cloudflare, Vercel |
| Sandboxes | Cloudflare Sandbox, e2b, Daytona, Vercel Sandbox |
| Channels | Slack, GitHub, Linear |
| Workflows | Durable orchestration around agent turns — dispatch, resume, sequence |
Four notes on why the axes are drawn there:
Harnesses. The AI SDK's adapter list is broader (Codex, Cursor, Cline, Deep Agents, fx, Grok Build, with Amp, Goose and Mastra listed as upcoming). Three is the starting set, not the ceiling; the contract is per-adapter, so more is a matter of testing rather than architecture. Claude Code and OpenCode run behind a sandbox bridge and therefore need a network-capable sandbox; Pi runs in the host process and does not.
Targets. Cloudflare and Vercel are both first-class rather than one plus a port. They fail differently — a Worker gets platform-managed recovery and a per-invocation CPU limit; a Node-shaped deployment gets a real filesystem and owns its own restart reconciliation — and a framework that treats one as the real target and the other as an afterthought leaks that asymmetry into every feature.
Sandboxes. Four providers, because a bridged harness needs a sandbox that can run real processes and expose a port, and that requirement is exactly where providers differ. The point of having a sandbox contract at all is that this choice stays a deployment decision.
Channels. Slack, GitHub and Linear — where work is actually assigned, rather than a chat surface bolted on afterwards. Their inbound shapes differ enough (a signed webhook, an event stream, a socket) that pretending they are one thing is how the abstraction goes wrong.
Part of the API is designed; most of the scope above is not. What exists is small on purpose
and it runs — examples/claude-code-docker drives a real turn on it.
| Subpath | What it is |
|---|---|
@pleasedev/core |
defineAgent — a harness adapter, a sandbox, and the workspace directory carried into it |
@pleasedev/core/sandbox |
defineSandbox, plus the backend contract — vendor-neutral types |
@pleasedev/core/sandbox/harness |
the contract rendered as AI SDK HarnessV1SandboxProvider, written once for every backend |
@pleasedev/core/sandbox/docker |
a local Docker backend. Host-only — it spawns the docker CLI, so it must never reach a Worker bundle |
@pleasedev/core/sandbox/local |
a host-process backend — no daemon, no image, and no isolation. Host-only for the same reason |
@pleasedev/core/sandbox/just-bash |
a virtual-shell backend over just-bash — no daemon, no image, no host process, and no real binaries. just-bash is an optional peer dependency |
@pleasedev/core/sandbox/microsandbox |
a microVM backend over microsandbox — isolation by hypervisor rather than by namespace. Optional peer dependency; type-checked but not yet run (see below) |
Splitting the harness translation from the backends is what keeps a second backend from re-deriving it, and the subpaths are what keep host-only code out of a target that cannot run it.
Two shapes are the argument rather than the API. The harness adapter is never wrapped:
createClaudeCode() comes from @ai-sdk/harness-claude-code and is passed straight through,
because that boundary is the AI SDK's and a wrapper here would only be an obligation to chase it.
And workspace is a declared input, because no adapter exposes agents, skills or
settingSources — a directory is the only route those have into a run.
Three of the four backends are covered by suites that run wherever their prerequisite is present —
local and just-bash everywhere, docker where a daemon is reachable. The microsandbox
backend is the exception and says so rather than implying otherwise: microsandbox ships no native
addon for darwin-x64, which is the platform it was written on, and on the Linux CI runner the
addon loads but the guest dies before its agent relay comes up, for want of a hypervisor. So its
behavioural suite has never been observed to pass, and its gate is a throwaway boot rather than an
import check, so that neither host reports a green suite it never ran. What is checked everywhere
is that its structural copies of the vendor's types still match the vendor's own declarations —
test/sandbox/microsandbox/vendor-shape.test.ts, enforced by tsc.
Decided: the scope table above, the name, the license (Apache-2.0), the stack
(Bun, TypeScript, Turborepo), the sandbox split, and the
declaration syntax — defineAgent / defineSandbox rather than a compiler-backed directive,
argued in docs/project-layout.md.
Undecided: most of the rest. How a workflow is expressed, what a channel handler receives, how evals are written, and where a deployment inlines the workspace for a target with no filesystem. Design discussion belongs in Discussions; issues are for bugs.
- Bun — the version is pinned in
mise.toml. - Optionally mise, which installs that pinned version for you.
git clone https://github.com/pleaseai/please.git
cd please
mise install # install the pinned bun version (skip if you manage bun yourself)
bun install # install dependenciesbun run lint # lint (bun run lint:fix to auto-fix)
bun run type-check # type-check all packages
bun run test # run the test suite
bun run build # build all packages
mise run ci # lint + type-check + test + buildpackages/
core/ # @pleasedev/core
src/
agent/ # defineAgent, and the workspace route into a session
sandbox/
contract/ # the backend contract
harness/ # HarnessV1SandboxProvider over that contract
docker/ # local Docker backend (host-only)
local/ # host-process backend (host-only, unisolated)
just-bash/ # virtual-shell backend (no host process, no real binaries)
microsandbox/ # microVM backend (host-only, hypervisor-isolated)
scripts/ # probes that measure the runtime rather than assume it
cli/ # @pleasedev/cli — unreleased; has no command yet
src/ui/ # the boot chrome `please dev` draws before the session starts
examples/
claude-code-docker/ # Claude Code in a local container, built on the API above
docs/
prior-art.md # what eve, flue, the AI SDK harnesses and the Agent SDK already do
project-layout.md # the layout argument, what is settled, and what is still open
dev-tui.md # `please dev`: what is decided, and what it waits on
The example is runnable, and is the shortest way to see what the framework does and does not do:
bun run examples/claude-code-docker/index.ts # needs Docker and an Anthropic credentialThe three probes under packages/core/scripts/ are runnable, and each answers a question the docs
would otherwise have to guess at:
bun run packages/core/scripts/probe-adapter-bootstrap.ts # no credentials needed
bun run packages/core/scripts/probe-claude-dir.ts # needs an Anthropic credential
bun run packages/core/scripts/probe-permissions.ts # needs an Anthropic credentialdocs/prior-art.md records what eve, flue and the AI SDK harness contract
actually do — read from their own documentation, with dates — so that design arguments here start
from what exists rather than from recollection.
docs/project-layout.md is the argument built on that record: what the
contract already decides for us, why the declaration syntax is a function rather than a directive,
and the questions still open.
docs/dev-tui.md is the one built on top of that: how an interactive
please dev divides the terminal between
@ai-sdk/tui and a boot chrome ported from
eve, and which defineAgent decisions the command is still waiting on.
See CONTRIBUTING.md. Please also read the Code of Conduct and, for vulnerabilities, SECURITY.md.
Apache-2.0 © Passion Factory