SDK API
Import sdk from the dedicated runtime entry point:
The sdk export is a lazy MiniAppPlatformApi. Importing it is safe in tests and build tools. Reading any property before The AI Platform installs the capability session throws an unsupported-environment error, so keep host access at a target or surface boundary.
Every host operation can cross a process or network boundary. Always await its result even when the type also permits a synchronous value through MiniAppMaybePromise.
Declare Permissions
Every authority-bearing host method must be declared before installation. Add each action ID you use to a permission.catalog, include it in a package level, and bind it from the calling contribution. Use authorization.allOf only when the action is required to project the contribution. Use authorization.onDemand when the contribution may mount without the action and checks it at the point of use. The autonomy value below is the minimum ceiling required by that method. Declarations are requests, not grants: installation review and current workspace policy decide whether a call is allowed, and a later revocation makes the operation reject. Metadata-only capability discovery, including sdk.terminal.v1.getCapabilities, does not require a persisted action.
Every package built with SDK 0.12 or later requires compatibility.tapHost to
exclude versions before 2.4.1. This package-wide floor covers packages that
use only pre-existing APIs as well as packages that use SDK 0.12 workspace
storage, embedding, batch VFS, or batch specialist actions.
SDK 0.13 packages require compatibility.tapHost to exclude versions before
2.5.5. SDK 0.12 packages retain their historical 2.4.1 floor.
A package that declares user-file authority must set compatibility.tapSdk to
exclude versions before 0.14.0 and compatibility.tapHost to exclude
versions before 2.9.0.
A package that declares an action.command must set compatibility.tapSdk to
exclude versions before 0.15.0 and compatibility.tapHost to exclude
versions before 2.9.0.
The browser-only openEditorProject helper appears in the table because it requests a host action even though it is imported from /web. Theme helpers, type exports, getPlatform, and capability-flag checks do not request a host action and therefore need no permission action.
sdk.storage, sdk.session, sdk.presence, sdk.http, sdk.credentials,
sdk.notifications, sdk.printing, sdk.files, and
sdk.terminal.v1.open use descriptor effects in addition to the calling
contribution's ordinary permission binding.
Declare a storage or presence effect whose resources contains each
namespace the contribution uses. HTTP requires an external-network effect
for each exact URL origin, such as https://api.vanta.com. Credential listing
or use also requires { "kind": "credentials", "resources": ["http"] }.
Secure installation sessions require
{ "kind": "credentials", "resources": ["package-session"] }.
Receipt-printer discovery, status, and submission require
{ "kind": "physical-output", "resources": ["receipt-printer"] }.
OS notifications require
{ "kind": "user-notification", "resources": ["os"] }.
Terminal open requires a terminal effect whose resource is the exact selected
profile (workspace-shell or neovim) and
{ "kind": "filesystem-mount", "resources": ["conversation-vfs:read-write"] }.
User-selected file access requires exactly one user-file effect whose
resources match the declared file actions: pick-open, pick-save, read,
overwrite, rename, delete, watch, or retain-handle. Declare every file
action exactly once in the desktop UI surface's authorization.onDemand;
user-file authority is invalid in authorization.allOf, on a non-UI
contribution, or on a non-desktop target.
Declare printing.receipt at the consuming package level as a reusable,
do, consequential action; listPrinters and getStatus consume listen
authority while submit consumes do authority.
Wildcards do not match an origin. The host derives workspace, package,
instance, document, and participant identity from the verified frame; none of
those are caller inputs.
The quickstart shows a complete catalog, level, role recommendation, and surface binding for channels.list. For a package that uses several methods, list each action once and bind only the actions needed by each contribution.
Authorization checks and consent
Use sdk.authorization.check to adapt the surface before a declared action is
invoked. Pass the action ID bound to the exact mounted contribution and the
autonomy needed by the planned operation. The returned decision is only a
snapshot: keep handling rejection from the operation because policy or
authority can change immediately afterward.
The check never executes the action, creates a grant, or prompts the human.
For an ordinary production miniapp surface, a matching persisted grant can
authorize actions whose consent is none, channel in the exact channel, or
reusable. Actions declared with once or fresh-decision deliberately
resolve to { allowed: false }, even when installation state retains a grant
row: those policies require a call-bound consent attestation, and the public
surface SDK does not currently expose a consent request API that can mint one.
Calling the protected SDK operation does not prompt for that permission either.
Do not weaken a consequential action to reusable consent just to make this check pass. Keep the production control unavailable with an explanation until a supported host-mediated flow exists. Use the positive fixture row in the Miniapp Test Lab to exercise the intended allowed journey, and a denied row to verify the fallback. Fixture authority validates miniapp behavior; it is not evidence that production call-bound consent is available. Undeclared action IDs reject instead of revealing authority held by another contribution.
Namespaced Storage
sdk.storage stores bounded, non-secret JSON under a package-controlled
namespace and key. The host prepends the authenticated workspace and exact
package identity, so two packages cannot address each other's data. get
returns a value and optimistic revision; a missing entry returns both as
null. Pass that exact revision to set or delete. Pass null only to
create a missing key.
Values are limited to plain finite JSON and five MiB. Secrets, credentials, binary assets, and signed URLs do not belong in this API. A revision conflict rejects; reload, merge, and retry instead of silently overwriting another realm's update.
Native Embeddings
sdk.embeddings exposes host-native local text embeddings without giving the
miniapp model-file paths or native runtime handles. Discover the catalog before
embedding: entries report whether a model is installed or downloadable, its
exact revision, dimensions, roles, normalization, input limits, resource
estimates when known, source, gating, and upstream license metadata. License
metadata is informational; TAP does not accept or manage an upstream license
on the miniapp's behalf.
recommend() ranks models for a requested modality and locality policy. The
0.12 desktop host supplies BAAI/bge-small-en-v1.5 for role-aware local text
retrieval. Callers pass the discovered model ID and revision back to embed();
the result includes the full embedding-space binding that must be stored with a
new zvec vector field. Model discovery and compute require separate granular
permissions, and both are always brokered by the host SDK boundary. Catalog
maxInputBytes is enforced independently for each input. Host-generated
vectors also carry an opaque attestation over the route, exact binding, and
complete numeric payload; preserve it when writing a host-verified vector.
Private Storage
Desktop hosts expose two opaque scopes with the same files, SQLite, and native zvec APIs:
sdk.storage.workspaceis bound to the host-stamped current workspace plus signed publisher/package identity.sdk.storage.profileis bound to the current local app profile plus signed publisher/package identity and is shared across workspaces.
Neither scope accepts a workspace, profile, or physical path from package code. Other packages and publishers cannot address it. Storage is plaintext unless the miniapp encrypts its own values; provider credentials belong in the host credential system.
Declare only the granular access a contribution needs. All four actions for a
scope can be granted together at installation. Read-only file callers can open
with { filesRead: true, filesWrite: false, sqlite: false, zvec: false }.
Profile and workspace routes have independent quotas and share a host-wide
safety cap. The manifest may request profileStorageQuotaBytes and
workspaceStorageQuotaBytes up to the host-controlled per-route ceiling.
File replacement, range writes, database commits, migrations, and checkpoints preserve the last complete snapshot. SQLite is parameterized and serialized across package surfaces. zvec uses its native on-disk format. Filters are a typed AST; arbitrary native filter strings are not accepted. Every new vector field pins an exact model revision and full embedding-space fingerprint. A write or query with another binding rejects even when dimensions match. A host-verified write also rejects if its vector payload or opaque attestation was altered. Collections created before SDK 0.12 remain valid as explicitly unbound legacy collections and are not rewritten.
Raw typed SQLite and zvec APIs remain available. reciprocalRankFusion is a
pure SDK utility for combining their ranked results with optional weights.
Both scopes retain data by default across updates, disablement, uninstall, and
reinstall. A package can instead set the matching profileStorageRetention or
workspaceStorageRetention lifecycle value to
delete-on-verified-revocation. Missing authority never triggers deletion.
Post-revocation cleanup is durably queued and resumes after an I/O failure or
restart. Installed Miniapp settings can clear profile data or data for the
current workspace only.
Desktop Test Lab operates through the same opaque native capability and never reveals a physical path.
Package-runtime MCP context
Feature-detect sdk.mcp inside a package-runtime MCP tool. Its
getExecutionContext() method returns host-stamped, call-scoped identity:
userId is nullable, and channelId is nullable for workspace- or user-scoped
execution. The host never fabricates a channel. A handler that selects
channel-specific state must reject null before reading storage.
During this call, sdk.storage.get reads an immutable point-in-time snapshot.
The host reloads the active verified descriptor, revalidates the exact server
and tool, current consumer lease, declared permission actions, and persisted
grants, then exposes only namespaces present in all three storage declarations:
the mcp.server authorization effects, the mcp.tool authorization effects,
and the tool's options.effects. A missing declaration contributes no
namespace. Data declared only by a sibling tool is invisible, and missing keys
still return { value: null, revision: null }.
Each invocation runs in a fresh, capability-minimal QuickJS realm, so module
globals cannot carry storage values or identity into a sibling tool or later
workspace call. The snapshot and identity are cleared after every success,
rejection, deadline interruption, or non-settling promise. Snapshot values are
recursively frozen.
sdk.storage.set and sdk.storage.delete always reject in this realm; MCP
handlers must use a separately authorized host workflow for mutations.
Secure Workspace Installation Session
sdk.session stores one small JSON session object in the operating system
credential store. The host derives the active TAP account, active workspace,
immutable installation ID, and canonical package ID. Channel, contribution,
frame, document, and release are excluded, so standalone and channel surfaces
of one installation share a login only when they belong to the same workspace,
and package updates retain it. Another workspace gets a separate login. A
reinstall gets a new installation ID and cannot recover the previous session.
The serialized value is limited to about two KiB for cross-platform credential
store compatibility. get intentionally returns the value to package
JavaScript; the keyring protects it only at rest. Do not put TAP's own bearer
or another host-managed integration credential here. Use the opaque HTTP
credential path instead. The descriptor must request
credentials:package-session, and the package must declare the dedicated
session.manage action. A stale release, switched account or workspace,
revoked grant, missing effect, disabled installation, or uninstalled package
fails closed.
Identity and Workspace
Three reads, three different authorities, because the answers expose different people.
sdk.user.current() needs no permission and prompts for nothing. Your mount
context already carries userId; the name attached to that id is not additional
authority. displayName is host-derived and cannot be supplied by a package. It is
empty when the profile has no usable name — a guest mount can do that — so render a
fallback.
Both workspace reads require a persisted workspace.read action with listen
autonomy, the same authority as listTeams and listProjects:
listMembers() returns joined members only. An invitation is not a teammate,
and listing pending invites would reveal hiring before it is announced. Each member
is a user id and a display name; the host's roster also holds email, role, title,
timezone, invitation timestamps and who invited whom, and none of that crosses the
boundary. The result contract is closed, so a field added upstream cannot begin
flowing to packages without a deliberate change here.
Feature-detect all three — a host older than this capability does not install them — and treat a denial as "render without names" rather than an error. A withheld grant is a normal state.
There is no avatar in any of these. The generated surface CSP is
img-src 'self' data:, so a remote image cannot load whatever the manifest
declares; draw initials, or inline bytes from your own backend as a data: URI.
React callers should use useUser, useWorkspace, and
useWorkspaceMembers instead of calling these directly, since render
cannot await.
Presence
sdk.presence carries bounded ephemeral JSON in a host-scoped namespace and
room. Call subscribe before join, refresh state with update, and call
leave during explicit teardown. The host also removes a participant when its
document or frame is retired.
Participant IDs, display names, and timestamps are host-stamped. App state cannot replace them. Presence is not durable storage or a permission grant; participants expire after missed heartbeats and must be prepared to rejoin.
HTTP and credentials
Feature-detect sdk.http, sdk.credentials, and hasHostHttpRequest === true
because non-desktop targets may omit these capabilities. sdk.http.request
sends bounded HTTP(S) through the native transport, so it is not subject to the
miniapp iframe's browser CORS path. The host validates the URL and limits,
requires persisted package grants, and asks the signed-in human for origin
consent. Credential-backed requests additionally revalidate the active
workspace and user before resolving a secret. Use the reserved
platform-session credential reference when the destination accepts the active
TAP account bearer; the host obtains and injects that bearer after authorization
without exposing it to miniapp JavaScript. It uses the same declared effect,
package grant, and human origin consent as other host-managed credentials; the
host does not maintain a separate destination list.
Surface Test Lab runs use the run-scoped exact-origin list instead of persisted
interactive consent and never read the host credential vault. A Surface
profile may explicitly expose metadata-only HTTP credential fixtures bound to
selected run aliases; aliases without a declaration remain invisible, and no
credential material enters the fixture. Those origins authorize host-mediated
sdk.http.request; they are not a browser-wide network sandbox for Playwright
test code.
listHttp returns IDs, types, display names, and non-secret metadata only. A
miniapp cannot read credential values. The host injects the selected secret,
redacts credential material from reflected response fields, suppresses binary
bodies on credential-backed requests, and rejects destination conflicts.
Request timeouts are capped at 120 seconds after consent; the SDK itself does
not race the native human-decision prompt with a shorter client timeout.
OS Notifications
Feature-detect sdk.notifications because compatible desktop and mobile hosts
provide it, while older and portable hosts can omit it. Package code supplies
only a non-empty message of at most 512 Unicode scalar values. The host
prefixes the message with the registered surface application name, owns all
native presentation metadata, and never accepts a package-supplied title,
subtitle, icon, urgency, sound, action, or deep link.
Messages and registered attribution names reject Unicode control, format,
line-separator, and paragraph-separator characters. The registered attribution
name is limited to 128 Unicode scalar values and is validated before package
installation.
A shown notification is rendered as
Design File Viewer: Export completed when the registered application name is
Design File Viewer. The current user's display name is not used for this
attribution.
show returns { disposition: 'shown' } when the native notification API
accepts the notification. It returns suppressed with
notifications-disabled, permission-denied, or rate-limited when the host
does not present it. The host respects the user's The AI Platform notification
preference and the current OS permission without prompting. Do not retry a
suppressed call in a loop or ask the user for OS permission from package code.
The host rate limit is scoped to the exact package installation and mounted
document.
Declare the effect on each contribution that calls show:
Define the action in the package's permission catalog and include it in an assignable level:
Receipt Printing
Feature-detect sdk.printing because only compatible desktop hosts provide it.
listPrinters returns bounded machine-local printer names, the system-default
marker, and the host-supported 58 mm and 80 mm paper profiles. getStatus and
submit both require the exact selected printer/profile. submit accepts only
a bounded semantic version-1 receipt whose text fields contain printable ASCII.
The host revalidates the destination on every call and owns rendering,
wrapping, feed, cut, and OS spooler access.
MiniAppReceiptLine excludes raw ESC/POS bytes and arbitrary markup. A stable
jobKey lets the host suppress repeat submissions within its installation,
workspace, and selected-destination journal. Spooler acknowledgement cannot
prove physical exactly-once output, so every result reports
physicalExactlyOnce: false; an uncertain submission can return
indeterminate.
Terminal Sessions
Feature-detect sdk.terminal?.v1 because portable and mobile targets omit the
terminal capability. getCapabilities reports availability for the two fixed
runtime profiles and the current host limits. It is metadata-only; availability
does not grant permission to open a session.
The initial desktop host reports both profiles unavailable. workspace-shell
remains closed until the sandbox can positively restrict readable roots to the
conversation grant plus fixed runtime files. neovim also requires a signed,
integrity-locked runtime with clean configuration. Do not substitute a PATH
binary or another profile when discovery reports one unavailable.
write accepts one non-empty Uint8Array of at most 64 KiB and resolves only
after the host acknowledges that command. resize is bounded by the reported
column and row limits. The events stream is strictly ordered and uses a
256-KiB byte-sized high-water mark; consuming data replenishes host output
credit. Cancelling the stream initiates session close. A terminal exit is the
last event and reports its sequence, exit code or signal, and whether the
session exited, was closed, was revoked, or failed.
Opening a session checks terminal.session.open, filesystem.read, and
filesystem.write with do authority. Port operations inherit that exact
owner- and document-bound lease; they do not prompt independently. The host
derives the conversation VFS mount and owns process cleanup. Miniapp code never
supplies an executable, argument vector, environment, working directory,
filesystem path, or native PTY identifier.
Declare the authorization on the contribution that opens the session. Define
the same three actions in the package's permission.catalog and include them in
an assignable level. This excerpt selects neovim; use workspace-shell
instead to request that profile.
Local Services
Desktop package surfaces expose the optional sdk.services.v1 contract.
Portable package code must still feature-detect sdk.services?.v1, because
mobile hosts and older desktop hosts do not install it.
A package may address only a local.service contribution declared in its
signed manifest. The host owns installation, integrity verification, executable
selection, arguments, environment, port allocation, readiness checks, restart
policy, and process cleanup.
See Local services for the complete manifest authority, packaging, Live TAP, diagnostics, and restart workflow.
ensureRunning will idempotently install and start the declared service, then
return its opaque generation and exact loopback origin. Its deadline comes from
the signed manifest; callers cannot provide a timeout or any process controls.
getStatus is read-only and returns one of unavailable, stopped,
installing, starting, running, or failed. A running generation can
change after a restart, so do not retain its endpoint across status changes.
Starting a service requires the contribution's exact local-service.run
authorization and matching local-service effect. Failure codes distinguish
unsupported hosts, denied permission, integrity or sandbox failures, resource
exhaustion, readiness timeouts, crash loops, and host failures. Treat unknown
codes from newer hosts as ordinary operation failures.
Channels
sdk.channels provides five operations:
MiniAppChannel describes a visible channel. MiniAppChannelMessage is intentionally opaque, bounded MiniAppJsonValue: the host versions timeline-row shapes independently, so narrow and validate a row before reading any fields. The timeline result also supplies the visible sequence. A former participant can remain readable through GetChannelAccessResult.visibleUntilSequence; do not interpret that as permission to write.
Access can change after a check. Handle rejection from the operation itself and do not infer authority from cached channel data.
Projects
sdk.projects uses CreateProjectOptions, GetProjectOptions, and UpdateProjectOptions. The corresponding CreateProjectResult, GetProjectResult, and UpdateProjectResult return the project ID or a MiniAppProject snapshot.
GetProjectResult.project can be null when the project is missing or outside the current capability scope.
Tasks
In React, useTasks from @theaiplatform/miniapp-sdk/react handles the list,
loading and busy state, enumerated failures, and reloading after a write:
Writes reload rather than patching local state — the host owns task shape (status normalization, assignee resolution, archived transitions), so echoing a locally-mutated task risks showing something the host would describe differently.
sdk.tasks is an optional MiniAppTasksApi backed by the same task silo as the built-in Tasks view. Feature-detect it because older hosts omit the capability. create, update, delete, and list accept the matching task option types and return task snapshots or archive results.
Use createWithReceipt when an interrupted workflow must safely replay task creation. It accepts a stable idempotencyKey, optional project and assignee IDs, and returns a durable MiniAppActionReceipt. The tasks silo journals the key inside the canonical CRDT document, so a replayed key returns the original task with a duplicate-suppressed receipt instead of writing a second one — the exactly-once contract is the canonical create_task mutation itself, not a second path layered over it.
delete is a soft archive: DeleteTaskResult reports the archived task ID rather than removing the record. Pass includeArchived to list to include archived tasks, and pass null to UpdateTaskOptions.dueDate to clear an existing due date. Task snapshots can become stale between calls, so handle rejection from each operation instead of trusting cached data.
list returns 25 tasks by default. Set limit to an integer from 1 through 50.
Pass nextCursor as the next call's cursor, and keep the same workspace,
includeArchived value, and limit. Treat cursors as opaque. Pagination is stable
when task data does not change. Pagination is not a snapshot across concurrent
changes, so a task that moves in the ordering can be omitted or returned again.
Workflows
sdk.workflows.list accepts optional ListWorkflowsOptions and returns ListWorkflowsResult, whose entries are MiniAppWorkflow values. invokeSaved accepts InvokeSavedWorkflowOptions; the optional invoke method accepts InvokeWorkflowOptions. Both return InvokeWorkflowResult.
Feature-detect the optional inline invoke method. A successful request reports status and may include a run ID; it does not imply that unrelated follow-up operations are authorized.
Authentication
sdk.auth is an optional MiniAppAuthApi. Its getUserProfile method returns a MiniAppUserProfile or null. The profile exposes a subject when available and host-approved public fields; it never contains raw platform credentials.
Treat a missing capability and a signed-out profile as normal states. Request the profile again after an account or scope change.
Virtual Files
sdk.vfs is an optional, desktop-only MiniAppVfsApi with provisionProjectChat, mkdir, writeFile, and writeFiles. Desktop hosts may additionally provide the optional readFile, stat, and list methods. Mobile hosts omit the namespace because they do not install the VFS host-action rail. File paths are relative to the selected conversation storage, not operating-system paths. readFile returns the file bytes, stat returns file-or-directory metadata, and list returns directory entries; pass an empty string to list to read the root. Reads are bound to the conversation of the mounting surface.
readFile rejects files larger than 16 MiB before the native read begins. The desktop host also resolves the final target against the authorized conversation and mounted roots, so symlinks that leave those roots are rejected. Read methods are feature-detected separately from the write methods and are not advertised on mobile targets or older hosts without the desktop VFS authority.
VFS mutations apply the same canonical-root check to existing symlink components. A symlink cannot redirect a write, directory creation, delete, or rename outside the authorized conversation or mounted roots.
writeFiles accepts 1 to 64 relative paths that remain unique after case-folding, with at most 16 MiB in the whole request. The host validates and authorizes the request once, creates the required parent directories, then writes the files. It is not a transaction; a provider failure can leave a prefix of the files written, so retries must use content that is safe to write again.
The VFS API does not expose raw host filesystem access or setup details. project_mount_not_ready means the expected folder is unavailable. It remains latched until the user reconnects the folder and selects Retry in the app. project_full_disk_access_required means macOS denied that native provisioning attempt. The host does not infer this code from the current system status after another failure. Follow the app's Full Disk Access prompt and restart the app before trying again. project_provision_failed means another local setup step could not finish. Follow the app's recovery prompt, then try provisioning again.
Miniapps cannot promote that state. Handle unavailable scope, conflicts, and deletion as recoverable failures.
Conversation Git
sdk.git is an optional, desktop-only MiniAppGitApi for the conversation's repository. Every method is bound to the conversation of the mounting surface, and no host filesystem path crosses the boundary.
resolveRuntimereports whether the host uses its embedded git or the system git.initializeRepositorycreates an empty repository in the conversation's VFS project root with themainbranch. It refuses to overwrite an existing repository.snapshotreports branch, head, dirty, staged, upstream, and remotes.changesreports uncommitted-change counters.diffreturns bounded staged and unstaged unified diffs for selected paths.stagestages explicitly selected paths and returns the staged diff plus an opaquestagedTreereview fence.approveStagerequires a fresh user gesture; the host selects the latest staged result captured bystage, displays that exact staged diff, and returns a review receipt only after host confirmation. The package does not choose the tree being approved.commitrequires both the staged-tree fence and the host-owned review receipt, so a package cannot commit content without an explicit approval step.createBranchcreates and checks out a branch from an exact reviewed branch and head.renameBranchrenames the conversation's branch.pushpushes the conversation's committed branches through the host's connected GitHub authority.openPullRequestopens or recovers a pull request; omittedasopens a draft,'open'marks it ready for review.pullRequestslists the pull-request records the host keeps for the conversation.worktreeslists the worktrees registered for a project.subscribereports repository-state transitions; it returns an unsubscribe function.
A commit made here is a user commit. It records no specialist attribution, and undo rewinds only specialist turns.
Conversation Snapshots
sdk.snapshot is an optional, desktop-only MiniAppSnapshotApi for the conversation's specialist-turn save-point history. status reports the current and total turn counters and whether undo is available, history lists the per-turn entries, undo rewinds exactly the latest applied specialist turn, and subscribe reports save-point transitions. The host resolves the conversation's canonical project; callers may omit projectId or provide it as an ownership check. This is turn history, not git history.
User-selected Files
sdk.files is an optional desktop-only MiniAppFilesApi for files the human
explicitly selects in a host-owned Open or Save dialog. It is separate from
conversation VFS and private package storage. The Miniapp receives an opaque,
owner-bound handle, safe metadata, and bytes; native paths and provider
credentials never cross the SDK boundary. Feature-detect the namespace before
showing file controls.
Call pickOpen or pickSave as the first asynchronous operation started by a
current human gesture. Do not await unrelated work before opening a picker.
pickOpen.accept takes lowercase, parameterless MIME types or dot-prefixed
extensions. pickSave takes a required suggestedName and an optional
mimeType. Cancelling either dialog rejects with file_cancelled.
metadata, read, readRange, and createReadStream operate only on an
issued handle. Metadata includes a host-issued handle refreshed to the returned
revision, plus the selected basename, safe content fields, and provenance, but
no path. Use metadata.handle for subsequent revision-fenced reads; never clone
or edit an older handle to advance its revision. A whole-file read defaults to
a 16 MiB maximum; lower it with maxBytes, or use bounded ranges and a readable
stream for larger files. Read streams request 256 KiB ranges by default and
accept a chunkBytes value from one byte through one MiB. Pass an AbortSignal
to stop SDK reads or streaming work.
Both write and createWriteStream publish atomically. Pass the exact revision
observed before the write and a caller-owned idempotency key. Generate one UUID
for each logical write; reuse it only when retrying that same revision and byte
sequence. A final revision conflict rejects with file_stale instead of
overwriting an external edit. A successful write returns a receipt with the
updated handle, revision, SHA-256 content hash, byte length, commit time, and
idempotency key. Use receipt.handle for later operations. For a write stream,
close its writable and await receipt; the receipt resolves only after the
atomic commit. Cancellation applies until close() dispatches that commit.
After that boundary, the SDK waits for the authoritative receipt or commit
error because publication can no longer be safely canceled. Abort before the
commit and failed commits leave the previous complete destination visible.
Idempotency receipt replay is limited to the current live host process. A hard host crash cannot publish a partial destination: the destination contains either the previous complete bytes or the atomically published replacement. Each in-flight stage lives in a transaction directory attributed by an owner-only app-data journal and a locked random marker. Journal records are installed from a synced private pending file with a crash-atomic, no-replace publication step. Before accepting bytes, the host also creates a pinned publication file and records the identities of the transaction directory, marker, staged content, and publication file. On restart, the host removes an owned transaction only when the recorded parent and directory identities match, the marker is unlocked and exact, and every remaining owned entry has its recorded identity. A completed publication that left only a stale record is retired without touching the destination.
Recovery fails closed when ownership proof is incomplete. An intent-only empty directory, partially written marker, replacement, symlink, mismatched entry, or unexpected entry stays untouched and journaled. A temporarily unavailable or renamed parent is retried when that same directory identity is selected again. This includes the narrow crash window after the private directory or its child files are created but before their identities become durable in the owned record. The host preserves that state instead of guessing from reserved names. The host never sweeps neighboring files merely because their names look like staging files. Cleanup revalidates every recorded identity immediately before removal and fails closed on an observed substitution. POSIX has no portable unlink-by-descriptor operation, so an uncooperative process running as the same OS user can still win the final gap between that identity read and the unlink system call. The API does not replay the lost in-memory receipt or guarantee strict compare-and-swap against a simultaneous uncooperative external writer.
rename changes only the selected file's basename and returns an updated
handle. delete removes the selected file and permanently revokes that handle.
Both require the exact revision the caller observed, the corresponding access
bit on the host-issued handle, and their separately declared consequential
permission. The mutation fence is independent of the revision at which the
handle was issued, so a revision explicitly returned by metadata or watch
is valid without forging a replacement handle. Prefer the refreshed handle
those receipts return because revision-fenced reads use the handle's embedded
revision. watch performs one bounded long poll for a revision change; call it
again after each receipt. A timeout is a successful unchanged receipt, while
revocation, provider loss, and external mutation retain their stable file error
or revision result.
MiniAppFileRenameOptions contains expectedRevision, a basename-only
newName, and an optional signal. A successful rename returns a
MiniAppFileRenameReceipt with the refreshed handle, resulting name and
revision, and numeric renamedAt timestamp. Keep the refreshed handle because
the pre-rename handle is stale. The host keeps the file in its selected
directory and rejects separators, traversal names, collisions, stale
revisions, and symlink substitutions.
MiniAppFileDeleteOptions contains expectedRevision and an optional
signal. A successful delete returns a MiniAppFileDeleteReceipt with the
revoked handleId, terminal revision, and numeric deletedAt timestamp.
Deletion cannot be undone through the handle. Any later operation with that
handle rejects with file_revoked.
MiniAppFileWatchOptions contains previousRevision, waitMs, and an optional
signal. waitMs must be from 0 through 30,000 milliseconds. The resulting
MiniAppFileWatchReceipt returns a host-issued handle refreshed to the
observed revision, repeats the handle ID and previous revision, and reports the
observed revision, change (unchanged, created, modified, or deleted),
numeric observedAt timestamp, and timedOut. A timeout sets timedOut: true
and change: 'unchanged'; it is not an error.
Picker and recovery actions run through the surface action broker. Metadata,
file bytes, mutation, and watch requests use short-lived exact grants over the
same-origin JSON Connect endpoint. They never travel through ordinary
postMessage, and neither transport exposes a native path.
A destination returned by pickSave uses the files.pick-save action for one
exact logical write. The host binds that picker provenance to the handle,
picker-issued revision, and idempotency key before authorization, so an
unavailable or lost grant response can be retried with those exact fields.
Changed write fields and every write to a file returned by pickOpen require
separately granted files.overwrite authority. The host tracks this
provenance; callers cannot select the permission by changing the handle.
revoke needs no new permission because it only reduces authority, and desktop
teardown revokes the surface's handles even if guest cleanup does not run.
Current hosts issue only session-scoped handles with recoverable: false.
Passing recoverable: true or calling recover rejects with
file_unsupported. The files.retain-handle action and retain-handle effect
resource reserve future recovery; that action requires fresh-decision
consent, which the public surface SDK cannot currently request. Do not declare
or request retained handles until a supporting host-mediated consent flow is
available.
A file.handler registration follows its exact package installation and active
release. Disabling, rolling back, or uninstalling that installation removes or
replaces its active handler authority. This lifecycle never deletes a selected
user file. Collaboration-owned artifacts remain subject to the collaboration
owner's retention policy, independently of the handler package installation.
Each handler role requires the referenced desktop surface to declare its exact
actions in authorization.onDemand. The manifest is rejected when any action
in this table is missing.
Every file action that a desktop UI surface requests belongs exactly once in
its authorization.onDemand. Its permission catalog entry uses
scopes: ["user"], directActors: ["human"], and delegatedActors: []. Add
one user-file effect whose resources are exactly the effect resources for
that surface's requested actions.
The final three actions are owner-bound and are not generic sdk.files
operations. The host owns default-handler settings; a package cannot assign
itself as the default. Collaboration authorities must enforce import disclosure
and artifact export at the boundary that owns the canonical artifact facts.
Do not request these actions for ordinary picker, read, or write operations.
After picker admission, file operations can reject with a stable
MiniAppFileErrorCode: file_denied, file_cancelled, file_stale,
file_revoked, file_unavailable, file_too_large, file_malformed,
file_encrypted, file_quota_exceeded, or file_unsupported. Picker admission
can reject with user-gesture-required before the native dialog opens. Use
isMiniAppHostActionError to distinguish that from file_denied, keep an
unknown-code fallback, and do not parse message text.
Direct inference
sdk.inference is an optional, low-level generation API for isolated tasks
such as comparing several candidate answers. It uses only host-managed or
workspace-shared provider routes. A miniapp selects a canonical model, but it
never receives provider credentials. Direct inference is available only to
desktop UI surfaces whose package excludes host versions before 2.3.5 in
compatibility.tapHost; continue to feature-detect the optional namespace.
Prefer a specialist when the task needs tools, durable conversational context, or product-specific behavior. Direct inference has no tools, attachments, or implicit history. Pass the conversation that owns the user action explicitly:
Every send returns the effective model and provider, token and cost usage when the provider reports it, latency, and the host-generated turn ID. The host also records content-free turn telemetry. The response itself remains ephemeral: only content the miniapp deliberately writes to VFS becomes durable.
Specialists
sdk.specialist is an optional MiniAppSpecialistApi. It can run a turn against
a specialist your package declares, join one to a channel, list a workspace, and
create a specialist. Feature-detect each optional method before showing its
action.
A specialist becomes invokable by being declared in your manifest, not by
being registered at runtime. See
Declaring a Specialist for the
four required pieces and the resolved <name>@<version> ID form. Workspace
visibility alone never grants authority: the host rejects an ID the current
package release does not declare.
Running a turn
Most surfaces should use runSpecialist, or the useSpecialist hook that
wraps it. They handle the parts every caller otherwise rewrites: feature
detection, distinguishing the failure causes, extracting text from the completion
parts, and validating the answer.
It never rejects. Every failure resolves as { ok: false } with an enumerated
SpecialistFailureReason, so a caller cannot accidentally treat "the
workspace withheld a grant" and "the provider is down" the same way.
In React, useSpecialist from @theaiplatform/miniapp-sdk/react adds the
state machine and a regenerate affordance:
turn.status is unsupported, idle, running, ready, or failed. A failure
outranks a previous answer, so a failed regenerate never leaves a stale result
presented as current. While a turn is in flight run is a no-op — repeat clicks
cannot stack up, which matches the host serializing turns on one room anyway.
Cancellation is not offered. The host exposes no way for a guest to stop a turn it
started, and an abort() that only stopped the caller listening would imply
otherwise while the turn kept running.
Calling the host method directly
runTurnWithTools accepts MiniAppSpecialistTurnOptions and resolves with
MiniAppSpecialistTurnResult once the turn reaches a terminal event.
streamTurnWithTools(options, observer) has the same terminal result and
authorization semantics. It additionally reports frozen full-body snapshots
through observer.onSnapshot:
Each snapshot has the exact shape
{ type: 'messageSnapshot', channelId, messageId, streamVersion, body }.
Versions rise per message and body is a replacement, not a delta. Observer
errors are isolated from the turn. Zero snapshots is valid: silent turns,
channel-less turns in a hidden host-owned room, disconnected realtime sessions,
and Test Lab specialist turns do not expose progress. In all cases the returned
promise still follows the same terminal behavior as runTurnWithTools.
Omitting channelId is the channel-less form. The host then runs the turn in
a private room it owns for your (workspace, package, specialist). That means:
- The turn needs only
specialists.invoke. It never needs achannels.*permission, because your app neither creates nor joins a channel — the host resolves its own room. - The room does not appear in the user's channel list. It is not a conversation they created.
- The room is persistent and keyed on that triple, so repeat calls continue one conversation rather than starting fresh.
That last point is the one to design around. A channel-less turn is not fire-and-forget: because the session stays live, you can offer regenerate or amend, and the specialist sees what it answered before. If you need a clean slate, say so in the prompt — there is no per-call reset.
Supplying channelId keeps the ordinary behaviour: the turn runs in that
channel, and the host checks the user's participation in it. By default it is
still silent — no message is written to the channel, and the answer reaches
you only through completionEvent.parts.
dispatch: true makes a channelled turn visible. The host persists your
content into the channel as an ordinary message and routes it, so the
specialist runtime writes the reply itself, with the provenance chat requires.
This mode requires channels.send-message with do autonomy in addition to
the base specialists.invoke grant. The calling surface must declare both
permissions, and the package must hold both grants.
Design around two consequences:
- Read the reply from the channel timeline, not the result. It lands as a
real specialist message, so
channels.getTimelineis what renders it. The returned completion is the turn's own record; treating it as the thing to display gives you a message the channel already has. - The specialist must already be seated in the channel. Joining one is
channels.manage-specialists, a separate grant — this path mentions and claims an existing participant, it does not add one.
dispatch says nothing on a channel-less turn, which is always routed this way.
Rooms and channels
A room is the underlying primitive: a conversation with participants and a message timeline. A channel is a room presented to the user — listed, joinable, discoverable. Every specialist turn runs in a room, because a room's message lifecycle is what starts and tracks the turn. A channel-less turn simply runs in a room that is never presented as a channel.
Failures to handle
Reject paths are distinct on purpose; surface them distinctly rather than collapsing them into one retry message:
A tool the specialist calls may need a credential the workspace has not configured. That surfaces as a completed turn whose content explains the tool failed — not as a rejection — so validate the answer against your own contract before trusting it.
Joining a specialist to a channel
joinToChannel adds one declared specialist to an existing channel.
prepareChannel prepares 1 to 64 exact package-declared specialist IDs while
sharing channel access, workspace catalog, and participant-roster reads. It
does not create the channel and does not require specialists.list because the
host uses the catalog only for internal identity resolution.
For several declared specialists, prepare them in one request:
IDs must be unique and the result preserves request order. The operation is not transactional: if a late step fails, some specialist runtime sessions may already exist even when chat participants were not updated. Do not blindly retry. Re-read channel state and recover through the domain flow appropriate to your miniapp.
Types
MiniAppCreateSpecialistOptions and MiniAppCreateSpecialistResult describe a
basic created specialist. MiniAppSpecialistInteractionMode,
MiniAppSpecialistConversationPart, and the turn option/result types describe
the complete request and terminal response for a tool-enabled turn. Hosts reject
invalid IDs, unsupported modes, out-of-range timeouts, and unauthorized channel
access.
upsertManaged registers a package-owned custom specialist at runtime and can
return MiniAppManagedSpecialistResult. It is not the way to make a
specialist invokable — use manifest declaration. MiniAppManagedSpecialist is
the bounded manifest it accepts; ownership, workspace, visibility, installation,
and verification metadata are derived by the host and rejected if a miniapp
supplies them.
Chat
sdk.chat is the required MiniAppChatApi. sendTextToChat selects the
active conversation, reveals the shared composer, and places text there.
Compatible hosts can also expose archiveConversation.
The method places text for the user; do not describe it as silently sending a user-authored message.
archiveConversation takes workspace, user, conversation, and project IDs and
resolves with null after archival. It is optional and can reject when the
current package session lacks authority. Use sdk.specialist.joinToChannel to
add a specialist to a channel.
Package-owned deep links
Compatible hosts expose sdk.chat.stageDeepLink so a miniapp can place a link
to one of its own resources in Chat without constructing or exposing a TAP
router URL. Declare chat.compose at do autonomy on the calling surface and
invoke the method synchronously from a fresh explicit user gesture. The
package supplies a display label and MiniAppDeepLinkTarget: a bounded JSON
object with a required kind. The host derives and binds workspace,
installation, package, release, digest, and surface provenance.
Chat renders a host-owned button, never an anchor. Activation requires the
same installed release and delivers MiniAppDeepLinkOpenRequest only to that
package surface. sdk.navigation.subscribeDeepLinks needs no additional
permission action because it receives only package-scoped targets; keep the
subscription for the surface lifetime and clean it up during unmount. Release
or generation drift fails closed.
Use stageDeepLinkWithRollback when staging is one step in a larger
transaction. It resolves with an opaque MiniAppStagedDeepLink. If a later
step fails, pass that exact handle to unstageDeepLink; rollback still
requires the declared chat.compose action but does not require a new gesture.
All four deep-link methods are optional for compatibility with older hosts.
Feature-detect them, reject malformed or unknown targets in the package, and
never treat the target or requestId as authority.
Retention Reporting
sdk.trr is a MiniAppTrrApi with getAggregate, getMdTrr, and getEcrt, which take a MiniAppTrrScope naming the cohort dimension to slice by, plus getDeathCauses, getRelationMix, and getSurvivalCounts, which name no cohort and take at most a workspace and a horizon. Every read reports only the calling principal's own local ledger. Declare trr.read for each of those reads except getEcrt, which needs the separate trr.read-cost. runSweep is not a read: it recomputes the verdicts those reads report and needs its own trr.sweep permission at do autonomy, so a package that declares only trr.read is refused.
The object is always present, so a property check is not a capability check — a host that does not support these reads installs methods that reject. Detect support by handling the rejection.
Branch on state rather than on cells.length: ok carries cells, withheld means the host suppressed cohorts, and no-data means the ledger holds nothing for that scope. Treat withheld as a normal outcome — a personal ledger commonly has too few distinct contributors to publish.
getEcrt is the exception to reading withheld as privacy suppression. Cohorts the host could not price are returned in cells carrying unpriced: true, while state reflects only the priced ones, so an all-unpriced answer arrives as withheld with a non-empty cells array. Inspect the cells before telling a user their cohort was too small.
Every cell carries cohortKeyHash, a content-free key, and may carry cohortLabel — the cohort's name (a project's title, or a specialist, model, or harness id) resolved by the host from local workspace state. Render the label when it is present and the hash when it is not; a labeled read names which project or specialist a number belongs to, so treat it as workspace data rather than as anonymous statistics.
Cells are MiniAppJsonValue because the host owns their shape and strips its research-only fields before a guest sees them, so it can add a field without republishing this package. getEcrt reports modeled spend per surviving code unit, never per provider token.
runSweep is the only write on this surface: it recomputes the retention verdicts the reads above report, needs the separate trr.sweep permission at do autonomy, and is throttled by the host so a rejection means "try later" rather than "failed". It returns no counts — the sweep tallies every principal's rows in the ledger while the reads are scoped to you — so re-read getSurvivalCounts once it resolves.
getDeathCauses and getRelationMix are the counted reads, and both carry window: 'all-time' because they span the whole ledger rather than a horizon — do not place them beside a horizon-scoped ratio without showing that label, and note that getDeathCauses counts edit charges rather than messages, so its totalDeaths legitimately exceeds deadCount from getSurvivalCounts. A relation absent from getRelationMix had no countable charge, which is not the same as zero, and no severity is reported because severity scores the relation and not the count. All three counted reads are scoped to the reader's own rows, so their state is only ok or no-data — no floor can withhold them.
Navigation and Feature Flags
sdk.navigation.open accepts OpenNavigationOptions with a platform-relative path. It is not a general URL or script launcher.
Desktop hosts may also expose sdk.navigation.openExternal. Feature-detect the method, then call it synchronously from a user click. It accepts OpenExternalNavigationOptions containing an absolute HTTPS URL, rejects credentials, and opens only the operating-system browser. The URL's exact canonical origin must appear in the calling workspace-scoped UI contribution's external-navigation effect, and that contribution must declare the on-demand navigation.open-external action. User-scoped surfaces cannot declare this authority, and packages that do must set compatibility.tapHost to a range that excludes versions before 2.3.4. There is no platform-wide origin allowlist, and external-navigation effects do not grant SDK HTTP access.
Declare the authority on the desktop surface that makes the call, together with the canonical permission action:
hasEditorView and hasHostHttpRequest are optional boolean feature flags. Treat values other than true as unavailable. When hasHostHttpRequest is true, the desktop host also supplies sdk.http and sdk.credentials; portable code should still check the objects before calling them.
Navigation rejects malformed or out-of-scope paths. Feature flags are snapshots; re-check them after a new surface realm mounts.
MiniAppOpenExternalErrorCode enumerates the bounded external-navigation failures: unsupported-host, authorization-denied, authorization-unavailable, user-gesture-required, stale-installation, origin-rejected, request-expired, and native-open-failed. Treat authorization-unavailable as retryable; it means the host could not reach or read the permission authority, not that the user denied access. Paths, query strings, fragments, credentials, and complete URLs are never included in host logs.
Host Action Errors
Host-backed operations can reject with MiniAppHostActionError. Its code is
a stable, non-secret machine-readable identifier; its message is safe to
show to the user. Use isMiniAppHostActionError to narrow an unknown rejection
instead of matching message text. The guard also validates the public error
shape, including the canonical code format.
Codes are intended for branching, telemetry, and assertions. A code can be added by a newer host, so handle unknown values as ordinary operation failures.
Artifact Types
A package declares a durable collaborative artifact type with an
artifact.type contribution. The contribution is host-declarative: the signed
manifest supplies type metadata, per-document sync protocols, schemas, and
bounded limits, while the platform owns transport, persistence, sessions,
receipts, and authority. The host never inspects package-owned protocol
payloads or domain types, so any package can register a board, design, or
domain record through the same contract Compose uses for documents.
artifact.type requires apiVersion: 1, lifecycleScope: "installation", and
exactly one desktop target with runtime: "host-declarative".
Validation is fail-closed at package build and install review:
typeIdand everysyncProtocolmust be rooted at the package namespace and end in a canonical.v<N>suffix, so one package cannot claim another package's type or protocol IDs.- Each document lane's
snapshotSchemaandoperationSchema(and the optionalanchorSchema,threadSchema,presenceSchema, andpreviewSchema) must be safe package-relative paths ending in.json. Ship the referenced schema files as real package assets. writeActionmust name an action declared by the package's ownpermission.catalogcontribution, with direct-human access, reusable consent, andautonomyCeiling: "do"; the action is dedicated to artifact-type writes, may be shared by artifact types, and must not authorize unrelated contributions.maxOperationBytesis bounded at 1 MiB per operation andmaxSnapshotBytesat 64 MiB per snapshot; the host enforces tighter effective ceilings at runtime.previewRenderermust reference a same-packageui.renderercontribution, andmigration.contributionIdmust reference a same-packagelocal.serviceorworkflowcontribution so migrations stay outside any UI surface. Whenmigrationis present,hostResourcesmust includereceipt-journalso the declared migration can use the platform receipt authority.- File association extensions are lowercase
.<alphanumeric>suffixes, directions areimport,export, orimport-export; extensions and MIME selectors must be unique across allartifact.typecontributions in the package release because dispatch has no type-specific tie-breaker; andhostResourcesis a closed set (sync-transport,snapshot-store,presence-directory,receipt-journal) with no duplicates. compatibility.minReaderVersionmust not exceedmaxWriterVersion, and neither may exceed the format version declared by the typeId suffix. Opening an older artifact negotiates against this range; downgrades fail explicitly when the installed release cannot read the artifact.
Runtime collaboration transport and authority are provided by the platform's
generic miniappCollaborativeArtifacts module. Until a host ships that module,
the contribution registers type metadata and schemas at install time but no
live sync session can start.
Artifact Context Resolution
An action.command launch carries
one MiniAppArtifactReference through the target surface's
context.launches subscription. The
reference is an immutable, short-lived authority handle for exactly one
channel-message, pull-request, task, or repository-issue. Artifact
content never appears in the launch message.
The current end-to-end invocation and resolver path is available in the Tauri
desktop host only. Package validation requires action.command to select
exactly the desktop target, and the native mobile host does not install
sdk.artifacts.
referenceId is the opaque host-minted bearer value used to redeem the
reference. kind narrows the host-versioned snapshot. artifactId is a
bounded display identity, such as a message ID, task ID, or canonical GitHub
URL, and is not authority. workspaceId names the invocation workspace, while
mintedAt and expiresAt bound the reference lifetime.
Do not construct, modify, persist, log, or place a reference in a URL. The host binds its opaque value to the exact principal, installation, package release, target, surface contribution, workspace, and local package generation that received it. Copying the visible coordinates into another object does not mint authority, and forwarding a valid object to another surface does not transfer authority.
resolve(options: { reference: MiniAppArtifactReference }) returns a
MiniAppMaybePromise<{ artifact: MiniAppResolvedArtifact | null }>. Feature-detect
sdk.artifacts, then pass the received object unchanged:
resolve reads the owning source at call time. In the current Tauri host,
artifact.snapshot contains at most 64 KiB of canonical JSON. The snapshot is
sanitized, bounded, and host-versioned, so narrow it by reference.kind and
tolerate additional fields. Do not treat it as a complete provider response or
retain it as an authority-bearing record. provenance names the source,
observation time, and nullable source revision used for that read.
linkedPlotIds contains at most 256 deduplicated IDs from the artifact owner's
canonical plot relation. An empty array means the host knows of no authoritative
relation. It does not prove that a plot does not exist, and packages must not
infer links from URLs, chat previews, or snapshot text.
The host authorizes artifacts.read before the source read and checks it again
after the read. A well-shaped request from the current workspace and an
authorized surface returns { artifact: null } when the reference is expired,
unknown, modified, bound to another surface, missing from its source, or
reported unavailable by that source. Invalid options or reference shapes, a
workspace mismatch, a permission denial or revocation, and a source read
failure reject the operation instead. Treat both outcomes as expected and
avoid automatic retries after expiresAt.
Generic plot and orchestration primitives
The desktop host currently installs sdk.codeIntel, sdk.workspace,
sdk.tasks, sdk.artifacts, and workflow run observation. The SDK installs
sdk.artifacts on every desktop target, so its presence does not prove that the
mounted surface declares or holds artifacts.read. It does not install
sdk.plots, sdk.home, or sdk.integrations, and it does not install a
task-linking method. Feature detection for those namespaces therefore returns
false. Their exported interfaces are reserved contract vocabulary for a host
that later supplies the required authoritative backing; they are not usable
runtime capabilities today. Direct raw host-action calls fail with
capability-unavailable as a defensive boundary.
The manifest does not accept plot definitions. Context-bound actions use only
the strict action.command
contract and cannot declare or imply a plot definition.
sdk.codeIntel is the read-only repository-scoped Code Knowledge Graph
capability. Callers pass a host-resolved project reference through
MiniAppCodeIntelScope — never a local path — and every result carries a
MiniAppCodeIntelProvenance envelope with the project id, graph build
revision, source commit, and confidence, with explicit nulls where the rail
publishes no value. Index status, semantic/intent/hybrid search, communities,
processes, branch diff, impact analysis, coverage, fragility, symbol history,
and specialist affinities share the MiniAppCodeIntelApi surface. The host
validates project ownership before the first graph read and retries a query if
the graph revision changes while it runs. Impact analysis returns one closure
per distinct requested symbol or path. Coverage and fragility query every
distinct path, while specialist affinity returns the highest score observed
for each specialist across all requested symbols.
sdk.workspace lists canonical workspace teams and projects
(MiniAppWorkspaceTeam and MiniAppProject) so a package can resolve
workspace-, team-, and project-scoped policy without inventing its own
organization model.
sdk.tasks.createWithReceipt accepts a stable idempotencyKey and returns a
durable MiniAppActionReceipt. Receipt-backed task creation validates the
project, channels, and assignees against authoritative workspace data before
one atomic task write. The persisted task uses canonical assignee metadata and
the validated project and channel identifiers.
sdk.workflows.getRun and sdk.workflows.subscribeRun observe one workflow
run as MiniAppWorkflowRun, including status, resolved bounded output,
failure detail, and the plot/revision correlation recorded for
invokeSaved({ correlation }). A correlation-journal failure after a run
starts is logged but does not report the successful start as failed.
subscribeRun emits
MiniAppWorkflowRunEvent only on real state transitions. Runs are
short-lived executors; canonical lifecycle state belongs in the plot.
Public Export Contract
Every exported name below is public. “Type only” rows have no runtime lifecycle of their own; the linked working example shows where the contract is consumed.