moltblock 0.4.0 → 0.6.0
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/{readme.md → README.md} +96 -1
- package/dist/agents.d.ts +4 -4
- package/dist/agents.js +30 -16
- package/dist/cli.js +6 -1
- package/dist/code-verifier.d.ts +13 -0
- package/dist/code-verifier.js +21 -0
- package/dist/composite-verifier.d.ts +21 -0
- package/dist/composite-verifier.js +42 -0
- package/dist/config.d.ts +57 -2
- package/dist/config.js +121 -46
- package/dist/domain-prompts.d.ts +21 -0
- package/dist/domain-prompts.js +33 -0
- package/dist/entity-base.d.ts +37 -0
- package/dist/entity-base.js +87 -0
- package/dist/graph-runner.d.ts +11 -1
- package/dist/graph-runner.js +15 -4
- package/dist/improvement.d.ts +1 -1
- package/dist/improvement.js +21 -9
- package/dist/index.d.ts +10 -3
- package/dist/index.js +16 -4
- package/dist/policy-verifier.d.ts +29 -0
- package/dist/policy-verifier.js +90 -0
- package/dist/risk.d.ts +13 -0
- package/dist/risk.js +63 -0
- package/dist/verifier-interface.d.ts +24 -0
- package/dist/verifier-interface.js +4 -0
- package/package.json +3 -2
- package/skill/SKILL.md +103 -0
package/dist/config.js
CHANGED
|
@@ -23,6 +23,13 @@ try {
|
|
|
23
23
|
catch {
|
|
24
24
|
// dotenv not required
|
|
25
25
|
}
|
|
26
|
+
// --- Provider defaults registry ---
|
|
27
|
+
const PROVIDER_DEFAULTS = {
|
|
28
|
+
openai: { baseUrl: "https://api.openai.com/v1", model: "gpt-4o", envKey: "OPENAI_API_KEY" },
|
|
29
|
+
google: { baseUrl: "https://generativelanguage.googleapis.com/v1beta/openai/", model: "gemini-2.0-flash", envKey: "GOOGLE_API_KEY" },
|
|
30
|
+
zai: { baseUrl: "https://api.z.ai/api/paas/v4", model: "glm-4.7-flash", envKey: "MOLTBLOCK_ZAI_API_KEY" },
|
|
31
|
+
local: { baseUrl: "http://localhost:1234/v1", model: "local", envKey: "" },
|
|
32
|
+
};
|
|
26
33
|
// --- Zod schemas (OpenClaw-style config) ---
|
|
27
34
|
export const BindingEntrySchema = z.object({
|
|
28
35
|
backend: z.string().describe("e.g. 'local' or 'zai' or 'openai'"),
|
|
@@ -33,8 +40,20 @@ export const BindingEntrySchema = z.object({
|
|
|
33
40
|
export const AgentConfigSchema = z.object({
|
|
34
41
|
bindings: z.record(z.string(), BindingEntrySchema).optional().describe("Per-role model bindings"),
|
|
35
42
|
});
|
|
43
|
+
export const PolicyRuleSchema = z.object({
|
|
44
|
+
id: z.string().describe("Unique rule identifier"),
|
|
45
|
+
description: z.string().describe("Human-readable rule description"),
|
|
46
|
+
target: z.enum(["artifact", "task", "both"]).describe("What to match against"),
|
|
47
|
+
pattern: z.string().describe("Regex pattern string"),
|
|
48
|
+
action: z.enum(["deny", "allow"]).describe("deny blocks; allow overrides deny in same category"),
|
|
49
|
+
category: z.string().describe("Rule category for allow/deny grouping"),
|
|
50
|
+
enabled: z.boolean().default(true).describe("Whether the rule is active"),
|
|
51
|
+
});
|
|
36
52
|
export const MoltblockConfigSchema = z.object({
|
|
37
53
|
agent: AgentConfigSchema.optional().describe("Agent defaults and bindings"),
|
|
54
|
+
policy: z.object({
|
|
55
|
+
rules: z.array(PolicyRuleSchema).optional().describe("Custom policy rules"),
|
|
56
|
+
}).optional().describe("Policy verifier configuration"),
|
|
38
57
|
});
|
|
39
58
|
export const ModelBindingSchema = z.object({
|
|
40
59
|
backend: z.string().describe("e.g. 'local' or 'zai' or 'openai'"),
|
|
@@ -138,18 +157,49 @@ export function loadMoltblockConfig() {
|
|
|
138
157
|
}
|
|
139
158
|
/**
|
|
140
159
|
* Parse OpenClaw config and convert to MoltblockConfig format.
|
|
141
|
-
*
|
|
160
|
+
* Handles multiple OpenClaw formats: agents.defaults.model.primary ("provider/model"),
|
|
161
|
+
* agent.bindings, providers section, and models section.
|
|
142
162
|
*/
|
|
143
163
|
function parseOpenClawConfig(data) {
|
|
144
164
|
if (!data || typeof data !== "object") {
|
|
145
165
|
return null;
|
|
146
166
|
}
|
|
147
167
|
const obj = data;
|
|
148
|
-
// OpenClaw may have agent.bindings or providers section
|
|
149
|
-
// Try to extract bindings from various possible locations
|
|
150
168
|
let bindings;
|
|
169
|
+
// Check for agents.defaults.model.primary (OpenClaw's actual format: "provider/model")
|
|
170
|
+
if (obj["agents"] && typeof obj["agents"] === "object") {
|
|
171
|
+
const agents = obj["agents"];
|
|
172
|
+
if (agents["defaults"] && typeof agents["defaults"] === "object") {
|
|
173
|
+
const defaults = agents["defaults"];
|
|
174
|
+
if (defaults["model"] && typeof defaults["model"] === "object") {
|
|
175
|
+
const modelConfig = defaults["model"];
|
|
176
|
+
const primary = modelConfig["primary"];
|
|
177
|
+
if (typeof primary === "string" && primary.includes("/")) {
|
|
178
|
+
const parts = primary.split("/");
|
|
179
|
+
const providerName = parts[0] ?? "";
|
|
180
|
+
const modelName = parts.slice(1).join("/");
|
|
181
|
+
const provider = PROVIDER_DEFAULTS[providerName.toLowerCase()];
|
|
182
|
+
if (providerName && provider) {
|
|
183
|
+
const apiKey = getApiKeyForBackend(providerName);
|
|
184
|
+
const binding = {
|
|
185
|
+
backend: providerName.toLowerCase(),
|
|
186
|
+
base_url: provider.baseUrl,
|
|
187
|
+
model: modelName || provider.model,
|
|
188
|
+
api_key: apiKey,
|
|
189
|
+
};
|
|
190
|
+
bindings = {
|
|
191
|
+
generator: binding,
|
|
192
|
+
critic: { ...binding },
|
|
193
|
+
judge: { ...binding },
|
|
194
|
+
verifier: { ...binding },
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
151
201
|
// Check for agent.bindings (same as moltblock)
|
|
152
|
-
if (obj["agent"] && typeof obj["agent"] === "object") {
|
|
202
|
+
if (!bindings && obj["agent"] && typeof obj["agent"] === "object") {
|
|
153
203
|
const agent = obj["agent"];
|
|
154
204
|
if (agent["bindings"] && typeof agent["bindings"] === "object") {
|
|
155
205
|
bindings = extractBindings(agent["bindings"]);
|
|
@@ -242,23 +292,66 @@ function getApiKeyForBackend(backend) {
|
|
|
242
292
|
return env("GOOGLE_API_KEY") || null;
|
|
243
293
|
}
|
|
244
294
|
if (backendLower === "zai") {
|
|
245
|
-
return env("MOLTBLOCK_ZAI_API_KEY") || null;
|
|
295
|
+
return env("MOLTBLOCK_ZAI_API_KEY") || env("ZAI_API_KEY") || null;
|
|
246
296
|
}
|
|
247
297
|
return null;
|
|
248
298
|
}
|
|
299
|
+
/**
|
|
300
|
+
* Auto-detect the best available provider from environment variables.
|
|
301
|
+
* Priority: explicit override > OPENAI_API_KEY > GOOGLE_API_KEY > MOLTBLOCK_ZAI_API_KEY/ZAI_API_KEY > local.
|
|
302
|
+
*/
|
|
303
|
+
export function detectProvider(overrideProvider, overrideModel) {
|
|
304
|
+
if (overrideProvider) {
|
|
305
|
+
const p = PROVIDER_DEFAULTS[overrideProvider.toLowerCase()];
|
|
306
|
+
if (!p) {
|
|
307
|
+
throw new Error(`Unknown provider "${overrideProvider}". Valid providers: ${Object.keys(PROVIDER_DEFAULTS).join(", ")}`);
|
|
308
|
+
}
|
|
309
|
+
const apiKey = p.envKey ? env(p.envKey) || null : null;
|
|
310
|
+
return {
|
|
311
|
+
backend: overrideProvider.toLowerCase(),
|
|
312
|
+
baseUrl: p.baseUrl,
|
|
313
|
+
model: overrideModel || p.model,
|
|
314
|
+
apiKey,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
// Scan env vars in priority order
|
|
318
|
+
const priority = [
|
|
319
|
+
{ name: "openai", envKey: "OPENAI_API_KEY" },
|
|
320
|
+
{ name: "google", envKey: "GOOGLE_API_KEY" },
|
|
321
|
+
{ name: "zai", envKey: "MOLTBLOCK_ZAI_API_KEY" },
|
|
322
|
+
{ name: "zai", envKey: "ZAI_API_KEY" },
|
|
323
|
+
];
|
|
324
|
+
for (const { name, envKey } of priority) {
|
|
325
|
+
const key = env(envKey);
|
|
326
|
+
if (key) {
|
|
327
|
+
const p = PROVIDER_DEFAULTS[name];
|
|
328
|
+
return {
|
|
329
|
+
backend: name,
|
|
330
|
+
baseUrl: p.baseUrl,
|
|
331
|
+
model: overrideModel || p.model,
|
|
332
|
+
apiKey: key,
|
|
333
|
+
};
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
// Fallback to local
|
|
337
|
+
const local = PROVIDER_DEFAULTS["local"];
|
|
338
|
+
return {
|
|
339
|
+
backend: "local",
|
|
340
|
+
baseUrl: local.baseUrl,
|
|
341
|
+
model: overrideModel || local.model,
|
|
342
|
+
apiKey: null,
|
|
343
|
+
};
|
|
344
|
+
}
|
|
249
345
|
/**
|
|
250
346
|
* Model bindings for Code Entity. Load from moltblock.json if present, then env overrides.
|
|
251
|
-
* If no JSON,
|
|
347
|
+
* If no JSON, auto-detects provider from env vars. API keys from env win over JSON.
|
|
252
348
|
*/
|
|
253
|
-
export function defaultCodeEntityBindings() {
|
|
349
|
+
export function defaultCodeEntityBindings(overrides) {
|
|
254
350
|
const cfg = loadMoltblockConfig();
|
|
255
|
-
const zaiKey = env("MOLTBLOCK_ZAI_API_KEY");
|
|
256
|
-
const localUrl = env("MOLTBLOCK_GENERATOR_BASE_URL") || "http://localhost:1234/v1";
|
|
257
|
-
const localModel = env("MOLTBLOCK_GENERATOR_MODEL") || "local";
|
|
258
351
|
const envUrl = (key, fallback) => env(key) || fallback;
|
|
259
352
|
const envModel = (key, fallback) => env(key) || fallback;
|
|
260
353
|
const bindingsFromJson = cfg?.agent?.bindings ?? {};
|
|
261
|
-
function bindingFor(role
|
|
354
|
+
function bindingFor(role) {
|
|
262
355
|
const entry = bindingsFromJson[role];
|
|
263
356
|
if (entry) {
|
|
264
357
|
const baseUrl = envUrl(`MOLTBLOCK_${role.toUpperCase()}_BASE_URL`, entry.base_url);
|
|
@@ -271,42 +364,24 @@ export function defaultCodeEntityBindings() {
|
|
|
271
364
|
const apiKey = envApiKey || entry.api_key || getApiKeyForBackend(entry.backend) || null;
|
|
272
365
|
return { backend: entry.backend, baseUrl, apiKey, model };
|
|
273
366
|
}
|
|
274
|
-
// No JSON:
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
}
|
|
278
|
-
|
|
279
|
-
const useZai = Boolean(zaiKey);
|
|
280
|
-
return {
|
|
281
|
-
backend: useZai ? "zai" : "local",
|
|
282
|
-
baseUrl: envUrl("MOLTBLOCK_CRITIC_BASE_URL", useZai ? "https://api.z.ai/api/paas/v4" : localUrl),
|
|
283
|
-
apiKey: useZai ? zaiKey : null,
|
|
284
|
-
model: envModel("MOLTBLOCK_CRITIC_MODEL", useZai ? "glm-4.7-flash" : localModel),
|
|
285
|
-
};
|
|
286
|
-
}
|
|
287
|
-
if (role === "judge") {
|
|
288
|
-
const useZai = Boolean(zaiKey);
|
|
289
|
-
return {
|
|
290
|
-
backend: useZai ? "zai" : "local",
|
|
291
|
-
baseUrl: envUrl("MOLTBLOCK_JUDGE_BASE_URL", useZai ? "https://api.z.ai/api/paas/v4" : localUrl),
|
|
292
|
-
apiKey: useZai ? zaiKey : null,
|
|
293
|
-
model: envModel("MOLTBLOCK_JUDGE_MODEL", useZai ? "glm-4.7-flash" : localModel),
|
|
294
|
-
};
|
|
295
|
-
}
|
|
296
|
-
if (role === "verifier") {
|
|
297
|
-
return {
|
|
298
|
-
backend: "local",
|
|
299
|
-
baseUrl: envUrl("MOLTBLOCK_VERIFIER_BASE_URL", localUrl),
|
|
300
|
-
apiKey: null,
|
|
301
|
-
model: envModel("MOLTBLOCK_VERIFIER_MODEL", localModel),
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
return { backend: defaultBackend, baseUrl: defaultBase, apiKey: defaultApiKey, model: defaultModel };
|
|
367
|
+
// No JSON entry for this role: auto-detect provider
|
|
368
|
+
const detected = detectProvider(overrides?.provider, overrides?.model);
|
|
369
|
+
const baseUrl = envUrl(`MOLTBLOCK_${role.toUpperCase()}_BASE_URL`, detected.baseUrl);
|
|
370
|
+
const model = envModel(`MOLTBLOCK_${role.toUpperCase()}_MODEL`, detected.model);
|
|
371
|
+
return { backend: detected.backend, baseUrl, apiKey: detected.apiKey, model };
|
|
305
372
|
}
|
|
306
373
|
return {
|
|
307
|
-
generator: bindingFor("generator"
|
|
308
|
-
critic: bindingFor("critic"
|
|
309
|
-
judge: bindingFor("judge"
|
|
310
|
-
verifier: bindingFor("verifier"
|
|
374
|
+
generator: bindingFor("generator"),
|
|
375
|
+
critic: bindingFor("critic"),
|
|
376
|
+
judge: bindingFor("judge"),
|
|
377
|
+
verifier: bindingFor("verifier"),
|
|
311
378
|
};
|
|
312
379
|
}
|
|
380
|
+
/**
|
|
381
|
+
* Load custom policy rules from moltblock config.
|
|
382
|
+
* Returns empty array if no config or no rules defined.
|
|
383
|
+
*/
|
|
384
|
+
export function loadPolicyRules() {
|
|
385
|
+
const cfg = loadMoltblockConfig();
|
|
386
|
+
return cfg?.policy?.rules ?? [];
|
|
387
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain prompt registry: maps domain names to role-specific system prompts.
|
|
3
|
+
*/
|
|
4
|
+
/** Prompt set for a single domain: one system prompt per agent role. */
|
|
5
|
+
export interface DomainPrompts {
|
|
6
|
+
generator: string;
|
|
7
|
+
critic: string;
|
|
8
|
+
judge: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Get prompts for a domain. Falls back to "general" if domain is unknown.
|
|
12
|
+
*/
|
|
13
|
+
export declare function getDomainPrompts(domain: string): DomainPrompts;
|
|
14
|
+
/**
|
|
15
|
+
* Register a custom domain with its prompts. Overwrites if already exists.
|
|
16
|
+
*/
|
|
17
|
+
export declare function registerDomain(domain: string, prompts: DomainPrompts): void;
|
|
18
|
+
/**
|
|
19
|
+
* List all registered domain names.
|
|
20
|
+
*/
|
|
21
|
+
export declare function listDomains(): string[];
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Domain prompt registry: maps domain names to role-specific system prompts.
|
|
3
|
+
*/
|
|
4
|
+
const registry = new Map();
|
|
5
|
+
// --- Built-in domains ---
|
|
6
|
+
registry.set("code", {
|
|
7
|
+
generator: "You are the Generator for a Code Entity. You produce a single TypeScript implementation that satisfies the user's task. Output only valid TypeScript code, no markdown fences or extra commentary. The code will be reviewed by a Critic and then verified by running tests.",
|
|
8
|
+
critic: "You are the Critic. Review the draft code for bugs, edge cases, and style. Be concise. List specific issues and suggestions. Do not rewrite the code; only critique.",
|
|
9
|
+
judge: "You are the Judge. Given the task, the draft code, and the critique, produce the final single TypeScript implementation. Output only valid TypeScript code, no markdown fences or extra commentary. Incorporate the critic's feedback. The result will be run through vitest.",
|
|
10
|
+
});
|
|
11
|
+
registry.set("general", {
|
|
12
|
+
generator: "You are the Generator. Produce a clear, complete response that satisfies the user's task. Focus on accuracy and completeness. Your output will be reviewed by a Critic.",
|
|
13
|
+
critic: "You are the Critic. Review the draft response for factual errors, gaps, unclear reasoning, and potential risks. Be concise. List specific issues and suggestions. Do not rewrite the response; only critique.",
|
|
14
|
+
judge: "You are the Judge. Given the task, the draft response, and the critique, produce the final response. Incorporate the critic's feedback. Ensure the result is accurate, safe, and complete.",
|
|
15
|
+
});
|
|
16
|
+
/**
|
|
17
|
+
* Get prompts for a domain. Falls back to "general" if domain is unknown.
|
|
18
|
+
*/
|
|
19
|
+
export function getDomainPrompts(domain) {
|
|
20
|
+
return registry.get(domain) ?? registry.get("general");
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Register a custom domain with its prompts. Overwrites if already exists.
|
|
24
|
+
*/
|
|
25
|
+
export function registerDomain(domain, prompts) {
|
|
26
|
+
registry.set(domain, prompts);
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* List all registered domain names.
|
|
30
|
+
*/
|
|
31
|
+
export function listDomains() {
|
|
32
|
+
return [...registry.keys()];
|
|
33
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Entity: pluggable verifier and domain support for any task type.
|
|
3
|
+
* CodeEntity remains unchanged for backward compatibility.
|
|
4
|
+
*/
|
|
5
|
+
import { type ModelBinding } from "./config.js";
|
|
6
|
+
import { WorkingMemory } from "./memory.js";
|
|
7
|
+
import { Store } from "./persistence.js";
|
|
8
|
+
import type { Verifier } from "./verifier-interface.js";
|
|
9
|
+
/** Options for constructing a generic Entity. */
|
|
10
|
+
export interface EntityOptions {
|
|
11
|
+
/** Verifier to gate artifacts. Defaults to PolicyVerifier. */
|
|
12
|
+
verifier?: Verifier;
|
|
13
|
+
/** Domain for agent prompts. Defaults to "general". */
|
|
14
|
+
domain?: string;
|
|
15
|
+
/** Per-role model bindings. Auto-detected if omitted. */
|
|
16
|
+
bindings?: Record<string, ModelBinding>;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Generic Entity: same Generator -> Critic -> Judge pipeline as CodeEntity,
|
|
20
|
+
* but with a pluggable verifier and domain-aware prompts.
|
|
21
|
+
*/
|
|
22
|
+
export declare class Entity {
|
|
23
|
+
private gateways;
|
|
24
|
+
private verifier;
|
|
25
|
+
private domain;
|
|
26
|
+
constructor(options?: EntityOptions);
|
|
27
|
+
/**
|
|
28
|
+
* One full loop: task -> Generator -> Critic -> Judge -> Verifier -> gating.
|
|
29
|
+
* Returns working memory with authoritative_artifact set only if verification passed.
|
|
30
|
+
*/
|
|
31
|
+
run(task: string, options?: {
|
|
32
|
+
testCode?: string;
|
|
33
|
+
store?: Store;
|
|
34
|
+
entityVersion?: string;
|
|
35
|
+
writeCheckpointAfter?: boolean;
|
|
36
|
+
}): Promise<WorkingMemory>;
|
|
37
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Generic Entity: pluggable verifier and domain support for any task type.
|
|
3
|
+
* CodeEntity remains unchanged for backward compatibility.
|
|
4
|
+
*/
|
|
5
|
+
import { runCritic, runGenerator, runJudge } from "./agents.js";
|
|
6
|
+
import { defaultCodeEntityBindings } from "./config.js";
|
|
7
|
+
import { LLMGateway } from "./gateway.js";
|
|
8
|
+
import { WorkingMemory } from "./memory.js";
|
|
9
|
+
import { hashMemory, recordOutcome } from "./persistence.js";
|
|
10
|
+
import { PolicyVerifier } from "./policy-verifier.js";
|
|
11
|
+
import { validateTask } from "./validation.js";
|
|
12
|
+
/**
|
|
13
|
+
* Generic Entity: same Generator -> Critic -> Judge pipeline as CodeEntity,
|
|
14
|
+
* but with a pluggable verifier and domain-aware prompts.
|
|
15
|
+
*/
|
|
16
|
+
export class Entity {
|
|
17
|
+
gateways;
|
|
18
|
+
verifier;
|
|
19
|
+
domain;
|
|
20
|
+
constructor(options) {
|
|
21
|
+
const resolvedBindings = options?.bindings ?? defaultCodeEntityBindings();
|
|
22
|
+
this.verifier = options?.verifier ?? new PolicyVerifier();
|
|
23
|
+
this.domain = options?.domain ?? "general";
|
|
24
|
+
this.gateways = {
|
|
25
|
+
generator: new LLMGateway(resolvedBindings["generator"]),
|
|
26
|
+
critic: new LLMGateway(resolvedBindings["critic"]),
|
|
27
|
+
judge: new LLMGateway(resolvedBindings["judge"]),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* One full loop: task -> Generator -> Critic -> Judge -> Verifier -> gating.
|
|
32
|
+
* Returns working memory with authoritative_artifact set only if verification passed.
|
|
33
|
+
*/
|
|
34
|
+
async run(task, options = {}) {
|
|
35
|
+
const { testCode, store, entityVersion = "0.5.0", writeCheckpointAfter = false, } = options;
|
|
36
|
+
// Validate input
|
|
37
|
+
const taskValidation = validateTask(task);
|
|
38
|
+
if (!taskValidation.valid) {
|
|
39
|
+
throw new Error(`Invalid task: ${taskValidation.error}`);
|
|
40
|
+
}
|
|
41
|
+
const t0 = performance.now();
|
|
42
|
+
const memory = new WorkingMemory();
|
|
43
|
+
memory.setTask(task);
|
|
44
|
+
// Inject long-term context from verified memory
|
|
45
|
+
if (store) {
|
|
46
|
+
const recent = store.getRecentVerified(5);
|
|
47
|
+
const parts = [];
|
|
48
|
+
for (const e of recent) {
|
|
49
|
+
if (e.content_preview) {
|
|
50
|
+
parts.push(e.content_preview.slice(0, 500));
|
|
51
|
+
}
|
|
52
|
+
else if (e.summary) {
|
|
53
|
+
parts.push(e.summary);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
memory.longTermContext = parts.length > 0 ? parts.join("\n---\n") : "";
|
|
57
|
+
}
|
|
58
|
+
// Run the agent pipeline with domain-aware prompts
|
|
59
|
+
await runGenerator(this.gateways["generator"], memory, store ?? null, this.domain);
|
|
60
|
+
await runCritic(this.gateways["critic"], memory, store ?? null, this.domain);
|
|
61
|
+
await runJudge(this.gateways["judge"], memory, store ?? null, this.domain);
|
|
62
|
+
// Run pluggable verifier
|
|
63
|
+
const ctx = {
|
|
64
|
+
task,
|
|
65
|
+
testCode,
|
|
66
|
+
domain: this.domain,
|
|
67
|
+
};
|
|
68
|
+
const result = await this.verifier.verify(memory, ctx);
|
|
69
|
+
memory.setVerification(result.passed, result.evidence);
|
|
70
|
+
// Record outcome and persist if verification passed
|
|
71
|
+
const latencySec = (performance.now() - t0) / 1000;
|
|
72
|
+
if (store) {
|
|
73
|
+
recordOutcome(store, memory.verificationPassed, latencySec, task.slice(0, 100));
|
|
74
|
+
}
|
|
75
|
+
if (store && memory.verificationPassed && memory.authoritativeArtifact) {
|
|
76
|
+
const artifactRef = `artifact_${Date.now()}`;
|
|
77
|
+
store.addVerified(artifactRef, `Verified artifact (${memory.authoritativeArtifact.length} chars)`, memory.authoritativeArtifact.slice(0, 2000));
|
|
78
|
+
if (writeCheckpointAfter) {
|
|
79
|
+
const graphHash = `entity-${this.domain}`;
|
|
80
|
+
const refs = [artifactRef];
|
|
81
|
+
const memHash = hashMemory(refs);
|
|
82
|
+
store.writeCheckpoint(entityVersion, graphHash, memHash, refs);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return memory;
|
|
86
|
+
}
|
|
87
|
+
}
|
package/dist/graph-runner.d.ts
CHANGED
|
@@ -5,6 +5,14 @@ import { type ModelBinding } from "./config.js";
|
|
|
5
5
|
import { AgentGraph } from "./graph-schema.js";
|
|
6
6
|
import { WorkingMemory } from "./memory.js";
|
|
7
7
|
import { Store } from "./persistence.js";
|
|
8
|
+
import type { Verifier } from "./verifier-interface.js";
|
|
9
|
+
/** Options for configuring the GraphRunner beyond bindings. */
|
|
10
|
+
export interface GraphRunnerOptions {
|
|
11
|
+
/** Pluggable verifier. If omitted, falls back to the existing vitest-based runVerifier. */
|
|
12
|
+
verifier?: Verifier;
|
|
13
|
+
/** Domain for agent prompts. Defaults to "code". */
|
|
14
|
+
domain?: string;
|
|
15
|
+
}
|
|
8
16
|
/**
|
|
9
17
|
* Runs a declarative agent graph: nodes (role + binding), edges (data flow).
|
|
10
18
|
* After all nodes run, verifier runs on the final node's output and gating is applied.
|
|
@@ -12,7 +20,9 @@ import { Store } from "./persistence.js";
|
|
|
12
20
|
export declare class GraphRunner {
|
|
13
21
|
private graph;
|
|
14
22
|
private gateways;
|
|
15
|
-
|
|
23
|
+
private pluggableVerifier?;
|
|
24
|
+
private domain;
|
|
25
|
+
constructor(graph: AgentGraph, bindings?: Record<string, ModelBinding>, options?: GraphRunnerOptions);
|
|
16
26
|
/**
|
|
17
27
|
* Execute graph: task in -> run nodes in topo order -> run verifier on final node -> gating.
|
|
18
28
|
* If store is provided and verification passed: admit to verified memory; optionally write checkpoint.
|
package/dist/graph-runner.js
CHANGED
|
@@ -14,8 +14,12 @@ import { runVerifier } from "./verifier.js";
|
|
|
14
14
|
export class GraphRunner {
|
|
15
15
|
graph;
|
|
16
16
|
gateways = new Map();
|
|
17
|
-
|
|
17
|
+
pluggableVerifier;
|
|
18
|
+
domain;
|
|
19
|
+
constructor(graph, bindings, options) {
|
|
18
20
|
this.graph = graph;
|
|
21
|
+
this.pluggableVerifier = options?.verifier;
|
|
22
|
+
this.domain = options?.domain ?? "code";
|
|
19
23
|
const resolvedBindings = bindings ?? defaultCodeEntityBindings();
|
|
20
24
|
for (const node of graph.nodes) {
|
|
21
25
|
if (node.role === "verifier") {
|
|
@@ -71,7 +75,7 @@ export class GraphRunner {
|
|
|
71
75
|
if (!gateway) {
|
|
72
76
|
throw new Error(`No gateway for binding '${node.binding}'`);
|
|
73
77
|
}
|
|
74
|
-
const out = await runRole(node.role, gateway, task, inputs, memory.longTermContext, store ?? null);
|
|
78
|
+
const out = await runRole(node.role, gateway, task, inputs, memory.longTermContext, store ?? null, this.domain);
|
|
75
79
|
memory.setSlot(nodeId, out);
|
|
76
80
|
}
|
|
77
81
|
// Set final candidate from final node
|
|
@@ -79,8 +83,15 @@ export class GraphRunner {
|
|
|
79
83
|
if (finalId) {
|
|
80
84
|
memory.finalCandidate = memory.getSlot(finalId);
|
|
81
85
|
}
|
|
82
|
-
// Run verification
|
|
83
|
-
|
|
86
|
+
// Run verification: pluggable verifier if provided, otherwise legacy runVerifier
|
|
87
|
+
if (this.pluggableVerifier) {
|
|
88
|
+
const ctx = { task, testCode, domain: this.domain };
|
|
89
|
+
const result = await this.pluggableVerifier.verify(memory, ctx);
|
|
90
|
+
memory.setVerification(result.passed, result.evidence);
|
|
91
|
+
}
|
|
92
|
+
else {
|
|
93
|
+
await runVerifier(memory, testCode);
|
|
94
|
+
}
|
|
84
95
|
// Record outcome and persist if verification passed
|
|
85
96
|
const latencySec = (performance.now() - t0) / 1000;
|
|
86
97
|
if (store) {
|
package/dist/improvement.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { StrategySuggestion } from "./types.js";
|
|
|
7
7
|
* Review recent outcomes and return suggested strategy updates (rule-based for MVP).
|
|
8
8
|
* Returns list of { role, suggestion } for human or governance to apply.
|
|
9
9
|
*/
|
|
10
|
-
export declare function critiqueStrategies(store: Store, recentCount?: number): StrategySuggestion[];
|
|
10
|
+
export declare function critiqueStrategies(store: Store, recentCount?: number, domain?: string): StrategySuggestion[];
|
|
11
11
|
/**
|
|
12
12
|
* Apply a new prompt for role (strategy update). Under governance, this would require approval.
|
|
13
13
|
*/
|
package/dist/improvement.js
CHANGED
|
@@ -6,7 +6,7 @@ import { getRecentOutcomes, recordOutcome, setStrategy, } from "./persistence.js
|
|
|
6
6
|
* Review recent outcomes and return suggested strategy updates (rule-based for MVP).
|
|
7
7
|
* Returns list of { role, suggestion } for human or governance to apply.
|
|
8
8
|
*/
|
|
9
|
-
export function critiqueStrategies(store, recentCount = 10) {
|
|
9
|
+
export function critiqueStrategies(store, recentCount = 10, domain = "code") {
|
|
10
10
|
const outcomes = getRecentOutcomes(store, recentCount);
|
|
11
11
|
if (outcomes.length < 3) {
|
|
12
12
|
return [];
|
|
@@ -15,14 +15,26 @@ export function critiqueStrategies(store, recentCount = 10) {
|
|
|
15
15
|
const failRate = 1.0 - passed / outcomes.length;
|
|
16
16
|
const suggestions = [];
|
|
17
17
|
if (failRate >= 0.5) {
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
18
|
+
if (domain === "code") {
|
|
19
|
+
suggestions.push({
|
|
20
|
+
role: "generator",
|
|
21
|
+
suggestion: "Add explicit instruction: output only valid TypeScript with no markdown fences or commentary.",
|
|
22
|
+
});
|
|
23
|
+
suggestions.push({
|
|
24
|
+
role: "judge",
|
|
25
|
+
suggestion: "Ensure Judge incorporates all critic feedback and outputs runnable code only.",
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
else {
|
|
29
|
+
suggestions.push({
|
|
30
|
+
role: "generator",
|
|
31
|
+
suggestion: "Add explicit instruction: produce clear, complete, and accurate responses. Avoid ambiguity.",
|
|
32
|
+
});
|
|
33
|
+
suggestions.push({
|
|
34
|
+
role: "judge",
|
|
35
|
+
suggestion: "Ensure Judge addresses all critic concerns and produces a safe, well-structured final response.",
|
|
36
|
+
});
|
|
37
|
+
}
|
|
26
38
|
}
|
|
27
39
|
return suggestions;
|
|
28
40
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Moltblock — framework for evolving composite intelligences (Entities).
|
|
3
3
|
*/
|
|
4
|
-
export declare const VERSION = "0.
|
|
4
|
+
export declare const VERSION = "0.6.0";
|
|
5
5
|
export type { ModelBinding, BindingEntry, AgentConfig, MoltblockConfig, ChatMessage, VerifiedMemoryEntry, CheckpointEntry, OutcomeEntry, InboxEntry, StrategySuggestion, ReceivedArtifact, GovernanceConfig, } from "./types.js";
|
|
6
6
|
export { WorkingMemory } from "./memory.js";
|
|
7
7
|
export { signArtifact, verifyArtifact, artifactHash } from "./signing.js";
|
|
8
|
-
export { loadMoltblockConfig, defaultCodeEntityBindings, getConfigSource, BindingEntrySchema, AgentConfigSchema, MoltblockConfigSchema, ModelBindingSchema, type ConfigSource, } from "./config.js";
|
|
8
|
+
export { loadMoltblockConfig, defaultCodeEntityBindings, detectProvider, getConfigSource, loadPolicyRules, BindingEntrySchema, AgentConfigSchema, MoltblockConfigSchema, ModelBindingSchema, PolicyRuleSchema, type BindingOverrides, type ConfigSource, type PolicyRuleConfig, } from "./config.js";
|
|
9
9
|
export { Store, hashGraph, hashMemory, auditLog, getGovernanceValue, setGovernanceValue, putInbox, getInbox, recordOutcome, getRecentOutcomes, getStrategy, setStrategy, } from "./persistence.js";
|
|
10
10
|
export { LLMGateway } from "./gateway.js";
|
|
11
11
|
export { runGenerator, runCritic, runJudge, runRole, } from "./agents.js";
|
|
12
12
|
export { AgentGraph, GraphNodeSchema, GraphEdgeSchema, AgentGraphSchema, type GraphNode, type GraphEdge, type AgentGraphData, } from "./graph-schema.js";
|
|
13
|
-
export { GraphRunner } from "./graph-runner.js";
|
|
13
|
+
export { GraphRunner, type GraphRunnerOptions } from "./graph-runner.js";
|
|
14
14
|
export { extractCodeBlock, runVitestOnCode, runVerifier } from "./verifier.js";
|
|
15
|
+
export type { Verifier, VerificationResult, VerifierContext, } from "./verifier-interface.js";
|
|
16
|
+
export { PolicyVerifier, type PolicyRule } from "./policy-verifier.js";
|
|
17
|
+
export { CodeVerifier } from "./code-verifier.js";
|
|
18
|
+
export { CompositeVerifier, type CompositeVerifierOptions } from "./composite-verifier.js";
|
|
19
|
+
export { getDomainPrompts, registerDomain, listDomains, type DomainPrompts, } from "./domain-prompts.js";
|
|
20
|
+
export { classifyRisk, type RiskLevel, type RiskClassification } from "./risk.js";
|
|
15
21
|
export { createGovernanceConfig, canMolt, triggerMolt, pause, resume, isPaused, emergencyShutdown, } from "./governance.js";
|
|
16
22
|
export { sendArtifact, receiveArtifacts } from "./handoff.js";
|
|
17
23
|
export { critiqueStrategies, applySuggestion, runEval, runImprovementCycle, } from "./improvement.js";
|
|
18
24
|
export { validateTask, validateTestCode, MAX_TASK_LENGTH, MIN_TASK_LENGTH, type ValidationResult, } from "./validation.js";
|
|
19
25
|
export { CodeEntity, loadEntityWithGraph } from "./entity.js";
|
|
26
|
+
export { Entity, type EntityOptions } from "./entity-base.js";
|
package/dist/index.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Moltblock — framework for evolving composite intelligences (Entities).
|
|
3
3
|
*/
|
|
4
|
-
export const VERSION = "0.
|
|
4
|
+
export const VERSION = "0.6.0";
|
|
5
5
|
// Memory
|
|
6
6
|
export { WorkingMemory } from "./memory.js";
|
|
7
7
|
// Signing
|
|
8
8
|
export { signArtifact, verifyArtifact, artifactHash } from "./signing.js";
|
|
9
9
|
// Config
|
|
10
|
-
export { loadMoltblockConfig, defaultCodeEntityBindings, getConfigSource, BindingEntrySchema, AgentConfigSchema, MoltblockConfigSchema, ModelBindingSchema, } from "./config.js";
|
|
10
|
+
export { loadMoltblockConfig, defaultCodeEntityBindings, detectProvider, getConfigSource, loadPolicyRules, BindingEntrySchema, AgentConfigSchema, MoltblockConfigSchema, ModelBindingSchema, PolicyRuleSchema, } from "./config.js";
|
|
11
11
|
// Persistence
|
|
12
12
|
export { Store, hashGraph, hashMemory, auditLog, getGovernanceValue, setGovernanceValue, putInbox, getInbox, recordOutcome, getRecentOutcomes, getStrategy, setStrategy, } from "./persistence.js";
|
|
13
13
|
// Gateway
|
|
@@ -18,8 +18,18 @@ export { runGenerator, runCritic, runJudge, runRole, } from "./agents.js";
|
|
|
18
18
|
export { AgentGraph, GraphNodeSchema, GraphEdgeSchema, AgentGraphSchema, } from "./graph-schema.js";
|
|
19
19
|
// Graph Runner
|
|
20
20
|
export { GraphRunner } from "./graph-runner.js";
|
|
21
|
-
// Verifier
|
|
21
|
+
// Verifier (legacy vitest-based)
|
|
22
22
|
export { extractCodeBlock, runVitestOnCode, runVerifier } from "./verifier.js";
|
|
23
|
+
// Policy Verifier
|
|
24
|
+
export { PolicyVerifier } from "./policy-verifier.js";
|
|
25
|
+
// Code Verifier (adapter)
|
|
26
|
+
export { CodeVerifier } from "./code-verifier.js";
|
|
27
|
+
// Composite Verifier
|
|
28
|
+
export { CompositeVerifier } from "./composite-verifier.js";
|
|
29
|
+
// Domain Prompts
|
|
30
|
+
export { getDomainPrompts, registerDomain, listDomains, } from "./domain-prompts.js";
|
|
31
|
+
// Risk Classification
|
|
32
|
+
export { classifyRisk } from "./risk.js";
|
|
23
33
|
// Governance
|
|
24
34
|
export { createGovernanceConfig, canMolt, triggerMolt, pause, resume, isPaused, emergencyShutdown, } from "./governance.js";
|
|
25
35
|
// Handoff
|
|
@@ -28,5 +38,7 @@ export { sendArtifact, receiveArtifacts } from "./handoff.js";
|
|
|
28
38
|
export { critiqueStrategies, applySuggestion, runEval, runImprovementCycle, } from "./improvement.js";
|
|
29
39
|
// Validation
|
|
30
40
|
export { validateTask, validateTestCode, MAX_TASK_LENGTH, MIN_TASK_LENGTH, } from "./validation.js";
|
|
31
|
-
// Entity
|
|
41
|
+
// Entity (code-specific, backward compat)
|
|
32
42
|
export { CodeEntity, loadEntityWithGraph } from "./entity.js";
|
|
43
|
+
// Entity (generic, pluggable)
|
|
44
|
+
export { Entity } from "./entity-base.js";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PolicyVerifier: rule-based verifier that catches dangerous patterns without an LLM call.
|
|
3
|
+
*/
|
|
4
|
+
import type { WorkingMemory } from "./memory.js";
|
|
5
|
+
import type { Verifier, VerificationResult, VerifierContext } from "./verifier-interface.js";
|
|
6
|
+
/** A single policy rule. */
|
|
7
|
+
export interface PolicyRule {
|
|
8
|
+
id: string;
|
|
9
|
+
description: string;
|
|
10
|
+
/** What to match against: "artifact", "task", or "both". */
|
|
11
|
+
target: "artifact" | "task" | "both";
|
|
12
|
+
/** Regex pattern string. */
|
|
13
|
+
pattern: string;
|
|
14
|
+
/** "deny" blocks the artifact; "allow" overrides deny rules in the same category. */
|
|
15
|
+
action: "deny" | "allow";
|
|
16
|
+
category: string;
|
|
17
|
+
enabled: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Rule-based policy verifier. Checks artifacts and tasks against deny/allow rules.
|
|
21
|
+
* Allow rules in the same category override deny rules.
|
|
22
|
+
*/
|
|
23
|
+
export declare class PolicyVerifier implements Verifier {
|
|
24
|
+
readonly name = "PolicyVerifier";
|
|
25
|
+
private rules;
|
|
26
|
+
constructor(customRules?: PolicyRule[]);
|
|
27
|
+
verify(memory: WorkingMemory, context?: VerifierContext): Promise<VerificationResult>;
|
|
28
|
+
private getTargetText;
|
|
29
|
+
}
|