Product App API Skill
Use this skill for Product App runtime code and platform capability work. Prefer current tool output, scaffold files, and package validation results over memory.
Runtime Model
ui.js runs in the iframe/browser environment. It uses the DOM, standard browser APIs, ESM imports, and window.app.
worker.js is optional backend logic running in a separate JavaScript Worker host. Custom app.call(method, params) calls only methods exported by worker.js.
source_manifest.json describes entrypoints: uiEntry, optional workerEntry, styleEntries, and buildMode. Current built-in apps use nativeEsm.
esm_dependencies.json is the browser import-map dependency array, not an object. Write [] when there are no dependencies.
- Extra source files are read from
source/. Do not handwrite private_surface_sources; edit package source files.
- A Product App surface
implementationRef must be app://<app-id>@<version>/surfaces/<surface-id>. Do not fall back to old bundle://surface-components/... refs.
window.app Capabilities
The runtime adapter exposes window.app. A common alias is:
const runtime = () => window.app || {};
Basic properties:
app.appId
app.appDataDir
app.workspaceDir
app.theme
app.locale
app.platform
app.mode
Host capabilities:
app.fs.readFile/writeFile/readdir/stat/mkdir/rm/copyFile/rename/appendFile
app.shell.exec(command, opts)
app.net.fetch(url, opts)
app.os.info()
app.storage.get(key) / app.storage.set(key, value)
app.dialog.open/save/message
app.clipboard.writeText/readText
app.ai.complete/chat/cancel/getModels
app.backend.call/cancel/status/cancelRun/cancelStaleRuns/turnText/onEvent/offEvent
app.host.fillChatInput(text)
app.deck.renderPage(opts)
app.log.debug/info/warn/error
app.onActivate/onDeactivate/onThemeChange/onLocaleChange
app.i18n.t(key, params, fallback), app.i18n.setMessages(messages), app.i18n.onChange(fn)
app.t(localeTable, fallback): selects text from { "zh-CN": "...", "en-US": "..." } using the current locale.
app.on(event, fn) / app.off(event, fn): subscribes to runtime events, worker:*, backend:event, and similar events.
app.ui: runtime UI Kit. UI details belong in product-app-ui-polish.
The current code does not expose an app.agentic.* namespace. Use app.backend.* for app-private Agent Component or Bridge Component bindings. Use app.ai.* only for direct model capability.
UI And Worker Boundary
- Put DOM, theme, locale, event binding, and user interaction in
ui.js.
- Enter host capabilities through
window.app for files, shell, network, system info, storage, AI, and backend calls. Do not import Sparo internal services directly.
- Write
worker.js only for custom business backend logic, and declare permissions.node.enabled = true.
- When
node.enabled = false, host primitives can still be used, but custom app.call(...) methods cannot.
- Worker code must not assume DOM,
window.app, or browser globals exist.
Permissions
Current surface runtime permissions shape:
{
"fs": { "read": ["{appdata}"], "write": ["{appdata}"] },
"shell": { "allow": ["git"] },
"net": { "allow": ["api.example.com"] },
"node": { "enabled": false, "max_memory_mb": 256, "timeout_ms": 30000 },
"ai": {
"enabled": true,
"allowed_models": ["primary", "fast"],
"max_tokens_per_request": 4096,
"rate_limit_per_minute": 20
}
}
Rules:
- Default to minimum permissions. If the app does not need user files, use only
{appdata} or omit fs.
- Path scopes include
{appdata}, {workspace}, {home}, {user-selected}, and absolute paths. {workspace} resolves only when a workspace is bound.
shell.allow is a command-name allowlist. For git capability, declare "git"; do not create a general shell channel.
net.allow is a domain allowlist. "*" means all network access and should not be the default.
permissions.ai.enabled controls app.ai.*. If allowed_models is absent, rely only on primary.
- Do not expand permissions to work around missing platform capability. Internal services such as WorkspaceService, GitService, TerminalService, LSP, Browser, Computer Use, and Config are not Product App APIs.
AI And Intelligent Backend
app.ai.* reuses the host AI client and requires no app API key:
const result = await app.ai.complete('Summarize the current input', {
systemPrompt: 'Output only a concise conclusion.',
model: 'fast',
maxTokens: 800,
temperature: 0.2,
});
const handle = await app.ai.chat(
[{ role: 'user', content: 'Generate three options' }],
{
model: 'primary',
onChunk: (chunk) => appendText(chunk.text || ''),
onDone: () => setBusy(false),
onError: (error) => showError(String(error?.message || error)),
},
);
app.backend.* calls declared backend bindings and is the right path for app-private Agent Components or Bridge Components:
const run = await app.backend.call('ppt.generate', input, {
entityId: state.deckId,
idempotencyKey: `generate:${state.deckId}:${Date.now()}`,
});
app.backend.onEvent((event) => {
if (event.actionRunId === run.actionRunId) updateProgress(event);
});
Key backend binding fields:
id
kind: agentComponent or bridgeComponent
componentId
- Optional
capabilityId
role
sessionPolicy: ephemeral, persistent, perEntity, shared
memoryScope: none, appInstance, entity, agentComponent
actions: { name, inputSchema, outputSchema, allowStatePatch }[]
If the Product App needs durable intelligent behavior, prefer an app-private Agent Component exposed through backend binding actions. Do not use a raw Agentic session as internal application state.
Built-In App References
builtin-ppt-live: shows complex modular source, app.backend.call('ppt.generate', ...), history/storage fallback, and theme/locale synchronization; use it for intelligent backend and large UI patterns.
builtin-harmony-dev / builtin-remotion-live: listen for productAppRuntimeRouteChange and refresh facts by workspace route; use them for workspace-aware apps.
Verification
After changes, collect evidence appropriate to the change:
- The package can be read and the lock can be refreshed.
ValidateProductAppPackage has no fatal error.
- Preview/runtime observation shows the UI is non-empty and key interactions work.
- Permissions, data, AI/backend behavior have matching runtime evidence; mark unrun capabilities as unverified.
1---2name: product-app-api3description: Sparo OS Product App runtime API guidance. Use when writing or reviewing Product App ui.js, worker.js, source_manifest.json, permissions, window.app APIs, app.ai, app.backend service actions, storage, fs/shell/net/os/dialog/clipboard calls, runtime events, or Product App runtime debugging.4---56# Product App API Skill78Use this skill for Product App runtime code and platform capability work. Prefer current tool output, scaffold files, and package validation results over memory.910## Runtime Model1112- `ui.js` runs in the iframe/browser environment. It uses the DOM, standard browser APIs, ESM imports, and `window.app`.13- `worker.js` is optional backend logic running in a separate JavaScript Worker host. Custom `app.call(method, params)` calls only methods exported by `worker.js`.14- `source_manifest.json` describes entrypoints: `uiEntry`, optional `workerEntry`, `styleEntries`, and `buildMode`. Current built-in apps use `nativeEsm`.15- `esm_dependencies.json` is the browser import-map dependency array, not an object. Write `[]` when there are no dependencies.16- Extra source files are read from `source/`. Do not handwrite `private_surface_sources`; edit package source files.17- A Product App surface `implementationRef` must be `app://<app-id>@<version>/surfaces/<surface-id>`. Do not fall back to old `bundle://surface-components/...` refs.1819## `window.app` Capabilities2021The runtime adapter exposes `window.app`. A common alias is:2223```javascript24const runtime = () => window.app || {};25```2627Basic properties:2829- `app.appId`30- `app.appDataDir`31- `app.workspaceDir`32- `app.theme`33- `app.locale`34- `app.platform`35- `app.mode`3637Host capabilities:3839- `app.fs.readFile/writeFile/readdir/stat/mkdir/rm/copyFile/rename/appendFile`40- `app.shell.exec(command, opts)`41- `app.net.fetch(url, opts)`42- `app.os.info()`43- `app.storage.get(key)` / `app.storage.set(key, value)`44- `app.dialog.open/save/message`45- `app.clipboard.writeText/readText`46- `app.ai.complete/chat/cancel/getModels`47- `app.backend.call/cancel/status/cancelRun/cancelStaleRuns/turnText/onEvent/offEvent`48- `app.host.fillChatInput(text)`49- `app.deck.renderPage(opts)`50- `app.log.debug/info/warn/error`51- `app.onActivate/onDeactivate/onThemeChange/onLocaleChange`52- `app.i18n.t(key, params, fallback)`, `app.i18n.setMessages(messages)`, `app.i18n.onChange(fn)`53- `app.t(localeTable, fallback)`: selects text from `{ "zh-CN": "...", "en-US": "..." }` using the current locale.54- `app.on(event, fn)` / `app.off(event, fn)`: subscribes to runtime events, `worker:*`, `backend:event`, and similar events.55- `app.ui`: runtime UI Kit. UI details belong in `product-app-ui-polish`.5657The current code does not expose an `app.agentic.*` namespace. Use `app.backend.*` for app-private Agent Component or Bridge Component bindings. Use `app.ai.*` only for direct model capability.5859## UI And Worker Boundary6061- Put DOM, theme, locale, event binding, and user interaction in `ui.js`.62- Enter host capabilities through `window.app` for files, shell, network, system info, storage, AI, and backend calls. Do not import Sparo internal services directly.63- Write `worker.js` only for custom business backend logic, and declare `permissions.node.enabled = true`.64- When `node.enabled = false`, host primitives can still be used, but custom `app.call(...)` methods cannot.65- Worker code must not assume DOM, `window.app`, or browser globals exist.6667## Permissions6869Current surface runtime permissions shape:7071```json72{73 "fs": { "read": ["{appdata}"], "write": ["{appdata}"] },74 "shell": { "allow": ["git"] },75 "net": { "allow": ["api.example.com"] },76 "node": { "enabled": false, "max_memory_mb": 256, "timeout_ms": 30000 },77 "ai": {78 "enabled": true,79 "allowed_models": ["primary", "fast"],80 "max_tokens_per_request": 4096,81 "rate_limit_per_minute": 2082 }83}84```8586Rules:8788- Default to minimum permissions. If the app does not need user files, use only `{appdata}` or omit `fs`.89- Path scopes include `{appdata}`, `{workspace}`, `{home}`, `{user-selected}`, and absolute paths. `{workspace}` resolves only when a workspace is bound.90- `shell.allow` is a command-name allowlist. For git capability, declare `"git"`; do not create a general shell channel.91- `net.allow` is a domain allowlist. `"*"` means all network access and should not be the default.92- `permissions.ai.enabled` controls `app.ai.*`. If `allowed_models` is absent, rely only on `primary`.93- Do not expand permissions to work around missing platform capability. Internal services such as WorkspaceService, GitService, TerminalService, LSP, Browser, Computer Use, and Config are not Product App APIs.9495## AI And Intelligent Backend9697`app.ai.*` reuses the host AI client and requires no app API key:9899```javascript100const result = await app.ai.complete('Summarize the current input', {101 systemPrompt: 'Output only a concise conclusion.',102 model: 'fast',103 maxTokens: 800,104 temperature: 0.2,105});106107const handle = await app.ai.chat(108 [{ role: 'user', content: 'Generate three options' }],109 {110 model: 'primary',111 onChunk: (chunk) => appendText(chunk.text || ''),112 onDone: () => setBusy(false),113 onError: (error) => showError(String(error?.message || error)),114 },115);116```117118`app.backend.*` calls declared backend bindings and is the right path for app-private Agent Components or Bridge Components:119120```javascript121const run = await app.backend.call('ppt.generate', input, {122 entityId: state.deckId,123 idempotencyKey: `generate:${state.deckId}:${Date.now()}`,124});125126app.backend.onEvent((event) => {127 if (event.actionRunId === run.actionRunId) updateProgress(event);128});129```130131Key backend binding fields:132133- `id`134- `kind`: `agentComponent` or `bridgeComponent`135- `componentId`136- Optional `capabilityId`137- `role`138- `sessionPolicy`: `ephemeral`, `persistent`, `perEntity`, `shared`139- `memoryScope`: `none`, `appInstance`, `entity`, `agentComponent`140- `actions`: `{ name, inputSchema, outputSchema, allowStatePatch }[]`141142If the Product App needs durable intelligent behavior, prefer an app-private Agent Component exposed through backend binding actions. Do not use a raw Agentic session as internal application state.143144## Built-In App References145146- `builtin-ppt-live`: shows complex modular source, `app.backend.call('ppt.generate', ...)`, history/storage fallback, and theme/locale synchronization; use it for intelligent backend and large UI patterns.147- `builtin-harmony-dev` / `builtin-remotion-live`: listen for `productAppRuntimeRouteChange` and refresh facts by workspace route; use them for workspace-aware apps.148149## Verification150151After changes, collect evidence appropriate to the change:152153- The package can be read and the lock can be refreshed.154- `ValidateProductAppPackage` has no fatal error.155- Preview/runtime observation shows the UI is non-empty and key interactions work.156- Permissions, data, AI/backend behavior have matching runtime evidence; mark unrun capabilities as unverified.