Skip to Content
B Berry Docs
Reference

Application authoring

The same maintained guide available through berry guide applications.

Source reference · cd46e0e78d73Read Markdown

Use gen_ui to load installed interface guidance when needed. Conversation and graphical UI are independent optional interfaces. Ordinary rendering requires no inference. The common WorkArea owns title and window actions; do not repeat them in the page. Declare an interface offer; scope:session explicitly binds session authority. A source export alone does not attach an interface.

Ordinary HTTP servers run as process services. Use runtime.service.start for continuing processes, exec for finite commands and temporary checks. A service is kept running through failures and restarts until runtime.service.stop and uses its own recipient:{root,entry,limits}. A separate runtime.service.connect is scoped to its execution, not durable ownership. Copy the exact prepared root. For portable process code, capture the complete program directory with the computer.retain_directory operation when granted. Pass its root reference unchanged to runtime.service.start.program: {root, entry}. For a composition import, use the receipt's root.$ref as the import address without an export field. Reference the import with !ref, read it through load and pass the returned address reference as program.root. A literal hash alone grants no read access. command supplies an interpreter when needed; the captured entry becomes its first argument. Dependencies must already be in the snapshot. cwd is separate mutable working data. The runtime materializes the exact retained files for each launch without dependency downloads. Program-content retention is not source-review evidence by itself. Include every program file in the independent verification obligation, with exact snapshot identity and recorded observations; block if the offered readers cannot inspect it. For static HTML, package resources and use serve or an interface offer. Use relative asset and API URLs (for example ./styles.css, not /styles.css): the host mounts retained sites below a scoped path. Copy the complete current URL from runtime.service.inspect, including its path and trailing slash. After replacement or a host restart, read that observation again; an earlier port may be closed. Ordinary local sites served with serve can load external fonts, styles, scripts, images and APIs under normal browser rules. Do not remove a chosen dependency merely because it is external. Restricted generated-UI publication profiles have their own contracts. Diagnose the actual failing request and current page. Mutable workspace commands remain useful for experiments but do not freeze code.

JSONL stdout frames contain exactly id and content. id is a nonempty string; content is the actual JSON input, not a string containing JSON. Serialize the whole frame once and append a newline. One connection selects one recipient. Route an action field inside content when several operations are needed; top-level method/entry fields cannot select another export. stdin first reports accepted:{executionId}, then output or a terminal {id,executionId,outcome}. Register HTTP waiters before sending stdout. Keep the response open until outcome or expose receipt/outcome polling. Never invent execution IDs or embed the owner bearer token in a process or browser. Finite recipients create fresh invocations; execution recipients address a retained input port. Code uses context.receive() and context.emit(value). Resident waiting needs no inference.

Object fields named base64 are not automatically model media. Preserve real uploaded bytes and MIME types, sending native media parts to capable models. Keep large media in durable files and bounded records, not an ever-growing base64 array in one State value. localStorage alone does not establish runtime-owned persistence across clients.

For editable data, use one application mutation boundary for human input, model results, imports and background updates. Each accepted change identifies the record and revision it read, preserves unrelated fields and commits the new data and any required undo history together. Determine the editable fields from the user's operation before generating their values. A model may interpret an ambiguous request, but its generated replacement must not grant itself more fields to change. Use a patch restricted to that scope, not a regenerated whole record. Keep exact supplied values outside generation; an omitted field retains its original bytes, including manual text, empty values and formatting. Reject out-of-scope fields before saving. The checked scoped_edit example in native_examples demonstrates this admission in ordinary application code. An asynchronous result cannot silently overwrite newer edits or a different selected record. Revision comparison and persistence form one atomic operation: two writes based on the same revision cannot both succeed. Reuse State's versioned write when it owns the data. A file-backed application's existing store must serialize the read, revision check, mutation and durable write for each record; locking only the final rename is too late. An atomic file replacement prevents a partial file, not a lost update. Missing or invalid expected revisions must not disable this check. If the operation permits retrying a version conflict, read current state and re-evaluate its business conditions before committing. Merely updating the version or merging data does not validate the earlier decision: another writer may already have completed the claimed action. Keep the last accepted document on failure. Undo/redo, when required, follows accepted changes from every producer, not just input-field events; replay uses the same save/version rules. Stage the next document and its history without modifying the live record; publish both only after the write succeeds. A failed write must leave the live document, revision and history cursor unchanged. Do not replace unreadable saved data with an empty document and then autosave over it; report the read failure. Observe a mixed human -> model -> undo -> redo sequence with exact before/after values. For asynchronous mutations, submit two changes from the same revision concurrently and verify that at most one commits; a conflict retains the accepted data and history. Test record switching while a save is queued when concurrent editing is part of the contract. These are application responsibilities over ordinary storage and process operations, not additional runtime execution types.

