Official skills repository for OS Loop AI. Skills are declarative, browser-executed modules that extend the agent's capabilities.
skills/
index.json # Registry of all skills
{skill-slug}/
manifest.json # Skill manifest (required)
module.ts # Skill implementation (required)
docs.md # User-facing documentation (optional)
config-schema.json # Configuration JSON Schema (optional)
Each skill lives in its own directory under skills/ using a kebab-case slug (e.g., web-summary, github-issue-tracker).
The manifest declares everything about a skill: what it does, what it needs, and how it behaves. It combines top-level metadata fields with the SkillManifest v2.0 schema.
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Skill slug (matches directory) |
description |
string | Yes | Short description of the skill |
version |
string | Yes | Semver version (e.g., 1.0.0) |
author |
string | Yes | Author name or organization |
| Field | Type | Required | Description |
|---|---|---|---|
schemaVersion |
"2.0" |
Yes | Always "2.0" |
capabilities |
string[] |
Yes | List of capabilities the skill provides |
requiredBindings |
SkillRequiredBinding[] |
Yes | Secrets or config the skill needs at install time |
permissions |
SkillPermission[] |
Yes | Permissions the skill requires |
inputSchema |
object |
Yes | JSON Schema for the skill's input (must have type: "object") |
outputSchema |
object |
Yes | JSON Schema for the skill's output (must have type: "object") |
compatibilityRequirements |
string[] |
Yes | Runtime compatibility requirements (see Valid Values) |
oauth |
SkillOAuthDefinition[] |
No | OAuth provider configurations |
llmUsage |
SkillLlmUsageDeclaration[] |
No | Declared LLM usage purposes |
wasm |
SkillWasmDefinition[] |
No | WASM module declarations |
views |
SkillViewDeclaration[] |
No | UI view declarations |
tools |
SkillToolDeclaration[] |
No | Tool registrations |
lifecycleHooks |
SkillLifecycleHook[] |
No | Declared lifecycle hooks |
sandbox |
SkillSandboxRequirements |
No | Sandbox configuration (defaults provided) |
requiredMcpServers |
SkillRequiredMcpServer[] |
No | Required MCP servers |
requiredMcpTools |
SkillRequiredMcpTool[] |
No | Required MCP tools |
supportedPlatforms |
BridgePlatform[] |
Yes | Platforms the skill supports: "macos", "windows", "linux". Empty array means all platforms. |
bridgeRequirement |
BridgeRequirement |
Yes | Bridge dependency: "never", "optional", or "required" |
workspaceSupport |
SkillWorkspaceSupport |
Yes | Workspace support: "none", "optional", or "required" |
workspaceSchemaVersion |
string | null |
Yes | Semver version of the workspace state schema. Use null when workspaceSupport is "none". |
longRunningSupport |
string |
Yes | Long-running support: "none", "optional", or "required" |
userInputSupport |
boolean |
Yes | Whether the skill requests user input during execution |
artifactVersioningSupport |
boolean |
Yes | Whether the skill creates versioned artifacts |
executionMode |
SkillExecutionMode |
Yes | Execution mode: "declarative", "flow", or "agentic" |
agenticConfig |
SkillAgenticConfig |
Yes | Agentic execution configuration (see below). Must be present even for non-agentic skills with enabled: false. |
{
"kind": "network | storage | vault | conversation | llm | browser | wasm | filesystem",
"scope": "string — describes the permission scope",
"description": "string — human-readable reason",
"browserPermissions": ["clipboard-read", "clipboard-write", "notifications", "geolocation", "camera", "microphone", "storage-persistent"]
}{
"name": "string — binding key",
"scope": "string — what the binding provides",
"description": "string — human-readable explanation",
"required": true,
"oauthProviderId": "string — optional, links to an oauth entry"
}{
"providerId": "string",
"displayName": "string",
"grantType": "authorization_code | client_credentials",
"authorizationUrl": "string",
"tokenUrl": "string",
"scopes": ["string"],
"pkceRequired": true,
"clientIdBinding": "string — references a requiredBinding",
"clientSecretBinding": "string — optional",
"redirectPath": "string",
"redirectMode": "popup | same-window",
"supportedEnvironments": ["browser-production", "browser-development"],
"responseMode": "query | fragment",
"clientAuthenticationMethod": "client_secret_post | client_secret_basic | none"
}{
"purposeId": "string — unique ID referenced in module code",
"kind": "classification | generation | extraction | summarization | analysis | structured-output | custom",
"description": "string",
"estimatedTokenBudget": 2000,
"modelPreference": "string | null",
"temperatureHint": 0.3
}{
"name": "string",
"description": "string",
"inputSchema": {},
"outputSchema": {},
"handlerExport": "string — name of the exported handler function in module.ts"
}{
"viewId": "string",
"kind": "panel | card | dialog | inline",
"entryComponent": "string",
"description": "string"
}{
"moduleId": "string",
"sourceKind": "inline_base64 | url | build_from_source",
"sourceUrl": "string — optional",
"sourceLanguage": "rust | c | cpp | assemblyscript",
"sourceRepository": "string — optional",
"buildCommand": "string — optional",
"exports": ["string"],
"memoryLimitMB": 64,
"auditHash": "string | null"
}{
"isolation": "strict | standard",
"maxExecutionMs": 30000,
"maxMemoryMB": 128,
"allowedGlobals": [],
"networkRules": [
{
"urlPattern": "https://api.example.com/*",
"methods": ["GET", "POST", "PUT", "PATCH", "DELETE"],
"description": "string"
}
]
}{
"enabled": false,
"requiresWorkspace": false,
"supportsBackgroundExecution": false,
"supportsRoleBasedExecution": false,
"maxStepsPerRun": null,
"defaultStepBudget": null
}| Field | Type | Description |
|---|---|---|
enabled |
boolean |
true for agentic skills, false for declarative/flow |
requiresWorkspace |
boolean |
If true, workspaceSupport must be "required" |
supportsBackgroundExecution |
boolean |
Whether the skill can run in the background |
supportsRoleBasedExecution |
boolean |
Whether the skill switches between named roles |
maxStepsPerRun |
number | null |
Hard step limit (halts execution when reached) |
defaultStepBudget |
number | null |
Soft step limit (auto-checkpoint when reached) |
For non-agentic skills, set enabled: false and all other fields to their zero values.
// MCP Server
{ "name": "string", "description": "string", "urlPattern": "string — optional" }
// MCP Tool
{ "serverName": "string", "toolName": "string", "description": "string" }The skill implementation file. Must export an execute function and may export optional lifecycle hooks.
export async function execute(
args: Record<string, unknown>,
host: SkillHostCapabilities,
): Promise<unknown> {
// Skill logic here
}args— Input matching the manifest'sinputSchemahost— Host capabilities injected by the runtime- Returns — Output matching the manifest's
outputSchema
export async function onInstall(host: SkillHostCapabilities): Promise<void> {}
export async function onUninstall(host: SkillHostCapabilities): Promise<void> {}
export async function onEnable(host: SkillHostCapabilities): Promise<void> {}
export async function onDisable(host: SkillHostCapabilities): Promise<void> {}
export async function onConfigure(config: Record<string, unknown>, host: SkillHostCapabilities): Promise<void> {}
export async function onUpgrade(fromVersion: string, host: SkillHostCapabilities): Promise<void> {}The host object provides the following APIs:
| Capability | Methods |
|---|---|
host.llm |
complete(req) — LLM completion with purposeId, messages, temperature, maxTokens |
host.vault |
resolveBinding(name) — Resolve a secret by binding name |
host.network |
fetch(url, init?) — HTTP fetch (enforced by sandbox networkRules) |
host.storage |
get(key), set(key, value), delete(key), list() — Key-value store scoped to the skill |
host.events |
emitProgress(progress, message), emitCustom(type, payload) — Emit runtime events |
host.tools |
registerTool(def, handler), callTool(name, args) — Tool registration and invocation |
host.oauth |
authorize(providerId), getToken(providerId), refresh(providerId), authStatus(providerId) |
host.wasm |
instantiate(moduleId) — Instantiate a declared WASM module |
host.log |
debug(msg, data?), info(msg, data?), warn(msg, data?), error(msg, data?) |
host.filesystem |
openFile(), openDirectory(), readFile(handleId), writeFile(handleId, data), listHandles() — Optional |
host.workspace |
getState(), setState(state), createArtifact(artifact), setPhase(phase) — Optional, present when a workspace is active |
host.skillId |
string — The skill instance ID |
host.executionId |
string — Unique ID for this execution |
host.runId |
string — The agent run ID |
Standard JSON Schema defining user-configurable settings for the skill. The root type must be "object".
{
"type": "object",
"properties": {
"apiEndpoint": {
"type": "string",
"description": "Custom API endpoint URL",
"default": "https://api.example.com"
},
"maxRetries": {
"type": "number",
"description": "Maximum number of retry attempts",
"default": 3
}
}
}Free-form Markdown documentation shown to users in the skill detail view. Should cover what the skill does, its inputs/outputs, required permissions, and usage examples.
The skills/index.json file is a registry of all skills in the repository. When present, the runtime uses it directly instead of scanning directories.
[
{
"name": "web-summary",
"description": "Fetches a web page and produces an LLM-generated summary",
"version": "1.0.0",
"author": "OS Loop",
"folderPath": "skills/web-summary",
"tags": ["web", "llm", "summarization"],
"compatibilityRequirements": []
}
]Each entry:
| Field | Type | Required | Description |
|---|---|---|---|
name |
string | Yes | Skill slug |
description |
string | Yes | Short description |
version |
string | Yes | Semver version |
author |
string | Yes | Author name |
folderPath |
string | Yes | Path from repo root (e.g., skills/web-summary) |
tags |
string[] | Yes | Searchable tags (registry-level, not a manifest field) |
supportedPlatforms |
string[] | No | Platform identifiers (mirrors manifest) |
bridgeRequirement |
string | No | Bridge dependency (mirrors manifest) |
workspaceSupport |
string | No | Workspace support level (mirrors manifest) |
workspaceSchemaVersion |
string | No | Workspace state schema version (mirrors manifest) |
compatibilityRequirements |
string[] | No | Runtime compatibility requirements |
longRunningSupport |
string | No | Long-running support level (mirrors manifest) |
userInputSupport |
boolean | No | Whether the skill requests user input |
artifactVersioningSupport |
boolean | No | Whether the skill creates versioned artifacts |
executionMode |
string | No | Execution mode (mirrors manifest) |
Note on
tagsvsrequiredBindings: Tags are registry-level metadata used for search and discovery — they live inindex.json, not in the manifest. Secret bindings are declared asrequiredBindingsin the manifest (notsecretBindings).
The OS Loop runtime resolves skills from GitHub repositories through this pipeline:
- Parse repository URL — Extracts
owner,repo,branch, andskillsDirectory(defaults toskills) - Fetch index — Checks for
{skillsDirectory}/index.jsonfirst - Fallback to directory scan — If no index exists, uses the GitHub Contents API to enumerate subdirectories
- Fetch artifacts — For each skill, fetches via raw.githubusercontent.com:
{skillsDirectory}/{slug}/manifest.json(required){skillsDirectory}/{slug}/module.ts(required){skillsDirectory}/{slug}/docs.md(optional){skillsDirectory}/{slug}/config-schema.json(optional)
- Validate and register — The runtime validates the manifest, compiles the TypeScript module, and registers the skill
-
Create a directory under
skills/with a kebab-case slug:skills/my-new-skill/ -
Create
manifest.jsonwith all required fields:- Set
schemaVersionto"2.0" - Declare
capabilities,permissions,inputSchema,outputSchema - Declare all execution-model fields explicitly:
executionMode,workspaceSupport,workspaceSchemaVersion,longRunningSupport,userInputSupport,artifactVersioningSupport,supportedPlatforms,bridgeRequirement,agenticConfig - Declare
llmUsageif the skill callshost.llm.complete() - Declare
requiredBindingsif the skill needs secrets - Configure
sandbox.networkRulesif the skill makes HTTP requests
- Set
-
Create
module.tswith theexecuteexport:- Accept
argsmatching yourinputSchema - Use
hostcapabilities as needed - Return output matching your
outputSchema
- Accept
-
Add documentation (optional but recommended):
docs.md— User-facing documentationconfig-schema.json— If the skill accepts user configuration
-
Register in
skills/index.json:{ "name": "my-new-skill", "description": "What the skill does", "version": "1.0.0", "author": "Your Name", "folderPath": "skills/my-new-skill", "tags": ["relevant", "tags"] } -
Submit a pull request
The examples/ directory contains reference skill skeletons for each execution mode:
| Example | Execution Mode | Key Features |
|---|---|---|
examples/declarative-skill/ |
declarative |
Minimal complete skill, no workspace, no bridge |
examples/flow-skill/ |
flow |
Multi-step with pause, user input, and checkpoint |
examples/agentic-skill/ |
agentic |
Full agentic loop with phases, roles, artifacts, step budgets |
examples/workspace-aware-skill/ |
declarative |
Optional workspace persistence for cross-session state |
examples/background-agentic-skill/ |
agentic |
Background execution with heavy checkpointing |
Each example includes a manifest.json, module.ts, and docs.md with a working implementation. See docs/execution-modes.md for detailed documentation of each mode.
Skills that need to execute commands on the host machine (installing tools, running CLI utilities) do so through the OS Loop Bridge. The bridge is the single permission authority — it manages whitelist matching, user approval prompts, and rejection decisions.
Important: The bridge may reject execution of any command, including those from official skills in this repository. Official status does not grant automatic execution privileges. Skills must always handle rejection gracefully regardless of their origin.
- Declare dependency: Skills needing system commands must include
"system_tools"in theircompatibilityRequirementsarray in bothmanifest.jsonandindex.json. They must also setbridgeRequirementto"required"and list theirsupportedPlatforms. - Provide provenance context: Every execute or install request must include a
contextblock so the bridge can trace where the request originated:{ "context": { "source": "skill", "skillId": "host.skillId value", "taskId": "host.executionId value", "runId": "host.runId value" } } - Provide template fields: For commands that may be repeated, include
commandTemplate,argsTemplate, andworkingDirectoryTemplatealongside the resolved values. Templates enable whitelist matching without storing resolved secrets. - Handle rejections: The bridge may reject any request. Skills must handle
accepted: falseresponses gracefully and surface the rejection reason to the agent. - Install always requires explicit approval: Tool installations are never whitelisted — the bridge always prompts the user. Do not retry automatically after a rejection. If a user denies an install, inform them and offer alternatives.
- Periodic task approval: When a skill's functionality is used within a periodic (scheduled) task that involves system tool operations, the task itself must be approved at creation or modification time. This is enforced by the web app's scheduler, not the bridge.
{
"command": "git",
"args": ["status"],
"commandTemplate": "git",
"argsTemplate": ["status"],
"workingDirectory": "/home/user/project",
"workingDirectoryTemplate": "/home/user/project",
"reason": "Check repository status",
"requestId": "req-abc-123",
"timeoutMs": 300000,
"context": {
"source": "skill",
"skillId": "my-git-skill",
"taskId": "exec-001",
"runId": "run-001"
}
}{
"accepted": true,
"runId": "run-xyz-456",
"decision": {
"source": "whitelist",
"message": "Matched existing whitelist rule"
}
}{
"accepted": false,
"reason": {
"code": "rejected_by_user",
"message": "User denied the command execution"
}
}Rejection codes: rejected_by_user, bridge_policy_denied, invalid_request.
If a skill uses bridge-backed execution, document the reason in docs.md — explain which commands it runs and why system access is necessary.
Skills can declare platform and bridge requirements to ensure they are only installed and executed in compatible environments.
An array of platform identifiers: "macos", "windows", "linux". An empty array (or absent field) means the skill works on all platforms. The web app resolves the current platform using bridge system info when available, or browser user-agent detection as a fallback.
"never"(default): The skill runs entirely in the browser. No bridge needed."optional": The skill works without the bridge but has enhanced features when it is connected (e.g., filesystem access, command execution)."required": The skill cannot function without the bridge. Installation is allowed but execution is blocked if the bridge is not connected.
Skills declare runtime requirements using the compatibilityRequirements array. The web app validates these at install and execution time.
Bridge capabilities (require OS Loop Bridge connection):
| Value | Description |
|---|---|
system_tools |
Tool discovery, installation, command execution |
system_info |
OS platform, architecture, shell, package managers |
filesystem |
Native filesystem access |
mcp_proxy |
MCP server proxying |
awake |
System sleep prevention |
tool_install_recipes |
Curated install recipes |
command_permissions |
Whitelist-based command permissions |
Browser capabilities (available in all environments):
| Value | Description |
|---|---|
web-crypto |
Web Crypto API (SubtleCrypto) |
indexeddb |
IndexedDB storage |
web-workers |
Web Worker support |
local-storage |
localStorage API |
fetch |
Fetch API |
Skills declaring bridge capabilities as requirements must also set bridgeRequirement to "required" (or "optional" if the capability enhances but is not essential).
When a skill's command execution requires a secret (API key, token, etc.), use the placeholder syntax in command arguments or environment variables:
__OSL_SECRET[<secretId>]__
- The
secretIdis the UUID of the secret stored in the user's local Vault. - The web runtime resolves placeholders to actual values before sending to the bridge.
- The bridge receives resolved values — it never sees placeholder IDs.
- Command output (stdout/stderr) is automatically redacted by the web runtime to prevent secret leakage.
- Skill manifests that reference
__OSL_SECRET[...]__placeholders must declare correspondingrequiredBindings.
Skills must never hardcode actual secret values in module code, documentation, or examples.
Both fields are optional. Skills without these fields are treated as cross-platform with no bridge requirement — matching the behavior of all existing skills.
Run npm run validate to check all skills against the manifest standard. The validator verifies platform values, bridge requirement consistency, module existence, and absence of raw secrets in documentation.
Skills that manage persistent, structured state across sessions can declare workspace support. Workspaces are runtime-owned by os-loop-ai — they are created, persisted, and managed in the browser via IndexedDB. Skills do not manage workspace storage directly.
Declare workspaceSupport when your skill:
- Maintains structured state that persists across multiple conversations or runs (e.g., a project tracker, a multi-step research workflow)
- Produces artifacts that need lifecycle management (draft, pending approval, approved, rejected, superseded)
- Needs to track phases, roles, or approval linkage as first-class persistent data
Do not use workspaces for:
- Simple stateless skills (use
"none", the default) - State that fits in a single key-value store entry (use
host.storageinstead) - Ephemeral state that only matters within a single conversation
{
"workspaceSupport": "required",
"workspaceSchemaVersion": "1.0.0"
}workspaceSupport:"none"(default) — skill does not use workspaces"optional"— skill can use workspaces for enhanced functionality but works without one"required"— skill requires an active workspace to function
workspaceSchemaVersion: semver string that declares the structure of the workspacestateandmetadataobjects. Must be set whenworkspaceSupportis"optional"or"required".
| Use case | workspaceSupport |
Why |
|---|---|---|
| Stateless computation (calculator, text transform) | "none" |
No state to persist. Every invocation is self-contained. |
| Note-taking, data collection, iterative research | "optional" |
Works in a single session without a workspace, but benefits from cross-session persistence. |
| Project tracking, multi-phase workflows, artifact production | "required" |
The skill's core value depends on structured persistent state. Without a workspace, there is nothing to operate on. |
Rule of thumb: If your skill's execute() function reads or writes host.workspace, it needs "optional" or "required". If removing the workspace would make the skill meaningless (not just less useful), use "required".
The agent uses the workspace_manage runtime tool to manage workspaces. This tool is automatically available when workspace-aware skills are installed.
Supported actions:
| Action | Description |
|---|---|
create |
Creates a new workspace for a skill |
get |
Retrieves workspace details |
list |
Lists workspaces for a skill |
update |
Updates workspace metadata or state |
pause |
Pauses a workspace (excluded from active snapshot) |
resume |
Resumes a paused workspace |
archive |
Archives a workspace (soft delete) |
set_phase |
Sets the current workflow phase |
set_role |
Sets the current agent role |
create_artifact |
Creates a tracked artifact in the workspace |
update_artifact |
Updates artifact content or status |
list_artifacts |
Lists artifacts for a workspace |
Context fallback: When a workspace is active in the current run, the workspaceId is automatically available in the tool execution context. Explicit workspaceId in args takes precedence over the context value.
Artifacts produced by skills follow a status lifecycle:
draft → pending_approval → approved
→ rejected
→ superseded
- draft: Initial state when created via
host.workspace.createArtifact(). - pending_approval: Submitted for review. The runtime links this to an approval ref.
- approved / rejected: Terminal states set through the approval system.
- superseded: A newer artifact replaces this one.
Skills create artifacts; the runtime and user manage their lifecycle.
Artifacts support version chains. When an artifact is superseded, the runtime links the new version to the old one:
| Field | Type | Description |
|---|---|---|
parentArtifactId |
UUIDv7 | null |
Previous version in the chain (null for first version) |
version |
number |
Incrementing version within the chain (1-based) |
createdByRunId |
UUIDv7 | null |
Which run produced this artifact |
createdByRole |
string | null |
Which agent role produced this artifact |
The runtime provides version chain queries:
getVersionChain(artifactId)— returns the full chain oldest-firstgetLatestVersion(workspaceId, type)— returns the highest-version non-superseded artifactsupersede(currentId, newArtifact)— atomically marks the current artifact as superseded and links the new one
Skills create artifacts via host.workspace.createArtifact(). The runtime automatically populates createdByRunId and createdByRole from the current execution context. Skills do not need to manage version chains directly — the runtime handles this when artifacts are superseded.
Workspaces use optimistic concurrency control via a lockVersion field. Every write to workspace state increments the version, and stale writes (where the caller's version doesn't match the current version) are rejected with a ConflictError.
This prevents silent data corruption when multiple browser tabs modify the same workspace. Skills do not need to handle ConflictError directly — the runtime catches it and surfaces a user-facing error.
The state object in a workspace is skill-defined. Declare the schema version in workspaceSchemaVersion and document the shape.
Example — project-tracker state (v1.0.0):
{
"projectName": "Q2 Launch",
"tasks": [
{ "id": "task-abc", "title": "Design API", "status": "done", "createdAt": "2025-01-15T10:00:00Z", "updatedAt": "2025-01-16T14:00:00Z" },
{ "id": "task-def", "title": "Implement auth", "status": "in-progress", "createdAt": "2025-01-16T09:00:00Z", "updatedAt": "2025-01-17T11:00:00Z" }
],
"createdAt": "2025-01-15T09:00:00Z"
}Example — note-organizer state (v1.0.0):
{
"notes": [
{ "id": "note-xyz", "title": "API design notes", "content": "...", "tags": ["api", "design"], "createdAt": "2025-01-15T10:00:00Z" }
]
}When evolving state schemas, bump workspaceSchemaVersion and handle migration in your module's execute() function.
The validator enforces:
workspaceSupportmust be one of:"none","optional","required"workspaceSchemaVersionmust be a string or null- If
workspaceSupportis"optional"or"required",workspaceSchemaVersionshould be set (warning if missing)
| Skill | workspaceSupport |
Description |
|---|---|---|
quick-calculator |
"none" |
Stateless math and unit conversion |
note-organizer |
"optional" |
Notes with optional persistence |
project-tracker |
"required" |
Multi-phase project management with artifacts |
startup-product-studio |
"required" |
Agentic multi-project product development with role-based execution |
The OS Loop runtime owns the lifecycle of long-running skill work. Skills that perform multi-step, resumable, or background work must not invent their own ad hoc persistence for run state, checkpoints, or event logs.
| Concern | Owner | Mechanism |
|---|---|---|
| Run lifecycle (start, progress, pause, resume, complete, fail) | Runtime | WorkspaceRun persistence + state machine |
| Checkpoints (snapshots for safe resumption) | Runtime | WorkspaceRunCheckpoint store |
| Event logs (structured history of run activity) | Runtime | WorkspaceRunEvent store |
| User input requests (blocking prompts during a run) | Runtime | UserInputRequest store |
| Bridge job tracking (async system commands) | Runtime | BridgeJobRef store |
| Bridge job concurrency control | Bridge | Global concurrency limit with automatic queuing |
| Recovery after reload, tab closure, bridge interruption | Runtime | Reconciliation service |
- Report progress via host capabilities:
host.run.updateProgress({ phase, step, percent, message }). - Request checkpoints via
host.run.createCheckpoint()at meaningful milestones so the runtime can resume from them. - Request user input via
host.run.requestUserInput({ title, message, inputSchema })instead of inventing custom prompt flows. - Store workspace-scoped application data (notes, project tasks, artifacts) in workspace state as usual — this is skill-owned data, not run state.
- Resume from checkpoints by reading the latest checkpoint from the host when restarted, and determining where to pick up based on the checkpoint's
currentPhase,currentStep, andrunStateSnapshot.
Skills with workspaceSupport: "required" and longRunningSupport enabled that perform multi-step workflows must model blocking points and recovery explicitly:
-
Model blocking points as structured requests, not prompt text. When the skill needs a user decision mid-run, call
host.run.requestUserInput({ title, message, inputSchema }). This transitions the run towaiting_user_inputand surfaces the prompt in the inbox and workspace detail view. Do not embed decision logic inside freeform LLM prompts — the runtime cannot track or resume from those. -
Use artifacts and phases to surface intermediate work. Instead of accumulating results inside hidden conversation context, create artifacts via
host.workspace.createArtifact()and advance the phase viahost.workspace.setPhase(). This makes progress visible to the user and inspectable by the agent through therun_manageandworkspace_managetools. -
Assume interruptions and recovery are normal runtime events. A long-running skill execution may be interrupted by browser reload, bridge disconnection, user-initiated pause, or session expiry. Design the skill so it can resume from the last checkpoint: persist meaningful progress via
host.run.createCheckpoint()at each milestone, and on restart, read the latest checkpoint to determine where to continue. -
Do not conflate workspace runs with periodic/scheduled tasks. A workspace run is a single continuous execution of a skill within a workspace context, with explicit waiting states and recovery semantics. A scheduled task is a recurring trigger that creates agent runs on a schedule. They serve different purposes and use different runtime APIs.
The bridge enforces a global concurrency limit on system jobs (commands and tool installations). Skills that launch bridge jobs benefit from this automatically:
- Jobs that exceed the bridge's concurrency limit are queued and start automatically when capacity becomes available.
- The bridge emits
bridge.job.createdandbridge.job.updatedevents as jobs transition throughQueued → Running → Completed/Failed/Terminated. - The runtime reconciler detects bridge job status changes (including
Queued → Running) and updates localBridgeJobRefrecords accordingly. - Skills that launch multiple bridge jobs (e.g., parallel installs or chained commands) will see them managed concurrently up to the bridge limit, with overflow queued transparently.
Skills do not need to implement queuing, throttling, or concurrency control for bridge jobs — the bridge manages this independently.
When a run terminates abnormally (bridge restart, timeout, browser reload), the runtime marks it as dirty — meaning the workspace may be in an inconsistent state. Dirty runs with partial execution are flagged as reviewRequired, which blocks new runs on the same workspace until the user takes one of these actions:
| Action | Effect |
|---|---|
| Acknowledge | Clears dirty + reviewRequired. User accepts the current state as-is. |
| Retry from checkpoint | Creates a new run from the last checkpoint. Clears the old run's dirty state. |
| Discard | Clears dirty + reviewRequired. Treats the partial state as abandoned. |
The runtime classifies dirty state based on the bridge's recoveryHint:
"bridge_restart"or"timeout"→ dirty (something ran, side-effects unknown)"spawn_failure"→ not dirty (nothing actually executed)null→ dirty (conservative default)
Skills do not participate in dirty-state classification or resolution — this is entirely runtime-owned. Skills should focus on creating checkpoints at meaningful milestones so the runtime has good recovery points.
- Do not persist run status, progress, or step counters independently.
- Do not implement custom recovery logic for browser reload or bridge disconnection.
- Do not poll the bridge directly for job status — the runtime reconciler handles this.
- Do not assume a run completed successfully without the runtime confirming it.
- Do not implement concurrency throttling for bridge jobs — the bridge manages this.
The OS Loop runtime provides dedicated user-facing management surfaces that automatically present workspace and run information to the user. Skill authors do not need to build their own UI — the runtime derives everything from standard API usage.
A global inbox surfaces items requiring user attention. The runtime automatically derives inbox entries from:
- Pending
UserInputRequestitems (created viahost.run.requestUserInput()) — shown as actionable cards the user can answer directly. - Pending approval requests — shown with a link to the approval center.
- Recoverable run failures — shown with a retry action.
- Bridge rejections and bridge-unavailable states — shown with reconnect or workspace navigation actions.
- Recently completed runs — shown as informational items.
Skills that use host.run.requestUserInput() get their prompts surfaced in both the inbox and the workspace detail view. No additional skill-side work is needed.
Each workspace has a dedicated detail page showing:
- Workspace metadata: name, description, status, current phase and role.
- Active run panel: status badge, progress bar, current step, waiting-for indicator.
- Run controls: Continue, Pause, Cancel, and Retry buttons — available based on the current run state machine transitions.
- Pending user input forms: inline forms for answering
UserInputRequestitems. - Artifacts list: all workspace artifacts with their approval status.
- Bridge jobs: active and completed bridge jobs with command and status.
- Run history: past completed/failed/cancelled runs.
- Skills that use
host.workspace.*(state, artifacts) andhost.run.*(progress, checkpoints, user input) automatically benefit from these surfaces. - Skills should produce structured state, artifacts, and events suitable for these surfaces — but they already do so via the existing host APIs.
- Skills should not implement their own inbox, notification, or dashboard logic. The runtime handles this.
- Long-running workspace-aware skills should assume the runtime provides: workspace page, inbox, and explicit continue/pause/retry actions.
- Slug format: kebab-case, matching
^[a-z][a-z0-9-]*$ - Versioning: Strict semver (
major.minor.patch) - Schemas:
inputSchemaandoutputSchemamust havetype: "object"at the root - No stubs: Every
module.tsmust contain a complete, working implementation - Permissions: Declare all permissions the skill requires — the runtime enforces them
- LLM usage: Every call to
host.llm.complete()must have a matchingllmUsageentry in the manifest with the samepurposeId - Network rules: Every URL the skill fetches must be covered by a
sandbox.networkRulesentry - Secret kinds: When declaring
requiredBindings, use appropriate secret kinds:token,password,api_key,oauth_client,oauth_refresh_token,certificate,generic - Platform & bridge: Declare
supportedPlatformsandbridgeRequirementin bothmanifest.jsonandindex.json. Bridge-required skills must include"system_tools"incompatibilityRequirements
Skills declare an executionMode in their manifest that determines runtime orchestration behavior. See docs/execution-modes.md for the full authoring guide, including how to describe skills so the main agent can reason about them correctly.
| Mode | Description | host.run |
|---|---|---|
declarative (default) |
Single invocation, no loop | Not available |
flow |
Step-driven, deterministic, can pause/checkpoint/request input | Available |
agentic |
Supervised sub-run with step budgets, checkpoints, pause/resume | Available with step tracking |
The main OS Loop AI agent uses executionMode, agenticConfig, workspaceSupport, and bridgeRequirement to decide how to launch, supervise, and explain skills to the user. Always declare these fields explicitly for flow and agentic skills. See Describing Skills for Agent Reasoning for authoring guidelines.
All existing skills in this repository use declarative mode.
MIT