remote #

Remote is Python running under another target's identity and capability set, through a host-owned namespace. You get what the target advertises, and a target that is not there is refused rather than emulated locally.

from klbr import remote

async with remote.connect("build-host") as kernel:
    print(kernel.identity.version, list(kernel.capabilities))

the trail #

connect ensures a namespace: it starts a real worker on that target and returns a handle bound to the namespace and generation it was issued. The binding is what makes a later call addressable, and a call whose binding is gone fails instead of quietly reconnecting.

kernel = await remote.connect("build-host")
print(await kernel.control.eval("sys.version_info[:2]"))

Control calls return handles, not values. Await the handle, poll() for what is ready, or watch it while it runs:

handle = kernel.control.execute("import time; time.sleep(30)")
print(handle.running, handle.output())
outcome = await handle.acancel()
print(outcome.state, outcome.confirmed)

Cancellation is three-valued on purpose: confirmed, requested, unknown. unknown means the transport did not confirm it, which is not the same as "stopped" - say so rather than assume it.

Shell is its own typed call with its own result type:

bash = await kernel.bash("uname -a")
print(bash.exit_code, bash.stdout, bash.stderr_truncated)

the target's tree, with your own verbs #

The five verbs you already have locally exist on the handle too, and they return the same shapes, because the target runs the same klbr.workspace this cell does:

listing = await kernel.ls("src", recursive=True)     # Listing of Entry
found = await kernel.search("needle", ".")           # SearchResult of TextMatch
file = await kernel.open_file("src/main.py")         # not a read, and not a stat
print(await file.text())
await file.write("print('rewritten')\n")
await file.replace("rewritten", "replaced")
ran = await kernel.run(("cargo", "check"), cwd=".")  # RunResult; argv, not a shell string
await kernel.delete("build", recursive=True)

open_file gives back a handle with the local names - .relative .text() .read() .exists() .hashlines() .write() .replace() .edit() - and every call is a remote request.

The refusals are the target's own, because they are the same rule and not a second copy of it: delete("build") on a non-empty directory comes back saying how many entries are in the way and to pass recursive=True. A run deadline that expires is reported in the result (timed_out, completed=False) rather than raised.

Underneath, kernel.pathlib, kernel.os, kernel.sys, kernel.shutil and kernel.bash stay available as the lower-level mirror of the target's own namespaces, one remote request per call. Use them for what the five verbs do not cover; use the verbs for the tree.

Advertised capabilities become attributes. If a target publishes a skill, it appears as kernel.<capability>.<callable>, and calling it returns a handle. Reflection gives you the real signature instead of a guess:

print(await kernel.some_capability.some_call.signature())

refusals #

There is no SSH fallback and no local fallback. If no placement is configured, every target is unavailable, and nothing will quietly run the work here instead. A capability that is not advertised is refused before anything runs. Opaque call dispatch to a relay target is deliberately refused until that target runs a dispatcher - a refusal, not something to retry.