Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 22 additions & 0 deletions .github/scripts/build_publish_payload.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,24 @@ def normalize_dependencies(raw_deps: Any) -> list[dict[str, Any]]:
raise ValueError(f"`dependencies` must be a map or list, got {type(raw_deps).__name__}")


def normalize_tags(raw_tags: Any) -> list[str]:
if raw_tags is None:
return []
if not isinstance(raw_tags, list):
raise ValueError(f"`tags` must be a list, got {type(raw_tags).__name__}")

normalized: list[str] = []
seen: set[str] = set()
for tag in raw_tags:
if not isinstance(tag, str):
raise ValueError("tags entries must be strings")
value = tag.strip().lower()
if value and value not in seen:
normalized.append(value)
seen.add(value)
return normalized


def derive_registry_function_name(function_id: str, metadata: dict[str, Any] | None) -> str:
metadata = metadata or {}
for key in ("registry_name", "name"):
Expand Down Expand Up @@ -348,6 +366,10 @@ def build_payload(
],
}

tags = normalize_tags(meta.get("tags"))
if tags:
payload["tags"] = tags

if deploy == "binary":
if not binaries:
raise ValueError("deploy=binary requires non-empty binaries")
Expand Down
55 changes: 55 additions & 0 deletions .github/scripts/tests/test_build_publish_payload.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""Tests for registry payload metadata assembled from iii.worker.yaml."""
from pathlib import Path

import pytest
from build_publish_payload import build_payload


def build_binary_payload(tmp_path: Path, manifest: str) -> dict[str, object]:
worker_dir = tmp_path / "smoke"
worker_dir.mkdir(parents=True)
(worker_dir / "iii.worker.yaml").write_text(manifest)
(worker_dir / "README.md").write_text("# smoke\n")
return build_payload(
repo_root=tmp_path,
worker="smoke",
version="1.0.0",
registry_tag="latest",
deploy="binary",
repo_url="https://github.com/iii-hq/workers",
interface={"functions": [], "triggers": []},
binaries={"x86_64-unknown-linux-gnu": {"url": "https://example.test/smoke.tgz"}},
image_tag="",
)


def test_manifest_tags_are_normalized_validated_and_optional(tmp_path: Path) -> None:
payload = build_binary_payload(
tmp_path,
"name: smoke\ntags:\n - ' SQL '\n - postgres\n - sql\n - ' '\n",
)
assert payload["tags"] == ["sql", "postgres"]

without_tags = tmp_path / "without-tags"
payload = build_binary_payload(without_tags, "name: smoke\n")
assert "tags" not in payload

empty_tags = tmp_path / "empty-tags"
payload = build_binary_payload(empty_tags, "name: smoke\ntags: []\n")
assert "tags" not in payload

whitespace_tags = tmp_path / "whitespace-tags"
payload = build_binary_payload(whitespace_tags, "name: smoke\ntags:\n - ' '\n")
assert "tags" not in payload

scalar = tmp_path / "scalar"
with pytest.raises(ValueError, match="`tags` must be a list"):
build_binary_payload(scalar, "name: smoke\ntags: sql\n")

empty_scalar = tmp_path / "empty-scalar"
with pytest.raises(ValueError, match="`tags` must be a list"):
build_binary_payload(empty_scalar, "name: smoke\ntags: ''\n")

invalid = tmp_path / "invalid"
with pytest.raises(ValueError, match="tags entries must be strings"):
build_binary_payload(invalid, "name: smoke\ntags:\n - sql\n - 7\n")
19 changes: 17 additions & 2 deletions .github/scripts/tests/test_validate_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
import sys
from pathlib import Path

import pytest

from _test_helpers import GIT_HERMETIC_ENV

SCRIPT = Path(__file__).resolve().parents[1] / "validate_worker.py"
Expand Down Expand Up @@ -101,6 +99,23 @@ def test_wrong_deploy_value_fails(self, tmp_path):
assert r.returncode != 0
assert "deploy" in r.stdout + r.stderr

def test_invalid_tags_fail(self, tmp_path):
for case, tags_yaml, expected in [
("scalar", "tags: sql\n", "must be a list"),
("entry", "tags:\n - sql\n - 7\n", "entries must be strings"),
]:
case_root = tmp_path / case
case_root.mkdir()
repo = make_worker(case_root, "smoke")
meta = repo / "smoke" / "iii.worker.yaml"
meta.write_text(meta.read_text() + tags_yaml)
init_git(repo)

r = run_script(repo, "smoke", source_changed=["smoke"])

assert r.returncode != 0
assert expected in r.stdout + r.stderr

def test_binary_bin_name_mismatch_fails(self, tmp_path):
# A binary worker whose `bin` differs from the worker name ships a
# release archive the resolver can't find (web/v1.1.x regression).
Expand Down
6 changes: 6 additions & 0 deletions .github/scripts/validate_worker.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,12 @@ def soft(msg: str) -> None:
hard(
f"{worker}/iii.worker.yaml language must be 'rust' | 'node' | 'python' | 'javascript'"
)
tags = m.raw.get("tags")
if tags is not None:
if not isinstance(tags, list):
hard(f"{worker}/iii.worker.yaml tags must be a list")
elif any(not isinstance(tag, str) for tag in tags):
hard(f"{worker}/iii.worker.yaml tags entries must be strings")
# The release archive's binary is named after `bin` (defaulting to the
# worker name); the resolver looks it up by worker name. They must match
# or `iii worker add {worker}` fails with "Binary not found in archive".
Expand Down
1 change: 1 addition & 0 deletions approval-gate/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: approval-gate
tags: [approval, human-in-the-loop, permissions, policy, security]
description: Policy and decision surface for human-held function calls — pre_trigger gate, pending inbox, per-session permission settings, and two notification trigger types.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions bridge/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: bridge
tags: [bridge, remote, websocket, federation, engine]
description: Bridge two iii engines — expose local functions on a remote engine and invoke/forward remote functions locally.
config:
url: ws://0.0.0.0:49134
Expand Down
1 change: 1 addition & 0 deletions browser/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: browser
tags: [browser, chromium, automation, web, cdp]
description: Interactive Chromium sessions on the iii bus. Navigate, act, read the page console, pick elements.
1 change: 1 addition & 0 deletions claude-code/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: claude-code
language: javascript
deploy: bundle
manifest: package.json
tags: [coding-agent, claude, anthropic, cli, headless]
description: Claude Code as an iii worker — claude::* functions run headless Claude Code turns, mirror raw messages onto claude::events, and stream AgentEvent frames onto agent::events.

runtime:
Expand Down
1 change: 1 addition & 0 deletions codex/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: codex
tags: [coding-agent, openai, codex-cli, headless, agent]
description: OpenAI Codex as an iii worker; codex::run/start/stop/status/sessions::list spawn the codex CLI for headless turns, mirror raw thread events onto codex::events, and stream AgentEvent frames onto agent::events.
1 change: 1 addition & 0 deletions console/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: console
tags: [console, dashboard, ui, web, admin]
description: Web console for iii — bundles the React UI and proxies the engine WebSocket on a single port.
1 change: 1 addition & 0 deletions context-manager/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: context-manager
tags: [context, tokens, compaction, pruning, llm]
description: Turns a raw conversation history plus a target model into a model-ready context — token counting, function-result pruning, and history compaction.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions cron/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: cron
tags: [cron, schedule, timer, job]
description: Schedule functions with cron expressions - registers the `cron` trigger type.
config:
adapter:
Expand Down
1 change: 1 addition & 0 deletions database/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: database
tags: [sql, sqlite, postgres, mysql, db]
description: Talk to PostgreSQL, MySQL, and SQLite from iii — query, execute, transactions, prepared statements, and change feeds.
1 change: 1 addition & 0 deletions devin/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: devin
tags: [coding-agent, devin, cloud, pr-review, agent]
description: Devin CLI + API as an iii worker; devin::run/start/stop/status/sessions::list follow the agent-worker family and stream AgentEvent frames onto agent::events, devin::session::* wrap the Devin cloud session lifecycle, devin::pr-review::* exposes Devin's review surface, and devin::api reaches any Devin v1/v3 endpoint.
13 changes: 13 additions & 0 deletions docs/architecture/iii-worker-yaml.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,19 @@ aarch64 gnu, and armv7 gnueabihf (the matrix lives in
| `interface_smoke: false` | Skip interface boot smoke + registry publish | `ci.yml`, `release.yml` |
| `runtime` / `scripts.start` | Local boot definition; presence routes publish boot to `iii-add` for non-binary/bundle deploys | `manifest_version.py deploy-mode`, `iii worker add` |
| `scripts.install` | Build command for local install | `iii worker add` (local source) |
| `tags` | Optional discovery aliases included in `POST /publish` | `build_publish_payload.py` |

`tags` must be a list of strings. The publish pipeline trims values, converts them to lowercase,
removes duplicates, and omits the field when no non-empty tags remain.
Workers with `interface_smoke: false` skip registry publishing entirely, so their manifests do not
declare tags.

```yaml
tags:
- http
- rest
- api
```

## Bundle-specific

Expand Down
2 changes: 1 addition & 1 deletion docs/sops/release.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ flowchart LR
- `cargo-run` — `cargo run` from source (remaining Rust workers)
3. Uses `config.collect.yaml` when present (sidecar-free boot).
4. Collects function + trigger interface (120 s timeout); asserts non-empty.
5. Resolves release assets into `payload.json`.
5. Resolves release assets and normalized manifest `tags` into `payload.json`.
6. `POST /publish` to `https://api.workers.iii.dev`.
7. `POST /w/<worker>/skills` when `skills/*.md` exist (skipped when absent).

Expand Down
1 change: 1 addition & 0 deletions email/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: email
tags: [email, smtp, imap, inbox, messaging]
description: Email worker — SMTP send and real-time IMAP read with IDLE push (email::*).

runtime:
Expand Down
1 change: 1 addition & 0 deletions grok/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: grok
tags: [coding-agent, grok, xai, cli, headless]
description: xAI Grok CLI as an iii worker; grok::run/start/stop/status/sessions::list spawn the grok CLI for headless turns, mirror raw streaming-json events onto grok::events, and stream AgentEvent frames onto agent::events.
1 change: 1 addition & 0 deletions harness/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: harness
tags: [agent, harness, loop, autonomous]
description: Thin durable turn loop that wires session-manager, context-manager, and llm-router into an agent loop; spawns sub-agents as child sessions.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions hermes/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: hermes
language: python
deploy: image
manifest: pyproject.toml
tags: [agent, messaging, chat, webhook, multi-channel]
description: Hermes agent as an iii worker — hermes::run runs headless Hermes turns with the iii runtime context, hermes::send delivers to 27+ messaging platforms, and inbound platform/webhook deliveries are republished onto hermes::events for iii workers to react to.

env:
Expand Down
1 change: 1 addition & 0 deletions http/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: http
tags: [http, rest, api, endpoint]
description: Expose functions as HTTP endpoints — registers the `http` trigger type and serves matched routes.
config:
port: 3111
Expand Down
1 change: 1 addition & 0 deletions iii-directory/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: iii-directory
tags: [introspect, registry, skills, directory]
description: Engine introspection (functions / triggers / workers), workers registry proxy, and filesystem-backed skill + prompt reader.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions image-resize/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,5 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: image-resize
tags: [image, resize, jpeg, png, webp, media]
description: III engine image resize worker (JPEG/PNG/WebP, EXIF orient, scale-to-fit / crop-to-fit)
1 change: 1 addition & 0 deletions llm-router/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: llm-router
tags: [llm, router, models, providers]
description: One front door + provider protocol in front of every LLM provider.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions mcp/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: mcp
tags: [mcp, model-context-protocol, bridge, http, agents]
description: MCP 2025-06-18 Streamable HTTP bridge for the iii engine.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions memory-consolidate/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: memory-consolidate
tags: [memory, deduplication, cleanup, schedule, maintenance]
description: Scheduled hygiene for the memory worker — deterministic dedup of near-duplicate memories, applied supersede-only through the public memory functions, pinned untouchable, with catch-up-on-boot scheduling. Install, stop, or remove it without touching stored memory.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions memory/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: memory
tags: [memory, rag, bm25, recall, agent, context]
description: Durable cross-session agent memory — named banks of always-injected markdown rules and auto-extracted memories, hybrid BM25 + entity recall, pinning, and supersede-never-delete history. Plain files on disk; every operation is a traced function.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions opencode/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: opencode
language: javascript
deploy: bundle
manifest: package.json
tags: [coding-agent, opencode, cli, headless, agent]
description: OpenCode as an iii worker — opencode::* functions run headless OpenCode turns, mirror raw JSON events onto opencode::events, and stream AgentEvent frames onto agent::events.

runtime:
Expand Down
1 change: 1 addition & 0 deletions pi/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ name: pi
language: javascript
deploy: bundle
manifest: package.json
tags: [coding-agent, pi, cli, headless, agent]
description: Pi coding agent as an iii worker — pi::* functions run headless Pi turns, mirror raw events onto pi::events, and stream AgentEvent frames onto agent::events.

runtime:
Expand Down
1 change: 1 addition & 0 deletions provider-anthropic/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: provider-anthropic
tags: [llm, anthropic, claude, messages, provider]
description: Anthropic Messages API provider worker; implements provider::anthropic::stream and provider::anthropic::refresh_models behind llm-router.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions provider-kimi/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: provider-kimi
tags: [llm, kimi, moonshot, chat-completions, provider]
description: Moonshot (Kimi) Chat Completions provider worker; implements provider::kimi::stream and provider::kimi::refresh_models behind llm-router.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions provider-llamacpp/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: provider-llamacpp
tags: [llm, llama.cpp, local, open-source, provider]
description: llama.cpp server (llama-server) Chat Completions provider worker; implements provider::llamacpp::stream and provider::llamacpp::refresh_models behind llm-router.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions provider-openai-codex/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: provider-openai-codex
tags: [llm, openai, codex, chatgpt, subscription, provider]
description: OpenAI Codex (ChatGPT subscription) provider; implements provider::openai-codex::stream and provider::openai-codex::refresh_models behind llm-router, sourcing credentials from the auth-credentials vault.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions provider-openai/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: provider-openai
tags: [llm, openai, responses, chat-completions, provider]
description: OpenAI Responses provider worker with Chat Completions compatibility; implements provider::openai::stream and provider::openai::refresh_models behind llm-router.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions provider-xai/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: provider-xai
tags: [llm, xai, grok, chat-completions, provider]
description: xAI Chat Completions provider worker; implements provider::xai::stream and provider::xai::refresh_models behind llm-router.

dependencies:
Expand Down
1 change: 1 addition & 0 deletions provider-zai/iii.worker.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ language: rust
deploy: binary
manifest: Cargo.toml
bin: provider-zai
tags: [llm, zai, glm, chat-completions, provider]
description: Z.AI Chat Completions provider worker; implements provider::zai::stream and provider::zai::refresh_models behind llm-router.

dependencies:
Expand Down
Loading
Loading