moltblock 0.8.0 → 0.11.1
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/dist/config.d.ts +2 -0
- package/dist/config.js +6 -4
- package/dist/entity-base.d.ts +6 -0
- package/dist/entity-base.js +34 -4
- package/dist/entity.d.ts +6 -0
- package/dist/entity.js +34 -4
- package/dist/gateway.d.ts +6 -0
- package/dist/gateway.js +21 -2
- package/dist/governance.js +10 -5
- package/dist/graph-runner.d.ts +4 -0
- package/dist/graph-runner.js +29 -4
- package/dist/index.d.ts +2 -2
- package/dist/index.js +2 -2
- package/dist/persistence.d.ts +5 -0
- package/dist/persistence.js +20 -8
- package/dist/policy-verifier.d.ts +2 -0
- package/dist/policy-verifier.js +9 -4
- package/dist/signing.js +5 -1
- package/dist/types.d.ts +4 -0
- package/package.json +1 -1
- package/{README.md → readme.md} +5 -0
- package/skill/SKILL.md +29 -14
package/dist/config.d.ts
CHANGED
|
@@ -70,6 +70,8 @@ export declare const ModelBindingSchema: z.ZodObject<{
|
|
|
70
70
|
baseUrl: z.ZodString;
|
|
71
71
|
apiKey: z.ZodDefault<z.ZodNullable<z.ZodString>>;
|
|
72
72
|
model: z.ZodDefault<z.ZodString>;
|
|
73
|
+
timeoutMs: z.ZodOptional<z.ZodNumber>;
|
|
74
|
+
maxRetries: z.ZodOptional<z.ZodNumber>;
|
|
73
75
|
}, z.core.$strip>;
|
|
74
76
|
export type ModelBinding = z.infer<typeof ModelBindingSchema>;
|
|
75
77
|
/** Track which config source was used */
|
package/dist/config.js
CHANGED
|
@@ -60,6 +60,8 @@ export const ModelBindingSchema = z.object({
|
|
|
60
60
|
baseUrl: z.string().describe("API base URL"),
|
|
61
61
|
apiKey: z.string().nullable().default(null).describe("Bearer token; null for local"),
|
|
62
62
|
model: z.string().default("default").describe("Model name for chat completion"),
|
|
63
|
+
timeoutMs: z.number().int().positive().optional().describe("Request timeout in ms (default 60000)"),
|
|
64
|
+
maxRetries: z.number().int().min(0).optional().describe("Max retries on transient errors (default 2)"),
|
|
63
65
|
});
|
|
64
66
|
/** Validate that a config path is within allowed directories (cwd, homedir, or tmpdir). */
|
|
65
67
|
function isAllowedConfigPath(filePath) {
|
|
@@ -131,8 +133,8 @@ export function loadMoltblockConfig() {
|
|
|
131
133
|
lastConfigSource = "moltblock";
|
|
132
134
|
return config;
|
|
133
135
|
}
|
|
134
|
-
catch {
|
|
135
|
-
|
|
136
|
+
catch (err) {
|
|
137
|
+
console.warn(`Warning: Failed to parse config ${moltblockFile}: ${err instanceof Error ? err.message : String(err)}`);
|
|
136
138
|
}
|
|
137
139
|
}
|
|
138
140
|
// Fallback to OpenClaw config
|
|
@@ -148,8 +150,8 @@ export function loadMoltblockConfig() {
|
|
|
148
150
|
return config;
|
|
149
151
|
}
|
|
150
152
|
}
|
|
151
|
-
catch {
|
|
152
|
-
|
|
153
|
+
catch (err) {
|
|
154
|
+
console.warn(`Warning: Failed to parse config ${openclawFile}: ${err instanceof Error ? err.message : String(err)}`);
|
|
153
155
|
}
|
|
154
156
|
}
|
|
155
157
|
lastConfigSource = "env";
|
package/dist/entity-base.d.ts
CHANGED
|
@@ -18,6 +18,7 @@ export interface EntityOptions {
|
|
|
18
18
|
/**
|
|
19
19
|
* Generic Entity: same Generator -> Critic -> Judge pipeline as CodeEntity,
|
|
20
20
|
* but with a pluggable verifier and domain-aware prompts.
|
|
21
|
+
* Supports degraded fallback: if critic/judge fails, execution continues with available data.
|
|
21
22
|
*/
|
|
22
23
|
export declare class Entity {
|
|
23
24
|
private gateways;
|
|
@@ -27,6 +28,11 @@ export declare class Entity {
|
|
|
27
28
|
/**
|
|
28
29
|
* One full loop: task -> Generator -> Critic -> Judge -> Verifier -> gating.
|
|
29
30
|
* Returns working memory with authoritative_artifact set only if verification passed.
|
|
31
|
+
*
|
|
32
|
+
* Degraded fallback:
|
|
33
|
+
* - Generator fails -> return immediately with verification failed + error evidence
|
|
34
|
+
* - Critic fails -> proceed to judge with empty critique
|
|
35
|
+
* - Judge fails -> use draft as final candidate
|
|
30
36
|
*/
|
|
31
37
|
run(task: string, options?: {
|
|
32
38
|
testCode?: string;
|
package/dist/entity-base.js
CHANGED
|
@@ -12,6 +12,7 @@ import { validateTask } from "./validation.js";
|
|
|
12
12
|
/**
|
|
13
13
|
* Generic Entity: same Generator -> Critic -> Judge pipeline as CodeEntity,
|
|
14
14
|
* but with a pluggable verifier and domain-aware prompts.
|
|
15
|
+
* Supports degraded fallback: if critic/judge fails, execution continues with available data.
|
|
15
16
|
*/
|
|
16
17
|
export class Entity {
|
|
17
18
|
gateways;
|
|
@@ -30,6 +31,11 @@ export class Entity {
|
|
|
30
31
|
/**
|
|
31
32
|
* One full loop: task -> Generator -> Critic -> Judge -> Verifier -> gating.
|
|
32
33
|
* Returns working memory with authoritative_artifact set only if verification passed.
|
|
34
|
+
*
|
|
35
|
+
* Degraded fallback:
|
|
36
|
+
* - Generator fails -> return immediately with verification failed + error evidence
|
|
37
|
+
* - Critic fails -> proceed to judge with empty critique
|
|
38
|
+
* - Judge fails -> use draft as final candidate
|
|
33
39
|
*/
|
|
34
40
|
async run(task, options = {}) {
|
|
35
41
|
const { testCode, store, entityVersion = "0.5.0", writeCheckpointAfter = false, } = options;
|
|
@@ -55,10 +61,34 @@ export class Entity {
|
|
|
55
61
|
}
|
|
56
62
|
memory.longTermContext = parts.length > 0 ? parts.join("\n---\n") : "";
|
|
57
63
|
}
|
|
58
|
-
//
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
64
|
+
// Generator — if it fails, we have nothing to work with
|
|
65
|
+
try {
|
|
66
|
+
await runGenerator(this.gateways["generator"], memory, store ?? null, this.domain);
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
70
|
+
memory.meta["generatorError"] = errMsg;
|
|
71
|
+
memory.setVerification(false, `Generator failed: ${errMsg}`);
|
|
72
|
+
return memory;
|
|
73
|
+
}
|
|
74
|
+
// Critic — if it fails, proceed to judge with empty critique (degraded)
|
|
75
|
+
try {
|
|
76
|
+
await runCritic(this.gateways["critic"], memory, store ?? null, this.domain);
|
|
77
|
+
}
|
|
78
|
+
catch (err) {
|
|
79
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
80
|
+
memory.meta["criticError"] = errMsg;
|
|
81
|
+
memory.setCritique("");
|
|
82
|
+
}
|
|
83
|
+
// Judge — if it fails, use draft as final candidate (degraded)
|
|
84
|
+
try {
|
|
85
|
+
await runJudge(this.gateways["judge"], memory, store ?? null, this.domain);
|
|
86
|
+
}
|
|
87
|
+
catch (err) {
|
|
88
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
89
|
+
memory.meta["judgeError"] = errMsg;
|
|
90
|
+
memory.setFinalCandidate(memory.draft);
|
|
91
|
+
}
|
|
62
92
|
// Run pluggable verifier
|
|
63
93
|
const ctx = {
|
|
64
94
|
task,
|
package/dist/entity.d.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { Store } from "./persistence.js";
|
|
|
8
8
|
/**
|
|
9
9
|
* Code Entity: Generator -> Critic -> Judge -> Verifier.
|
|
10
10
|
* Uses working memory and per-role LLM gateways.
|
|
11
|
+
* Supports degraded fallback: if critic/judge fails, execution continues with available data.
|
|
11
12
|
*/
|
|
12
13
|
export declare class CodeEntity {
|
|
13
14
|
private gateways;
|
|
@@ -16,6 +17,11 @@ export declare class CodeEntity {
|
|
|
16
17
|
* One full loop: task in -> Generator -> Critic -> Judge -> Verifier -> gating.
|
|
17
18
|
* If store is provided and verification passed: admit to verified memory; optionally write checkpoint.
|
|
18
19
|
* Returns working memory with authoritative_artifact set only if verification passed.
|
|
20
|
+
*
|
|
21
|
+
* Degraded fallback:
|
|
22
|
+
* - Generator fails -> return immediately with verification failed + error evidence
|
|
23
|
+
* - Critic fails -> proceed to judge with empty critique
|
|
24
|
+
* - Judge fails -> use draft as final candidate
|
|
19
25
|
*/
|
|
20
26
|
run(task: string, options?: {
|
|
21
27
|
testCode?: string;
|
package/dist/entity.js
CHANGED
|
@@ -13,6 +13,7 @@ import { runVerifier } from "./verifier.js";
|
|
|
13
13
|
/**
|
|
14
14
|
* Code Entity: Generator -> Critic -> Judge -> Verifier.
|
|
15
15
|
* Uses working memory and per-role LLM gateways.
|
|
16
|
+
* Supports degraded fallback: if critic/judge fails, execution continues with available data.
|
|
16
17
|
*/
|
|
17
18
|
export class CodeEntity {
|
|
18
19
|
gateways;
|
|
@@ -28,6 +29,11 @@ export class CodeEntity {
|
|
|
28
29
|
* One full loop: task in -> Generator -> Critic -> Judge -> Verifier -> gating.
|
|
29
30
|
* If store is provided and verification passed: admit to verified memory; optionally write checkpoint.
|
|
30
31
|
* Returns working memory with authoritative_artifact set only if verification passed.
|
|
32
|
+
*
|
|
33
|
+
* Degraded fallback:
|
|
34
|
+
* - Generator fails -> return immediately with verification failed + error evidence
|
|
35
|
+
* - Critic fails -> proceed to judge with empty critique
|
|
36
|
+
* - Judge fails -> use draft as final candidate
|
|
31
37
|
*/
|
|
32
38
|
async run(task, options = {}) {
|
|
33
39
|
const { testCode, store, entityVersion = "0.2.0", writeCheckpointAfter = false, } = options;
|
|
@@ -59,10 +65,34 @@ export class CodeEntity {
|
|
|
59
65
|
}
|
|
60
66
|
memory.longTermContext = parts.length > 0 ? parts.join("\n---\n") : "";
|
|
61
67
|
}
|
|
62
|
-
//
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
// Generator — if it fails, we have nothing to work with
|
|
69
|
+
try {
|
|
70
|
+
await runGenerator(this.gateways["generator"], memory, store ?? null);
|
|
71
|
+
}
|
|
72
|
+
catch (err) {
|
|
73
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
74
|
+
memory.meta["generatorError"] = errMsg;
|
|
75
|
+
memory.setVerification(false, `Generator failed: ${errMsg}`);
|
|
76
|
+
return memory;
|
|
77
|
+
}
|
|
78
|
+
// Critic — if it fails, proceed with empty critique (degraded)
|
|
79
|
+
try {
|
|
80
|
+
await runCritic(this.gateways["critic"], memory, store ?? null);
|
|
81
|
+
}
|
|
82
|
+
catch (err) {
|
|
83
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
84
|
+
memory.meta["criticError"] = errMsg;
|
|
85
|
+
memory.setCritique("");
|
|
86
|
+
}
|
|
87
|
+
// Judge — if it fails, use draft as final candidate (degraded)
|
|
88
|
+
try {
|
|
89
|
+
await runJudge(this.gateways["judge"], memory, store ?? null);
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
93
|
+
memory.meta["judgeError"] = errMsg;
|
|
94
|
+
memory.setFinalCandidate(memory.draft);
|
|
95
|
+
}
|
|
66
96
|
await runVerifier(memory, testCode);
|
|
67
97
|
// Record outcome and persist if verification passed
|
|
68
98
|
const latencySec = (performance.now() - t0) / 1000;
|
package/dist/gateway.d.ts
CHANGED
|
@@ -3,8 +3,14 @@
|
|
|
3
3
|
* Supports OpenAI, Anthropic Claude, Google Gemini, local LLMs (LM Studio, Ollama), and more.
|
|
4
4
|
*/
|
|
5
5
|
import type { ModelBinding, ChatMessage } from "./types.js";
|
|
6
|
+
/**
|
|
7
|
+
* Extract only the hostname from a URL for safe error messages.
|
|
8
|
+
* Strips path, query params, credentials, and port.
|
|
9
|
+
*/
|
|
10
|
+
export declare function sanitizeBaseUrl(baseUrl: string): string;
|
|
6
11
|
/**
|
|
7
12
|
* One client per role; uses OpenAI-compatible API with base_url and optional api_key.
|
|
13
|
+
* Supports configurable timeout and retry via the OpenAI SDK.
|
|
8
14
|
*/
|
|
9
15
|
export declare class LLMGateway {
|
|
10
16
|
private client;
|
package/dist/gateway.js
CHANGED
|
@@ -3,6 +3,19 @@
|
|
|
3
3
|
* Supports OpenAI, Anthropic Claude, Google Gemini, local LLMs (LM Studio, Ollama), and more.
|
|
4
4
|
*/
|
|
5
5
|
import OpenAI from "openai";
|
|
6
|
+
/**
|
|
7
|
+
* Extract only the hostname from a URL for safe error messages.
|
|
8
|
+
* Strips path, query params, credentials, and port.
|
|
9
|
+
*/
|
|
10
|
+
export function sanitizeBaseUrl(baseUrl) {
|
|
11
|
+
try {
|
|
12
|
+
const url = new URL(baseUrl);
|
|
13
|
+
return url.hostname;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return "<invalid-url>";
|
|
17
|
+
}
|
|
18
|
+
}
|
|
6
19
|
/**
|
|
7
20
|
* If model is 'local' or empty and base_url is localhost, use first available model from API.
|
|
8
21
|
*/
|
|
@@ -27,6 +40,7 @@ async function resolveLocalModel(client, baseUrl, configured) {
|
|
|
27
40
|
}
|
|
28
41
|
/**
|
|
29
42
|
* One client per role; uses OpenAI-compatible API with base_url and optional api_key.
|
|
43
|
+
* Supports configurable timeout and retry via the OpenAI SDK.
|
|
30
44
|
*/
|
|
31
45
|
export class LLMGateway {
|
|
32
46
|
client;
|
|
@@ -38,6 +52,8 @@ export class LLMGateway {
|
|
|
38
52
|
this.client = new OpenAI({
|
|
39
53
|
baseURL: binding.baseUrl,
|
|
40
54
|
apiKey: binding.apiKey ?? "not-needed",
|
|
55
|
+
timeout: binding.timeoutMs ?? 60_000,
|
|
56
|
+
maxRetries: binding.maxRetries ?? 2,
|
|
41
57
|
});
|
|
42
58
|
this.model = binding.model;
|
|
43
59
|
}
|
|
@@ -58,8 +74,11 @@ export class LLMGateway {
|
|
|
58
74
|
});
|
|
59
75
|
}
|
|
60
76
|
catch (err) {
|
|
61
|
-
const
|
|
62
|
-
|
|
77
|
+
const host = sanitizeBaseUrl(this.binding.baseUrl);
|
|
78
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
79
|
+
// Strip any key-like strings from the error message
|
|
80
|
+
const safeMsg = msg.replace(/[A-Za-z0-9_\-]{20,}/g, "[REDACTED]");
|
|
81
|
+
throw new Error(`LLM request failed (model=${this.model}, host=${host}): ${safeMsg}`);
|
|
63
82
|
}
|
|
64
83
|
const choice = resp.choices[0];
|
|
65
84
|
if (!choice?.message) {
|
package/dist/governance.js
CHANGED
|
@@ -51,11 +51,16 @@ export function triggerMolt(store, entityVersion, config, options = {}) {
|
|
|
51
51
|
if (!allowed) {
|
|
52
52
|
return { success: false, message: reason };
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
54
|
+
// Wrap checkpoint + governance state + audit in a transaction for atomicity
|
|
55
|
+
const db = store.getDb();
|
|
56
|
+
const txn = db.transaction(() => {
|
|
57
|
+
store.writeCheckpoint(entityVersion, graphHash || "molt", memoryHash || "", artifactRefs);
|
|
58
|
+
const now = Date.now() / 1000;
|
|
59
|
+
setGovernanceValue(store, "last_molt_at", now.toString());
|
|
60
|
+
setGovernanceValue(store, "entity_version", entityVersion);
|
|
61
|
+
auditLog(store, "molt", `version=${entityVersion} graph_hash=${graphHash}`);
|
|
62
|
+
});
|
|
63
|
+
txn();
|
|
59
64
|
return { success: true, message: "Molt completed" };
|
|
60
65
|
}
|
|
61
66
|
/**
|
package/dist/graph-runner.d.ts
CHANGED
|
@@ -27,11 +27,15 @@ export declare class GraphRunner {
|
|
|
27
27
|
* Execute graph: task in -> run nodes in topo order -> run verifier on final node -> gating.
|
|
28
28
|
* If store is provided and verification passed: admit to verified memory; optionally write checkpoint.
|
|
29
29
|
* Returns working memory with slots filled and authoritative_artifact set iff verification passed.
|
|
30
|
+
*
|
|
31
|
+
* @param options.continueOnError - If true, node failures are recorded but execution continues.
|
|
32
|
+
* Failed nodes produce empty output. Default false (throws on first failure).
|
|
30
33
|
*/
|
|
31
34
|
run(task: string, options?: {
|
|
32
35
|
testCode?: string;
|
|
33
36
|
store?: Store;
|
|
34
37
|
entityVersion?: string;
|
|
35
38
|
writeCheckpointAfter?: boolean;
|
|
39
|
+
continueOnError?: boolean;
|
|
36
40
|
}): Promise<WorkingMemory>;
|
|
37
41
|
}
|
package/dist/graph-runner.js
CHANGED
|
@@ -39,9 +39,12 @@ export class GraphRunner {
|
|
|
39
39
|
* Execute graph: task in -> run nodes in topo order -> run verifier on final node -> gating.
|
|
40
40
|
* If store is provided and verification passed: admit to verified memory; optionally write checkpoint.
|
|
41
41
|
* Returns working memory with slots filled and authoritative_artifact set iff verification passed.
|
|
42
|
+
*
|
|
43
|
+
* @param options.continueOnError - If true, node failures are recorded but execution continues.
|
|
44
|
+
* Failed nodes produce empty output. Default false (throws on first failure).
|
|
42
45
|
*/
|
|
43
46
|
async run(task, options = {}) {
|
|
44
|
-
const { testCode, store, entityVersion = "0.2.0", writeCheckpointAfter = false, } = options;
|
|
47
|
+
const { testCode, store, entityVersion = "0.2.0", writeCheckpointAfter = false, continueOnError = false, } = options;
|
|
45
48
|
const t0 = performance.now();
|
|
46
49
|
const memory = new WorkingMemory();
|
|
47
50
|
memory.setTask(task);
|
|
@@ -73,10 +76,32 @@ export class GraphRunner {
|
|
|
73
76
|
}
|
|
74
77
|
const gateway = this.gateways.get(node.binding);
|
|
75
78
|
if (!gateway) {
|
|
76
|
-
|
|
79
|
+
const err = `No gateway for binding '${node.binding}'`;
|
|
80
|
+
if (continueOnError) {
|
|
81
|
+
if (!memory.meta["nodeErrors"]) {
|
|
82
|
+
memory.meta["nodeErrors"] = {};
|
|
83
|
+
}
|
|
84
|
+
memory.meta["nodeErrors"][nodeId] = err;
|
|
85
|
+
memory.setSlot(nodeId, "");
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
throw new Error(err);
|
|
89
|
+
}
|
|
90
|
+
try {
|
|
91
|
+
const out = await runRole(node.role, gateway, task, inputs, memory.longTermContext, store ?? null, this.domain);
|
|
92
|
+
memory.setSlot(nodeId, out);
|
|
93
|
+
}
|
|
94
|
+
catch (err) {
|
|
95
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
96
|
+
if (!continueOnError) {
|
|
97
|
+
throw err;
|
|
98
|
+
}
|
|
99
|
+
if (!memory.meta["nodeErrors"]) {
|
|
100
|
+
memory.meta["nodeErrors"] = {};
|
|
101
|
+
}
|
|
102
|
+
memory.meta["nodeErrors"][nodeId] = errMsg;
|
|
103
|
+
memory.setSlot(nodeId, "");
|
|
77
104
|
}
|
|
78
|
-
const out = await runRole(node.role, gateway, task, inputs, memory.longTermContext, store ?? null, this.domain);
|
|
79
|
-
memory.setSlot(nodeId, out);
|
|
80
105
|
}
|
|
81
106
|
// Set final candidate from final node
|
|
82
107
|
const finalId = this.graph.getFinalNodeId();
|
package/dist/index.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
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.11.1";
|
|
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
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
|
-
export { LLMGateway } from "./gateway.js";
|
|
10
|
+
export { LLMGateway, sanitizeBaseUrl } 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
13
|
export { GraphRunner, type GraphRunnerOptions } from "./graph-runner.js";
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Moltblock — framework for evolving composite intelligences (Entities).
|
|
3
3
|
*/
|
|
4
|
-
export const VERSION = "0.
|
|
4
|
+
export const VERSION = "0.11.1";
|
|
5
5
|
// Memory
|
|
6
6
|
export { WorkingMemory } from "./memory.js";
|
|
7
7
|
// Signing
|
|
@@ -11,7 +11,7 @@ export { loadMoltblockConfig, defaultCodeEntityBindings, detectProvider, getConf
|
|
|
11
11
|
// Persistence
|
|
12
12
|
export { Store, hashGraph, hashMemory, auditLog, getGovernanceValue, setGovernanceValue, putInbox, getInbox, recordOutcome, getRecentOutcomes, getStrategy, setStrategy, } from "./persistence.js";
|
|
13
13
|
// Gateway
|
|
14
|
-
export { LLMGateway } from "./gateway.js";
|
|
14
|
+
export { LLMGateway, sanitizeBaseUrl } from "./gateway.js";
|
|
15
15
|
// Agents
|
|
16
16
|
export { runGenerator, runCritic, runJudge, runRole, } from "./agents.js";
|
|
17
17
|
// Graph
|
package/dist/persistence.d.ts
CHANGED
|
@@ -35,6 +35,11 @@ export declare class Store {
|
|
|
35
35
|
/** Get internal db for helper functions */
|
|
36
36
|
getDb(): Database.Database;
|
|
37
37
|
close(): void;
|
|
38
|
+
/**
|
|
39
|
+
* Explicit resource cleanup via Symbol.dispose.
|
|
40
|
+
* Enables `using store = new Store(...)` with TypeScript 5.2+ explicit resource management.
|
|
41
|
+
*/
|
|
42
|
+
[Symbol.dispose](): void;
|
|
38
43
|
}
|
|
39
44
|
/**
|
|
40
45
|
* Stable hash for graph config (for checkpoint).
|
package/dist/persistence.js
CHANGED
|
@@ -25,6 +25,8 @@ export class Store {
|
|
|
25
25
|
}
|
|
26
26
|
}
|
|
27
27
|
this.db = new Database(p);
|
|
28
|
+
// Enable WAL mode for better concurrent read/write performance
|
|
29
|
+
this.db.pragma("journal_mode = WAL");
|
|
28
30
|
if (p !== ":memory:") {
|
|
29
31
|
try {
|
|
30
32
|
fs.chmodSync(p, 0o600);
|
|
@@ -174,6 +176,13 @@ export class Store {
|
|
|
174
176
|
close() {
|
|
175
177
|
this.db.close();
|
|
176
178
|
}
|
|
179
|
+
/**
|
|
180
|
+
* Explicit resource cleanup via Symbol.dispose.
|
|
181
|
+
* Enables `using store = new Store(...)` with TypeScript 5.2+ explicit resource management.
|
|
182
|
+
*/
|
|
183
|
+
[Symbol.dispose]() {
|
|
184
|
+
this.close();
|
|
185
|
+
}
|
|
177
186
|
}
|
|
178
187
|
/**
|
|
179
188
|
* Stable hash for graph config (for checkpoint).
|
|
@@ -292,12 +301,15 @@ export function getStrategy(store, role) {
|
|
|
292
301
|
*/
|
|
293
302
|
export function setStrategy(store, role, content) {
|
|
294
303
|
const db = store.getDb();
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
304
|
+
const txn = db.transaction(() => {
|
|
305
|
+
const versionStmt = db.prepare("SELECT COALESCE(MAX(version), 0) as maxVersion FROM strategies WHERE entity_id = ? AND role = ?");
|
|
306
|
+
const versionRow = versionStmt.get(store.entityId, role);
|
|
307
|
+
const version = (versionRow?.maxVersion ?? 0) + 1;
|
|
308
|
+
const stmt = db.prepare(`
|
|
309
|
+
INSERT INTO strategies (entity_id, role, version, content, created_at)
|
|
310
|
+
VALUES (?, ?, ?, ?, ?)
|
|
311
|
+
`);
|
|
312
|
+
stmt.run(store.entityId, role, version, content, Date.now() / 1000);
|
|
313
|
+
});
|
|
314
|
+
txn();
|
|
303
315
|
}
|
|
@@ -19,10 +19,12 @@ export interface PolicyRule {
|
|
|
19
19
|
/**
|
|
20
20
|
* Rule-based policy verifier. Checks artifacts and tasks against deny/allow rules.
|
|
21
21
|
* Allow rules in the same category override deny rules.
|
|
22
|
+
* Regexes are pre-compiled in the constructor for performance.
|
|
22
23
|
*/
|
|
23
24
|
export declare class PolicyVerifier implements Verifier {
|
|
24
25
|
readonly name = "PolicyVerifier";
|
|
25
26
|
private rules;
|
|
27
|
+
private compiledRules;
|
|
26
28
|
constructor(customRules?: PolicyRule[]);
|
|
27
29
|
verify(memory: WorkingMemory, context?: VerifierContext): Promise<VerificationResult>;
|
|
28
30
|
private getTargetText;
|
package/dist/policy-verifier.js
CHANGED
|
@@ -30,15 +30,22 @@ const BUILTIN_RULES = [
|
|
|
30
30
|
/**
|
|
31
31
|
* Rule-based policy verifier. Checks artifacts and tasks against deny/allow rules.
|
|
32
32
|
* Allow rules in the same category override deny rules.
|
|
33
|
+
* Regexes are pre-compiled in the constructor for performance.
|
|
33
34
|
*/
|
|
34
35
|
export class PolicyVerifier {
|
|
35
36
|
name = "PolicyVerifier";
|
|
36
37
|
rules;
|
|
38
|
+
compiledRules;
|
|
37
39
|
constructor(customRules) {
|
|
38
40
|
this.rules = [...BUILTIN_RULES];
|
|
39
41
|
if (customRules) {
|
|
40
42
|
this.rules.push(...customRules);
|
|
41
43
|
}
|
|
44
|
+
// Pre-compile all regexes once
|
|
45
|
+
this.compiledRules = this.rules.map((rule) => ({
|
|
46
|
+
rule,
|
|
47
|
+
regex: new RegExp(rule.pattern, "i"),
|
|
48
|
+
}));
|
|
42
49
|
}
|
|
43
50
|
async verify(memory, context) {
|
|
44
51
|
const artifact = memory.finalCandidate || "";
|
|
@@ -46,23 +53,21 @@ export class PolicyVerifier {
|
|
|
46
53
|
const violations = [];
|
|
47
54
|
const allowedCategories = new Set();
|
|
48
55
|
// First pass: collect allowed categories
|
|
49
|
-
for (const rule of this.
|
|
56
|
+
for (const { rule, regex } of this.compiledRules) {
|
|
50
57
|
if (!rule.enabled || rule.action !== "allow")
|
|
51
58
|
continue;
|
|
52
59
|
const text = this.getTargetText(rule.target, artifact, task);
|
|
53
|
-
const regex = new RegExp(rule.pattern, "i");
|
|
54
60
|
if (regex.test(text)) {
|
|
55
61
|
allowedCategories.add(rule.category);
|
|
56
62
|
}
|
|
57
63
|
}
|
|
58
64
|
// Second pass: check deny rules (skip allowed categories)
|
|
59
|
-
for (const rule of this.
|
|
65
|
+
for (const { rule, regex } of this.compiledRules) {
|
|
60
66
|
if (!rule.enabled || rule.action !== "deny")
|
|
61
67
|
continue;
|
|
62
68
|
if (allowedCategories.has(rule.category))
|
|
63
69
|
continue;
|
|
64
70
|
const text = this.getTargetText(rule.target, artifact, task);
|
|
65
|
-
const regex = new RegExp(rule.pattern, "i");
|
|
66
71
|
if (regex.test(text)) {
|
|
67
72
|
violations.push(`[${rule.id}] ${rule.description}`);
|
|
68
73
|
}
|
package/dist/signing.js
CHANGED
|
@@ -42,7 +42,11 @@ function getSecret(entityId) {
|
|
|
42
42
|
return key;
|
|
43
43
|
}
|
|
44
44
|
catch {
|
|
45
|
-
//
|
|
45
|
+
// Weak deterministic fallback requires explicit opt-in
|
|
46
|
+
if (process.env["MOLTBLOCK_INSECURE_DEV_SIGNING"] !== "1") {
|
|
47
|
+
throw new Error(`No MOLTBLOCK_SIGNING_KEY set and filesystem unavailable. ` +
|
|
48
|
+
`Set MOLTBLOCK_SIGNING_KEY for signing, or set MOLTBLOCK_INSECURE_DEV_SIGNING=1 to allow weak dev fallback.`);
|
|
49
|
+
}
|
|
46
50
|
console.warn(`Warning: Using weak default signing key for entity "${entityId}". ` +
|
|
47
51
|
`Set MOLTBLOCK_SIGNING_KEY for secure artifact signing.`);
|
|
48
52
|
return Buffer.from(`dev-only-insecure-key-${entityId}`, "utf-8");
|
package/dist/types.d.ts
CHANGED
|
@@ -7,6 +7,10 @@ export interface ModelBinding {
|
|
|
7
7
|
baseUrl: string;
|
|
8
8
|
apiKey: string | null;
|
|
9
9
|
model: string;
|
|
10
|
+
/** Request timeout in milliseconds (default 60000). */
|
|
11
|
+
timeoutMs?: number;
|
|
12
|
+
/** Max retries on transient errors (default 2). */
|
|
13
|
+
maxRetries?: number;
|
|
10
14
|
}
|
|
11
15
|
/** JSON config binding entry (from moltblock.json) */
|
|
12
16
|
export interface BindingEntry {
|
package/package.json
CHANGED
package/{README.md → readme.md}
RENAMED
|
@@ -297,6 +297,11 @@ const codeEntity = new Entity({
|
|
|
297
297
|
- v0.2 — MVP Entity implementation (spec + Code Entity loop + graph, memory, improvement, governance, handoff)
|
|
298
298
|
- v0.3 — Multi-Entity collaboration (orchestration and tooling)
|
|
299
299
|
- v0.6 — Pluggable verification, policy rules, generic entity, risk classification, OpenClaw skill
|
|
300
|
+
- v0.8 — Quick wins, error handling, test coverage, license fix
|
|
301
|
+
- v0.9 — Core reliability: gateway retry/timeout, graph-runner error handling, entity degraded fallback
|
|
302
|
+
- v0.10 — Test coverage: agents, graph-runner, handoff, integration tests; test helpers
|
|
303
|
+
- v0.10.1 — Persistence hardening: WAL mode, transactions, regex caching, signing key opt-in
|
|
304
|
+
- v0.11 — CI coverage enforcement, stricter tsconfig, documentation fixes
|
|
300
305
|
|
|
301
306
|
---
|
|
302
307
|
|
package/skill/SKILL.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: moltblock - Trust Layer for AI Agents
|
|
3
3
|
description: Verification gating for AI-generated artifacts. Policy checks and code verification to catch dangerous patterns before execution.
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.11.0
|
|
5
5
|
metadata:
|
|
6
6
|
openclaw:
|
|
7
7
|
requires:
|
|
@@ -12,10 +12,14 @@ metadata:
|
|
|
12
12
|
- moltblock.json
|
|
13
13
|
- ~/.moltblock/moltblock.json
|
|
14
14
|
primaryEnv: OPENAI_API_KEY
|
|
15
|
+
optionalEnv:
|
|
16
|
+
- ANTHROPIC_API_KEY
|
|
17
|
+
- GOOGLE_API_KEY
|
|
18
|
+
- ZAI_API_KEY
|
|
15
19
|
homepage: https://github.com/moltblock/moltblock
|
|
16
20
|
install:
|
|
17
21
|
- kind: node
|
|
18
|
-
package: moltblock@0.
|
|
22
|
+
package: moltblock@0.11.0
|
|
19
23
|
bins: [moltblock]
|
|
20
24
|
---
|
|
21
25
|
|
|
@@ -27,9 +31,9 @@ Moltblock provides verification gating for AI-generated artifacts. It runs polic
|
|
|
27
31
|
|
|
28
32
|
**What moltblock does:**
|
|
29
33
|
- Generates code via LLM API calls, then runs policy checks against the output
|
|
30
|
-
- When `--test` is provided, executes vitest to verify generated code against
|
|
34
|
+
- When `--test` is provided, executes vitest to verify generated code against a user-provided test file (see **Security: Test Execution** below)
|
|
31
35
|
- Reads its own config files (`moltblock.json`, `~/.moltblock/moltblock.json`) if present
|
|
32
|
-
- API keys are read from environment variables at runtime
|
|
36
|
+
- API keys are read from environment variables at runtime and sent only to the configured LLM provider endpoint
|
|
33
37
|
|
|
34
38
|
## When to Use
|
|
35
39
|
|
|
@@ -46,7 +50,7 @@ Verify a task before execution.
|
|
|
46
50
|
### Usage
|
|
47
51
|
|
|
48
52
|
```bash
|
|
49
|
-
npx moltblock@0.
|
|
53
|
+
npx moltblock@0.11.0 "<task description>" --provider <provider> --json
|
|
50
54
|
```
|
|
51
55
|
|
|
52
56
|
### Parameters
|
|
@@ -61,20 +65,20 @@ npx moltblock@0.8.0 "<task description>" --provider <provider> --json
|
|
|
61
65
|
|
|
62
66
|
### Environment Variables
|
|
63
67
|
|
|
64
|
-
|
|
65
|
-
- `OPENAI_API_KEY` — OpenAI
|
|
66
|
-
- `ANTHROPIC_API_KEY` — Anthropic/Claude
|
|
67
|
-
- `GOOGLE_API_KEY` — Google/Gemini
|
|
68
|
-
- `ZAI_API_KEY` — ZAI
|
|
68
|
+
Moltblock auto-detects the LLM provider from whichever API key is set. If no key is set, it falls back to a local LLM at `localhost:1234`. Set **one** of these for a cloud provider:
|
|
69
|
+
- `OPENAI_API_KEY` — OpenAI (primary)
|
|
70
|
+
- `ANTHROPIC_API_KEY` — Anthropic/Claude (optional)
|
|
71
|
+
- `GOOGLE_API_KEY` — Google/Gemini (optional)
|
|
72
|
+
- `ZAI_API_KEY` — ZAI (optional)
|
|
69
73
|
|
|
70
74
|
### Example
|
|
71
75
|
|
|
72
76
|
```bash
|
|
73
77
|
# Verify a task
|
|
74
|
-
npx moltblock@0.
|
|
78
|
+
npx moltblock@0.11.0 "implement a function that validates email addresses" --json
|
|
75
79
|
|
|
76
80
|
# Verify code with tests
|
|
77
|
-
npx moltblock@0.
|
|
81
|
+
npx moltblock@0.11.0 "implement a markdown-to-html converter" --test ./tests/markdown.test.ts --json
|
|
78
82
|
```
|
|
79
83
|
|
|
80
84
|
### Output (JSON mode)
|
|
@@ -95,13 +99,13 @@ npx moltblock@0.8.0 "implement a markdown-to-html converter" --test ./tests/mark
|
|
|
95
99
|
Use directly with npx (recommended, no install needed):
|
|
96
100
|
|
|
97
101
|
```bash
|
|
98
|
-
npx moltblock@0.
|
|
102
|
+
npx moltblock@0.11.0 "your task" --json
|
|
99
103
|
```
|
|
100
104
|
|
|
101
105
|
Or install globally:
|
|
102
106
|
|
|
103
107
|
```bash
|
|
104
|
-
npm install -g moltblock@0.
|
|
108
|
+
npm install -g moltblock@0.11.0
|
|
105
109
|
```
|
|
106
110
|
|
|
107
111
|
## Configuration
|
|
@@ -130,6 +134,17 @@ See the [full configuration docs](https://github.com/moltblock/moltblock#configu
|
|
|
130
134
|
- npm: [npmjs.com/package/moltblock](https://www.npmjs.com/package/moltblock)
|
|
131
135
|
- License: MIT
|
|
132
136
|
|
|
137
|
+
## Security: Test Execution
|
|
138
|
+
|
|
139
|
+
When `--test` is used, moltblock writes LLM-generated code to a temporary file and runs vitest against it using the user-provided test file. **This executes LLM-generated code in a Node.js process on the host machine.** Mitigations:
|
|
140
|
+
|
|
141
|
+
- The test file path must be provided explicitly by the user — moltblock does not select or generate test files
|
|
142
|
+
- Generated code is written to `os.tmpdir()` and cleaned up after execution
|
|
143
|
+
- Policy rules run **before** test execution to deny known dangerous patterns (e.g. `rm -rf`, `eval`, `child_process`, filesystem writes)
|
|
144
|
+
- Without `--test`, no code execution occurs — only policy checks run against the generated artifact
|
|
145
|
+
|
|
146
|
+
**Residual risk:** Policy rules are pattern-based and cannot catch all dangerous code. LLM-generated code executed via `--test` may perform arbitrary actions within the permissions of the Node.js process. Users should review generated code or run moltblock in a sandboxed environment when verifying untrusted tasks.
|
|
147
|
+
|
|
133
148
|
## Disclaimer
|
|
134
149
|
|
|
135
150
|
Moltblock reduces risk but does not eliminate it. Verification is best-effort — policy rules and LLM-based checks can miss dangerous patterns. Always review generated artifacts before executing them. The authors and contributors are not responsible for any damage, data loss, or security incidents resulting from the use of this tool. Use at your own risk.
|