@suluk/agents 0.1.1 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/model-select.ts +13 -3
- package/test/model-select.test.ts +21 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@suluk/agents",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Suluk Agent composition (C027): lint + project an `x-suluk-agents` map (skills + deterministic routes + by-name sub-agents) to a Claude plugin AND an OpenRouter/OpenAI-compatible manifest — one contract, two artifacts, zero network at generate time. Determinism is DECLARED not enforced; the matcher never reads an agent field. CANDIDATE tooling — NOT official OAS.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
package/src/model-select.ts
CHANGED
|
@@ -18,7 +18,7 @@ import { policiesFor } from "./policy";
|
|
|
18
18
|
*/
|
|
19
19
|
export type ResolvedTarget =
|
|
20
20
|
| { kind: "pinned"; model: string }
|
|
21
|
-
| { kind: "router"; model: "openrouter/auto"; allowedModels: string[]; costQualityTradeoff: number }
|
|
21
|
+
| { kind: "router"; model: "openrouter/auto"; allowedModels: string[]; costQualityTradeoff: number; provider?: { zdr: true } }
|
|
22
22
|
| { kind: "latest"; model: string; note: string };
|
|
23
23
|
|
|
24
24
|
export interface SkillModelResolution {
|
|
@@ -92,13 +92,23 @@ export function skillModels(doc: OpenAPIv4Document, agentName: string, skillName
|
|
|
92
92
|
const snapshotHash = declared ? null : catalog.snapshotHash;
|
|
93
93
|
|
|
94
94
|
const mode = skill?.modelResolve ?? "pinned";
|
|
95
|
+
const governed = isGoverned(doc, agentName);
|
|
96
|
+
// ZDR (C030, verified 2026-06-13 — `provider:{zdr:true}` combines with `model:"openrouter/auto"`, 200 live): we have no
|
|
97
|
+
// per-model ZDR FACT to pin against, so ZDR is enforceable ONLY at runtime via the router → a `zdr` skill resolves to the
|
|
98
|
+
// ROUTER regardless of `modelResolve`. Authors who pinned get it: the pin can't honor ZDR, the router can.
|
|
99
|
+
const wantsZdr = !!skill?.modelRequire?.zdr;
|
|
95
100
|
// GOVERNANCE GATE (mechanical): a governed skill MUST pin — router/latest are non-reproducible + cannot bind an endpoint.
|
|
96
|
-
if (
|
|
101
|
+
if (governed && mode !== "pinned")
|
|
97
102
|
throw new Error(`@suluk/agents: skill "${skillName}" of agent "${agentName}" is GOVERNED by an operator policy — modelResolve:"${mode}" is inadmissible (a governed skill must be "pinned" for reproducible, auditable, endpoint-bindable selection). Remove the policy or use "pinned".`);
|
|
103
|
+
// ZDR-under-governance is unsatisfiable via OpenRouter: ZDR needs the router (`provider.zdr`), governance forces a pin, and
|
|
104
|
+
// region/license have NO endpoint knob — fail loud rather than silently dropping the ZDR constraint or the governance pin.
|
|
105
|
+
if (governed && wantsZdr)
|
|
106
|
+
throw new Error(`@suluk/agents: skill "${skillName}" of agent "${agentName}" requires ZDR (modelRequire.zdr) AND is GOVERNED by an operator policy — unsatisfiable: ZDR is enforced only via the router's provider.zdr, but a governed skill must pin (region/license have no OpenRouter endpoint knob). Drop one.`);
|
|
98
107
|
|
|
99
108
|
let target: ResolvedTarget;
|
|
100
109
|
let pickPinned: boolean;
|
|
101
|
-
if (
|
|
110
|
+
if (wantsZdr) { target = { kind: "router", model: "openrouter/auto", allowedModels: ids, costQualityTradeoff: deriveCQT(skill), provider: { zdr: true } }; pickPinned = false; }
|
|
111
|
+
else if (mode === "router") { target = { kind: "router", model: "openrouter/auto", allowedModels: ids, costQualityTradeoff: deriveCQT(skill) }; pickPinned = false; }
|
|
102
112
|
else if (mode === "latest") { target = { kind: "latest", model: toLatestAlias(ids[0] ?? ""), note: "~-latest defers the concrete version to request time — NOT reproducible (recorded in the why-explainer)" }; pickPinned = false; }
|
|
103
113
|
else { target = { kind: "pinned", model: ids[0] ?? "" }; pickPinned = true; }
|
|
104
114
|
|
|
@@ -96,3 +96,24 @@ describe("C030 resolution target — pin (default) / router (delegate) / latest,
|
|
|
96
96
|
expect(deriveCQT({ modelProfile: "max-reasoning" })).toBe(0);
|
|
97
97
|
});
|
|
98
98
|
});
|
|
99
|
+
|
|
100
|
+
describe("C030 ZDR — verified 2026-06-13 (provider.zdr + openrouter/auto combine, 200 live)", () => {
|
|
101
|
+
test("a modelRequire.zdr skill resolves to the ROUTER with provider:{zdr:true} EVEN at the default (pinned) mode — no per-model ZDR fact to pin against", () => {
|
|
102
|
+
const d = doc();
|
|
103
|
+
d["x-suluk-agents"]!.conin.skills!.operate = { modelProfile: "balanced", modelRequire: { zdr: true } }; // no modelResolve ⇒ default pinned
|
|
104
|
+
const r = skillModels(d, "conin", "operate", SEED_CATALOG);
|
|
105
|
+
expect(r.target.kind).toBe("router");
|
|
106
|
+
if (r.target.kind === "router") {
|
|
107
|
+
expect(r.target.provider).toEqual({ zdr: true });
|
|
108
|
+
expect(r.target.allowedModels).toEqual(r.ids); // ZDR is enforced at the endpoint; the fence still enumerates survivors
|
|
109
|
+
}
|
|
110
|
+
expect(r.pickPinned).toBe(false); // the served id is logged-not-pinned (ZDR endpoint chosen at runtime)
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test("ZDR + an operator policy is UNSATISFIABLE via OpenRouter — fails loud (ZDR needs the router, governance forces a pin)", () => {
|
|
114
|
+
const d = doc();
|
|
115
|
+
d["x-suluk-policy"] = { fleet: { appliesTo: ["#/x-suluk-agents/conin"], modelAllowlist: ["google/gemini-2.5-flash"] } };
|
|
116
|
+
d["x-suluk-agents"]!.conin.skills!.operate = { modelProfile: "balanced", modelRequire: { zdr: true } };
|
|
117
|
+
expect(() => skillModels(d, "conin", "operate", SEED_CATALOG)).toThrow(/ZDR|unsatisfiable/i);
|
|
118
|
+
});
|
|
119
|
+
});
|