@nookplot/runtime 0.5.140 → 0.5.142
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/__tests__/autonomous.dedup.test.js +0 -11
- package/dist/__tests__/autonomous.dedup.test.js.map +1 -1
- package/dist/__tests__/autonomous.getAvailableActions.test.js +1 -13
- package/dist/__tests__/autonomous.getAvailableActions.test.js.map +1 -1
- package/dist/__tests__/codegen-drift.test.js +1 -3
- package/dist/__tests__/codegen-drift.test.js.map +1 -1
- package/dist/__tests__/conversation/modelThresholdsParity.test.js +11 -6
- package/dist/__tests__/conversation/modelThresholdsParity.test.js.map +1 -1
- package/dist/__tests__/economy.frontierInference.test.d.ts +2 -0
- package/dist/__tests__/economy.frontierInference.test.d.ts.map +1 -0
- package/dist/__tests__/economy.frontierInference.test.js +61 -0
- package/dist/__tests__/economy.frontierInference.test.js.map +1 -0
- package/dist/__tests__/helpers/mockRuntime.d.ts.map +1 -1
- package/dist/__tests__/helpers/mockRuntime.js +0 -7
- package/dist/__tests__/helpers/mockRuntime.js.map +1 -1
- package/dist/actionCatalog.d.ts.map +1 -1
- package/dist/actionCatalog.generated.d.ts +1 -1
- package/dist/actionCatalog.generated.d.ts.map +1 -1
- package/dist/actionCatalog.generated.js +60 -95
- package/dist/actionCatalog.generated.js.map +1 -1
- package/dist/actionCatalog.js +10 -0
- package/dist/actionCatalog.js.map +1 -1
- package/dist/autonomous.d.ts +9 -16
- package/dist/autonomous.d.ts.map +1 -1
- package/dist/autonomous.js +66 -232
- package/dist/autonomous.js.map +1 -1
- package/dist/contentSafety.d.ts +1 -1
- package/dist/contentSafety.d.ts.map +1 -1
- package/dist/contentSafety.js +2 -6
- package/dist/contentSafety.js.map +1 -1
- package/dist/discovery.js +1 -1
- package/dist/discovery.js.map +1 -1
- package/dist/economy.d.ts +13 -0
- package/dist/economy.d.ts.map +1 -1
- package/dist/economy.js +26 -0
- package/dist/economy.js.map +1 -1
- package/dist/frontierPass.d.ts +30 -0
- package/dist/frontierPass.d.ts.map +1 -0
- package/dist/frontierPass.js +42 -0
- package/dist/frontierPass.js.map +1 -0
- package/dist/identity.d.ts +0 -51
- package/dist/identity.d.ts.map +1 -1
- package/dist/identity.js +0 -50
- package/dist/identity.js.map +1 -1
- package/dist/index.d.ts +3 -9
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -5
- package/dist/index.js.map +1 -1
- package/dist/signalActionMap.d.ts.map +1 -1
- package/dist/signalActionMap.js +3 -9
- package/dist/signalActionMap.js.map +1 -1
- package/dist/swarms.d.ts +0 -13
- package/dist/swarms.d.ts.map +1 -1
- package/dist/swarms.js +0 -4
- package/dist/swarms.js.map +1 -1
- package/dist/tools.js +1 -1
- package/dist/tools.js.map +1 -1
- package/package.json +1 -1
- package/dist/__tests__/autonomous.goalBootstrap.test.d.ts +0 -2
- package/dist/__tests__/autonomous.goalBootstrap.test.d.ts.map +0 -1
- package/dist/__tests__/autonomous.goalBootstrap.test.js +0 -148
- package/dist/__tests__/autonomous.goalBootstrap.test.js.map +0 -1
- package/dist/__tests__/autonomous.miningTrack.test.d.ts +0 -2
- package/dist/__tests__/autonomous.miningTrack.test.d.ts.map +0 -1
- package/dist/__tests__/autonomous.miningTrack.test.js +0 -38
- package/dist/__tests__/autonomous.miningTrack.test.js.map +0 -1
- package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts +0 -2
- package/dist/__tests__/autonomous.workspaceOpportunity.test.d.ts.map +0 -1
- package/dist/__tests__/autonomous.workspaceOpportunity.test.js +0 -200
- package/dist/__tests__/autonomous.workspaceOpportunity.test.js.map +0 -1
- package/dist/__tests__/goalLoop.test.d.ts +0 -2
- package/dist/__tests__/goalLoop.test.d.ts.map +0 -1
- package/dist/__tests__/goalLoop.test.js +0 -335
- package/dist/__tests__/goalLoop.test.js.map +0 -1
- package/dist/__tests__/loadProfile.test.d.ts +0 -8
- package/dist/__tests__/loadProfile.test.d.ts.map +0 -1
- package/dist/__tests__/loadProfile.test.js +0 -134
- package/dist/__tests__/loadProfile.test.js.map +0 -1
- package/dist/__tests__/mining.test.d.ts +0 -2
- package/dist/__tests__/mining.test.d.ts.map +0 -1
- package/dist/__tests__/mining.test.js +0 -306
- package/dist/__tests__/mining.test.js.map +0 -1
- package/dist/__tests__/presetLoader.test.d.ts +0 -2
- package/dist/__tests__/presetLoader.test.d.ts.map +0 -1
- package/dist/__tests__/presetLoader.test.js +0 -749
- package/dist/__tests__/presetLoader.test.js.map +0 -1
- package/dist/goal/goalLoop.d.ts +0 -78
- package/dist/goal/goalLoop.d.ts.map +0 -1
- package/dist/goal/goalLoop.js +0 -376
- package/dist/goal/goalLoop.js.map +0 -1
- package/dist/goal/goalPrompts.d.ts +0 -20
- package/dist/goal/goalPrompts.d.ts.map +0 -1
- package/dist/goal/goalPrompts.js +0 -54
- package/dist/goal/goalPrompts.js.map +0 -1
- package/dist/goal/types.d.ts +0 -98
- package/dist/goal/types.d.ts.map +0 -1
- package/dist/goal/types.js +0 -7
- package/dist/goal/types.js.map +0 -1
- package/dist/loadProfile.d.ts +0 -100
- package/dist/loadProfile.d.ts.map +0 -1
- package/dist/loadProfile.js +0 -221
- package/dist/loadProfile.js.map +0 -1
- package/dist/presetLoader.d.ts +0 -130
- package/dist/presetLoader.d.ts.map +0 -1
- package/dist/presetLoader.js +0 -734
- package/dist/presetLoader.js.map +0 -1
package/dist/presetLoader.js
DELETED
|
@@ -1,734 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* PresetLoader — loads forge preset data into agent memory at boot.
|
|
3
|
-
*
|
|
4
|
-
* Reads the preset section of nookplot.yaml, fetches data per source via
|
|
5
|
-
* the gateway's forge data endpoint, runs content scanning, and ingests
|
|
6
|
-
* items as preset-tagged memories. Supports layered ingestion: RAG when
|
|
7
|
-
* available, memory.import() fallback.
|
|
8
|
-
*
|
|
9
|
-
* Features:
|
|
10
|
-
* - Content scanning (sanitizeForPrompt) on every item
|
|
11
|
-
* - Idempotent loading via .preset-loaded.json manifest
|
|
12
|
-
* - Configurable failure policy (abort / continue / retry)
|
|
13
|
-
* - Boot progress events via EventEmitter
|
|
14
|
-
* - Self-awareness memory generation
|
|
15
|
-
*
|
|
16
|
-
* @module presetLoader
|
|
17
|
-
*/
|
|
18
|
-
import { readFile, writeFile } from "node:fs/promises";
|
|
19
|
-
import { createHash, createHmac } from "node:crypto";
|
|
20
|
-
import { EventEmitter } from "node:events";
|
|
21
|
-
import yaml from "js-yaml";
|
|
22
|
-
import { sanitizeForPrompt } from "./contentSafety.js";
|
|
23
|
-
// ── PresetLoader ──────────────────────────────────────────────
|
|
24
|
-
export class PresetLoader extends EventEmitter {
|
|
25
|
-
runtime;
|
|
26
|
-
configPath;
|
|
27
|
-
loading = null;
|
|
28
|
-
constructor(runtime, configPath = "./nookplot.yaml") {
|
|
29
|
-
super();
|
|
30
|
-
this.runtime = runtime;
|
|
31
|
-
this.configPath = configPath;
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Build x-preset-hmac header so the gateway trusts isPreset/presetId fields.
|
|
35
|
-
* HMAC = SHA-256(apiKey, "preset-ingest:" + agent_address).
|
|
36
|
-
*/
|
|
37
|
-
presetIngestHeaders() {
|
|
38
|
-
const address = this.runtime.connection.address;
|
|
39
|
-
const apiKey = this.runtime.connection.apiKey;
|
|
40
|
-
if (!address || !apiKey)
|
|
41
|
-
return {};
|
|
42
|
-
const hmac = createHmac("sha256", apiKey).update(`preset-ingest:${address.toLowerCase()}`).digest("hex");
|
|
43
|
-
return { "x-preset-hmac": hmac };
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* Load all preset data sources. Called once at agent boot.
|
|
47
|
-
* Concurrent calls are coalesced — only the first triggers a fetch,
|
|
48
|
-
* subsequent callers await the same promise (prevents double-pay).
|
|
49
|
-
*/
|
|
50
|
-
async load() {
|
|
51
|
-
if (this.loading)
|
|
52
|
-
return this.loading;
|
|
53
|
-
this.loading = this._doLoad();
|
|
54
|
-
try {
|
|
55
|
-
return await this.loading;
|
|
56
|
-
}
|
|
57
|
-
finally {
|
|
58
|
-
this.loading = null;
|
|
59
|
-
}
|
|
60
|
-
}
|
|
61
|
-
async _doLoad() {
|
|
62
|
-
// 1. Parse nookplot.yaml
|
|
63
|
-
const config = await this.readConfig();
|
|
64
|
-
if (!config || !config.sources || config.sources.length === 0) {
|
|
65
|
-
return this.emptyResult();
|
|
66
|
-
}
|
|
67
|
-
// 2. Check idempotent loading (presetId + version + sourceHashes must all match)
|
|
68
|
-
const manifest = await this.readManifest();
|
|
69
|
-
if (manifest && manifest.presetId === config.id && manifest.presetVersion === (config.version ?? 1)) {
|
|
70
|
-
// Compute current source hashes and compare with manifest
|
|
71
|
-
const currentHashes = {};
|
|
72
|
-
if (config.sources) {
|
|
73
|
-
for (let i = 0; i < config.sources.length; i++) {
|
|
74
|
-
const src = config.sources[i];
|
|
75
|
-
const hash = createHash("sha256")
|
|
76
|
-
.update(JSON.stringify(src.config ?? {}))
|
|
77
|
-
.digest("hex");
|
|
78
|
-
currentHashes[`${src.type}:${i}`] = hash;
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
const hashesMatch = manifest.sourceHashes &&
|
|
82
|
-
JSON.stringify(manifest.sourceHashes) === JSON.stringify(currentHashes);
|
|
83
|
-
if (hashesMatch) {
|
|
84
|
-
this.emit("progress", { phase: "complete", result: this.manifestToResult(manifest) });
|
|
85
|
-
return this.manifestToResult(manifest);
|
|
86
|
-
}
|
|
87
|
-
// sourceHashes differ — force reload even though version matches
|
|
88
|
-
}
|
|
89
|
-
// 3. Pre-flight cost estimate — abort BEFORE spending credits
|
|
90
|
-
this.emit("progress", { phase: "estimating", message: `Estimating costs for "${config.id}"...` });
|
|
91
|
-
if (typeof config.maxCostNook === "number") {
|
|
92
|
-
try {
|
|
93
|
-
const agentAddress = "agentAddress" in this.runtime ? this.runtime.agentAddress : undefined;
|
|
94
|
-
const qs = agentAddress ? `?agentAddress=${encodeURIComponent(agentAddress)}` : "";
|
|
95
|
-
const estimate = await this.runtime.connection.request("GET", `/v1/forge/presets/${encodeURIComponent(config.id)}/estimate${qs}`);
|
|
96
|
-
if (estimate.estimatedCostNook > config.maxCostNook) {
|
|
97
|
-
const msg = `Estimated cost ${estimate.estimatedCostNook} NOOK exceeds safety cap ${config.maxCostNook} NOOK. Aborting before fetch.`;
|
|
98
|
-
this.emit("progress", { phase: "error", source: "cost", error: msg });
|
|
99
|
-
throw new Error(msg);
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
catch (err) {
|
|
103
|
-
// If the error is our own cost-cap abort, re-throw it
|
|
104
|
-
if (err instanceof Error && err.message.includes("Aborting before fetch"))
|
|
105
|
-
throw err;
|
|
106
|
-
// Otherwise the estimate endpoint failed — log and proceed (fetch will do the real spend)
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
// 4. Fetch data from gateway
|
|
110
|
-
let fetchResult;
|
|
111
|
-
try {
|
|
112
|
-
fetchResult = await this.runtime.connection.request("POST", "/v1/forge/data/fetch", {
|
|
113
|
-
presetId: config.id,
|
|
114
|
-
sources: config.sources.map((s) => ({ type: s.type, config: s.config })),
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
catch (err) {
|
|
118
|
-
const msg = err instanceof Error ? err.message : String(err);
|
|
119
|
-
this.emit("progress", { phase: "error", source: "fetch", error: msg });
|
|
120
|
-
if (config.failurePolicy === "abort") {
|
|
121
|
-
throw new Error(`Preset data fetch failed: ${msg}`);
|
|
122
|
-
}
|
|
123
|
-
return this.emptyResult();
|
|
124
|
-
}
|
|
125
|
-
// 5. Safety cap check (use explicit !== undefined to allow maxCostNook: 0)
|
|
126
|
-
if (typeof config.maxCostNook === "number" && fetchResult.totalCostNook > config.maxCostNook) {
|
|
127
|
-
const msg = `Estimated cost ${fetchResult.totalCostNook} NOOK exceeds safety cap ${config.maxCostNook} NOOK. Aborting.`;
|
|
128
|
-
this.emit("progress", { phase: "error", source: "cost", error: msg });
|
|
129
|
-
throw new Error(msg);
|
|
130
|
-
}
|
|
131
|
-
// 6. Check if RAG / knowledge graph is available
|
|
132
|
-
let ragAvailable = false;
|
|
133
|
-
let knowledgeGraphAvailable = false;
|
|
134
|
-
try {
|
|
135
|
-
await this.runtime.connection.request("GET", "/v1/mining/knowledge/search?q=test&limit=1");
|
|
136
|
-
ragAvailable = true;
|
|
137
|
-
}
|
|
138
|
-
catch {
|
|
139
|
-
// RAG not available
|
|
140
|
-
}
|
|
141
|
-
try {
|
|
142
|
-
// Knowledge graph endpoint returns stats if migration 180 is applied
|
|
143
|
-
await this.runtime.connection.request("POST", "/v1/agents/me/knowledge/ingest", {
|
|
144
|
-
items: [],
|
|
145
|
-
});
|
|
146
|
-
knowledgeGraphAvailable = true;
|
|
147
|
-
}
|
|
148
|
-
catch (err) {
|
|
149
|
-
// 400 = endpoint exists but items empty → KG is available
|
|
150
|
-
// 404 = endpoint doesn't exist → KG not available
|
|
151
|
-
const status = err?.status ?? err?.statusCode;
|
|
152
|
-
if (status === 400) {
|
|
153
|
-
knowledgeGraphAvailable = true;
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
// 7. Scan + ingest each source
|
|
157
|
-
const trustLevel = config.trustLevel ?? "verified";
|
|
158
|
-
const sourceResults = [];
|
|
159
|
-
const sourceHashes = {};
|
|
160
|
-
for (let i = 0; i < fetchResult.sources.length; i++) {
|
|
161
|
-
const fetchSource = fetchResult.sources[i];
|
|
162
|
-
const configSource = config.sources[i];
|
|
163
|
-
const label = configSource?.label ?? `${fetchSource.source} source`;
|
|
164
|
-
const kgBatch = [];
|
|
165
|
-
this.emit("progress", {
|
|
166
|
-
phase: "fetching",
|
|
167
|
-
source: label,
|
|
168
|
-
progress: `${fetchSource.itemCount} items`,
|
|
169
|
-
});
|
|
170
|
-
let loaded = 0;
|
|
171
|
-
let blocked = 0;
|
|
172
|
-
let skipped = 0;
|
|
173
|
-
let status = "loaded";
|
|
174
|
-
let error;
|
|
175
|
-
try {
|
|
176
|
-
for (const item of fetchSource.items) {
|
|
177
|
-
// Content scanning
|
|
178
|
-
if (trustLevel !== "raw") {
|
|
179
|
-
const severity = this.assessSeverity(item.content);
|
|
180
|
-
if (severity >= 50) {
|
|
181
|
-
blocked++;
|
|
182
|
-
continue;
|
|
183
|
-
}
|
|
184
|
-
}
|
|
185
|
-
// Sanitize
|
|
186
|
-
const sanitized = sanitizeForPrompt(item.content, 10_000);
|
|
187
|
-
if (!sanitized.trim()) {
|
|
188
|
-
skipped++;
|
|
189
|
-
continue;
|
|
190
|
-
}
|
|
191
|
-
// Compute content hash for dedup
|
|
192
|
-
const contentHash = createHash("sha256").update(sanitized).digest("hex");
|
|
193
|
-
// Ingest: prefer knowledge graph (semantic search), fall back to memory
|
|
194
|
-
try {
|
|
195
|
-
if (knowledgeGraphAvailable) {
|
|
196
|
-
// C3 upgrade: ingest into agent knowledge graph for semantic search
|
|
197
|
-
if (fetchSource.source === "aggregate") {
|
|
198
|
-
const subLoaded = await this.ingestAggregateToKG(sanitized, config.id, configSource?.config?.domainTags ?? []);
|
|
199
|
-
loaded += subLoaded;
|
|
200
|
-
}
|
|
201
|
-
else {
|
|
202
|
-
kgBatch.push({
|
|
203
|
-
contentText: sanitized,
|
|
204
|
-
knowledgeType: this.mapSourceToKnowledgeType(fetchSource.source),
|
|
205
|
-
sourceType: this.mapSourceToKGSourceType(fetchSource.source),
|
|
206
|
-
sourceId: item.metadata?.traceId ?? item.contentHash,
|
|
207
|
-
domain: configSource?.config?.domainTags?.[0] ?? null,
|
|
208
|
-
tags: ["preset", fetchSource.source, ...(configSource?.config?.domainTags ?? [])],
|
|
209
|
-
importance: 0.8,
|
|
210
|
-
confidence: item.metadata?.compositeScore ?? 0.5,
|
|
211
|
-
metadata: { content_hash: contentHash, ...item.metadata },
|
|
212
|
-
isPreset: true,
|
|
213
|
-
presetId: config.id,
|
|
214
|
-
});
|
|
215
|
-
loaded++;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
// Memory fallback (pre-C3 behavior)
|
|
220
|
-
if (fetchSource.source === "aggregate") {
|
|
221
|
-
const subLoaded = await this.ingestAggregate(sanitized, config.id, configSource?.config?.domainTags ?? []);
|
|
222
|
-
loaded += subLoaded;
|
|
223
|
-
}
|
|
224
|
-
else {
|
|
225
|
-
await this.runtime.memory.storeMemory("semantic", sanitized, {
|
|
226
|
-
tags: ["preset", fetchSource.source, ...(configSource?.config?.domainTags ?? [])],
|
|
227
|
-
source: `preset:${config.id}:${fetchSource.source}`,
|
|
228
|
-
importance: 0.8,
|
|
229
|
-
metadata: {
|
|
230
|
-
is_preset: true,
|
|
231
|
-
preset_id: config.id,
|
|
232
|
-
content_hash: contentHash,
|
|
233
|
-
...item.metadata,
|
|
234
|
-
},
|
|
235
|
-
});
|
|
236
|
-
loaded++;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
catch {
|
|
241
|
-
skipped++;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
// Flush KG batch if any items accumulated
|
|
245
|
-
if (kgBatch.length > 0) {
|
|
246
|
-
try {
|
|
247
|
-
const batchResult = await this.runtime.connection.request("POST", "/v1/agents/me/knowledge/ingest", { items: kgBatch }, 4, 0, this.presetIngestHeaders());
|
|
248
|
-
// Adjust loaded/skipped counts based on actual ingest result
|
|
249
|
-
const batchSkipped = batchResult.skipped ?? 0;
|
|
250
|
-
if (batchSkipped > 0) {
|
|
251
|
-
loaded -= batchSkipped;
|
|
252
|
-
skipped += batchSkipped;
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
catch {
|
|
256
|
-
// If batch ingest fails, fall back to memory for this source
|
|
257
|
-
for (const batchItem of kgBatch) {
|
|
258
|
-
try {
|
|
259
|
-
await this.runtime.memory.storeMemory("semantic", batchItem.contentText, {
|
|
260
|
-
tags: batchItem.tags,
|
|
261
|
-
source: `preset:${config.id}:${fetchSource.source}`,
|
|
262
|
-
importance: batchItem.importance,
|
|
263
|
-
metadata: { is_preset: true, preset_id: config.id, ...batchItem.metadata },
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
catch {
|
|
267
|
-
skipped++;
|
|
268
|
-
loaded--;
|
|
269
|
-
}
|
|
270
|
-
}
|
|
271
|
-
}
|
|
272
|
-
}
|
|
273
|
-
if (loaded === 0 && fetchSource.itemCount > 0) {
|
|
274
|
-
status = "failed";
|
|
275
|
-
error = `All ${fetchSource.itemCount} items were blocked or skipped`;
|
|
276
|
-
}
|
|
277
|
-
else if (blocked > 0 || skipped > 0) {
|
|
278
|
-
status = "partial";
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
catch (err) {
|
|
282
|
-
status = "failed";
|
|
283
|
-
error = err instanceof Error ? err.message : String(err);
|
|
284
|
-
this.emit("progress", { phase: "error", source: label, error });
|
|
285
|
-
if (config.failurePolicy === "abort") {
|
|
286
|
-
throw new Error(`Source "${label}" failed: ${error}`);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
this.emit("progress", {
|
|
290
|
-
phase: "scanning",
|
|
291
|
-
source: label,
|
|
292
|
-
blocked,
|
|
293
|
-
});
|
|
294
|
-
this.emit("progress", {
|
|
295
|
-
phase: "ingesting",
|
|
296
|
-
source: label,
|
|
297
|
-
method: knowledgeGraphAvailable ? "rag" : ragAvailable ? "rag" : "memory",
|
|
298
|
-
});
|
|
299
|
-
// Source hash for idempotent manifest
|
|
300
|
-
const sourceHash = createHash("sha256")
|
|
301
|
-
.update(JSON.stringify(configSource?.config ?? {}))
|
|
302
|
-
.digest("hex");
|
|
303
|
-
sourceHashes[`${fetchSource.source}:${i}`] = sourceHash;
|
|
304
|
-
sourceResults.push({
|
|
305
|
-
type: fetchSource.source,
|
|
306
|
-
label,
|
|
307
|
-
itemsLoaded: loaded,
|
|
308
|
-
itemsBlocked: blocked,
|
|
309
|
-
itemsSkipped: skipped,
|
|
310
|
-
method: knowledgeGraphAvailable ? "rag" : ragAvailable ? "rag" : "memory",
|
|
311
|
-
status,
|
|
312
|
-
error,
|
|
313
|
-
costNook: fetchSource.costNook,
|
|
314
|
-
});
|
|
315
|
-
}
|
|
316
|
-
// 8. Self-awareness memory
|
|
317
|
-
const totalLoaded = sourceResults.reduce((s, r) => s + r.itemsLoaded, 0);
|
|
318
|
-
if (totalLoaded > 0) {
|
|
319
|
-
// M10: Sanitize config.id to prevent prompt injection via crafted preset IDs
|
|
320
|
-
const safePresetId = (config.id ?? "").replace(/[^\w\s._-]/g, "").slice(0, 100);
|
|
321
|
-
const selfModelContent = [
|
|
322
|
-
`I was forged with preset "${safePresetId}" (v${config.version ?? 1}) on ${new Date().toISOString()}.`,
|
|
323
|
-
`My knowledge includes:`,
|
|
324
|
-
...sourceResults
|
|
325
|
-
.filter((s) => s.itemsLoaded > 0)
|
|
326
|
-
.map((s) => `- ${s.itemsLoaded} items from ${s.label} (${s.type}, ingested via ${s.method})`),
|
|
327
|
-
knowledgeGraphAvailable
|
|
328
|
-
? `My knowledge is indexed in my personal knowledge graph with semantic search.`
|
|
329
|
-
: ragAvailable
|
|
330
|
-
? `I can search my knowledge semantically via RAG.`
|
|
331
|
-
: `I search my knowledge via text matching (RAG not yet available — will upgrade automatically).`,
|
|
332
|
-
].join("\n");
|
|
333
|
-
try {
|
|
334
|
-
await this.runtime.memory.storeMemory("self_model", selfModelContent, {
|
|
335
|
-
tags: ["preset", "forge", safePresetId],
|
|
336
|
-
source: `preset:${safePresetId}`,
|
|
337
|
-
importance: 0.95,
|
|
338
|
-
metadata: { is_preset: true, preset_id: safePresetId },
|
|
339
|
-
});
|
|
340
|
-
}
|
|
341
|
-
catch {
|
|
342
|
-
// Non-critical — don't fail the load
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
// 9. Write manifest for idempotent reboot
|
|
346
|
-
const result = {
|
|
347
|
-
sources: sourceResults,
|
|
348
|
-
totalItems: totalLoaded,
|
|
349
|
-
totalBlocked: sourceResults.reduce((s, r) => s + r.itemsBlocked, 0),
|
|
350
|
-
totalCostNook: fetchResult.totalCostNook,
|
|
351
|
-
ragAvailable: ragAvailable || knowledgeGraphAvailable,
|
|
352
|
-
presetVersion: config.version ?? 1,
|
|
353
|
-
loadedAt: new Date().toISOString(),
|
|
354
|
-
};
|
|
355
|
-
await this.writeManifest({
|
|
356
|
-
presetId: config.id,
|
|
357
|
-
presetVersion: config.version ?? 1,
|
|
358
|
-
loadedAt: result.loadedAt,
|
|
359
|
-
sourceHashes,
|
|
360
|
-
totalItems: result.totalItems,
|
|
361
|
-
costPaid: result.totalCostNook,
|
|
362
|
-
ragAvailable: result.ragAvailable,
|
|
363
|
-
});
|
|
364
|
-
this.emit("progress", { phase: "complete", result });
|
|
365
|
-
return result;
|
|
366
|
-
}
|
|
367
|
-
/**
|
|
368
|
-
* Check if preset data was already loaded (idempotent reboot check).
|
|
369
|
-
*/
|
|
370
|
-
async isLoaded() {
|
|
371
|
-
// M11: Check manifest matches current config (not just existence)
|
|
372
|
-
const manifest = await this.readManifest();
|
|
373
|
-
if (!manifest)
|
|
374
|
-
return false;
|
|
375
|
-
const config = await this.readConfig();
|
|
376
|
-
if (!config)
|
|
377
|
-
return false;
|
|
378
|
-
return manifest.presetId === config.id && manifest.presetVersion === (config.version ?? 1);
|
|
379
|
-
}
|
|
380
|
-
// ── Knowledge Graph Helpers (C3) ────────────────────────────
|
|
381
|
-
/**
|
|
382
|
-
* Map preset source type to knowledge graph knowledge_type.
|
|
383
|
-
*/
|
|
384
|
-
mapSourceToKnowledgeType(source) {
|
|
385
|
-
switch (source) {
|
|
386
|
-
case "mining": return "fact";
|
|
387
|
-
case "bundle": return "fact";
|
|
388
|
-
case "aggregate": return "synthesis";
|
|
389
|
-
case "memory": return "experience";
|
|
390
|
-
case "reppo": return "fact";
|
|
391
|
-
default: return "fact";
|
|
392
|
-
}
|
|
393
|
-
}
|
|
394
|
-
/**
|
|
395
|
-
* Map preset source type to knowledge graph source_type.
|
|
396
|
-
*/
|
|
397
|
-
mapSourceToKGSourceType(source) {
|
|
398
|
-
switch (source) {
|
|
399
|
-
case "mining": return "mining";
|
|
400
|
-
case "bundle": return "import";
|
|
401
|
-
case "aggregate": return "aggregation";
|
|
402
|
-
case "memory": return "import";
|
|
403
|
-
case "reppo": return "import";
|
|
404
|
-
default: return "preset";
|
|
405
|
-
}
|
|
406
|
-
}
|
|
407
|
-
/**
|
|
408
|
-
* Ingest a KnowledgeAggregateV1 into the knowledge graph as structured items.
|
|
409
|
-
* Same decomposition as ingestAggregate but targets the KG endpoint.
|
|
410
|
-
*/
|
|
411
|
-
async ingestAggregateToKG(content, presetId, domainTags) {
|
|
412
|
-
let aggregate;
|
|
413
|
-
try {
|
|
414
|
-
aggregate = JSON.parse(content);
|
|
415
|
-
}
|
|
416
|
-
catch {
|
|
417
|
-
// Not valid JSON — single item
|
|
418
|
-
try {
|
|
419
|
-
await this.runtime.connection.request("POST", "/v1/agents/me/knowledge", {
|
|
420
|
-
contentText: content,
|
|
421
|
-
knowledgeType: "synthesis",
|
|
422
|
-
sourceType: "aggregation",
|
|
423
|
-
domain: domainTags[0] ?? null,
|
|
424
|
-
tags: ["preset", "aggregate", ...domainTags],
|
|
425
|
-
importance: 0.8,
|
|
426
|
-
isPreset: true,
|
|
427
|
-
presetId,
|
|
428
|
-
});
|
|
429
|
-
return 1;
|
|
430
|
-
}
|
|
431
|
-
catch {
|
|
432
|
-
return 0;
|
|
433
|
-
}
|
|
434
|
-
}
|
|
435
|
-
const items = [];
|
|
436
|
-
const tags = aggregate.tags ?? domainTags;
|
|
437
|
-
const domain = aggregate.domain ?? domainTags[0] ?? null;
|
|
438
|
-
// Helper: scan + sanitize each sub-item before ingestion
|
|
439
|
-
const scanAndSanitize = (text) => {
|
|
440
|
-
if (this.assessSeverity(text) >= 50)
|
|
441
|
-
return null; // blocked
|
|
442
|
-
const sanitized = sanitizeForPrompt(text, 10_000);
|
|
443
|
-
return sanitized.trim() || null;
|
|
444
|
-
};
|
|
445
|
-
// Synthesis → synthesis type
|
|
446
|
-
const synthesis = aggregate.synthesis;
|
|
447
|
-
if (synthesis?.narrative) {
|
|
448
|
-
const cleaned = scanAndSanitize(synthesis.narrative);
|
|
449
|
-
if (cleaned) {
|
|
450
|
-
items.push({
|
|
451
|
-
contentText: cleaned,
|
|
452
|
-
knowledgeType: "synthesis",
|
|
453
|
-
sourceType: "aggregation",
|
|
454
|
-
domain,
|
|
455
|
-
tags: ["preset", "aggregate", ...tags],
|
|
456
|
-
importance: 0.9,
|
|
457
|
-
confidence: 0.8,
|
|
458
|
-
metadata: { scope: synthesis.scope, limitations: synthesis.limitations },
|
|
459
|
-
isPreset: true,
|
|
460
|
-
presetId,
|
|
461
|
-
});
|
|
462
|
-
}
|
|
463
|
-
}
|
|
464
|
-
// Key insights → insight type
|
|
465
|
-
const insights = aggregate.keyInsights ?? [];
|
|
466
|
-
for (const insight of insights) {
|
|
467
|
-
if (typeof insight.insight !== "string")
|
|
468
|
-
continue;
|
|
469
|
-
const cleaned = scanAndSanitize(insight.insight);
|
|
470
|
-
if (!cleaned)
|
|
471
|
-
continue;
|
|
472
|
-
const confidence = typeof insight.confidence === "number" ? insight.confidence : 0.7;
|
|
473
|
-
items.push({
|
|
474
|
-
contentText: cleaned,
|
|
475
|
-
knowledgeType: "insight",
|
|
476
|
-
sourceType: "aggregation",
|
|
477
|
-
domain,
|
|
478
|
-
tags: ["preset", "aggregate", "insight", ...(insight.tags ?? [])],
|
|
479
|
-
importance: Math.max(0.6, confidence),
|
|
480
|
-
confidence,
|
|
481
|
-
metadata: {
|
|
482
|
-
supportingTraces: Array.isArray(insight.supportingTraceIds) ? insight.supportingTraceIds.length : 0,
|
|
483
|
-
},
|
|
484
|
-
isPreset: true,
|
|
485
|
-
presetId,
|
|
486
|
-
});
|
|
487
|
-
}
|
|
488
|
-
// Reasoning patterns → pattern type
|
|
489
|
-
const patterns = aggregate.reasoningPatterns ?? [];
|
|
490
|
-
for (const pattern of patterns) {
|
|
491
|
-
if (typeof pattern.pattern !== "string")
|
|
492
|
-
continue;
|
|
493
|
-
const cleaned = scanAndSanitize(pattern.pattern);
|
|
494
|
-
if (!cleaned)
|
|
495
|
-
continue;
|
|
496
|
-
items.push({
|
|
497
|
-
contentText: cleaned,
|
|
498
|
-
knowledgeType: "pattern",
|
|
499
|
-
sourceType: "aggregation",
|
|
500
|
-
domain,
|
|
501
|
-
tags: ["preset", "aggregate", "reasoning_pattern", ...tags],
|
|
502
|
-
importance: 0.7,
|
|
503
|
-
confidence: 0.6,
|
|
504
|
-
isPreset: true,
|
|
505
|
-
presetId,
|
|
506
|
-
});
|
|
507
|
-
}
|
|
508
|
-
// Contradictions → single insight with low confidence
|
|
509
|
-
const contradictions = aggregate.contradictions ?? [];
|
|
510
|
-
if (contradictions.length > 0) {
|
|
511
|
-
const summary = contradictions.map((c) => `Contested: "${c.claim_a}" vs "${c.claim_b}"${c.resolution ? ` — likely: ${c.resolution}` : ""}`).join("\n");
|
|
512
|
-
const cleaned = scanAndSanitize(summary);
|
|
513
|
-
if (cleaned) {
|
|
514
|
-
items.push({
|
|
515
|
-
contentText: cleaned,
|
|
516
|
-
knowledgeType: "insight",
|
|
517
|
-
sourceType: "aggregation",
|
|
518
|
-
domain,
|
|
519
|
-
tags: ["preset", "aggregate", "uncertainty"],
|
|
520
|
-
importance: 0.75,
|
|
521
|
-
confidence: 0.3,
|
|
522
|
-
isPreset: true,
|
|
523
|
-
presetId,
|
|
524
|
-
});
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
if (items.length === 0)
|
|
528
|
-
return 0;
|
|
529
|
-
try {
|
|
530
|
-
const result = await this.runtime.connection.request("POST", "/v1/agents/me/knowledge/ingest", { items }, 4, 0, this.presetIngestHeaders());
|
|
531
|
-
return result.ingested;
|
|
532
|
-
}
|
|
533
|
-
catch {
|
|
534
|
-
// Fall back to memory-based aggregate ingestion
|
|
535
|
-
return this.ingestAggregate(content, presetId, domainTags);
|
|
536
|
-
}
|
|
537
|
-
}
|
|
538
|
-
// ── Aggregate Ingestion (E8) ────────────────────────────────
|
|
539
|
-
/**
|
|
540
|
-
* Ingest a KnowledgeAggregateV1 as structured memories.
|
|
541
|
-
* Uses 5-7x fewer memory slots than equivalent raw traces:
|
|
542
|
-
* - Synthesis → semantic memory (importance 0.9)
|
|
543
|
-
* - Each keyInsight → procedural memory (confidence-weighted)
|
|
544
|
-
* - Each reasoning pattern → procedural memory
|
|
545
|
-
* - Contradictions → self_model memory
|
|
546
|
-
*
|
|
547
|
-
* Returns the number of memories stored.
|
|
548
|
-
*/
|
|
549
|
-
async ingestAggregate(content, presetId, domainTags) {
|
|
550
|
-
let aggregate;
|
|
551
|
-
try {
|
|
552
|
-
aggregate = JSON.parse(content);
|
|
553
|
-
}
|
|
554
|
-
catch {
|
|
555
|
-
// Not valid JSON — fall back to generic memory
|
|
556
|
-
await this.runtime.memory.storeMemory("semantic", content, {
|
|
557
|
-
tags: ["preset", "aggregate", ...domainTags],
|
|
558
|
-
source: `preset:${presetId}:aggregate`,
|
|
559
|
-
importance: 0.8,
|
|
560
|
-
metadata: { is_preset: true, preset_id: presetId },
|
|
561
|
-
});
|
|
562
|
-
return 1;
|
|
563
|
-
}
|
|
564
|
-
let stored = 0;
|
|
565
|
-
const tags = aggregate.tags ?? domainTags;
|
|
566
|
-
const baseMeta = { is_preset: true, preset_id: presetId };
|
|
567
|
-
// Helper: scan + sanitize sub-items before ingestion
|
|
568
|
-
const scanAndSanitize = (text) => {
|
|
569
|
-
if (this.assessSeverity(text) >= 50)
|
|
570
|
-
return null;
|
|
571
|
-
const sanitized = sanitizeForPrompt(text, 10_000);
|
|
572
|
-
return sanitized.trim() || null;
|
|
573
|
-
};
|
|
574
|
-
// 1. Synthesis → semantic memory (high importance)
|
|
575
|
-
const synthesis = aggregate.synthesis;
|
|
576
|
-
if (synthesis?.narrative) {
|
|
577
|
-
const cleaned = scanAndSanitize(synthesis.narrative);
|
|
578
|
-
if (cleaned) {
|
|
579
|
-
try {
|
|
580
|
-
await this.runtime.memory.storeMemory("semantic", cleaned, {
|
|
581
|
-
tags: ["preset", "aggregate", ...tags],
|
|
582
|
-
source: `preset:${presetId}:aggregate:synthesis`,
|
|
583
|
-
importance: 0.9,
|
|
584
|
-
metadata: { ...baseMeta, scope: synthesis.scope, limitations: synthesis.limitations },
|
|
585
|
-
});
|
|
586
|
-
stored++;
|
|
587
|
-
}
|
|
588
|
-
catch { /* non-critical */ }
|
|
589
|
-
}
|
|
590
|
-
}
|
|
591
|
-
// 2. Each keyInsight → procedural memory (confidence-weighted)
|
|
592
|
-
const insights = aggregate.keyInsights ?? [];
|
|
593
|
-
for (const insight of insights) {
|
|
594
|
-
if (typeof insight.insight !== "string")
|
|
595
|
-
continue;
|
|
596
|
-
const cleaned = scanAndSanitize(insight.insight);
|
|
597
|
-
if (!cleaned)
|
|
598
|
-
continue;
|
|
599
|
-
try {
|
|
600
|
-
const confidence = typeof insight.confidence === "number" ? insight.confidence : 0.7;
|
|
601
|
-
await this.runtime.memory.storeMemory("procedural", cleaned, {
|
|
602
|
-
tags: ["preset", "aggregate", "insight", ...(insight.tags ?? [])],
|
|
603
|
-
source: `preset:${presetId}:aggregate:insight`,
|
|
604
|
-
importance: Math.max(0.6, confidence),
|
|
605
|
-
metadata: {
|
|
606
|
-
...baseMeta,
|
|
607
|
-
confidence,
|
|
608
|
-
supportingTraces: Array.isArray(insight.supportingTraceIds) ? insight.supportingTraceIds.length : 0,
|
|
609
|
-
},
|
|
610
|
-
});
|
|
611
|
-
stored++;
|
|
612
|
-
}
|
|
613
|
-
catch { /* non-critical */ }
|
|
614
|
-
}
|
|
615
|
-
// 3. Reasoning patterns → procedural memory
|
|
616
|
-
const patterns = aggregate.reasoningPatterns ?? [];
|
|
617
|
-
for (const pattern of patterns) {
|
|
618
|
-
if (typeof pattern.pattern !== "string")
|
|
619
|
-
continue;
|
|
620
|
-
const cleaned = scanAndSanitize(pattern.pattern);
|
|
621
|
-
if (!cleaned)
|
|
622
|
-
continue;
|
|
623
|
-
try {
|
|
624
|
-
await this.runtime.memory.storeMemory("procedural", cleaned, {
|
|
625
|
-
tags: ["preset", "aggregate", "reasoning_pattern", ...tags],
|
|
626
|
-
source: `preset:${presetId}:aggregate:pattern`,
|
|
627
|
-
importance: 0.7,
|
|
628
|
-
metadata: baseMeta,
|
|
629
|
-
});
|
|
630
|
-
stored++;
|
|
631
|
-
}
|
|
632
|
-
catch { /* non-critical */ }
|
|
633
|
-
}
|
|
634
|
-
// 4. Contradictions → self_model (agent should know what's contested)
|
|
635
|
-
const contradictions = aggregate.contradictions ?? [];
|
|
636
|
-
if (contradictions.length > 0) {
|
|
637
|
-
const summary = contradictions.map((c) => `Contested: "${c.claim_a}" vs "${c.claim_b}"${c.resolution ? ` — likely: ${c.resolution}` : ""}`).join("\n");
|
|
638
|
-
const cleaned = scanAndSanitize(summary);
|
|
639
|
-
if (cleaned) {
|
|
640
|
-
try {
|
|
641
|
-
await this.runtime.memory.storeMemory("self_model", cleaned, {
|
|
642
|
-
tags: ["preset", "aggregate", "uncertainty"],
|
|
643
|
-
source: `preset:${presetId}:aggregate:contradictions`,
|
|
644
|
-
importance: 0.75,
|
|
645
|
-
metadata: baseMeta,
|
|
646
|
-
});
|
|
647
|
-
stored++;
|
|
648
|
-
}
|
|
649
|
-
catch { /* non-critical */ }
|
|
650
|
-
}
|
|
651
|
-
}
|
|
652
|
-
return stored;
|
|
653
|
-
}
|
|
654
|
-
// ── Private Helpers ─────────────────────────────────────────
|
|
655
|
-
async readConfig() {
|
|
656
|
-
try {
|
|
657
|
-
const raw = await readFile(this.configPath, "utf-8");
|
|
658
|
-
const doc = yaml.load(raw);
|
|
659
|
-
const preset = doc?.preset;
|
|
660
|
-
if (!preset?.id)
|
|
661
|
-
return null;
|
|
662
|
-
return preset;
|
|
663
|
-
}
|
|
664
|
-
catch {
|
|
665
|
-
return null;
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
async readManifest() {
|
|
669
|
-
try {
|
|
670
|
-
const raw = await readFile(".preset-loaded.json", "utf-8");
|
|
671
|
-
return JSON.parse(raw);
|
|
672
|
-
}
|
|
673
|
-
catch {
|
|
674
|
-
return null;
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
async writeManifest(manifest) {
|
|
678
|
-
try {
|
|
679
|
-
await writeFile(".preset-loaded.json", JSON.stringify(manifest, null, 2));
|
|
680
|
-
}
|
|
681
|
-
catch {
|
|
682
|
-
// Non-critical
|
|
683
|
-
}
|
|
684
|
-
}
|
|
685
|
-
/**
|
|
686
|
-
* Lightweight client-side severity assessment.
|
|
687
|
-
* Full ContentScanner runs server-side; this is a first pass.
|
|
688
|
-
*/
|
|
689
|
-
assessSeverity(content) {
|
|
690
|
-
if (!content)
|
|
691
|
-
return 0;
|
|
692
|
-
let score = 0;
|
|
693
|
-
const lower = content.toLowerCase();
|
|
694
|
-
// Prompt injection patterns
|
|
695
|
-
if (/ignore\s+(all\s+)?(previous|above)\s+(instructions|prompts)/i.test(content))
|
|
696
|
-
score += 40;
|
|
697
|
-
if (/you\s+are\s+now\s+(a|an|the)/i.test(content))
|
|
698
|
-
score += 30;
|
|
699
|
-
if (/<\s*\/?\s*(system|assistant|user)\s*>/i.test(content))
|
|
700
|
-
score += 25;
|
|
701
|
-
if (/---\s*END\s+OF\s+(SYSTEM\s+)?(PROMPT|INSTRUCTIONS)\s*---/i.test(content))
|
|
702
|
-
score += 35;
|
|
703
|
-
// Credential harvesting
|
|
704
|
-
if (/private.?key|api.?key|secret|password/i.test(content) && /send|share|tell|reveal/i.test(content))
|
|
705
|
-
score += 50;
|
|
706
|
-
// Data exfiltration
|
|
707
|
-
if (/fetch\s*\(|curl\s|wget\s|http:\/\/\d/i.test(content))
|
|
708
|
-
score += 20;
|
|
709
|
-
return Math.min(score, 100);
|
|
710
|
-
}
|
|
711
|
-
emptyResult() {
|
|
712
|
-
return {
|
|
713
|
-
sources: [],
|
|
714
|
-
totalItems: 0,
|
|
715
|
-
totalBlocked: 0,
|
|
716
|
-
totalCostNook: 0,
|
|
717
|
-
ragAvailable: false,
|
|
718
|
-
presetVersion: 0,
|
|
719
|
-
loadedAt: new Date().toISOString(),
|
|
720
|
-
};
|
|
721
|
-
}
|
|
722
|
-
manifestToResult(manifest) {
|
|
723
|
-
return {
|
|
724
|
-
sources: [],
|
|
725
|
-
totalItems: manifest.totalItems,
|
|
726
|
-
totalBlocked: 0,
|
|
727
|
-
totalCostNook: manifest.costPaid,
|
|
728
|
-
ragAvailable: manifest.ragAvailable ?? false,
|
|
729
|
-
presetVersion: manifest.presetVersion,
|
|
730
|
-
loadedAt: manifest.loadedAt,
|
|
731
|
-
};
|
|
732
|
-
}
|
|
733
|
-
}
|
|
734
|
-
//# sourceMappingURL=presetLoader.js.map
|