diff --git a/.gitignore b/.gitignore index a7b8870..abdf268 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,9 @@ data/ .env .env.* !.env.example +.claude/ +CLAUDE.md +package-lock.json +pnpm-lock.yaml +yarn.lock + diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 4f45a0e..a85f1fe 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -1,51 +1,45 @@ # AgentOS architecture -AgentOS is an agent operating system built on the [iii engine](https://github.com/iii-hq/iii). -The repo ships **narrow workers** (one binary per domain), declarative -configuration (hands, integrations, agents), and surfaces (`cli`, `tui`). -Everything coordinates through iii primitives — `register_function`, -`register_trigger`, `iii.trigger` — over the engine's WebSocket on port -49134. There is no agent runtime that lives outside iii. +AgentOS is an agent operating system built on the [iii engine](https://github.com/iii-hq/iii). The repo ships **65 narrow workers** (one binary per domain), declarative config (hands, integrations, agents), and two surfaces (`crates/cli`, `crates/tui`). Everything coordinates through iii primitives — `register_function`, `register_trigger`, `iii.trigger` — over the engine's WebSocket on port 49134. ## Repository layout ``` agentos/ -├── workers/ Narrow iii workers (one binary each) -│ ├── agent-core/ ReAct loop agent::* -│ ├── bridge/ External runtime adapters bridge::* -│ ├── council/ Proposals + activity log council::* -│ ├── directive/ Hierarchical goal alignment directive::* -│ ├── embedding/ SentenceTransformers (Python) embedding::* -│ ├── hierarchy/ Org graph (cycle-safe) hierarchy::* -│ ├── ledger/ Budget + spend tracking ledger::* -│ ├── llm-router/ Provider routing llm::* -│ ├── memory/ Narrow agent memory memory::* -│ ├── mission/ Task lifecycle mission::* -│ ├── pulse/ Scheduled invocation pulse::* -│ ├── realm/ Multi-tenant isolation realm::* -│ ├── security/ Combined guardrails + audit security::* -│ └── wasm-sandbox/ wasmtime fuel-metered wasm::* -│ -├── crates/ Surfaces (clients, not workers) -│ ├── cli/ Command-line interface -│ └── tui/ 21-screen terminal dashboard -│ -├── src/ TypeScript workers (54 files, ~23k LOC) -│ └── ... a2a, browser, cron, evolve, swarm, etc. -│ -├── hands/ Agent personas as TOML config (consumed by hand-runner) -├── integrations/ MCP server configs as TOML -├── agents/ Agent templates as markdown -│ -├── config.yaml iii engine boot config (workers: + modules:) -└── .github/workflows/ci.yml End-to-end CI (build, test, e2e smoke, namespace check) +├── workers/ 64 Rust workers + 1 Python worker +├── crates/ +│ ├── cli/ Command-line client (HTTP → iii-http on 3111) +│ └── tui/ Terminal dashboard +├── e2e/ vitest end-to-end suite (live engine + workers) +├── tests/ Rust integration tests +├── hands/ Agent personas (TOML, consumed by hand-runner) +├── integrations/ MCP server configs (TOML, consumed by mcp-client) +├── agents/ Agent templates (markdown) +├── workflows/ Pre-defined workflow YAMLs +├── plugin/ Reusable agent/command/skill/hook bundles +├── config.yaml iii engine boot config +└── .github/workflows/ci.yml Build + test + e2e ``` +## Workers + +| Group | Workers | Function namespaces | +|---|---|---| +| Reasoning | `agent-core` `llm-router` `council` `swarm` `directive` `mission` | `agent::*` `llm::*` `council::*` `swarm::*` `directive::*` `mission::*` | +| State | `realm` `memory` `ledger` `vault` `context-manager` `context-cache` | `realm::*` `memory::*` `ledger::*` `vault::*` `context::*` | +| Coordination | `orchestrator` `workflow` `hierarchy` `coordination` `task-decomposer` | `orchestrator::*` `workflow::*` `hierarchy::*` `task::*` | +| Execution | `wasm-sandbox` `browser` `code-agent` `hand-runner` `lsp-tools` | `wasm::*` `browser::*` `code::*` `hand::*` `lsp::*` | +| Safety | `security` `security-headers` `security-map` `security-zeroize` `skill-security` `approval` `approval-tiers` `rate-limiter` `loop-guard` | `security::*` `approval::*` `rate::*` `loop::*` | +| Surfaces | `a2a` `a2a-cards` `mcp-client` `skillkit-bridge` `bridge` `streaming` | `a2a::*` `mcp::*` `skillkit::*` `bridge::*` `stream::*` | +| Channels | `channel-{bluesky, discord, email, linkedin, mastodon, matrix, reddit, signal, slack, teams, telegram, twitch, webex, whatsapp}` | `channel::*` | +| Telemetry | `telemetry` `pulse` `session-lifecycle` `session-replay` `feedback` `eval` `evolve` `hashline` `hooks` `cron` | `telemetry::*` `pulse::*` `session::*` `eval::*` `feedback::*` | +| Embeddings | `embedding` (Python) | `embedding::*` | + +Total: 257 registered functions across 65 workers (64 Rust + 1 Python). + ## Worker manifest -Every directory under `workers/` ships an `iii.worker.yaml` that declares -its registry shape: +Every directory under `workers/` ships an `iii.worker.yaml`: ```yaml iii: v1 @@ -59,42 +53,11 @@ description: ... CI's `validate iii.worker.yaml` job enforces this on every PR. -## Function namespaces - -| namespace | worker | shape | -|---|---|---| -| `agent::` | agent-core | `chat`, `create`, `list`, `delete`, `list_tools` | -| `bridge::` | bridge | `register`, `invoke`, `run`, `list`, `cancel` | -| `council::` | council | `submit`, `decide`, `override`, `proposals`, `activity`, `verify` | -| `directive::` | directive | `create`, `get`, `update`, `list`, `ancestry` | -| `embedding::` | embedding (py) | `generate` | -| `hierarchy::` | hierarchy | `set`, `tree`, `chain`, `find`, `remove` | -| `ledger::` | ledger | `set_budget`, `spend`, `check`, `summary` | -| `llm::` | llm-router | `route`, `complete`, `usage`, `providers` | -| `memory::` | memory | `store`, `recall`, `consolidate`, `evict`, `delete` | -| `mission::` | mission | `create`, `list`, `checkout`, `transition`, `comment`, `release` | -| `pulse::` | pulse | `register`, `tick`, `invoke`, `status`, `toggle` | -| `realm::` | realm | `create`, `get`, `update`, `delete`, `list`, `import`, `export` | -| `security::` | security | `audit`, `scan`, `scan_injection`, `check_capability`, `set_capabilities`, `verify_audit`, `docker_exec`, `sign_manifest`, `verify_manifest` | -| `wasm::` | wasm-sandbox | `execute`, `validate`, `list_modules` | - -`sandbox::*` is reserved for the **builtin iii-sandbox** worker (iii -v0.11.4-next.4), which boots ephemeral microVMs from OCI rootfs. -AgentOS workers never register under that namespace; the -`no sandbox::* clash with builtin` CI job enforces this. - ## Engine boot -`config.yaml` (iii v0.11.4 schema) declares the seven baseline workers -the engine spawns: `iii-http`, `iii-state`, `iii-stream`, `iii-queue`, -`iii-pubsub`, `iii-cron`, `iii-observability`. AgentOS workers are -spawned alongside as separate processes — each connects to the engine -WebSocket via `register_worker("ws://localhost:49134", ...)` and stays -resident. +`config.yaml` (iii v0.11.4 schema) declares the seven baseline modules the engine spawns: `iii-http`, `iii-state`, `iii-stream`, `iii-queue`, `iii-pubsub`, `iii-cron`, `iii-observability`. AgentOS workers spawn alongside as separate processes — each connects to the engine WebSocket via `register_worker("ws://localhost:49134", ...)` and stays resident. -The engine WebSocket port (49134) is configurable per-worker via the -`III_WS_URL` environment variable, with `ws://localhost:49134` as the -default. Containerized deploys override. +The engine WebSocket port is configurable via `III_WS_URL` (default `ws://localhost:49134`). ## Calling a function from another worker @@ -107,7 +70,7 @@ iii.trigger(TriggerRequest { }).await? ``` -Or fire-and-forget: +Fire-and-forget: ```rust let iii_c = iii.clone(); @@ -121,55 +84,45 @@ tokio::spawn(async move { }); ``` -This is the only inter-worker contract. There is no shared in-process -state; everything goes through `iii.trigger`. +This is the only inter-worker contract. There is no shared in-process state. + +## Sandbox primitives — two surfaces + +| namespace | worker | semantics | +|---|---|---| +| `sandbox::create` / `sandbox::exec` / `sandbox::list` / `sandbox::stop` | **builtin** iii-sandbox (v0.11.4-next.4) | Ephemeral microVMs from OCI rootfs (Python, Node presets). Full Linux. | +| `wasm::execute` / `wasm::validate` / `wasm::list_modules` | agentos `wasm-sandbox` | wasmtime, fuel-metered, sub-millisecond cold start. | -## TypeScript workers (src/) +CI's `no sandbox::* clash with builtin` job greps the workspace to ensure no agentos worker registers `sandbox::*`. -The 54 TypeScript files in `src/` predate the Rust `workers/` -decomposition. Several are duplicates of Rust workers -(`src/memory.ts` ↔ `workers/memory`, `src/security.ts` ↔ -`workers/security`, `src/llm-router.ts` ↔ `workers/llm-router`, -`src/agent-core.ts` ↔ `workers/agent-core`); the rest are TypeScript- -only features (`a2a`, `swarm`, `eval`, `evolve`, `feedback`, -`hand-runner`, etc). +## Atomic state ops -Long-term these will collapse into `workers/` as well, with the Rust -versions becoming canonical for the duplicated domains and the -TypeScript-only features moving each to `workers//` with -`language: node, deploy: image` manifests. That migration is a -follow-up — not in this iteration. +iii v0.11.4 exposes `state::update` / `stream::update` with `UpdateOp::set`, `UpdateOp::increment`, `UpdateOp::append`, plus nested shallow-merge paths. Workers prefer these over `state::list + state::set` race patterns when mutating lists or counters. + +`council::activity` still uses a manual hash-chain on `state::list + state::set` — a separate refactor will move it onto `UpdateOp::append` once the chain protocol tolerates concurrent appends without compare-and-swap. ## Surfaces (cli, tui) -`crates/cli` and `crates/tui` are clients, not workers. They speak -HTTP to `iii-http` on port 3111. They register no functions and -declare no `iii.worker.yaml`. Future work should move them onto the -iii client SDK directly so they call workers via `iii.trigger` instead -of REST. +`crates/cli` and `crates/tui` are clients, not workers. They speak HTTP to `iii-http` on port 3111. They register no functions. Future work moves them onto the iii client SDK so they call workers via `iii.trigger` directly. -## Hands, integrations, agents +## Hands, integrations, agents, workflows These are **declarative config**, not workers: -- `hands//HAND.toml` — agent persona (system prompt, allowed - tools, schedule, dashboard metrics) consumed at runtime by - `src/hand-runner.ts`. -- `integrations/.toml` — MCP server connection details (transport, - command, OAuth scopes), consumed by `src/mcp-client.ts`. -- `agents//...` — markdown templates for spawning agent - personas. +- `hands//HAND.toml` — agent persona (system prompt, allowed tools, schedule), consumed by the `hand-runner` worker. +- `integrations/.toml` — MCP server connection details (transport, command, OAuth scopes), consumed by the `mcp-client` worker. +- `agents//...` — markdown templates for spawning agent personas. +- `workflows/.yaml` — pre-defined workflow definitions for the `workflow` worker. -None of these ship as registered functions; they configure workers that -do. +None ship as registered functions; they configure workers that do. ## Versioning - iii engine: **v0.11.4-next.4** - iii-sdk (Rust): **=0.11.4-next.4** in workspace `Cargo.toml` +- iii-sdk (Node): **0.11.4-next.4** in root `package.json` (e2e tests only) - iii-sdk (Python): **>=0.11.3** in `workers/embedding/pyproject.toml` -- agentos workspace: `version = "0.0.1"` (reserved for behavioral proof - against live infra, not feature completeness) +- agentos workspace: `version = "0.0.1"` (reserved for behavioral proof against live infra, not feature completeness) ## CI @@ -177,23 +130,17 @@ Five jobs run on every PR: | job | gate | |---|---| -| `rust build + test` | `cargo build --release` + `cargo test --workspace` (831 tests) | +| `rust build + test` | `cargo build --release` + `cargo test --workspace` (1281 tests) | | `validate iii.worker.yaml` | every `workers//iii.worker.yaml` parses and matches its folder | | `no sandbox::* clash with builtin` | grep ensures no agentos worker registers `sandbox::*` | -| `e2e smoke` | starts engine + 13 workers, asserts ports listen, ≥30 functions register, no namespace clash | +| `e2e smoke` | starts engine + workers, asserts ports listen, ≥30 functions register, no namespace clash | | `e2e full` | runs vitest e2e suite against the live stack — gated on `AGENTOS_API_KEY` secret | -Plus `.github/workflows/vercel-deploy.yml` (separate workflow): pushes -to `main` touching `website/**` trigger a Vercel Deploy Hook so the -docs site stays current with main. +Plus `.github/workflows/vercel-deploy.yml`: pushes to `main` touching `website/**` trigger a Vercel Deploy Hook. ## Dependencies (declarative chain-install) -iii v0.11.4-next.4 added a `dependencies:` map in `iii.worker.yaml` -that lets `iii worker add ./workers/agent-core` chain-install -`llm-router`, `memory`, `security` from the registry. AgentOS workers -do not yet declare deps because they aren't published to the registry -— once registry publishing lands, agent-core gets: +iii v0.11.4-next.4 added a `dependencies:` map in `iii.worker.yaml` that lets `iii worker add ./workers/agent-core` chain-install `llm-router`, `memory`, `security` from the registry. AgentOS workers do not yet declare deps because they aren't published to the registry — once publishing lands, agent-core gets: ```yaml dependencies: @@ -202,31 +149,6 @@ dependencies: security: ^0.0.1 ``` -## Sandbox primitives — the two distinct surfaces - -| namespace | worker | semantics | -|---|---|---| -| `sandbox::create` / `sandbox::exec` / `sandbox::list` / `sandbox::stop` | **builtin** iii-sandbox (v0.11.4-next.4) | Ephemeral microVMs booted from OCI rootfs (Python, Node presets). Full Linux. | -| `wasm::execute` / `wasm::validate` / `wasm::list_modules` | agentos `wasm-sandbox` | wasmtime fuel-metered, instruction-level, sub-millisecond cold start. | - -Different cost profiles. The CI namespace-clash job ensures the two -never collide. - -## Atomic state ops (iii v0.11.4) - -iii now exposes `state::update` / `stream::update` with `UpdateOp::set`, -`UpdateOp::increment`, `UpdateOp::append`, plus nested shallow-merge -paths. Workers should prefer these over `state::list + state::set` race -patterns when adding to a list or counter. - -`council::activity` still uses the manual hash-chain on `state::list + -state::set` — a separate refactor will move it onto `UpdateOp::append` -once the chain protocol is redesigned to tolerate concurrent appends -without compare-and-swap. - ## File-by-file responsibilities -For deeper detail on any worker, read its `src/main.rs` (Rust) or -`main.py` (Python). Each is intentionally small (5-10 registered -functions, 300-2000 LOC) so it's possible to hold the whole worker in -your head before touching it. +For deeper detail on any worker, read its `src/main.rs` (Rust) or `main.py` (Python). Each is intentionally small (5–10 registered functions, 300–2000 LOC). diff --git a/CLAUDE.md b/CLAUDE.md deleted file mode 100644 index 4d99b08..0000000 --- a/CLAUDE.md +++ /dev/null @@ -1,54 +0,0 @@ -# AgentSOS Project Guide - -## Quick Start -```bash -npm test # Run all tests (vitest --run) -npm run dev # Start engine + watch mode -npm run start # Production start -``` - -## Architecture -- **Runtime**: TypeScript workers on iii-sdk (Worker/Function/Trigger primitives) -- **SDK**: `iii-sdk` v0.9.0 — direct `registerWorker()` usage with object-style `trigger({ function_id, payload })`; fire-and-forget via `TriggerAction.Void()` -- **Each worker file**: calls `registerWorker(ENGINE_URL, { workerName, otel })` directly, then registers functions + HTTP/cron triggers -- **Entry point**: `src/index.ts` imports all workers -- **43 TypeScript workers**, Rust crates for CLI/TUI/security, Python for embeddings - -## Code Conventions -- ESM only (`"type": "module"`, `.js` extensions in imports) -- No code comments — code should be self-documenting -- Auth pattern: `if (req.headers) requireAuth(req)` — skips auth for internal trigger calls, enforces for HTTP -- Error handling: `safeCall(fn, fallback, { operation })` for external calls that may fail -- IDs: validated with `sanitizeId()` (alphanumeric + `_-:.`, max 256 chars) -- SSRF: `assertNoSsrf(url)` before any outbound HTTP (DNS resolution check) -- Security: timing-safe HMAC via `safeEqual()`, AES-256-GCM vault - -## Test Pattern -All tests follow the same structure in `src/__tests__/`: -1. Inline KV store mock (`kvStore`, `getScope`, `resetKv`, `seedKv`) -2. `mockTrigger` handling `state::get/set/list/update/delete` + domain mocks -3. `vi.mock("iii-sdk")` with `handlers` map capturing registered functions -4. Mock shared modules: `utils.js`, `logger.js`, `metrics.js`, `errors.js` -5. `safeCall` mock: `async (fn, fallback, _context?) => { try { return await fn(); } catch { return fallback; } }` -6. `beforeAll(async () => import("../module.js"))` to register handlers -7. `call(id, input)` helper to invoke handlers - -Shared test helpers available at `src/__tests__/helpers.ts` (KV mock, request builders). - -## KV Scopes -State is stored in scoped key-value stores via `trigger("state::get/set/list", { scope, key })`: -- `agents`, `sessions:{agentId}`, `skills`, `memory:{agentId}` -- `evolved_functions`, `eval_results`, `eval_suites` -- `feedback_decisions`, `feedback_policy` -- `cost_records`, `cost_daily`, `swarms`, `replay` -- `kg_temporal:{agentId}`, `browser_sessions` - -## Key Files -- `src/shared/config.ts` — SDK init, path traversal guard, secret getter -- `src/shared/errors.ts` — AppError, classifyError, withRetry, safeCall -- `src/shared/utils.ts` — requireAuth, assertNoSsrf, sanitizeId, splitMessage -- `src/shared/validate.ts` — safeInt, safeString, safeArray, safePagination -- `src/types.ts` — shared type definitions -- `src/evolve.ts` — dynamic function evolution (vm sandbox) -- `src/eval.ts` — production eval harness -- `src/feedback.ts` — feedback loop (keep/improve/kill) diff --git a/Cargo.lock b/Cargo.lock index d11a5bd..7558360 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -127,9 +127,41 @@ dependencies = [ [[package]] name = "agentos-browser" version = "0.0.1" +dependencies = [ + "anyhow", + "chrono", + "iii-sdk", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "url", + "uuid", +] + +[[package]] +name = "agentos-channel-bluesky" +version = "0.0.1" +dependencies = [ + "anyhow", + "chrono", + "iii-sdk", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "agentos-channel-discord" +version = "0.0.1" dependencies = [ "anyhow", "iii-sdk", + "reqwest", "serde", "serde_json", "tokio", @@ -170,6 +202,35 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "agentos-channel-mastodon" +version = "0.0.1" +dependencies = [ + "anyhow", + "iii-sdk", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "agentos-channel-matrix" +version = "0.0.1" +dependencies = [ + "anyhow", + "iii-sdk", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "uuid", +] + [[package]] name = "agentos-channel-reddit" version = "0.0.1" @@ -229,6 +290,20 @@ dependencies = [ "tracing-subscriber", ] +[[package]] +name = "agentos-channel-telegram" +version = "0.0.1" +dependencies = [ + "anyhow", + "iii-sdk", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "agentos-channel-twitch" version = "0.0.1" @@ -730,6 +805,56 @@ dependencies = [ "uuid", ] +[[package]] +name = "agentos-security-headers" +version = "0.0.1" +dependencies = [ + "anyhow", + "iii-sdk", + "reqwest", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "url", +] + +[[package]] +name = "agentos-security-map" +version = "0.0.1" +dependencies = [ + "anyhow", + "ed25519-dalek", + "hex", + "hmac", + "iii-sdk", + "rand 0.8.6", + "serde", + "serde_json", + "sha2", + "subtle", + "tokio", + "tracing", + "tracing-subscriber", +] + +[[package]] +name = "agentos-security-zeroize" +version = "0.0.1" +dependencies = [ + "anyhow", + "dashmap", + "iii-sdk", + "regex", + "serde", + "serde_json", + "tokio", + "tracing", + "tracing-subscriber", + "zeroize", +] + [[package]] name = "agentos-session-lifecycle" version = "0.0.1" @@ -760,6 +885,23 @@ dependencies = [ "uuid", ] +[[package]] +name = "agentos-skill-security" +version = "0.0.1" +dependencies = [ + "anyhow", + "base64", + "ed25519-dalek", + "iii-sdk", + "regex", + "serde", + "serde_json", + "spki", + "tokio", + "tracing", + "tracing-subscriber", +] + [[package]] name = "agentos-skillkit-bridge" version = "0.0.1" @@ -984,6 +1126,17 @@ version = "1.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-trait" version = "0.1.89" @@ -1209,6 +1362,15 @@ dependencies = [ "static_assertions", ] +[[package]] +name = "concurrent-queue" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ca0197aee26d1ae37445ee532fefce43251d24cc7c166799f4d46817f1d3973" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "console" version = "0.15.11" @@ -1240,6 +1402,15 @@ version = "0.9.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8" +[[package]] +name = "convert_case" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "core-foundation" version = "0.10.1" @@ -1769,6 +1940,27 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "event-listener" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e13b66accf52311f30a0db42147dadea9850cb48cd070028831ae5f5d4b856ab" +dependencies = [ + "concurrent-queue", + "parking", + "pin-project-lite", +] + +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "fallible-iterator" version = "0.3.0" @@ -2822,6 +3014,12 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "parking" +version = "2.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f38d5652c16fde515bb1ecef450ab0f6a219d619a7274976324d5e377f7dceba" + [[package]] name = "parking_lot" version = "0.12.5" @@ -2994,6 +3192,15 @@ dependencies = [ "syn", ] +[[package]] +name = "proc-macro-crate" +version = "3.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e67ba7e9b2b56446f1d419b1d807906278ffa1a658a8a5d8a39dcb1f5a78614f" +dependencies = [ + "toml_edit 0.25.4+spec-1.1.0", +] + [[package]] name = "proc-macro2" version = "1.0.106" @@ -3321,6 +3528,12 @@ version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" +[[package]] +name = "relative-path" +version = "1.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba39f3699c378cd8970968dcbff9c43159ea4cfbd88d43c00b22f2ef10a435d2" + [[package]] name = "reqwest" version = "0.12.28" @@ -3376,6 +3589,53 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rquickjs" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c5227859c4dfc83f428e58f9569bf439e628c8d139020e7faff437e6f5abaa0" +dependencies = [ + "rquickjs-core", + "rquickjs-macro", +] + +[[package]] +name = "rquickjs-core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e82e0ca83028ad5b533b53b96c395bbaab905a5774de4aaf1004eeacafa3d85d" +dependencies = [ + "async-lock", + "relative-path", + "rquickjs-sys", +] + +[[package]] +name = "rquickjs-macro" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b4d2eccd988a924a470a76fbd81a191b22d1f5f4f4619cf5662a8c1ab4ca1db7" +dependencies = [ + "convert_case", + "fnv", + "ident_case", + "indexmap", + "proc-macro-crate", + "proc-macro2", + "quote", + "rquickjs-core", + "syn", +] + +[[package]] +name = "rquickjs-sys" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7fed0097b0b4fbb2a87f6dd3b995a7c64ca56de30007eb7e867dfdfc78324ba5" +dependencies = [ + "cc", +] + [[package]] name = "rustc-demangle" version = "0.1.27" @@ -4101,7 +4361,7 @@ dependencies = [ "serde", "serde_spanned 0.6.9", "toml_datetime 0.6.11", - "toml_edit", + "toml_edit 0.22.27", ] [[package]] @@ -4137,6 +4397,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "toml_datetime" +version = "1.1.1+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.22.27" @@ -4151,6 +4420,18 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.25.4+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7193cbd0ce53dc966037f54351dbbcf0d5a642c7f0038c382ef9e677ce8c13f2" +dependencies = [ + "indexmap", + "toml_datetime 1.1.1+spec-1.1.0", + "toml_parser", + "winnow", +] + [[package]] name = "toml_parser" version = "1.0.9+spec-1.1.0" diff --git a/Cargo.toml b/Cargo.toml index 32a5f8d..023bd32 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,12 +10,17 @@ members = [ "workers/approval-tiers", "workers/bridge", "workers/browser", + "workers/channel-bluesky", + "workers/channel-discord", "workers/channel-email", "workers/channel-linkedin", + "workers/channel-mastodon", + "workers/channel-matrix", "workers/channel-reddit", "workers/channel-signal", "workers/channel-slack", "workers/channel-teams", + "workers/channel-telegram", "workers/channel-twitch", "workers/channel-webex", "workers/channel-whatsapp", @@ -46,8 +51,12 @@ members = [ "workers/rate-limiter", "workers/realm", "workers/security", + "workers/security-headers", + "workers/security-map", + "workers/security-zeroize", "workers/session-lifecycle", "workers/session-replay", + "workers/skill-security", "workers/skillkit-bridge", "workers/streaming", "workers/telemetry", diff --git a/README.md b/README.md index 5a5ff87..88e80fd 100644 --- a/README.md +++ b/README.md @@ -6,933 +6,144 @@

