npx skills add ...
npx skills add iii-hq/iii --skill iii-core-primitives
Use when registering iii functions, binding triggers, selecting sync/void/enqueue invocation, creating workers, inspecting the live worker registry, installing registry workers, authoring custom triggers, moving channel data, or adapting external HTTP functions across TypeScript, Python, and Rust.
npx skills add iii-hq/iii --skill iii-core-primitives
iii has three top-level primitives:
orders::validateUse :: in function IDs, leading slashes in HTTP api_path, and expression for cron config.
Register local handlers when you control the implementation. Register HTTP-invoked functions when iii should call an existing external endpoint.
| Shape | Use for |
|---|---|
registerFunction(id, handler, options?) | Local worker code |
registerFunction(id, HttpInvocationConfig, options?) | Existing HTTP services |
registerTrigger({ type, function_id, config, metadata? }) | Binding an event source |
trigger({ function_id, payload, action?, timeout? }) | Calling any function by ID |
Functions and triggers can carry metadata for ownership, discovery, and generated skills. Do not put secrets in metadata.
A worker is any process that connects to the engine and registers functions or trigger types. There are two common paths:
| Task | Use |
|---|---|
| Create your own worker | Write SDK code that calls registerWorker, registerFunction, and registerTrigger |
| Add an existing capability | Browse https://workers.iii.dev/, then call compose::add worker=<name> |
| Pin a worker version | compose::add worker=<name>@<version> |
| Declare a local worker | Add worker: path://./workers/my-worker under containers: |
| Reproduce a project | Commit the exact versions in worker-compose.yaml |
The public worker registry at workers.iii.dev is for installable workers such as HTTP, state,
queue, pub/sub, cron, observability, sandbox, database, shell, console, and other capability
workers. Those workers may ship their own function-level skills; do not duplicate every capability
as a top-level iii skill.
Use iii.worker.yaml when iii should start a local worker project:
The manifest describes how to start the process. Once running, the WebSocket connection and function registrations are what make the worker part of iii.
The engine keeps a live registry of connected workers, registered functions, triggers, and trigger types. Read it through the built-in discovery functions:
| Function | Returns |
|---|---|
engine::workers::list | Connected workers and metrics |
engine::functions::list | Registered functions |
engine::triggers::list | Registered triggers |
engine::trigger-types::list | Advertised trigger types and schemas |
For topology changes, bind triggers to engine::workers-available or
engine::functions-available.
| Trigger type | Registration config | Handler payload |
|---|---|---|
http | { api_path: "/orders/:id", http_method: "POST" } | { query_params, path_params, headers, path, method, body } |
cron | { expression: "0 0 9 * * * *" } | { trigger, job_id, scheduled_time, actual_time } |
durable:subscriber | { topic: "payments" } | The queued message payload |
subscribe | { topic: "orders.created" } | The published event payload |
state | { scope: "orders", key?: "order-123" } | { event_type, scope, key, old_value, new_value } |
stream | { stream_name, group_id, item_id? } | Stream event details |
log | { level: "warn" } | OpenTelemetry-style log data |
Add condition_function_id to built-in trigger config when the handler should only run if a boolean
condition function returns true.
| Mode | Shape | Use when |
|---|---|---|
| Sync | trigger({ function_id, payload }) | The caller needs the result |
| Void | TriggerAction.Void() | Optional side effect, no result needed |
| Enqueue | TriggerAction.Enqueue({ queue }) | Reliable async work with queue policy |
Use enqueue for work that must complete with retries. Use void for analytics, notifications, and other non-critical side effects.
registerTriggerType({ id, description }, handler) when the event source is
not built in. Keep listener setup in registerTrigger and cleanup in unregisterTrigger.createChannel() for binary or streaming data that should not be serialized into
JSON payloads. Pass readerRef or writerRef through a function payload.HttpInvocationConfig for legacy APIs, third-party endpoints, or
immutable services. Use environment variable names for auth fields, not raw secrets.schemars::JsonSchema; Python can use
type hints or Pydantic; Node can pass JSON Schema manually.iii-engine-config.iii-sdk-reference.iii-architecture-patterns.iii-error-handling.