From 60c7c033dd1d732d1c6c846770cd5cc8ca80b89f Mon Sep 17 00:00:00 2001 From: artale Date: Mon, 29 Jun 2026 02:04:00 +0200 Subject: [PATCH] feat: track hermes moa access safely --- src/fable5/fableflow-manifest.test.ts | 3 +++ src/fable5/fableflow-manifest.ts | 5 ++++- src/fable5/model-access-receipt.test.ts | 10 ++++++++++ src/fable5/model-access-receipt.ts | 2 +- src/index.ts | 1 + 5 files changed, 19 insertions(+), 2 deletions(-) diff --git a/src/fable5/fableflow-manifest.test.ts b/src/fable5/fableflow-manifest.test.ts index 1b561f9..9fa34f8 100644 --- a/src/fable5/fableflow-manifest.test.ts +++ b/src/fable5/fableflow-manifest.test.ts @@ -25,6 +25,7 @@ describe("fableflow manifest", () => { "factory-reconcile", "model-access", "fugu-ultra", + "hermes-moa", "rsi-canary", ])); }); @@ -50,7 +51,9 @@ describe("fableflow manifest", () => { expect(edges).toContain("agent-chain-workers>zte-gate"); expect(edges).toContain("factory-reconcile>git-proxy-8099"); expect(edges).toContain("model-access>fugu-ultra"); + expect(edges).toContain("model-access>hermes-moa"); expect(edges).toContain("fugu-ultra>agent-chain-workers"); + expect(edges).toContain("hermes-moa>agent-chain-workers"); expect(edges).toContain("deploy-webhook-8098>git-proxy-8099"); }); diff --git a/src/fable5/fableflow-manifest.ts b/src/fable5/fableflow-manifest.ts index ca65259..056d76d 100644 --- a/src/fable5/fableflow-manifest.ts +++ b/src/fable5/fableflow-manifest.ts @@ -69,7 +69,8 @@ export function createFableFlowManifest(): FableFlowManifest { node("factory-reconcile", "Factory Reconcile", "receipt", "verify", "factory", "live receipt", "Live reconciliation overrides memory", "src/fable5/factory-reconciliation.ts", ".fable/factory/reconciliation-live.json", "RC", 960, 360), node("model-access", "Model Access", "receipt", "verify", "models", "access check", "Model access claims require fresh receipt", "src/fable5/model-access-receipt.ts", "fable.model_access.receipt.v1", "MA", 0, 540), node("fugu-ultra", "Fugu Ultra", "model", "verify", "models", "orchestration model", "Sakana Fugu Ultra candidate; unknown until model-access receipt verifies official access", "sakana/fugu-ultra access probe", "fable.model_access.receipt.v1", "FU", 240, 540), - node("rsi-canary", "RSI Canary", "receipt", "verify", "rsi", "canary only", "Canary repair proven; broad autopatch remains unproven without fresh receipt", "fable rsi receipts", ".fable/rsi/canary-live-*.json", "RS", 480, 540), + node("hermes-moa", "Hermes MoA", "model", "verify", "models", "mixture preset", "Hermes Mixture of Agents preset; unknown until model-access receipt verifies configuration", "hermes model --provider moa", "fable.model_access.receipt.v1", "HM", 480, 540), + node("rsi-canary", "RSI Canary", "receipt", "verify", "rsi", "canary only", "Canary repair proven; broad autopatch remains unproven without fresh receipt", "fable rsi receipts", ".fable/rsi/canary-live-*.json", "RS", 720, 540), ], edges: [ edge("feedbackpilot-intake", "forgejo-intake", "intake", "opens issue"), @@ -83,7 +84,9 @@ export function createFableFlowManifest(): FableFlowManifest { edge("deploy-webhook-8098", "git-proxy-8099", "legacy", "superseded by"), edge("forgejo-writeback", "forgejo", "intake", "comments/labels"), edge("model-access", "fugu-ultra", "verify", "tracks access"), + edge("model-access", "hermes-moa", "verify", "tracks access"), edge("fugu-ultra", "agent-chain-workers", "verify", "candidate worker after verified access"), + edge("hermes-moa", "agent-chain-workers", "verify", "candidate worker after verified preset"), edge("model-access", "agent-chain-workers", "verify", "provider evidence"), edge("rsi-canary", "zte-gate", "verify", "canary proof only"), ], diff --git a/src/fable5/model-access-receipt.test.ts b/src/fable5/model-access-receipt.test.ts index 6fc3d25..2936571 100644 --- a/src/fable5/model-access-receipt.test.ts +++ b/src/fable5/model-access-receipt.test.ts @@ -43,6 +43,16 @@ describe("model access receipt", () => { expect(receipt.reasons).toContain("one or more model surfaces are unknown"); }); + it("tracks Hermes MoA without claiming access", () => { + const receipt = createModelAccessReceipt({ + probes: [{ surface: "hermes-moa", command: "hermes model --provider moa", status: "unknown", model: "moa", evidence: "Hermes MoA preset required; not probed" }], + }); + + expect(receipt.decision).toBe("blocked"); + expect(receipt.claims).toEqual([]); + expect(receipt.reasons).toContain("one or more model surfaces are unknown"); + }); + it("writes a JSON receipt", () => { const dir = fs.mkdtempSync(path.join(os.tmpdir(), "model-access-")); const file = path.join(dir, "receipt.json"); diff --git a/src/fable5/model-access-receipt.ts b/src/fable5/model-access-receipt.ts index 653188e..3ec97b8 100644 --- a/src/fable5/model-access-receipt.ts +++ b/src/fable5/model-access-receipt.ts @@ -1,7 +1,7 @@ import * as fs from "node:fs"; import * as path from "node:path"; -export type ModelAccessSurface = "pi" | "openrouter" | "openai" | "chatgpt-cyber" | "sakana-fugu" | "other"; +export type ModelAccessSurface = "pi" | "openrouter" | "openai" | "chatgpt-cyber" | "sakana-fugu" | "hermes-moa" | "other"; export type ModelAccessStatus = "available" | "blocked" | "missing" | "unknown"; export interface ModelAccessProbe { diff --git a/src/index.ts b/src/index.ts index 804a0f8..1c92a74 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1969,6 +1969,7 @@ fable { surface: "pi" as const, command: "pi --list-models cyber", status: "unknown" as const, evidence: "not probed by this fail-closed receipt command" }, { surface: "chatgpt-cyber" as const, command: "https://chatgpt.com/cyber", status: "unknown" as const, evidence: "browser/login-gated surface not probed by this command" }, { surface: "sakana-fugu" as const, command: "sakana/fugu-ultra access probe", status: "unknown" as const, model: "fugu-ultra", evidence: "not probed; do not buy or bypass with VPN" }, + { surface: "hermes-moa" as const, command: "hermes model --provider moa", status: "unknown" as const, model: "moa", evidence: "Hermes MoA preset required; not probed" }, ]; const out = opts.output ?? path.join(".fable", "api", "model-access-live.json"); const receipt = createModelAccessReceipt({ probes });