-

The agent OS that evolves itself.

- -

- Three primitives. 51 workers. Agents that write, test, and improve their own functions at runtime.
- Built on iii-engine — 18% overhead vs raw function calls. -

+

An agent OS built as 65 narrow workers on iii primitives.

License - Tests - Workers - LLM Providers - Security Layers + Workers + Functions + iii-sdk

- Website · + Architecture · Quickstart · - Why AgentOS · - Architecture · - Intelligence · - GitHub + Workers · + GitHub

```bash -curl -fsSL https://raw.githubusercontent.com/iii-hq/agentos/main/scripts/install.sh | sh -agentos start +curl -fsSL https://install.iii.dev/iii/main/install.sh | sh +git clone https://github.com/iii-experimental/agentos && cd agentos +cargo build --workspace --release +iii --config config.yaml & +./target/release/agentos-* & ``` -Two commands. Zero config. Boots the engine, 51 workers, and a 25-screen TUI dashboard. +That's it. Engine boots, 64 Rust workers connect, 257 functions register over WebSocket on port 49134. -## What Makes This Different +## Three primitives -Most agent frameworks give you chains, graphs, and prompt templates. AgentOS gives you three primitives: +| Primitive | What it does | +|-----------|--------------| +| **Worker** | One Rust binary per domain. Connects to the engine, registers functions. | +| **Function** | A named handler — `agent::chat`, `llm::route`, `memory::search`. | +| **Trigger** | Binds a function to HTTP, cron, or pub/sub event. | -| Primitive | What It Does | -|-----------|-------------| -| **Worker** | A process that connects to the engine and registers functions | -| **Function** | A callable unit of work — agents, tools, security, memory, everything | -| **Trigger** | Binds a function to HTTP, cron, queue, or pub/sub | +Every capability — reasoning, state, sandboxing, channels — is a Function on a Worker. There is no shared in-process bus. Workers talk to each other via `iii.trigger`. -That's it. Every capability — from LLM routing to swarm coordination to self-evolving functions — is a plain function on the [iii-engine](https://iii.dev) bus. No frameworks, no vendor lock-in, no magic. +## Workers -**What you get out of the box:** -- **Self-evolving functions** — agents write, test, and improve their own code at runtime -- **Self-curating memory** — agents reflect on conversations and extract durable facts automatically -- **Multi-agent orchestration** — plan features, decompose tasks, spawn workers, monitor progress -- **Session recovery** — health scanning detects stale/dead agents and auto-recovers them -- **18 security layers** — RBAC, WASM sandbox, Merkle audit, encrypted vault, timing-safe HMAC -- **25 LLM providers** — swap between Anthropic, OpenAI, Google, Ollama, or 20 others. One config change. -- **40 channel adapters** — Slack, Discord, WhatsApp, Telegram, and 36 more - -

- AgentOS architecture: Rust (18 crates), TypeScript (51 workers), Python (embeddings) on iii-engine -

- -## Install - -```bash -curl -fsSL https://raw.githubusercontent.com/iii-hq/agentos/main/scripts/install.sh | sh -``` +64 Rust + 1 Python, grouped by responsibility: -Installs both **iii-engine** and **agentos** binary to `~/.local/bin`. +| Group | Workers | +|-------|---------| +| Reasoning | `agent-core`, `llm-router`, `council`, `swarm`, `directive`, `mission` | +| State | `realm`, `memory`, `ledger`, `vault`, `context-manager`, `context-cache` | +| Coordination | `orchestrator`, `workflow`, `hierarchy`, `coordination`, `task-decomposer` | +| Execution | `wasm-sandbox`, `browser`, `code-agent`, `hand-runner`, `lsp-tools` | +| Safety | `security`, `security-headers`, `security-map`, `security-zeroize`, `skill-security`, `approval`, `approval-tiers`, `rate-limiter`, `loop-guard` | +| Surfaces | `a2a`, `a2a-cards`, `mcp-client`, `skillkit-bridge`, `bridge`, `streaming` | +| Channels | `channel-{bluesky, discord, email, linkedin, mastodon, matrix, reddit, signal, slack, teams, telegram, twitch, webex, whatsapp}` | +| Telemetry | `telemetry`, `pulse`, `session-lifecycle`, `session-replay`, `feedback`, `eval`, `evolve`, `hashline`, `hooks`, `cron` | +| Embeddings | `embedding` (Python) | -```bash -AGENTOS_VERSION=v0.1.0 curl -fsSL ... | sh # specific version -BIN_DIR=/usr/local/bin curl -fsSL ... | sh # custom install dir -``` +Each worker ships `iii.worker.yaml` declaring its registry shape. CI validates conformance on every PR. ## Quickstart -```bash -# Install and start (zero config — auto-detects ANTHROPIC_API_KEY from env) -agentos start - -# Chat with an agent -agentos chat - -# Open the 25-screen terminal dashboard -agentos tui -``` - -`agentos start` handles everything: creates `~/.agentos/` on first run, downloads iii-engine if missing, generates config, boots the engine and all 51 workers. Ctrl+C to stop. - -### Set an API key (if not in environment) +Boot the engine and a single worker: ```bash -agentos config set-key anthropic sk-ant-... +iii --config config.yaml & +./target/release/agentos-realm & ``` -### Development mode +Call a function via HTTP (engine's `iii-http` listens on port 3111): ```bash -# Start with hot reload -agentos init --quick -npm run dev - -# Run tests -npm test # 1,748 TypeScript tests -cargo test --workspace # Rust crate tests +curl -X POST http://127.0.0.1:3111/v1/realms \ + -H 'Content-Type: application/json' \ + -d '{"name":"prod","description":"production"}' ``` -## Architecture - -**Polyglot by design** — Rust for hot path, TypeScript for iteration speed, Python for ML. - -Every component connects to the iii-engine over WebSocket and registers functions. Functions call other functions via `trigger()`. That's it. - -### Rust Crates (18) - -| Crate | Purpose | LOC | -|-------|---------|-----| -| `agent-core` | ReAct agent loop — orchestrates LLM calls, tool execution, memory | ~320 | -| `security` | RBAC, Merkle audit chain, taint tracking, Ed25519 signing, tool policy, Docker sandbox | ~700 | -| `memory` | Session/episodic memory, recall, consolidation, eviction | ~840 | -| `llm-router` | Routes to 25 LLM providers with complexity-based model selection | ~320 | -| `wasm-sandbox` | Executes untrusted code in WASM via wasmtime | ~180 | -| `cli` | 50+ commands across 15 subcommand groups | ~700 | -| `tui` | 25-screen terminal dashboard (ratatui) | ~350 | -| `api` | Rust HTTP API layer | ~200 | -| `hand-runner` | Autonomous hand execution engine | ~150 | -| `workflow` | Rust workflow step execution | ~200 | -| `realm` | Multi-tenant isolation domains with export/import | ~280 | -| `hierarchy` | Agent org structure with cycle-safe DFS tree building | ~250 | -| `directive` | Hierarchical goal alignment with optimistic concurrency | ~280 | -| `mission` | Task lifecycle with state machine and atomic checkout | ~350 | -| `ledger` | Budget enforcement with soft/hard limits and versioned CAS | ~300 | -| `council` | Governance proposals with SHA-256 merkle-chained audit trail | ~450 | -| `pulse` | Scheduled agent invocation with context-aware ticks | ~250 | -| `bridge` | External runtime adapters (Process/HTTP/ClaudeCode/Codex/Cursor/OpenCode) | ~300 | - -### TypeScript Workers (51) - -| Worker | Purpose | -|--------|---------| -| `api.ts` | OpenAI-compatible REST API | -| `agent-core.ts` | Agent loop with fail-closed security gates | -| `workflow.ts` | Multi-step workflow engine (5 step modes) | -| `tools.ts` | Built-in tool registry (22 tools) | -| `tools-extended.ts` | Extended tools (38 tools: scheduling, media, data, system, code) | -| `security.ts` | Prompt injection scanning, content filtering | -| `security-map.ts` | Mutual Authentication Protocol (MAP) with timing-safe HMAC | -| `security-headers.ts` | HTTP security headers | -| `security-zeroize.ts` | Sensitive data auto-zeroing | -| `skills.ts` | Skill discovery and execution | -| `skill-security.ts` | Skill manifest validation and sandboxing | -| `skillkit-bridge.ts` | SkillKit marketplace integration (15K+ skills) | -| `streaming.ts` | SSE streaming for chat responses | -| `approval.ts` | Human-in-the-loop approval gates | -| `approval-tiers.ts` | Auto/async/sync approval tier classification | -| `memory.ts` | TypeScript memory layer (profile modeling, session search) | -| `memory-reflection.ts` | Self-curating memory reflection (auto-extracts facts, discovers skills) | -| `llm-router.ts` | 25-provider LLM routing with complexity scoring | -| `model-catalog.ts` | 47 models with pricing and capability metadata | -| `mcp-client.ts` | Model Context Protocol client | -| `a2a.ts` | Agent-to-Agent protocol (JSON-RPC 2.0) | -| `a2a-cards.ts` | A2A agent card discovery | -| `vault.ts` | AES-256-GCM encrypted vault with PBKDF2 key derivation | -| `browser.ts` | Headless browser automation with SSRF protection | -| `context-manager.ts` | Context window budget management | -| `context-monitor.ts` | Structured context compression (5-phase with iterative summaries) | -| `cost-tracker.ts` | Per-agent cost tracking | -| `hooks.ts` | Lifecycle hook execution (8 hook types) | -| `rate-limiter.ts` | GCRA rate limiting | -| `loop-guard.ts` | Infinite loop detection with circuit breaker | -| `swarm.ts` | Multi-agent swarm coordination | -| `knowledge-graph.ts` | Entity-relation knowledge graph | -| `session-replay.ts` | Session action recording and playback | -| `tool-profiles.ts` | Tool filtering profiles (8 profiles) | -| `hand-runner.ts` | Autonomous hand execution | -| `migration.ts` | Framework migration utilities | -| `dashboard.ts` | Dashboard data aggregation | -| `telemetry.ts` | OpenTelemetry metrics (SDK-native, auto worker CPU/memory/event-loop) | -| `cron.ts` | Scheduled jobs (session cleanup, cost aggregation, rate limit reset) | -| `code-agent.ts` | Specialized coding agent | -| `evolve.ts` | Dynamic function evolution (LLM code gen + vm sandbox + DAG branching) | -| `eval.ts` | Production eval harness (pluggable scorers, suites, inline auto-scoring) | -| `feedback.ts` | Feedback loop (auto-review, improve/kill, promote, signal injection) | -| `artifact-dag.ts` | DAG-based content artifact exchange (push/fetch/diff/leaves/history) | -| `coordination.ts` | Inter-agent coordination board (channels, threaded posts, pinning) | -| `session-lifecycle.ts` | Session state machine with declarative reaction rules | -| `task-decomposer.ts` | Recursive task decomposition with hierarchical IDs | -| `recovery.ts` | Session health scanning and automated recovery | -| `orchestrator.ts` | Multi-agent orchestrator (plan, decompose, spawn, monitor) | -| `channels/*.ts` | 40 channel adapters | - -### Python Workers (1) - -| Worker | Purpose | -|--------|---------| -| `embedding/main.py` | Text embeddings via SentenceTransformers (fallback: hash-based) | - -## Tools (60+) - -Tools are organized into categories and filtered by profile to optimize token usage: - -| Category | Count | Examples | -|----------|-------|---------| -| File Operations | 6 | read, write, list, search, apply_patch, watch | -| Web | 4 | search, fetch, screenshot, browser actions | -| Code | 5 | analyze, format, lint, test, explain | -| Shell | 2 | exec, spawn | -| Data | 8 | json_parse/stringify/query/transform, csv_parse/stringify, yaml_parse/stringify | -| Memory | 3 | store, recall, search | -| Scheduling | 4 | schedule_reminder, cron_create/list/delete | -| Collaboration | 4 | todo_create/list/update/delete | -| Media | 5 | image_analyze, audio_transcribe, tts_speak, media_download, image_generate_prompt | -| Knowledge Graph | 3 | kg_add, kg_query, kg_visualize | -| Inter-Agent | 3 | agent_list, agent_delegate, channel_send | -| System | 5 | env_get, system_info, process_list, disk_usage, network_check | -| Utility | 4 | uuid_generate, hash_compute, regex_match/replace | -| Database | 4 | db_query, db_insert, db_update, db_delete | - -### Tool Profiles - -Profiles filter tools per agent to reduce token overhead: - -| Profile | Tools Included | -|---------|---------------| -| `chat` | web_search, web_fetch, memory_recall, memory_store | -| `code` | file_*, shell_exec, code_*, apply_patch | -| `research` | web_*, browser_*, memory_* | -| `ops` | shell_exec, system_*, process_*, disk_*, network_* | -| `data` | json_*, csv_*, yaml_*, regex_*, file_* | -| `full` | All tools | - -## Control Plane - -The control plane layer provides full agent orchestration — multi-tenant isolation, org hierarchies, goal alignment, task management, budget enforcement, governance, scheduling, and external runtime adapters. - -All 8 crates are Rust workers on iii-sdk, exposing 45 functions via 44 HTTP endpoints and 2 PubSub triggers. +Or trigger directly via the iii SDK: ```rust -trigger("realm::create", json!({ "name": "production", "description": "Prod environment" })) -trigger("hierarchy::set", json!({ "realmId": "r-1", "agentId": "agent-ceo", "title": "CEO" })) -trigger("directive::create", json!({ "realmId": "r-1", "title": "Ship v2", "level": "realm" })) -trigger("mission::create", json!({ "realmId": "r-1", "title": "Build auth", "directiveId": "dir-1" })) -trigger("ledger::set_budget", json!({ "realmId": "r-1", "monthlyCents": 500000 })) -trigger("council::submit", json!({ "realmId": "r-1", "kind": "hire_agent", "title": "Hire researcher" })) -trigger("pulse::register", json!({ "realmId": "r-1", "agentId": "agent-ops", "intervalSecs": 300 })) -trigger("bridge::invoke", json!({ "realmId": "r-1", "runtimeId": "rt-1", "input": "Review PR #42" })) -``` - -| Crate | Endpoints | Key Features | -|-------|-----------|-------------| -| `realm` | 7 REST | Multi-tenant isolation, export/import with secret scrubbing | -| `hierarchy` | 5 REST | Org charts, cycle detection (DFS), capability search, chain-of-command | -| `directive` | 5 REST | Goal trees, ancestry tracing, optimistic concurrency (CAS) | -| `mission` | 7 REST | State machine (Backlog→Queued→Active→Review→Complete), atomic checkout | -| `ledger` | 4 REST + 1 PubSub | Soft/hard budget limits, versioned CAS, spend tracking by agent/model/provider | -| `council` | 6 REST + 1 PubSub | Proposal governance, SHA-256 merkle audit chain, agent override (pause/resume/terminate) | -| `pulse` | 4 REST | Scheduled invocation, context modes (thin/full), budget-gated ticks | -| `bridge` | 5 REST | 6 runtime adapters, path traversal prevention, process timeout, JoinHandle cleanup | - -## Swarms - -Multi-agent swarm coordination for complex tasks: - -```typescript -trigger("swarm::create", { - goal: "Research and write a technical blog post", - agents: ["researcher", "writer", "editor"], - strategy: "sequential" -}) -``` - -Strategies: `parallel`, `sequential`, `consensus`, `hierarchical`. - -## Knowledge Graph - -Entity-relation graph for structured knowledge: - -```typescript -trigger("kg::add_entity", { type: "project", name: "agentos", properties: { ... } }) -trigger("kg::add_relation", { from: "agentos", to: "iii-engine", type: "built_on" }) -trigger("kg::query", { entity: "agentos", depth: 2 }) -``` - -## Artifact DAG - -Git-style DAG-based content exchange for swarms. Agents push versioned artifacts with parent references, enabling branching histories, diffs, and frontier discovery. - -```typescript -const node = await trigger("artifact::push", { - content: { report: "Q1 analysis..." }, - parentIds: ["art_abc123"], - agentId: "analyst-1", - swarmId: "swarm_research", - metadata: { type: "report", format: "markdown" } -}) - -const leaves = await trigger("artifact::leaves", { swarmId: "swarm_research" }) - -const diff = await trigger("artifact::diff", { nodeIdA: "art_abc123", nodeIdB: node.nodeId }) -``` - -**6 functions**: `artifact::push`, `artifact::fetch`, `artifact::children`, `artifact::leaves`, `artifact::diff`, `artifact::history` - -- Content-addressed with SHA-256 hashing (first 16 hex chars) -- Parent validation on push (all parentIds must exist) -- Swarm-scoped publishing via PubSub (`artifact:{swarmId}` topic) -- Frontier discovery (leaves with no children, excluding orphans) -- 512KB max content size per artifact - -## Coordination Board - -Persistent inter-agent communication channels with threaded posts and pinning. - -```typescript -await trigger("coord::create_channel", { - name: "design-decisions", - description: "Architecture discussions", - agentId: "architect-1" -}) - -await trigger("coord::post", { - channelId: "chan_abc123", - agentId: "architect-1", - content: "Proposal: switch to event sourcing for audit trail" -}) - -await trigger("coord::reply", { - channelId: "chan_abc123", - parentId: "post_xyz789", - agentId: "reviewer-1", - content: "Agreed — aligns with our immutability requirements" -}) - -await trigger("coord::pin", { channelId: "chan_abc123", postId: "post_xyz789" }) -``` - -**6 functions**: `coord::create_channel`, `coord::post`, `coord::reply`, `coord::list_channels`, `coord::read`, `coord::pin` - -- Threaded replies via `parentId` -- Pin/unpin with 25-pin limit per channel -- 1,000-post limit per channel -- Auth enforced on post and reply (HTTP requests) -- PubSub notifications on `coord:{channelId}` topic - -## Agent Intelligence - -Self-improving agent capabilities that run automatically during normal operation. - -### Memory Reflection - -Agents periodically curate their own memory. Every 5 turns, a background reflection extracts durable facts from the conversation and stores them as `[Curated]` entries for future recall. - -```typescript -trigger("reflect::check_turn", { agentId: "agent-1", sessionId: "s1", iterations: 3 }) -trigger("reflect::curate_memory", { agentId: "agent-1", sessionId: "s1" }) -trigger("reflect::discover_skills", { agentId: "agent-1", sessionId: "s1", iterations: 8 }) -``` - -- **3 functions**: `reflect::check_turn`, `reflect::curate_memory`, `reflect::discover_skills` -- Auto-extracts preferences, decisions, learnings from conversation -- Auto-discovers reusable skills from tool-heavy sessions (5+ iterations) via `evolve::generate` -- Builds user profile automatically (`memory::user_profile::update`) -- Fire-and-forget — never blocks the chat response - -### Structured Context Compression - -5-phase compression that preserves context quality: - -1. **Prune** — truncate old tool results (>200 chars) -2. **Sanitize** — fix orphaned tool call/result pairs -3. **Merge** — combine consecutive system messages -4. **Protect** — reserve 40% of token budget for recent context -5. **Summarize** — LLM generates structured summary (Goal / Progress / Decisions / Files / Next Steps / Critical Context) - -Iterative: detects existing summaries and updates them instead of re-summarizing from scratch. - -### User Profile Modeling - -Agents build a persistent understanding of who they're working with: - -```typescript -trigger("memory::user_profile::update", { agentId: "agent-1", updates: { workStyle: "concise" } }) -trigger("memory::user_profile::get", { agentId: "agent-1" }) -trigger("memory::session_search", { agentId: "agent-1", query: "deployment pipeline" }) -``` - -- Profile auto-injected as `[User Profile]` system message in every chat -- Cross-session search with keyword + recency scoring -- Deep-merge updates (objects merged, arrays concatenated) - -### Smart Model Routing - -Complexity-aware model selection with per-agent tier support: - -```typescript -trigger("llm::route", { message: "Step 1: create DB. Then build API.", toolCount: 5, agentTier: "premium" }) -``` - -- Detects multi-step instructions, multiple code blocks, reasoning keywords -- `economy` tier: always routes to fastest model -- `premium` tier: never below mid-tier -- Default: automatic complexity scoring - -## Orchestration - -Multi-agent coordination and lifecycle management. - -### Session Lifecycle - -Formal state machine for agent sessions with declarative reaction rules: - -```text -spawning → working → blocked → working (retry) -working → pr_open → review → merged → done -working → failed → recovering → working -``` - -```typescript -trigger("lifecycle::transition", { agentId: "agent-1", newState: "working" }) -trigger("lifecycle::add_reaction", { - from: "working", to: "blocked", - action: "send_to_agent", - payload: { message: "You appear stuck. What's blocking you?" }, - escalateAfter: 3, -}) -``` - -- **5 functions**: `lifecycle::transition`, `lifecycle::get_state`, `lifecycle::add_reaction`, `lifecycle::list_reactions`, `lifecycle::check_all` -- Validates transitions, fires hooks on state changes -- 4 reaction types: `send_to_agent`, `notify`, `escalate`, `auto_recover` -- Auto-scans all agents every 2 minutes via cron - -### Task Decomposition - -Recursive task breakdown with hierarchical IDs and status propagation: - -```typescript -const { rootId, tasks } = await trigger("task::decompose", { - description: "Build a user authentication system with OAuth and rate limiting" -}) - -await trigger("task::spawn_workers", { rootId }) -``` - -- **5 functions**: `task::decompose`, `task::get`, `task::update_status`, `task::list`, `task::spawn_workers` -- Hierarchical IDs: `1` → `1.1` → `1.1.2` -- Status propagation: all siblings complete → parent complete, child fails → parent blocked -- Each leaf task gets its own agent via `tool::agent_spawn` - -### Session Recovery - -Automated health scanning and recovery for agent sessions: - -```typescript -trigger("recovery::scan", {}) -trigger("recovery::recover", { agentId: "agent-1" }) -``` - -- **5 functions**: `recovery::scan`, `recovery::validate`, `recovery::classify`, `recovery::recover`, `recovery::report` -- Classification: `healthy`, `degraded`, `dead`, `unrecoverable` -- Degraded → wake-up message, Dead → circuit breaker reset + restart, Unrecoverable → escalate to human -- Auto-scans every 10 minutes via cron - -### Orchestrator - -Meta-agent that plans and coordinates multi-agent work: - -```typescript -const { planId, analysis } = await trigger("orchestrator::plan", { - description: "Build a real-time analytics dashboard", - autoExecute: true, -}) - -await trigger("orchestrator::status", { planId }) -await trigger("orchestrator::intervene", { planId, action: "pause" }) -``` - -- **4 functions**: `orchestrator::plan`, `orchestrator::execute`, `orchestrator::status`, `orchestrator::intervene` -- LLM analyzes feature complexity, estimates agents needed, generates decomposition prompt -- Decomposes tasks, registers lifecycle reactions, spawns workers -- Human intervention: pause, resume, cancel, redirect - -### Signal Injection - -Push external signals (CI failures, review comments) directly into agent sessions: - -```typescript -trigger("feedback::inject_signal", { - agentId: "agent-1", - signalType: "ci_failure", - content: "Build failed: TypeError in auth.ts line 42", - metadata: { source: "github-actions" }, -}) -``` - -- **3 functions**: `feedback::inject_signal`, `feedback::register_source`, `feedback::list_signals` -- Signal types: `ci_failure`, `review_comment`, `merge_conflict`, `dependency_update`, `custom` -- Auto-routes to agent via `tool::agent_send` - -## Dynamic Function Evolution - -Agents can write, register, evaluate, and improve functions at runtime. The evolve-eval-feedback loop turns AgentOS from a static orchestrator into a self-evolving system. - -```text -Agent goal/spec - ↓ -evolve::generate → LLM writes function code - ↓ -evolve::register → security scan → vm sandbox → register on iii bus - ↓ -eval::suite → invoke N times → score each → aggregate - ↓ -feedback::review → analyze scores → KEEP / IMPROVE / KILL - ↓ ↓ ↓ - ↓ evolve::generate evolve::unregister - ↓ (with feedback) - ↓ -feedback::promote → draft → staging → production -``` - -### Evolve (8 functions, 8 endpoints) - -```typescript -trigger("evolve::generate", { goal: "Double a number", name: "doubler", agentId: "agent-1" }) -trigger("evolve::register", { functionId: "evolved::doubler_v1" }) -trigger("evolve::list", { status: "production" }) - -trigger("evolve::fork", { functionId: "evolved::doubler_v1", goal: "Handle negative numbers", agentId: "agent-2" }) -trigger("evolve::leaves", { name: "doubler" }) -trigger("evolve::lineage", { functionId: "evolved::doubler_v3" }) -``` - -- LLM generates function code from a goal/spec -- Code runs in a `node:vm` sandbox (no fetch, fs, process, require, setTimeout, eval) -- Sandboxed `trigger()` proxy only allows `evolved::`, `tool::`, `llm::` prefixes -- Pre-registration security scan via `skill::pipeline` -- Lifecycle: `draft` → `staging` → `production` → `deprecated` → `killed` -- **DAG branching**: fork from any version (not just latest), discover frontier leaves, trace full lineage to root - -### Eval (6 functions, 5 endpoints) - -```typescript -trigger("eval::run", { functionId: "evolved::doubler_v1", input: { value: 5 }, expected: { doubled: 10 } }) -trigger("eval::suite", { suiteId: "suite_doubler" }) -trigger("eval::compare", { functionIdA: "evolved::doubler_v1", functionIdB: "evolved::doubler_v2", testCases: [...] }) -``` - -- Pluggable scorers: `exact_match`, `llm_judge`, `semantic_similarity`, `custom` -- Inline auto-scoring on every evolved function call (configurable: auto/sampled/manual/off) -- Score formula: correctness (50%) + safety (25%) + latency (15%) + cost (10%) - -### Feedback (7 functions, 6 endpoints + cron) - -```typescript -trigger("feedback::review", { functionId: "evolved::doubler_v1" }) -trigger("feedback::promote", { functionId: "evolved::doubler_v1", targetStatus: "production" }) -trigger("feedback::leaderboard", {}) -``` - -- Auto-review every 6 hours via cron -- Decision algorithm: kill (≥3 failures in last 5), improve (avg < 0.5), keep -- Recursive improvement: up to 3 attempts with LLM feedback -- Leaderboard ranking by overall score - -## Session Replay - -Record and replay full agent sessions for debugging: - -```typescript -trigger("replay::record", { sessionId, agentId, action: "tool_call", data: { toolId, args } }) -trigger("replay::get", { sessionId }) -trigger("replay::summary", { sessionId }) -``` - -## CLI Commands - -``` -agentos init [--quick] Initialize project -agentos start Start all workers -agentos stop Stop all workers -agentos status [--json] Show system status -agentos health [--json] Health check -agentos doctor [--json] [--repair] Diagnose issues - -agentos agent new [template] Create agent from template -agentos agent list List all agents -agentos agent chat Interactive chat -agentos agent kill Stop an agent -agentos agent spawn