A personal streamline — *.streamline.json

The *.streamline.json format

Raw Markdown the file as the skill ships it All pages ↓

One file, one streamline, owned by one person: the device and private parts in one flat object. No manifest, no signature, no write API: the person imports it in the app (Files, AirDrop, or a streamline://import?data=<base64url> link). Model calls go straight to the provider with the person's own key from the app's vault. The file never contains a key, token or sign-in; the app refuses one that looks like it does.

Contents

  • Example
  • Fields
  • Tool policy
  • Templates and renderers
  • Checklist

Example

{
  "schema": "streamline.personal/1",
  "id": "orders_desk",
  "name": "Orders",
  "description": "Look up and update my shop's orders.",
  "icon": "shippingbox.fill",
  "color": { "light": "#1D5C8C", "dark": "#5AA9E6" },
  "instructions": "You help one person handle one order per chat. Use the tools. Changes wait for their tap. A result with ok:false is the system's answer: relay it plainly.",
  "model": { "provider": "anthropic", "id": "claude-sonnet-5-5", "effort": "medium", "maxTokens": 32000 },
  "mcp": [ { "id": "shop", "name": "Shop", "url": "https://mcp.shop.example/mcp", "auth": "oauth",
             "tools": { "readOnly": ["order_get", "*_list", "*_search"], "confirm": ["refund_*", "order_cancel"],
                        "confirmWhenArgs": ["settings"], "hidden": ["admin_*"], "direct": ["order_get"] },
             "renderers": { "order_get": "order" } } ],
  "templates": { "order": { "root": { "type": "stack", "spacing": 6, "padding": 14, "children": [
    { "type": "text", "text": "Order {{ $.number }}", "style": "headline" },
    { "type": "keyValue", "rows": [ { "label": "Total", "value": "{{ $.total | money }}" },
                                    { "label": "Status", "value": "{{ $.status | title }}" } ] } ] } } },
  "imageHosts": ["img.shop.example"],
  "welcome": "Each chat is one order. Give me an order number or a customer.",
  "placeholder": "Order number, customer, refund…",
  "quickPhrases": [ { "label": "Today's orders", "text": "Show today's orders", "send": true } ]
}

Fields

FieldReqNotes
schemanostreamline.personal/1.
idno[a-z0-9_]{2,32}; made from the name when absent. Importing an existing id replaces that streamline.
nameyes
description, icon, color, themenoAs in streamlines.md (theme: a preset id, two-sided, or { base?, accent? }; an own theme inline is not built).
logonodata:image/png;base64,… (or jpeg), ≤ 256 KB decoded, square: inline so the file stays one file. Drawn where the icon would be; the icon stays the fallback.
instructionsnoThe system prompt. No business rules as rules, no secrets.
briefsnoPersonal briefs to include first, in order (personal/briefs/<id>.md beside the file; briefs.md).
modelnoprovider anthropic; id (default claude-sonnet-5-5; Haiku 5.5 and Opus 5.5 offered); effort; maxTokens (32000). The key is vault:<id> if the person set one, else vault:default; never write a key.
mcp[]noid, name, url (https; http://localhost only in debug builds), auth oauth | none (built), password | api-key (designed), tools (policy), renderers, reauthOn.
mcp[].reauthOnnoBusiness error codes that mean "the session behind this token ended", for servers that answer them with HTTP 200 instead of 401. The app discards the token, asks the person to sign in again and retries. oauth only.
templatesno{ id: { "root": node } }, ids [A-Za-z0-9_.-]{1,64}; the same language and limits (templates.md). A failing template is left out; its tools get the automatic card.
imageHostsnoHosts template images may load from.
attachmentsnoThe paperclip, the same object as an organization's (streamlines.md, Attachments). The files go from the device straight to the model with the person's key; chatMaxBytes (default 16 MB) keeps a stream within one request.
scannoThe scan button, the same object as an organization's (streamlines.md, Attachments and scanning). A fast-path action.tool must be in a server's tools.direct, and not in tools.confirm when modes allows continuous or sweep scanning.
archivenoanytime (the default for a personal streamline), whenDone or never.
welcomenoLocal bubbles; paragraphs split on a blank line.
placeholdernoComposer hint.
quickPhrases[]nolabel, text, send (default true). The first four are also the starter chips.
composer.suggestions[]noChips that follow the stream's state after the first message, the same rules as an organization's (streamlines.md, Suggestions). A personal file has no overview, so rules read last.tool / last.result.*, chat.*, stream.*, user.*, tenant.* (overview.* is suggestions.noOverview); a tool chip's tool must be in a server's tools.direct. Example: { "when": { "path": "last.tool", "eq": "invoices_list" }, "label": "Open the first", "text": "Open invoice {{ last.result.invoices[0].id }}", "send": true }.
composer.modelSuggestionsnotrue lets the model end each reply with up to three things the person might say next, shown as chips only when no rule holds. Off by default; about 90 tokens a turn on the person's key; the chips are words only and never reach the transcript. Personal files only.

Tool policy

Per server, all lists optional, * globs: readOnly (run untapped), confirm (always tap), confirmWhenArgs (tap only when called with any argument), hidden (never offered, refused if called), direct (a card button may call it). Precedence: hidden > confirm > confirmWhenArgs > readOnly > annotations > a tap. See tool-policy.md.

Templates and renderers

renderers maps a tool to a template id in the same file. The card binds $ to structuredContent, or to data for an {ok, data} result. Draft templates from a real result (run the read tool once with the person signed in, or ask for a sample with invented data), never from a guessed shape.

Checklist

  • [ ] name set; id matches [a-z0-9_]{2,32}
  • [ ] Every server inspected; every tool settled as readOnly, confirm, confirmWhenArgs or hidden
  • [ ] Writes confirm-gated; admin and bulk tools hidden
  • [ ] No key, token, password or personal data anywhere in the file
  • [ ] Every renderer names a template in the file; templates checked
  • [ ] https:// URLs (localhost only for a debug test)
  • [ ] The checker is clean