AgentMove
Moves: a coding agent's session on a laptop (Claude Code, Codex, OpenCode, Hermes or OpenClaw) copies itself onto a NoClick agent and carries on there. The laptop keeps its own session.
Your side makes the move and follows it; the agent in the session does the rest, from the guide its prompt names:
move = noclick.AgentMove.create(name="Docs fixer")
print(move.prompt) # paste into the session you want moved
...
move.refresh()
print(move.status, move.arrived["files"]) # moving, what it carried so far
print(move.agent_id, move.conversation) # once doneThe agent's side runs with the move's own key, which its code is redeemed for once (AgentMove.redeem): it can
reach nothing but the move, and ends with it.
key = noclick.AgentMove.redeem("nc_move_...", host="ana-mbp")
moving = noclick.AgentMove.current(client=noclick.Client(api_key=key))
moving.upload_session(tar_bytes, harness="claude_code", cwd="/Users/ana/code/acme")
moving.finish()class AgentMoveA move from a laptop: waiting for its code to be redeemed, moving while what the agent carries
arrives (arrived), finishing while the agent is made (steps), then done with agent_id and
conversation; failed (finish again), canceled or expired.
- Like every live object, it also has hydrate(), refresh() and raw.
| id | str | |
| status | str | One of |
| code | str | None | The one-time code (from |
| prompt | str | None | What a person pastes into the session to move (from |
| guide_url | str | The guide the moving agent follows. |
| workspace_id | str | None | The space the agent joins. |
| draft | dict[str, Any] | What the move was made with: |
| source | dict[str, Any] | None | Where it moves from, as the moving agent says: |
| arrived | dict[str, Any] | What arrived so far: |
| steps | list[dict[str, Any]] | The finish's progress, in order: each |
| lands_as | dict[str, Any] | None | Where it lands (on the moving side): the agent's |
| question | str | None | What a finish asks before it makes anything (a session that carried no MCP server and no skill: does it have none?). |
| endpoints | list[str] | None | The move's calls (on the moving side), one line each: method, path and what it does. |
| agent_id | str | None | The agent it made, once done. |
| conversation | str | None | The agent's conversation holding the moved session. |
| error | str | None | Why it failed. |
| expires_at | Any | When it ends if nobody finishes it. |
| created_at | Any |
createclassmethodasync
noclick.AgentMove.create(
*, workspace=None, name=None, harness=None, model=None, model_connection=None,
user=..., client=None
) -> AgentMoveMake a move: a one-time code, good once for 30 minutes, and the prompt a person pastes into the coding-agent session they want moved. The agent there follows the guide the prompt names, carries what its work needs, and finishes the move, which makes the agent.
Args:
workspace: The space (a Workspace or its id) the agent joins.
name: Its name; omitted, the moving agent's choice, else its folder's.
harness: claude_code, codex, opencode, hermes or openclaw; omitted, the session's own.
model: Its model in the harness's format; omitted, the session's or the harness's default.
model_connection: The connection holding its model's key or plan; omitted, the account's own that runs it.
NoneNoneNoneNoneNoneWho the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)The move, with code and prompt (only here).
move = noclick.AgentMove.create(name="Docs fixer", harness="claude_code")
print(move.prompt)from_idclassmethod
noclick.AgentMove.from_id(move_id, *, client=None, user=...) -> AgentMoveA lazy reference to a move by id.
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)Who the call acts for. Defaults to the user set by noclick.as_user (none: the account itself).
A move that loads the first time you read a field. Nothing is sent yet.
move = noclick.AgentMove.from_id("8c1f3e2a-4b5d-4e6f-9a7b-0c1d2e3f4a5b")
print(move.status)cancelasync
agent_move.cancel() -> AgentMoveCall the move off: its key stops working and what arrived goes. A finished move's agent stays.
This move, canceled.
noclick.AgentMove.from_id("8c1f3e2a-4b5d-4e6f-9a7b-0c1d2e3f4a5b").cancel()connect_sign_inasync
agent_move.connect_sign_in(name, credential) -> AgentMoveGive a tool server the move carried needing sign-in the person's sign-in to it: the mcp_oauth
connection NoClick's own MCP sign-in made for its URL.
This move, its carried entry for the server no longer needing sign-in.
noclick.AgentMove.from_id("8c1f3e2a-4b5d-4e6f-9a7b-0c1d2e3f4a5b").connect_sign_in("notion", "cr_1")redeemstatic
noclick.AgentMove.redeem(code, *, host=None, base_url=None) -> strRedeem a move code for the move's own key, once. Keep it out of anything you print or save in your transcript: write it to a file only you read.
NoneNoneThe key: it reaches only this move, for an hour.
key = noclick.AgentMove.redeem("nc_move_...", host="ana-mbp")currentclassmethodasync
noclick.AgentMove.current(*, client=None) -> AgentMoveThe move the client's key belongs to (a move's own key, from redeem).
The client to send with; defaults to the module client built from NOCLICK_API_KEY.
None)The move, with lands_as and the limits.
moving = noclick.AgentMove.current(client=noclick.Client(api_key=key))
print(moving.lands_as["cwd"])set_sourceasync
agent_move.set_source(
*, harness, host, cwd, cli_version=None, session_id=None, title=None, model=None
) -> AgentMoveSay where the session moves from: its harness, machine and folder.
NoneNoneNoneNoneThis move.
moving.set_source(harness="claude_code", host="ana-mbp", cwd="/Users/ana/code/acme")upload_sessionasync
agent_move.upload_session(data, *, harness, cwd, session_id=None) -> AgentMoveThe session's own files: a tar of paths relative to the harness's home (~/.claude for Claude Code).
NoneThis move; arrived["session"] says what NoClick read.
moving.upload_session(tar_bytes, harness="claude_code", cwd="/Users/ana/code/acme", session_id=sid)repoasync
agent_move.repo(*, name=None, branch=None) -> dict[str, Any]A NoClick repo for the code (made once) and a push url, good for 10 minutes: treat it as a secret.
NoneNonerepo_id, name, branch, path, url, expires_at and cwd.
pushed = moving.repo()
# git push <pushed["url"]> HEAD:refs/heads/<pushed["branch"]>put_fileasync
agent_move.put_file(path, data) -> AgentMoveA file from outside the repo, placed at /workspace/ (up to 100 MB).
This move.
moving.put_file("notes/spec.md", open("/Users/ana/notes/spec.md", "rb").read())consentasync
agent_move.consent(secrets, *, confirmed) -> AgentMoveThe secrets the person said yes to carrying, each {name, kind, to} (what it is and where it goes, as
they read it; never a value). confirmed=True once they said yes in the session. A secret is carried only
once it's listed here; this replaces the list sent before.
This move; arrived["consent"] lists them.
moving.consent([{"name": "linear-token", "kind": "Linear API key", "to": "env LINEAR_API_KEY"}],
confirmed=True)put_secretasync
agent_move.put_secret(name, value, *, to=None) -> AgentMoveOne secret the session uses, sealed as it lands, and where it goes in the copy: {"env": NAME},
{"file": path, "mode": "600"}, {"model": provider} (the model key it runs on), or none for one a tool
server's header or the checkout refers to by name.
NoneThis move; arrived["secrets"] names it, never its value.
moving.put_secret("linear-token", os.environ["LINEAR_API_KEY"], to={"env": "LINEAR_API_KEY"})set_setupasync
agent_move.set_setup(commands) -> AgentMoveWhat the copy's sandbox runs as it starts (after its secrets' files are written): the CLIs and packages the session used.
This move.
moving.set_setup(["npm install -g @linear/cli"])set_checkoutasync
agent_move.set_checkout(
url, branch, *, path=None, secret=None, username=None
) -> AgentMoveThe session's own git remote (an https URL), checked out in the copy at branch (where the session
pushed its work), signed in with the carried secret secret's value when it must be.
NoneNoneNoneThis move; lands_as["cwd"] is where it's checked out.
moving.set_checkout("https://github.com/acme/web.git", "noclick/ana-mbp", secret="github-token")plan_linkasync
agent_move.plan_link() -> dict[str, Any]The one-tap sign-in to the plan the session's harness runs on (Claude's, ChatGPT's), for the person: its tokens never move. Once they sign in, the copy runs on it.
{connection, integration, status, url, expires_at}.
print(moving.plan_link()["url"])add_mcp_serverasync
agent_move.add_mcp_server(
name, url, *, transport='http', headers=None, sign_in=None
) -> AgentMoveA remote MCP server it used (a public https URL); the agent gets its tools. A header's value is text,
or {"secret": name} for a carried secret's (a login written as text is refused).
Args:
sign_in: "browser" for a server whose login lives in a browser (a claude.ai connector, an OAuth
sign-in): it moves with no headers and no login, and the person signs in to it once on NoClick.
'http'NoneNoneThis move.
moving.add_mcp_server("deepwiki", "https://mcp.deepwiki.com/mcp")
moving.add_mcp_server("notion", "https://mcp.notion.com/mcp", sign_in="browser")add_skillasync
agent_move.add_skill(name, data) -> AgentMoveA skill: its folder as a tar, SKILL.md at its top.
This move.
moving.add_skill("release-notes", tar_bytes)add_memoryasync
agent_move.add_memory(name, content, *, description) -> AgentMoveSomething it learned, kept in its NoClick memory.
This move.
moving.add_memory("deploy-steps", "Deploy with make ship.", description="How this repo deploys")set_noteasync
agent_move.set_note(text) -> AgentMoveThe handoff note: what it's in the middle of and what's next. The agent reads it first.
This move.
moving.set_note("Halfway through the retry fix; next, a test for the third retry.")set_instructionsasync
agent_move.set_instructions(text) -> AgentMoveIts instructions: what it was told for every project (a user-level CLAUDE.md or AGENTS.md).
This move.
moving.set_instructions(open(os.path.expanduser("~/.claude/CLAUDE.md")).read())finishasync
agent_move.finish(
*, name=None, model=None, model_connection=None, harness=None, none=None
) -> AgentMoveMake the agent from what arrived, step by step, and end the move's key. Finish again after a failed step to pick up there.
Args:
harness: The harness it runs on, when the Draft screen named none (a carried key the session's own
harness doesn't run on is refused naming the ones it does).
model: Its model, in that harness's format, when the Draft screen named none.
none: What the session has none of (mcp_servers, skills), said outright: a finish that carried
neither asks first (question).
NoneNoneNoneNoneNoneThis move: done, with agent_id and conversation; or still moving with a question.
moving.finish()
moving.finish(harness="opencode", model="openrouter/anthropic/claude-sonnet-4.5")