diff --git a/CHANGELOG.md b/CHANGELOG.md index da3f29f..7d40da6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,10 +9,30 @@ Changelog tracking starts with 0.2.0. Prior versions were not tracked. ## [Unreleased] +## [0.7.1] - 2026-06-10 + +This release restores capsule tool discovery and ships the first two post-0.7 additions to the +host surface. The headline is the `tool_describe` macro fix: every tool capsule built on 0.7.0 +answered the kernel's describe fan-out with nothing, so every collector — prompt-builder, the +registry, the `astrid mcp serve` MCP bridge — saw zero tools. Rebuilding a tool capsule against +0.7.1 fixes the producer side — `astrid-sdk = "0.7"` consumers resolve it automatically with no +`Cargo.toml` change — but it is necessary, not sufficient: the consumer side (`prompt-builder`) +must also grant the describe-fan-out ACLs its `collect_tool_schemas()` already uses and invalidate +its KV-cached tool schemas, or the fan-out is `CapabilityDenied` / serves a stale list and a +0.7.1-only rebuild still surfaces zero tools to the LLM (`unicity-astrid/astrid#892`, `#625`). +Riding along, two additive surfaces: the `astrid:process` persistent-process tier +(background children that outlive the pooled capsule instance, reattachable by id from a later +invocation) and `capabilities::enumerate` (a capsule can ground its behaviour in the capabilities +it actually holds instead of hard-coding assumptions). No breaking changes. + +### Fixed + +- **`#[capsule]`-generated `tool_describe` now publishes its descriptor for the describe fan-out.** The macro built the `{tools, description}` descriptor and *returned* it as `CapsuleResult{action:"continue"}` — but under the current ABI an interceptor's return value is not fanned out to the caller, so no SDK-macro tool capsule ever answered `tool.v1.request.describe` and every collector (prompt-builder, registry, the sage-mcp bridge behind `astrid mcp serve`) collected 0 tools. The arm now publishes the descriptor on `tool.v1.response.describe.self` before returning — the `.self` suffix is routing-only (satisfies the route layer's ≥5-segment trailing-`*` arity); the responder's real `source_id` rides in kernel-stamped message metadata. Verified end-to-end: a tool capsule rebuilt against this SDK took the MCP bridge's `tools/list` from 0 tools to its full set. Regression guards: `tool_describe_publishes_response_for_fanout`, `capsule_without_tools_has_no_describe_publish`. (#56) + ### Added -- **`capabilities::enumerate` — list the calling capsule's own held capability names.** The list dual of `capabilities::check`: returns the capability categories declared in this capsule's `[capabilities]` manifest block (`host_process`, `net_connect`, `fs_read`, …) — the names, not the scoped arguments within them (allowlists, `host:port`, paths). Argument-free (the kernel already knows the caller) and infallible — an empty list is the valid "no capabilities" answer — so a reusable capsule can ground its behaviour in what it can actually do instead of hard-coding it, avoiding code-vs-manifest drift. Backed by unicity-astrid/wit#13's `astrid:sys/host.enumerate-capabilities`; contracts submodule bumped accordingly. -- **`process` persistent-process tier — `Command::spawn_persistent`, `PersistentProcess`, and `process::{attach, list, status_many}`.** Mirrors the host `astrid:process@1.0.0` persistent tier: a background child that **outlives the pooled, stateless instance** that started it (unlike `Process`, whose kernel resource is reaped on instance reset). `Command` gains the persistent-only builder knobs — `label`, `keep_stdin_open`, `overflow`, `log_ring_bytes`, `max_lifetime`, `idle_timeout`, `exit_retention`, `limits` — and a `spawn_persistent()` terminal returning a `PersistentProcess` keyed by an opaque `ProcessId`. `PersistentProcess` exposes `status` / `read_logs` (drain) / `read_since` (non-draining cursor, byte-faithful `LogChunk`) / `write_stdin` / `close_stdin` / `signal` / `wait` (bounded) / `stop` (consumes the handle; SIGTERM→grace→SIGKILL, frees the slot) / `release` (consumes). Persist the `ProcessId` (e.g. in KV) and `process::attach(id)` from a later invocation to reattach — the SDK `attach` is a thin id-wrapper, so it works without the host's deferred `attach` resource fn; the first id-keyed call validates ownership. `process::list` / `status_many` enumerate the capsule+principal's persistent processes. New types: `ProcessId`, `ProcessInfo`, `ProcessPhase`, `LogStream`, `LogCursor`, `LogChunk`, `OverflowPolicy`, `ResourceLimits`; `Signal` gains `Stop` / `Cont`. The host's `watch` / `unwatch` lifecycle-event channel and resource-limit enforcement are not yet wired (poll via `status` + bounded `wait`). Backed by unicity-astrid/wit#12; contracts submodule bumped to the merged `astrid:process@1.0.0` persistent tier. +- **`capabilities::enumerate` — list the calling capsule's own held capability names.** The list dual of `capabilities::check`: returns the capability categories declared in this capsule's `[capabilities]` manifest block (`host_process`, `net_connect`, `fs_read`, …) — the names, not the scoped arguments within them (allowlists, `host:port`, paths). Argument-free (the kernel already knows the caller) and infallible — an empty list is the valid "no capabilities" answer — so a reusable capsule can ground its behaviour in what it can actually do instead of hard-coding it, avoiding code-vs-manifest drift. Backed by unicity-astrid/wit#13's `astrid:sys/host.enumerate-capabilities`; contracts submodule bumped accordingly. (#53) +- **`process` persistent-process tier — `Command::spawn_persistent`, `PersistentProcess`, and `process::{attach, list, status_many}`.** Mirrors the host `astrid:process@1.0.0` persistent tier: a background child that **outlives the pooled, stateless instance** that started it (unlike `Process`, whose kernel resource is reaped on instance reset). `Command` gains the persistent-only builder knobs — `label`, `keep_stdin_open`, `overflow`, `log_ring_bytes`, `max_lifetime`, `idle_timeout`, `exit_retention`, `limits` — and a `spawn_persistent()` terminal returning a `PersistentProcess` keyed by an opaque `ProcessId`. `PersistentProcess` exposes `status` / `read_logs` (drain) / `read_since` (non-draining cursor, byte-faithful `LogChunk`) / `write_stdin` / `close_stdin` / `signal` / `wait` (bounded) / `stop` (consumes the handle; SIGTERM→grace→SIGKILL, frees the slot) / `release` (consumes). Persist the `ProcessId` (e.g. in KV) and `process::attach(id)` from a later invocation to reattach — the SDK `attach` is a thin id-wrapper, so it works without the host's deferred `attach` resource fn; the first id-keyed call validates ownership. `process::list` / `status_many` enumerate the capsule+principal's persistent processes. New types: `ProcessId`, `ProcessInfo`, `ProcessPhase`, `LogStream`, `LogCursor`, `LogChunk`, `OverflowPolicy`, `ResourceLimits`; `Signal` gains `Stop` / `Cont`. The host's `watch` / `unwatch` lifecycle-event channel and resource-limit enforcement are not yet wired (poll via `status` + bounded `wait`). Backed by unicity-astrid/wit#12; contracts submodule bumped to the merged `astrid:process@1.0.0` persistent tier. (#53) ## [0.7.0] - 2026-05-26 diff --git a/Cargo.toml b/Cargo.toml index 59569b6..dbe26a9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -10,7 +10,7 @@ members = [ exclude = ["examples/test-capsule"] [workspace.package] -version = "0.7.0" +version = "0.7.1" edition = "2024" authors = ["Joshua J. Bouw ", "Unicity Labs "] license = "MIT OR Apache-2.0" @@ -18,9 +18,9 @@ repository = "https://github.com/unicity-astrid/sdk-rust" rust-version = "1.94" [workspace.dependencies] -astrid-sdk = { path = "astrid-sdk", version = "0.7.0" } -astrid-sdk-macros = { path = "astrid-sdk-macros", version = "0.7.0" } -astrid-sys = { path = "astrid-sys", version = "0.7.0" } +astrid-sdk = { path = "astrid-sdk", version = "0.7.1" } +astrid-sdk-macros = { path = "astrid-sdk-macros", version = "0.7.1" } +astrid-sys = { path = "astrid-sys", version = "0.7.1" } astrid-types = { version = "0.7" } serde = { version = "1.0", features = ["derive"] } serde_json = "1.0"