A value the user marks as exact never passes through a model. Extract it in code before any inference with the checked exact_values example of native_examples: a quoted string, or the text after "exactly:" up to the end of its sentence without the sentence's final period, applied verbatim; let the model produce only the rest. A delivered application honored "to exactly: A better launch plan." once and then wrote "Ready after restart." with the period, failing an explicit exactness requirement.

Show progress and errors in empty, loading and populated states. Use accessible labels and revalidate assets or use immutable URLs during iteration. Keep the native role of every interactive element: a button given another role, such as listitem or tab, stops being a button to assistive technology and automation. Put list or grouping semantics on a wrapper element instead. Verify edits with before/after data, undo with restored values, and exports with actual downloaded bytes. Successful clicks and HTTP status are insufficient. Verify a page journey with one browser.run_code script run whole; it accepts dialogs and uploads workspace files through setInputFiles. Automated checks must not require the user to dismiss dialogs. Inspect actual pixels after the final revision for visual requirements. A capture limitation does not authorize taking user focus or pretending that the screenshot exists.

Embedded presentation is part of the application contract

Berry displays a process's HTTP UI in an isolated iframe on another local origin. A server header Content-Security-Policy: frame-ancestors 'self' or X-Frame-Options: SAMEORIGIN/DENY blocks that UI even when standalone navigation and HTTP requests work. For a local application use an explicit frame-ancestors allowlist covering its Berry client, such as http://127.0.0.1:* http://localhost:*. Keep the other CSP restrictions appropriate to the application's own assets; do not remove security headers wholesale or grant the embedded page owner credentials. Scope this to local presentation; remote deployment requires its actual client origins. Check the response headers and test a real cross-origin embedded page before claiming that the UI works in Berry. Standalone browser.navigate is insufficient for frame policy. Observe console/network errors and pixels, not only DOM text. An application can show useful empty/loading/error states before inference returns. The embedded work area is often narrower than a standalone browser window: its width can be well under 1100 CSS pixels beside the client's own navigation. Keep the primary panes of a multi-pane application usable side by side at about 900 CSS pixels, and collapse to a single column only below that. A layout whose editor and chat fall far below the fold in the embedded view does not satisfy a request to work with them beside each other. Panes must never overlap at any width: lay them out with grid or flex, give every pane min-width: 0 and its own scrolling, and position nothing absolutely over a neighbouring pane. A control that another element covers cannot be clicked, even when it looks enabled; a disabled field lying over the Send button failed an otherwise working application at the embedded width.

The app must be operable at the current stage of development. Leave its exact candidate service running for independent verification. Stop obsolete services only; do not clean away test evidence or the only running candidate. Report its exact service ID and URL. Starting a routine does not start its process or attach an HTTP interface. A JSON manifest packaged as a resource is inert data unless some declared executable path actually consumes it. Service desired state controls recovery: runtime.service.stop records desired: stopped, and nothing restarts a stopped service. Retained stopped records are history, not competing running applications.

Before starting a continuing resource, decide its purpose and end condition: delivery, temporary verification, a separate user-requested copy, or replacement of an exact earlier service. Keep service IDs and finite exec session IDs with their purpose in your work notes and checkpoint report. A name, port, executable or program root alone never proves that two runs have the same purpose or data.

Use runtime.service.replace with the exact old id and complete new start configuration when revising that same application and a brief interruption is acceptable. The owner validates, stops and joins the old resource, then starts the new one; the receipt identifies the successor. Invalid configuration leaves the old run alone. Failed startup after a confirmed stop does not roll back automatically. Inspect the recorded result. For uninterrupted comparison, start a separate copy with its own port and data, verify it, then stop the exact superseded service. Use ordinary serve/runtime.service.start for an independent copy; never replace another task's service because it runs the same code. If runtime.service.replace is unavailable, perform that explicit stop/start sequence with the available Tools.

Run a temporary verification server as an exec session with its own port, data location and timeout, not as a service, which would be kept running. Terminate it as soon as that check finishes, including failed checks, and confirm it exited before deleting its disposable data. A finite exec session is continued through computer.write_stdin; terminate:true stops it when no further step needs it. A yielded Tool call, closed view, or completed builder round does not stop an independent resource. At every report/checkpoint inspect your own receipts: stop probes that have served their purpose, retain the candidate and requested background work, and record why any still-running temporary resource is needed. Unconfirmed termination remains a limitation to resolve, never a successful cleanup. Preserve original History evidence and unrelated resources.

Complete process bridge example

The server uses ordinary Node IO. Reserve stdout for frames, stderr for diagnostics. Register the pending request before writing; one id identifies one request. The accepted frame is not an HTTP success payload. Handle rejection, failure and unknown terminal outcomes. The application owns durable request records and recovery; restarting a process does not replay lost in-memory Promises safely. Retain the admitted executionId beside the application's own request identity and terminal result. Make that correlation inspectable through the application's ordinary diagnostic/data interface without exposing credentials. A request can then be investigated without rerunning it; runtime.thread.read and runtime.execution.observe provide the corresponding permitted runtime observations.

import {createInterface} from 'node:readline';
import {randomUUID} from 'node:crypto';
const pending = new Map();
const lines = createInterface({input: process.stdin});
lines.on('line', line => {
  let message;
  try { message = JSON.parse(line); }
  catch { console.error('Invalid runtime reply'); return; }
  const request = pending.get(message.id);
  if (!request) return;
  if (message.accepted) {
    request.executionId = message.accepted.executionId;
    return;
  }
  if (!message.error && !message.outcome) return;
  pending.delete(message.id);
  request.resolve(message);
});
export function invoke(content) {
  const id = randomUUID();
  return new Promise((resolve, reject) => {
    pending.set(id, {resolve, reject});
    process.stdout.write(JSON.stringify({id, content}) + '\n', error => {
      if (error) { pending.delete(id); reject(error); }
    });
  });
}

invoke returns the whole terminal frame, including a refusal or a non-completed outcome. The HTTP handler must persist that frame with its application request, check error and outcome.status, and only then use outcome.content as a successful application value. Keep executionId in the diagnostic record; do not discard it while translating the reply into UI data.

This finite snippet explains correlation; a production server must also resolve outstanding waiters on stdin closure and implement the requested persistence and cancellation policy. Do not resend an uncertain request under a new identity.

Packaged interfaces and HTTP sites

The manifest interfaces field follows the shared InterfaceOffer contract. The index names a retained resource URI. The associated entry and action entries must be public callable exports. scope: session requires entry and attaches to that exact session; otherwise the offer is standalone. Export association alone is not attachment. Actions name finite handlers with explicit uses and limits. Session state and thread input or publication updates require the corresponding access declaration and granted Tools. Conversation plus a declared interface uses the common auxiliary dock; a UI-only application uses the main region. No second title/header is needed. Use gen_ui for the installed renderer's bridge contract before writing an offer. To attach an HTTP service view, the application's own invocation calls serve or runtime.service.start with scope: session. The runtime resolves that admitted call's stable session; input never supplies an arbitrary session ID. Conversation uses the auxiliary dock and a graphical-only session uses the whole workspace. Omit scope for independent services, including builder-owned tests. Attachment grants no new capabilities and does not couple stopping the service to closing its view.

serve uses packaged resources and exposes declared exports through its scoped HTTP API. runtime.service.start runs a real executable and can connect stdout/stdin to a finite prepared export. Use the path requested by the user; do not replace a required process chain with a mock, direct provider call or static fake response. A prepared root binds authority and models; a service ID identifies lifecycle; an executionId identifies one request. Keep these values separate and exact.

On this page