diff --git a/.gitmodules b/.gitmodules index 9f4f7aa..32cfcd2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,4 @@ [submodule "_blocks/registries"] path = _blocks/registries - url = git@github.com:KeiSeiLab/kei-registries.git + url = https://github.com/KeiSeiLab/kei-registries.git + shallow = true diff --git a/_blocks/registries b/_blocks/registries index 14a1e62..7aaa6a7 160000 --- a/_blocks/registries +++ b/_blocks/registries @@ -1 +1 @@ -Subproject commit 14a1e62db6bff685e9b707fd368e6bbe42e9a819 +Subproject commit 7aaa6a79b00271d9c08ac4f5c1f0e2d523a49da0 diff --git a/_primitives/_rust/kei-model-router/src/agent_shell_dna.rs b/_primitives/_rust/kei-model-router/src/agent_shell_dna.rs index 94629c1..2f91414 100644 --- a/_primitives/_rust/kei-model-router/src/agent_shell_dna.rs +++ b/_primitives/_rust/kei-model-router/src/agent_shell_dna.rs @@ -1,5 +1,10 @@ //! Agent-shell DNA — 5-segment per-invocation identifier. //! +//! **Consumer:** `keisei-marketplace` (not yet wired into kei-model-router's +//! routing/posterior; planned for v0.18 when the marketplace pushes invocation +//! records into the shared ledger). See `docs/DNA-MIGRATION.md` for the +//! two-format coexistence policy. +//! //! Format emitted by `keisei-marketplace/src/lib/cryptoid.ts::agentDna`: //! //! `agent-shell::::::::-` diff --git a/_primitives/_rust/kei-model-router/src/escalate.rs b/_primitives/_rust/kei-model-router/src/escalate.rs index 3e52abb..f808ec1 100644 --- a/_primitives/_rust/kei-model-router/src/escalate.rs +++ b/_primitives/_rust/kei-model-router/src/escalate.rs @@ -109,7 +109,7 @@ mod tests { #[test] fn haiku_escalates_to_sonnet_within_anthropic() { let r = reg(); - assert_eq!(next_model("claude-haiku-4-5", "anthropic", &r), EscalationResult::Next("claude-sonnet-4-6")); + assert_eq!(next_model("claude-haiku-4-5-20251001", "anthropic", &r), EscalationResult::Next("claude-sonnet-4-6")); } #[test] @@ -136,7 +136,7 @@ mod tests { #[test] fn next_variant_carries_model_id() { let r = reg(); - assert!(matches!(next_model("claude-haiku-4-5", "anthropic", &r), EscalationResult::Next("claude-sonnet-4-6"))); + assert!(matches!(next_model("claude-haiku-4-5-20251001", "anthropic", &r), EscalationResult::Next("claude-sonnet-4-6"))); } #[test] diff --git a/_primitives/_rust/kei-model-router/src/pricing.rs b/_primitives/_rust/kei-model-router/src/pricing.rs index b20dded..9461b28 100644 --- a/_primitives/_rust/kei-model-router/src/pricing.rs +++ b/_primitives/_rust/kei-model-router/src/pricing.rs @@ -54,7 +54,7 @@ pub enum Model { impl Model { pub fn slug(&self) -> &'static str { match self { - Self::Haiku45 => "claude-haiku-4-5", + Self::Haiku45 => "claude-haiku-4-5-20251001", Self::Sonnet46 => "claude-sonnet-4-6", Self::Opus47 => "claude-opus-4-7", } @@ -72,7 +72,7 @@ impl Model { pub fn from_slug(s: &str) -> Option { match s { - "haiku" | "haiku-4.5" | "claude-haiku-4-5" => Some(Self::Haiku45), + "haiku" | "haiku-4.5" | "claude-haiku-4-5" | "claude-haiku-4-5-20251001" => Some(Self::Haiku45), "sonnet" | "sonnet-4.6" | "claude-sonnet-4-6" => Some(Self::Sonnet46), "opus" | "opus-4.7" | "claude-opus-4-7" => Some(Self::Opus47), _ => None, @@ -131,7 +131,7 @@ mod tests { #[test] fn haiku_output_1m_is_500m_microcents() { let r = reg(); - let c = cost_micro_cents("claude-haiku-4-5", 0, 1_000_000, &r).unwrap(); + let c = cost_micro_cents("claude-haiku-4-5-20251001", 0, 1_000_000, &r).unwrap(); assert_eq!(c, 500_000_000); } diff --git a/_primitives/_rust/kei-model-router/src/select_posterior.rs b/_primitives/_rust/kei-model-router/src/select_posterior.rs index a4f76a5..0467974 100644 --- a/_primitives/_rust/kei-model-router/src/select_posterior.rs +++ b/_primitives/_rust/kei-model-router/src/select_posterior.rs @@ -172,7 +172,7 @@ mod tests { let c = fresh_db(); for i in 0..30 { c.execute( - "INSERT INTO agents VALUES (?1,'tc1','claude-haiku-4-5','functional',0)", + "INSERT INTO agents VALUES (?1,'tc1','claude-haiku-4-5-20251001','functional',0)", rusqlite::params![format!("a{i}")], ) .unwrap(); diff --git a/docs/DNA-INDEX.md b/docs/DNA-INDEX.md index 9a18545..5cfae5d 100644 --- a/docs/DNA-INDEX.md +++ b/docs/DNA-INDEX.md @@ -1,6 +1,6 @@ # KeiSeiKit DNA Encyclopedia -> Auto-generated from kei-registry. Last regenerated: 2026-05-13T16:01:55Z. +> Auto-generated from kei-registry. Last regenerated: 2026-05-14T04:37:36Z. > Total blocks: 679. Per-type breakdown: | Type | Count | diff --git a/docs/DNA-MIGRATION.md b/docs/DNA-MIGRATION.md new file mode 100644 index 0000000..b331e1b --- /dev/null +++ b/docs/DNA-MIGRATION.md @@ -0,0 +1,79 @@ +# DNA Migration — two formats coexist + +> Status lock 2026-05-14. Authoritative on which format to use when. + +## Two formats, two granularities + +| Format | Layout | Used by | Purpose | +|---|---|---|---| +| **task-class** (4-segment, legacy) | `::::::-` | `kei-ledger` agent forks (RULE 0.12), internal agent invocations | Internal-agent identity; same prompt re-runs cluster on same task-class | +| **agent-shell** (5-segment, new) | `agent-shell::::::::-` | `keisei-marketplace` user-level invocations | User-shell identity; carries provider+model in the wire format so the marketplace UI / billing pipeline can join on it without parsing JSON | + +Hex lengths differ on purpose — 8-char nonce was sufficient for single-machine +ledger; 16-char (64-bit) is required for marketplace where N concurrent +sessions across the public install base push birthday collision into reach. + +## Which format does my code emit? + +- Writing a substrate-internal agent spawn (sub-agent of an orchestrator, + background ML run, ledger row for `kei-fork`) → **task-class** via + `kei-shared::dna::compose(...)` (or equivalent helper in your crate). +- Writing a marketplace user-facing invocation (chat message hitting + `/v1/chat/completions`, agent the user picked from the public catalog) → + **agent-shell** via `cryptoid.ts::agentDna(...)` in the marketplace. + +When in doubt, ask: "does this row in the ledger correspond to a particular +human user clicking a button?" If yes → agent-shell. If no → task-class. + +## Parser table + +| You have a string | Parse it with | +|---|---| +| `kei-shared::dna::*` or `kei-model-router::dna_class::*` | `dna_class::role` / `dna_class::caps` / `dna_class::task_class_dna` / `dna_class::agent_class_dna` | +| `agent-shell::*` | `kei-model-router::agent_shell_dna::parse` (Rust) or `cryptoid.ts::parseAgentDna` (TS) | + +Both parsers tolerate `None` / `null` on malformed input — never panic. + +## Ledger join + +When `agent_runs` (marketplace, agent-shell) needs to join `kei-ledger.agents` +(KSK, task-class), use the explicit translation: + +``` +agent-shell DNA → drop prefix → use (provider, model, caps) as filter, + use scope_sha+body_sha as join keys +``` + +There is intentionally **no** lossless round-trip between the two formats — +they carry different information. agent-shell carries provider+model that +task-class does not. + +## Cross-language contract + +Field names on the parsed struct are aligned per 2026-05-14: + +| Rust (`agent_shell_dna::AgentShellDna`) | TypeScript (`ParsedAgentDna`) | +|---|---| +| `provider` | `provider` | +| `model` | `model` | +| `caps` | `caps` | +| `scope_sha` | `scope_sha` | +| `body_sha` | `body_sha` | +| `nonce` | `nonce` | + +snake_case on both sides (TS field names exempted from camelCase convention +for cross-language consistency). JSON round-trip is byte-equal. + +## Migration history + +- 2026-05-13 — `agent_shell_dna` cube added to `kei-model-router` (issue: marketplace needs provider-aware DNA). +- 2026-05-13 — `cryptoid.ts::agentDna` added in marketplace. +- 2026-05-14 — fields aligned snake_case; legacy 8-hex DNAs explicitly REJECTED by TS `parseAgentDna` (return `null`). + +## Not migrated yet + +- `kei-shared/src/dna.rs` does not exist as a separate crate in this tree + yet; the canonical 4-segment implementation lives in + `_primitives/_rust/kei-model-router/src/dna_class.rs`. When kei-shared + is extracted, `dna_class` moves there and `agent_shell_dna` follows. + Update `docs/DNA-FORMAT.md::SSoT` pointer at that time.