Iii Core Primitives logo

Iii Core Primitives

OrganizationPopular
iii-hq
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.

Overview

Publisheriii-hq
Repositoryiii
Skill nameiii-core-primitives
Stars
18.8K
Forks
1.3K
Bundled files
Instructions only
Links
  • Markdown instructions

    A SKILL.md file the model loads on demand, so it only costs tokens when a request actually matches.

  • Works with any LLM

    AI skills are plain Markdown, not provider-specific code, so this works with GPT, Claude, Gemini, Grok, or a local model.

  • Self-contained

    Everything the model needs lives in the instructions — no extra files to sync.

  • Open source

    Published by iii-hq on GitHub. Read the source before you install it.

Installation

Install the Iii Core Primitives AI skill in TypingMind to use it with any LLM, or drop it into another agent that reads SKILL.md.

1

Install in TypingMind

TypingMind installs a skill straight from its GitHub folder — it reads SKILL.md, bundles the resource files, and stores the result locally.

  1. Open the app and go to Plugins → Skills.
  2. Choose "Install from GitHub".
  3. Paste the skill folder URL below and confirm.
    https://github.com/iii-hq/iii/tree/main/skills/iii-core-primitives
  4. Enable the skill in any chat where you want it available.
Plugins → Skills → Add skill → From GitHub URL, then paste the folder URL and press Continue.
2

Install in another agent

Any agent that reads the Agent Skills format can use this skill — copy the folder into that agent's skills directory.

Claude Code — .claude/skills
git clone --depth 1 https://github.com/iii-hq/iii.git /tmp/iii
mkdir -p .claude/skills
cp -r /tmp/iii/skills/iii-core-primitives .claude/skills/iii-core-primitives
Restart Claude Code after copying so it picks up the new skill.

Use it in TypingMind

Enable Iii Core Primitives in any TypingMind chat and the model takes it from there. Its name and description sit in the system prompt, and the moment a request matches, the model loads the full instructions itself — you never invoke it by hand, and it costs no tokens until it is actually used.

The model loads Iii Core Primitives on its own as soon as a request matches it.

Works with any AI model

AI skills are plain Markdown instructions rather than provider-specific code, so Iii Core Primitives is not tied to the model it was written for. Install it once in TypingMind and use it with GPT-5, Claude, Gemini, Grok, DeepSeek, Mistral, Llama, or a local model you run yourself — all on your own API keys.

  • Loaded only when it is needed

    The system prompt carries just the name and description. The instructions are fetched on the first matching request, so an idle skill costs nothing.

  • Switch models mid-chat

    Because the skill is instructions rather than code, changing model does not break it — the next model reads the same SKILL.md.

Skill instructions

This is the SKILL.md content the model loads. Read it before installing — a skill is instructions your model will follow.

Core Primitives

iii has three top-level primitives:

  • Function: a named unit of work such as orders::validate
  • Trigger: an event source bound to a function
  • Worker: a process that connects to the engine and executes functions

Use :: in function IDs, leading slashes in HTTP api_path, and expression for cron config.

Function Registration

Register local handlers when you control the implementation. Register HTTP-invoked functions when iii should call an existing external endpoint.

ShapeUse 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.

Workers and Registry

A worker is any process that connects to the engine and registers functions or trigger types. There are two common paths:

TaskUse
Create your own workerWrite SDK code that calls registerWorker, registerFunction, and registerTrigger
Add an existing capabilityBrowse https://workers.iii.dev/, then call compose::add worker=<name>
Pin a worker versioncompose::add worker=<name>@<version>
Declare a local workerAdd worker: path://./workers/my-worker under containers:
Reproduce a projectCommit 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, database, shell, console, rbac-proxy, and other capability workers. (iii-sandbox and iii-observability are engine-owned and are not added through the registry.) Those workers may ship their own function-level skills; do not duplicate every capability as a top-level iii skill.

Worker Manifest

Use iii.worker.yaml when iii should start a local worker project:

yaml
name: math-worker
runtime:
  kind: python
  package_manager: pip
  entry: math_worker.py
scripts:
  install: "pip install -r requirements.txt"
  start: "python math_worker.py"

The manifest describes how to start the process. Once running, the WebSocket connection and function registrations are what make the worker part of iii.

Live Engine Registry

The engine keeps a live registry of connected workers, registered functions, triggers, and trigger types. Read it through the built-in discovery functions:

FunctionReturns
engine::workers::listConnected workers and metrics
engine::functions::listRegistered functions
engine::triggers::listRegistered triggers
engine::trigger-types::listAdvertised trigger types and schemas

For topology changes, bind triggers to engine::workers-available or engine::functions-available.

Built-In Trigger Shapes

Trigger typeRegistration configHandler 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.

Invocation Modes

ModeShapeUse when
Synctrigger({ function_id, payload })The caller needs the result
VoidTriggerAction.Void()Optional side effect, no result needed
EnqueueTriggerAction.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.

Namespaces

Every worker registers in a namespace: the namespace init option, else III_NAMESPACE, else the engine's default. Compose sets III_NAMESPACE to its daemon's namespace for every worker it starts, so all workers in one worker-compose.yaml share a namespace and call each other by bare function id. Engine-owned functions (engine::*, configuration::*, stream::*) live in default; engine::* resolves there implicitly, the others need namespace: "default" on the call from a namespaced worker. Routing is strict and ids are never prefixed with a namespace. From the CLI, iii trigger -n <namespace> <function> selects it. Details and per-language examples are in iii-sdk-reference.

Code Examples

TypeScript

typescript
import { registerWorker, TriggerAction } from "iii-sdk";

const iii = registerWorker("ws://localhost:49134", { workerName: "orders-worker" });

iii.registerFunction("orders::validate", async (order) => {
  if (!order.id) throw new Error("missing order id");
  return { ...order, valid: true };
});

iii.registerFunction("orders::process", async (order) => {
  const validated = await iii.trigger({ function_id: "orders::validate", payload: order });
  await iii.trigger({
    function_id: "orders::charge",
    payload: validated,
    action: TriggerAction.Enqueue({ queue: "payments" }),
  });
  return { accepted: true, orderId: validated.id };
});

iii.registerTrigger({
  type: "http",
  function_id: "orders::process",
  config: { api_path: "/orders", http_method: "POST" },
});

Python

python
from iii import register_worker

iii = register_worker("ws://localhost:49134")

def validate(order):
    if not order.get("id"):
        raise ValueError("missing order id")
    return {**order, "valid": True}

def process(order):
    validated = iii.trigger({"function_id": "orders::validate", "payload": order})
    iii.trigger({
        "function_id": "orders::charge",
        "payload": validated,
        "action": {"type": "enqueue", "queue": "payments"},
    })
    return {"accepted": True, "orderId": validated["id"]}

iii.register_function("orders::validate", validate)
iii.register_function("orders::process", process)
iii.register_trigger({
    "type": "http",
    "function_id": "orders::process",
    "config": {"api_path": "/orders", "http_method": "POST"},
})

Rust

rust
use iii_sdk::{register_worker, InitOptions, RegisterFunction, TriggerAction};
use iii_sdk::protocol::{RegisterTriggerInput, TriggerRequest};
use serde_json::json;

let iii = register_worker("ws://127.0.0.1:49134", InitOptions::default());

iii.register_function("orders::validate", RegisterFunction::new(|order: serde_json::Value| {
    if order["id"].is_null() {
        return Err("missing order id".into());
    }
    Ok(json!({ "valid": true, "order": order }))
}))?;

let process_client = iii.clone();
iii.register_function("orders::process", RegisterFunction::new_async(move |order: serde_json::Value| {
    let iii = process_client.clone();
    async move {
        let validated = iii.trigger(TriggerRequest::new("orders::validate", order)).await?;
        iii.trigger(TriggerRequest {
            function_id: "orders::charge".into(),
            payload: validated.clone(),
            action: Some(TriggerAction::Enqueue { queue: "payments".into() }),
            timeout_ms: None,
        }).await?;
        Ok(json!({ "accepted": true, "order": validated }))
    }
}))?;

iii.register_trigger(RegisterTriggerInput {
    trigger_type: "http".into(),
    function_id: "orders::process".into(),
    config: json!({ "api_path": "/orders", "http_method": "POST" }),
    metadata: None,
})?;

Advanced Primitive Patterns

  • Custom triggers: use registerTriggerType({ id, description }, handler) when the event source is not built in. Keep listener setup in registerTrigger and cleanup in unregisterTrigger.
  • Channels: use createChannel() for binary or streaming data that should not be serialized into JSON payloads. Pass readerRef or writerRef through a function payload.
  • HTTP-invoked functions: use HttpInvocationConfig for legacy APIs, third-party endpoints, or immutable services. Use environment variable names for auth fields, not raw secrets.
  • Schemas: Rust can derive request/response schemas with schemars::JsonSchema; Python can use type hints or Pydantic; Node can pass JSON Schema manually.

When to Use

  • Use this skill for function registration, trigger binding, trigger payload shapes, invocation mode decisions, worker creation, worker registry access, trigger conditions, custom trigger types, channels, and HTTP-invoked functions.
  • Use this when a task spans TypeScript, Python, or Rust examples for the same iii primitive.

Boundaries

  • For engine ports, adapters, engine-owned workers, RBAC (rbac-proxy), and deployment config, use iii-engine-config.
  • For SDK-specific package exports and language caveats, use iii-sdk-reference.
  • For complete backend designs such as workflows, CQRS, agentic systems, and reactive apps, use iii-architecture-patterns.
  • For failed invocations, timeouts, RBAC denials, and retryability, use iii-error-handling.
  • Worker-backed capability details live with the worker docs, not as top-level iii skills.

Frequently asked questions

What does the Iii Core Primitives AI skill do?

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.

Why use Iii Core Primitives on TypingMind?

Because you install it once and use it with any model. Iii Core Primitives is plain Markdown rather than provider-specific code, so the same skill runs on GPT-5, Claude, Gemini, Grok, or a local model — and you can switch model mid-chat without it breaking. TypingMind runs on your own API keys, so you pay providers directly instead of a per-seat subscription, and your skills and chats stay in your own storage.

How do I install Iii Core Primitives in TypingMind?

Open Plugins → Skills → Install from GitHub in TypingMind and paste https://github.com/iii-hq/iii/tree/main/skills/iii-core-primitives. TypingMind reads its SKILL.md and installs it as a skill you can enable per chat.

Which AI models can use Iii Core Primitives?

Any model you connect in TypingMind. AI skills are plain Markdown instructions rather than provider-specific code, so GPT, Claude, Gemini, Grok, and local models can all load this skill when a request matches it.

How many AI models can I use with Iii Core Primitives?

As many as you like. As long as a model supports skills, you can use Iii Core Primitives with it — GPT, Claude, Gemini, Grok, DeepSeek, Mistral, Llama and more — all on TypingMind with your own API keys.

Is the Iii Core Primitives AI skill free?

It is published on GitHub by iii-hq. Check the repository for licensing terms. You only pay your own AI provider for the tokens you use.

What are AI skills?

An AI skill is a reusable instruction bundle that teaches an AI model how to do one specific task. It follows the open Agent Skills format: a SKILL.md file with a name and description, plus any scripts, templates or reference files the model may need. The model reads the instructions only when your request matches the skill, so an installed skill costs nothing until it is used.

How are AI skills different from plugins or MCP servers?

A plugin or MCP server gives a model new tools to call — code that runs somewhere and returns a result. An AI skill gives the model knowledge and process instead: how to approach a task, which steps to follow, what good output looks like. Skills are plain Markdown, so they need no server, no API key and no runtime, and they work with any model.

View all

Set up your own AI workspace now

Get notified about new features and future giveaways by subscribing to our newsletter 👇