diff --git a/Cargo.lock b/Cargo.lock
index ad03715..f76ff73 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -1690,7 +1690,7 @@ checksum = "9e1c906769ad99c88eaa54e728060edef082f8e358ff32030cb7c7d315e81109"
[[package]]
name = "tree-ring-memory-cli"
-version = "0.15.2"
+version = "0.15.3"
dependencies = [
"chrono",
"clap",
@@ -1709,7 +1709,7 @@ dependencies = [
[[package]]
name = "tree-ring-memory-core"
-version = "0.15.2"
+version = "0.15.3"
dependencies = [
"chrono",
"libc",
@@ -1725,7 +1725,7 @@ dependencies = [
[[package]]
name = "tree-ring-memory-sqlite"
-version = "0.15.2"
+version = "0.15.3"
dependencies = [
"rusqlite",
"serde",
diff --git a/Cargo.toml b/Cargo.toml
index e3e6b04..8de24e2 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -7,7 +7,7 @@ members = [
resolver = "2"
[workspace.package]
-version = "0.15.2"
+version = "0.15.3"
edition = "2021"
license = "MIT"
authors = ["TerminallyLazy"]
diff --git a/README.md b/README.md
index 9204288..6632517 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ framework-agnostic and does not replace either protocol.
Tree Ring Memory is in protocol-preview status. Current launch links:
- Launch page:
-- Launch release:
+- Launch release:
- Launch discussion:
- Rust-native CLI article:
- Feedback issue:
@@ -259,8 +259,8 @@ sh install.sh --project --init --release latest
sh install.sh --global --install-dir "$HOME/.local"
sh install.sh --no-animation # stable output; kept for explicit script usage
sh install.sh --no-path-update
-sh install.sh --release 0.15.2
-sh install.sh --archive-url https://example/tree-ring-memory-0.15.2-darwin-arm64.tar.gz --archive-sha256
+sh install.sh --release 0.15.3
+sh install.sh --archive-url https://example/tree-ring-memory-0.15.3-darwin-arm64.tar.gz --archive-sha256
```
After install, rerun onboarding anytime:
diff --git a/crates/tree-ring-memory-cli/Cargo.toml b/crates/tree-ring-memory-cli/Cargo.toml
index 3105496..aaef226 100644
--- a/crates/tree-ring-memory-cli/Cargo.toml
+++ b/crates/tree-ring-memory-cli/Cargo.toml
@@ -26,8 +26,8 @@ semver.workspace = true
sha2.workspace = true
tempfile.workspace = true
uuid.workspace = true
-tree-ring-memory-core = { path = "../tree-ring-memory-core", version = "0.15.2" }
-tree-ring-memory-sqlite = { path = "../tree-ring-memory-sqlite", version = "0.15.2" }
+tree-ring-memory-core = { path = "../tree-ring-memory-core", version = "0.15.3" }
+tree-ring-memory-sqlite = { path = "../tree-ring-memory-sqlite", version = "0.15.3" }
[dev-dependencies]
rusqlite.workspace = true
diff --git a/crates/tree-ring-memory-cli/src/activation/adapters.rs b/crates/tree-ring-memory-cli/src/activation/adapters.rs
index bba9c2c..0687a6d 100644
--- a/crates/tree-ring-memory-cli/src/activation/adapters.rs
+++ b/crates/tree-ring-memory-cli/src/activation/adapters.rs
@@ -15,9 +15,8 @@ pub const AGENT_ZERO_PLUGIN_MANIFEST_ENV: &str = "TREE_RING_AGENT_ZERO_PLUGIN_MA
const AGENT_ZERO_CAPABILITY_FILE: &str = "activation-capability.json";
const AGENT_ZERO_CAPABILITY_KIND: &str = "tree-ring-agent-zero-plugin-capability";
-const AGENT_ZERO_PLUGIN_VERSION: &str = "3.1.0";
-const AGENT_ZERO_TREE_RING_MIN_VERSION: &str = "0.14.0";
-const AGENT_ZERO_TREE_RING_MINOR_VERSION: &str = "0.14";
+const AGENT_ZERO_CAPABILITY_CONTRACTS: &[(&str, &str, &str)] =
+ &[("3.1.0", "0.14.0", "0.14"), ("3.2.0", "0.15.3", "0.15")];
const MAX_AGENT_ZERO_CAPABILITY_BYTES: u64 = 16 * 1024;
/// Project paths used by activation. Adapter plans always target this root.
@@ -163,13 +162,18 @@ fn read_agent_zero_plugin_manifest(
&read_regular_file_no_follow(&descriptor)?,
)
.ok()?;
+ let trusted_contract = AGENT_ZERO_CAPABILITY_CONTRACTS.iter().any(
+ |(plugin_version, minimum_version, minor_version)| {
+ capability.plugin_version == *plugin_version
+ && capability.tree_ring_version.min == *minimum_version
+ && capability.tree_ring_version.minor == *minor_version
+ },
+ );
if capability.schema_version != 1
|| capability.kind != AGENT_ZERO_CAPABILITY_KIND
|| capability.plugin_id != AGENT_ZERO_PLUGIN_ID
- || capability.plugin_version != AGENT_ZERO_PLUGIN_VERSION
|| capability.activation_protocol_version != ACTIVATION_PROTOCOL_VERSION
- || capability.tree_ring_version.min != AGENT_ZERO_TREE_RING_MIN_VERSION
- || capability.tree_ring_version.minor != AGENT_ZERO_TREE_RING_MINOR_VERSION
+ || !trusted_contract
|| !capability.enabled
{
return None;
@@ -177,8 +181,11 @@ fn read_agent_zero_plugin_manifest(
let plugin_yaml = descriptor.parent()?.join("plugin.yaml");
let plugin_yaml = read_regular_file_no_follow(&plugin_yaml)?;
- plugin_yaml_matches_capability(std::str::from_utf8(&plugin_yaml).ok()?)
- .then_some(AgentZeroPluginManifest::compatible())
+ plugin_yaml_matches_capability(
+ std::str::from_utf8(&plugin_yaml).ok()?,
+ &capability.plugin_version,
+ )
+ .then_some(AgentZeroPluginManifest::compatible())
}
/// Opens one regular capability file without following its final path entry.
@@ -215,7 +222,7 @@ fn read_regular_file_no_follow(_path: &Path) -> Option> {
None
}
-fn plugin_yaml_matches_capability(plugin_yaml: &str) -> bool {
+fn plugin_yaml_matches_capability(plugin_yaml: &str, expected_version: &str) -> bool {
let mut name = None;
let mut version = None;
for line in plugin_yaml.lines() {
@@ -237,7 +244,7 @@ fn plugin_yaml_matches_capability(plugin_yaml: &str) -> bool {
_ => {}
}
}
- name == Some(AGENT_ZERO_PLUGIN_ID) && version == Some(AGENT_ZERO_PLUGIN_VERSION)
+ name == Some(AGENT_ZERO_PLUGIN_ID) && version == Some(expected_version)
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize)]
@@ -1106,6 +1113,37 @@ mod tests {
Some(AgentZeroPluginManifest::compatible())
);
+ fs::write(
+ plugin.join("plugin.yaml"),
+ "name: tree_ring_memory\nversion: 3.1.0\n",
+ )
+ .unwrap();
+ fs::write(
+ &descriptor,
+ r#"{"schema_version":1,"kind":"tree-ring-agent-zero-plugin-capability","plugin_id":"tree_ring_memory","plugin_version":"3.1.0","activation_protocol_version":1,"tree_ring_version":{"min":"0.14.0","minor":"0.14"},"enabled":true}"#,
+ )
+ .unwrap();
+ assert_eq!(
+ read_agent_zero_plugin_manifest(&project, &descriptor),
+ Some(AgentZeroPluginManifest::compatible())
+ );
+
+ fs::write(&descriptor, capability_document(true)).unwrap();
+ fs::write(
+ plugin.join("plugin.yaml"),
+ "name: tree_ring_memory\nversion: 3.2.0\n",
+ )
+ .unwrap();
+ fs::write(
+ &descriptor,
+ capability_document(true).replace(
+ "\"min\":\"0.15.3\",\"minor\":\"0.15\"",
+ "\"min\":\"0.14.0\",\"minor\":\"0.14\"",
+ ),
+ )
+ .unwrap();
+ assert!(read_agent_zero_plugin_manifest(&project, &descriptor).is_none());
+
fs::write(&descriptor, capability_document(false)).unwrap();
assert!(read_agent_zero_plugin_manifest(&project, &descriptor).is_none());
@@ -1125,7 +1163,7 @@ mod tests {
assert!(read_agent_zero_plugin_manifest(&project, &descriptor).is_none());
fs::write(
plugin.join("plugin.yaml"),
- "name: tree_ring_memory\nversion: 3.1.0\n",
+ "name: tree_ring_memory\nversion: 3.2.0\n",
)
.unwrap();
assert!(
@@ -1145,7 +1183,7 @@ mod tests {
fs::create_dir_all(&symlink_parent).unwrap();
fs::write(
symlink_parent.join("plugin.yaml"),
- "name: tree_ring_memory\nversion: 3.1.0\n",
+ "name: tree_ring_memory\nversion: 3.2.0\n",
)
.unwrap();
let symlink_descriptor = symlink_parent.join(AGENT_ZERO_CAPABILITY_FILE);
@@ -1157,7 +1195,7 @@ mod tests {
fn write_capability_descriptor(plugin: &Path, enabled: bool) -> PathBuf {
fs::write(
plugin.join("plugin.yaml"),
- "name: tree_ring_memory\nversion: 3.1.0\n",
+ "name: tree_ring_memory\nversion: 3.2.0\n",
)
.unwrap();
let descriptor = plugin.join(AGENT_ZERO_CAPABILITY_FILE);
@@ -1167,7 +1205,7 @@ mod tests {
fn capability_document(enabled: bool) -> String {
format!(
- r#"{{"schema_version":1,"kind":"tree-ring-agent-zero-plugin-capability","plugin_id":"tree_ring_memory","plugin_version":"3.1.0","activation_protocol_version":1,"tree_ring_version":{{"min":"0.14.0","minor":"0.14"}},"enabled":{enabled}}}"#
+ r#"{{"schema_version":1,"kind":"tree-ring-agent-zero-plugin-capability","plugin_id":"tree_ring_memory","plugin_version":"3.2.0","activation_protocol_version":1,"tree_ring_version":{{"min":"0.15.3","minor":"0.15"}},"enabled":{enabled}}}"#
)
}
}
diff --git a/crates/tree-ring-memory-cli/tests/harness_activation_acceptance.rs b/crates/tree-ring-memory-cli/tests/harness_activation_acceptance.rs
index c1e572f..0a8606e 100644
--- a/crates/tree-ring-memory-cli/tests/harness_activation_acceptance.rs
+++ b/crates/tree-ring-memory-cli/tests/harness_activation_acceptance.rs
@@ -500,7 +500,7 @@ fn agent_zero_plugin_descriptor_bootstraps_passive_binding_then_receipt_backed_a
// state until a live compatible descriptor is available again.
fs::write(
&descriptor,
- r#"{"schema_version":1,"kind":"tree-ring-agent-zero-plugin-capability","plugin_id":"tree_ring_memory","plugin_version":"3.1.0","activation_protocol_version":1,"tree_ring_version":{"min":"0.14.0","minor":"0.14"},"enabled":false}"#,
+ r#"{"schema_version":1,"kind":"tree-ring-agent-zero-plugin-capability","plugin_id":"tree_ring_memory","plugin_version":"3.2.0","activation_protocol_version":1,"tree_ring_version":{"min":"0.15.3","minor":"0.15"},"enabled":false}"#,
)
.unwrap();
let disabled =
@@ -918,13 +918,13 @@ fn install_agent_zero_plugin(base: &Path) -> PathBuf {
fs::create_dir_all(&plugin).unwrap();
fs::write(
plugin.join("plugin.yaml"),
- "name: tree_ring_memory\nversion: 3.1.0\n",
+ "name: tree_ring_memory\nversion: 3.2.0\n",
)
.unwrap();
let descriptor = plugin.join("activation-capability.json");
fs::write(
&descriptor,
- r#"{"schema_version":1,"kind":"tree-ring-agent-zero-plugin-capability","plugin_id":"tree_ring_memory","plugin_version":"3.1.0","activation_protocol_version":1,"tree_ring_version":{"min":"0.14.0","minor":"0.14"},"enabled":true}"#,
+ r#"{"schema_version":1,"kind":"tree-ring-agent-zero-plugin-capability","plugin_id":"tree_ring_memory","plugin_version":"3.2.0","activation_protocol_version":1,"tree_ring_version":{"min":"0.15.3","minor":"0.15"},"enabled":true}"#,
)
.unwrap();
descriptor
diff --git a/crates/tree-ring-memory-sqlite/Cargo.toml b/crates/tree-ring-memory-sqlite/Cargo.toml
index 64911c9..690a3aa 100644
--- a/crates/tree-ring-memory-sqlite/Cargo.toml
+++ b/crates/tree-ring-memory-sqlite/Cargo.toml
@@ -16,7 +16,7 @@ rusqlite.workspace = true
serde.workspace = true
serde_json.workspace = true
sha2.workspace = true
-tree-ring-memory-core = { path = "../tree-ring-memory-core", version = "0.15.2" }
+tree-ring-memory-core = { path = "../tree-ring-memory-core", version = "0.15.3" }
uuid.workspace = true
[dev-dependencies]
diff --git a/docs/press-kit.md b/docs/press-kit.md
index eb73ff7..9280d21 100644
--- a/docs/press-kit.md
+++ b/docs/press-kit.md
@@ -23,10 +23,10 @@ DOX/Revolve adapters, framework discovery, and a terminal TUI.
- Category: AI agents, developer tools, local-first software, Rust CLI
- License: MIT
- Status: protocol-preview
-- Current version: 0.15.2
+- Current version: 0.15.3
- Website:
- Repository:
-- Launch release:
+- Launch release:
- Launch discussion:
- Homebrew tap:
- Feedback:
diff --git a/marketing/README.md b/marketing/README.md
index eca106c..6fd66b3 100644
--- a/marketing/README.md
+++ b/marketing/README.md
@@ -309,7 +309,7 @@ python3 marketing/scripts/build-campaign-cards.py
- Repository: `https://github.com/TerminallyLazy/Tree-Ring-Memory`
- Launch page: `https://terminallylazy.github.io/Tree-Ring-Memory/`
-- Launch release: `https://github.com/TerminallyLazy/Tree-Ring-Memory/releases/tag/v0.15.2`
+- Launch release: `https://github.com/TerminallyLazy/Tree-Ring-Memory/releases/tag/v0.15.3`
- Launch discussion: `https://github.com/TerminallyLazy/Tree-Ring-Memory/discussions/27`
- Homebrew tap: `https://github.com/TerminallyLazy/homebrew-tree-ring`
- Agent-Skills.md main repo listing: