Skip to content

DocsGuides

Files and shell

Each session has its own private directory on the Computer's runtime, with two parts:

  • files/ is the session's working directory. The shell starts there and the agent writes its outputs there. File paths in the API are relative to it.
  • attachments/ holds files you add for the agent. They also appear inside the working directory as attachments/<name>.

Browser downloads and artifacts are stored beside them, in the same session.

Session files last as long as the Computer's runtime, including across a restart of the Computer's services. They are not restored after computer.stop or reattachment. Artifacts are tracked session outputs, not backups: they belong to the active runtime, so save important files in your own storage before you stop the Computer or destroy the session.

session.destroy keeps the session's directory on the runtime but out of reach of the API. session.destroy(clean: true) deletes it, with its working files, attachments and artifacts.

Files and artifacts

Files and artifacts overlap, but they aren't two views of the same bytes. The shell and the agent work in the working directory, which can change at any time. An artifact is a fixed copy with its own id: a file you uploaded, a browser download, or a file the agent named in a turn's final result. Uploads and agent outputs are copied into artifact storage. A browser download is stored once, in the session's downloads area, and listed as an artifact.

Files Artifacts
Address a path (out/report.csv) an id (art_...)
Best for shell pipelines and the agent's own reads knowing which turn produced a file, and downloading results
Lifetime the session, while the runtime runs the session, while the runtime runs; deleted by destroy(clean: true) and not restored after reattachment
Linked to a turn no agent outputs carry a turn_id; uploads and browser downloads belong to the session

When a turn ends, each file the agent names in its result is checked and copied into an immutable artifact tagged with that turn's turn_id, even when the turn was canceled or ran out of time. A scratch file the agent writes without naming it isn't registered. So you can list what the agent delivered in a turn (the turn's events carry its turn_id), fetch the bytes while the session exists, and keep them in your own store. The same list also holds your uploads and the session's browser downloads.

Read and write files

ruby
# The working directory, as files:
session.list_files(pattern: "*.csv")      # one directory level; pass path: for a subdirectory
bytes = session.read_file("out/report.csv")

# The session's artifacts:
session.list_artifacts                    # newest first
session.list_artifacts(turn_id: turn_id)  # one turn's agent outputs only, without downloads or uploads
session.upload_artifact("logo.png", File.binread("logo.png"))
session.read_artifact(artifact_id)
session.download_artifacts_zip            # all of them, as a .zip

Each artifact has a filename (for example filled_w9.pdf). Show and save it under that name, not under its id.

Give the agent files

write_file puts a file in attachments/, not in the working directory itself. The agent is told about new attachments when its next turn starts, or when a paused turn resumes after your answer:

ruby
session.write_file("brief.md", File.read("brief.md"))
# The next session.agent.run sees "brief.md" as an attachment.
session.read_file("attachments/brief.md")  # read it back

Use write_file for input documents the agent needs. The name must be a single file name, with no directories. Attachments are separate from the files the agent creates in its working directory, and they aren't artifacts.

Collect browser downloads

A download that finishes in a session's browser is registered with that session as an artifact with created_by: "download". Your application reads it through the session's artifacts; there is no separate claim step. A download the Computer can't link to a session waits as an unowned download until you move it into a session with computer.claim_orphan_download(guid:, session_name:) or discard it; computer.orphan_downloads lists them.

List without turn_id to include browser downloads. Only the agent's outputs carry a turn ID, so a list or ZIP filtered by turn leaves out downloads and uploads, even ones that arrived while that turn was running.

ruby
session.list_artifacts.each do |artifact|
  next unless artifact.created_by == "download"

  bytes = session.read_artifact(artifact.id)
  # Save bytes in your application's own file store, under artifact.filename.
  # Record artifact.id only after that save succeeds, so retries skip it.
end

To collect every output, include created_by: "agent" as well, and skip "upload" so the inputs you sent don't come back as new outputs. Keep the set of IDs you have saved for each session, and list again to retry failed saves before you release the Computer. The end of a turn is a good time to collect, but it doesn't prove every browser download has finished. These files stay on the runtime until your application saves them.

The shell

Each session has its own bash shell. It starts in the working directory, with $PINE_SESSION set to the session's name.

Run commands with exec

ruby
failed_with = nil
result = session.exec("make test", timeout_ms: 30_000) do |event|
  case event.type
  when :stdout then puts event.text                      # output, standard error included
  when :error then failed_with = event.error&.dig("evalue") # the exit code, as text
  end
end
result.stdout                       # everything the command printed
result.exit_code                    # 0 when it succeeded

exec runs one command per call and streams its output back as it runs. Standard error is merged into standard output, so everything arrives as :stdout events and in result.stdout; result.stderr stays empty. A command that succeeds ends with result.exit_code set to 0. A command that exits with another code ends with an :error event whose error["evalue"] holds the code, and in the Ruby SDK 0.3.12 result.exit_code is then nil. Every call runs in the session's same shell, so the current directory and exported variables carry over from one command to the next. Pass cwd: to run in another directory; like cd, it carries over too. timeout_ms: stops the command when the time is up; without it, a command can run for up to 24 hours.

A dropped exec stream can't be resumed: sending the request again runs the command again. exec raises PineSandbox::ConflictError (409) in two cases: a person holds control of the session, or the session's shell was lost. Read session.control_state to tell them apart: if no person holds control, call session.recreate_terminal! and retry.

The stream sends the same 20-second keep-alive as the agent's events (see Agent: keep-alive).

For an interactive program (a REPL, an editor), run it without interaction through exec: pass its input as arguments or a heredoc, or split the work into a few exec calls. There is no interactive terminal for your code.

Outbound SSH and generated keys

The Computer includes the ssh, scp and sftp clients, for connections to public hosts. It doesn't run an SSH server or accept inbound shell connections. SSH from the shell leaves through the Computer's normal internet connection, which can have a different country and IP address from the one the browser uses.

For work by Pine's agent, keep long-lived private keys and certificates in your own secret store. Send them through the run's secrets field, under names you choose. Pine adds no SSH-specific secret name or format; the goal says which credential to use. Send the expected host key or fingerprint too, and don't turn off SSH host verification.

The agent can generate a new identity when you ask it to. That identity works for the current Computer runtime, but its private key isn't kept by default. Before you install the public key as a lasting way to sign in, keep another verified administrator path, unless you have already arranged secure storage of the private key outside the Computer. An ordinary artifact is no place to store a private key.

API reference

The routes are in the Computer API: files and artifacts (/v1/sessions/{name}/files, /v1/sessions/{name}/artifacts/*) under Agent, /sessions/{name}/exec under Shell and Tabs, and unowned downloads under State and Recovery.

What persists across stop and start, including the browser checkpoint, is in Persistence.

Private beta Approved developers get the integration guides, the API reference and the SDKs.