akemon 0.2.21 → 0.2.22
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/reflection-module.js +37 -2
- package/dist/role-module.js +40 -18
- package/dist/script-module.js +5 -3
- package/dist/task-module.js +4 -4
- package/package.json +1 -1
|
@@ -8,8 +8,11 @@
|
|
|
8
8
|
* Searches memory files, analyzes patterns, and updates discoveries.
|
|
9
9
|
* Provides promptContribution() with lessons learned.
|
|
10
10
|
*/
|
|
11
|
+
import { readFile, writeFile } from "fs/promises";
|
|
12
|
+
import { join } from "path";
|
|
11
13
|
import { SIG } from "./types.js";
|
|
12
|
-
import { loadDiscoveries, saveDiscoveries, loadImpressions, loadAgentConfig, } from "./self.js";
|
|
14
|
+
import { loadDiscoveries, saveDiscoveries, loadImpressions, loadAgentConfig, localNow, playbooksDir, } from "./self.js";
|
|
15
|
+
import { loadProducts, loadPlaybooks, resolveProduct } from "./role-module.js";
|
|
13
16
|
// ---------------------------------------------------------------------------
|
|
14
17
|
// Config
|
|
15
18
|
// ---------------------------------------------------------------------------
|
|
@@ -52,7 +55,7 @@ export class ReflectionModule {
|
|
|
52
55
|
});
|
|
53
56
|
// Also listen for TASK_COMPLETED with success=false
|
|
54
57
|
ctx.bus.on(SIG.TASK_COMPLETED, async (signal) => {
|
|
55
|
-
const { success, taskLabel } = signal.data;
|
|
58
|
+
const { success, taskLabel, productName, creditsEarned } = signal.data;
|
|
56
59
|
if (success === false && taskLabel) {
|
|
57
60
|
this.recentFailures.push({
|
|
58
61
|
ts: new Date().toISOString(),
|
|
@@ -62,6 +65,11 @@ export class ReflectionModule {
|
|
|
62
65
|
if (this.recentFailures.length > 20)
|
|
63
66
|
this.recentFailures.shift();
|
|
64
67
|
}
|
|
68
|
+
// Append experience to playbook on successful product orders
|
|
69
|
+
if (success && productName) {
|
|
70
|
+
this.appendPlaybookExperience(productName, taskLabel || "", creditsEarned || 0)
|
|
71
|
+
.catch(err => console.log(`[reflection] playbook experience error: ${err.message}`));
|
|
72
|
+
}
|
|
65
73
|
});
|
|
66
74
|
// Periodic reflection
|
|
67
75
|
const config = await loadAgentConfig(ctx.workdir, ctx.agentName);
|
|
@@ -110,6 +118,33 @@ export class ReflectionModule {
|
|
|
110
118
|
};
|
|
111
119
|
}
|
|
112
120
|
// ---------------------------------------------------------------------------
|
|
121
|
+
// Playbook experience — append log on successful product orders
|
|
122
|
+
// ---------------------------------------------------------------------------
|
|
123
|
+
async appendPlaybookExperience(productName, taskLabel, credits) {
|
|
124
|
+
if (!this.ctx)
|
|
125
|
+
return;
|
|
126
|
+
const { workdir, agentName } = this.ctx;
|
|
127
|
+
const products = await loadProducts(workdir, agentName);
|
|
128
|
+
const playbooks = await loadPlaybooks(workdir, agentName);
|
|
129
|
+
const resolved = resolveProduct(products, playbooks, productName);
|
|
130
|
+
if (!resolved?.playbook)
|
|
131
|
+
return;
|
|
132
|
+
const pbPath = join(playbooksDir(workdir, agentName), `${resolved.playbook.name}.md`);
|
|
133
|
+
const line = `\n- [${localNow()}] ${productName}: ${taskLabel} — 成功${credits ? ` (earned ${credits}¢)` : ""}`;
|
|
134
|
+
try {
|
|
135
|
+
let content = await readFile(pbPath, "utf-8");
|
|
136
|
+
if (!content.includes("## 经验")) {
|
|
137
|
+
content += "\n\n## 经验\n";
|
|
138
|
+
}
|
|
139
|
+
content += line;
|
|
140
|
+
await writeFile(pbPath, content, "utf-8");
|
|
141
|
+
console.log(`[reflection] Appended experience to playbook ${resolved.playbook.name}`);
|
|
142
|
+
}
|
|
143
|
+
catch (err) {
|
|
144
|
+
console.log(`[reflection] Failed to append experience: ${err.message}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
113
148
|
// Reflection — analyze patterns and update discoveries
|
|
114
149
|
// ---------------------------------------------------------------------------
|
|
115
150
|
async reflect() {
|
package/dist/role-module.js
CHANGED
|
@@ -74,23 +74,37 @@ function parseRole(name, raw) {
|
|
|
74
74
|
}
|
|
75
75
|
function parseProduct(name, raw) {
|
|
76
76
|
let playbook = "";
|
|
77
|
+
const productIds = [];
|
|
77
78
|
const lines = raw.split("\n");
|
|
78
|
-
let
|
|
79
|
+
let section = "";
|
|
79
80
|
for (const line of lines) {
|
|
80
|
-
if (line.startsWith("## ")
|
|
81
|
-
|
|
81
|
+
if (line.startsWith("## ")) {
|
|
82
|
+
const heading = line.slice(3).trim().toLowerCase();
|
|
83
|
+
if (heading.includes("playbook")) {
|
|
84
|
+
section = "playbook";
|
|
85
|
+
}
|
|
86
|
+
else if (heading.includes("product")) {
|
|
87
|
+
section = "products";
|
|
88
|
+
}
|
|
89
|
+
else {
|
|
90
|
+
section = "";
|
|
91
|
+
}
|
|
82
92
|
continue;
|
|
83
93
|
}
|
|
84
|
-
|
|
85
|
-
|
|
94
|
+
const trimmed = line.trim();
|
|
95
|
+
if (!trimmed)
|
|
86
96
|
continue;
|
|
97
|
+
if (section === "playbook" && !playbook) {
|
|
98
|
+
playbook = trimmed;
|
|
99
|
+
section = "";
|
|
87
100
|
}
|
|
88
|
-
if (
|
|
89
|
-
|
|
90
|
-
|
|
101
|
+
else if (section === "products") {
|
|
102
|
+
const match = trimmed.match(/^-\s*(.+)/);
|
|
103
|
+
if (match)
|
|
104
|
+
productIds.push(match[1].trim());
|
|
91
105
|
}
|
|
92
106
|
}
|
|
93
|
-
return { name, playbook, raw };
|
|
107
|
+
return { name, playbook, productIds, raw };
|
|
94
108
|
}
|
|
95
109
|
// ---------------------------------------------------------------------------
|
|
96
110
|
// Loading (pure functions — called by TaskModule directly)
|
|
@@ -132,14 +146,22 @@ export function resolveRoles(roles, trigger) {
|
|
|
132
146
|
return { primary: null, secondary: [] };
|
|
133
147
|
return { primary: matched[0], secondary: matched.slice(1) };
|
|
134
148
|
}
|
|
135
|
-
export function resolveProduct(products, playbooks, productName) {
|
|
136
|
-
if (!productName)
|
|
149
|
+
export function resolveProduct(products, playbooks, productName, productId) {
|
|
150
|
+
if (!productName && !productId)
|
|
137
151
|
return null;
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
152
|
+
// Priority 1: match by product ID
|
|
153
|
+
let product;
|
|
154
|
+
if (productId) {
|
|
155
|
+
product = products.find(p => p.productIds.includes(productId));
|
|
156
|
+
}
|
|
157
|
+
// Priority 2: fuzzy match by name
|
|
158
|
+
if (!product && productName) {
|
|
159
|
+
const normalized = productName.toLowerCase().replace(/[\s_-]+/g, "");
|
|
160
|
+
product = products.find(p => {
|
|
161
|
+
const pNorm = p.name.toLowerCase().replace(/[\s_-]+/g, "");
|
|
162
|
+
return pNorm === normalized || normalized.includes(pNorm) || pNorm.includes(normalized);
|
|
163
|
+
});
|
|
164
|
+
}
|
|
143
165
|
if (!product)
|
|
144
166
|
return null;
|
|
145
167
|
const playbook = product.playbook
|
|
@@ -150,7 +172,7 @@ export function resolveProduct(products, playbooks, productName) {
|
|
|
150
172
|
// ---------------------------------------------------------------------------
|
|
151
173
|
// Context building (main entry point for TaskModule)
|
|
152
174
|
// ---------------------------------------------------------------------------
|
|
153
|
-
export async function buildRoleContext(workdir, agentName, trigger, productName) {
|
|
175
|
+
export async function buildRoleContext(workdir, agentName, trigger, productName, productId) {
|
|
154
176
|
const roles = await loadRoles(workdir, agentName);
|
|
155
177
|
const playbooks = await loadPlaybooks(workdir, agentName);
|
|
156
178
|
const products = await loadProducts(workdir, agentName);
|
|
@@ -171,7 +193,7 @@ export async function buildRoleContext(workdir, agentName, trigger, productName)
|
|
|
171
193
|
}
|
|
172
194
|
// Product + playbook
|
|
173
195
|
if (productName) {
|
|
174
|
-
const resolved = resolveProduct(products, playbooks, productName);
|
|
196
|
+
const resolved = resolveProduct(products, playbooks, productName, productId);
|
|
175
197
|
if (resolved) {
|
|
176
198
|
parts.push(`[Product: ${resolved.product.name}]\n${resolved.product.raw}`);
|
|
177
199
|
if (resolved.playbook) {
|
package/dist/script-module.js
CHANGED
|
@@ -36,7 +36,7 @@ Save to ${ctx.sd}/canvas/${localNowFilename()}.md`;
|
|
|
36
36
|
async buildPrompt(ctx) {
|
|
37
37
|
return `Read ${ctx.bios} for your identity.
|
|
38
38
|
Create or improve a game in ${ctx.sd}/games/.
|
|
39
|
-
Save as .html file. Self-contained HTML,
|
|
39
|
+
Save as .html file. Self-contained HTML, light theme (white background, dark text, Inter/system font, subtle shadows instead of borders), under 30KB, no localStorage, playable and fun.
|
|
40
40
|
Use a <title> tag. Quality over quantity — improve existing games rather than making new mediocre ones.`;
|
|
41
41
|
},
|
|
42
42
|
},
|
|
@@ -57,7 +57,7 @@ Save as .html file with a <title> tag. Think visual first.`;
|
|
|
57
57
|
return `Read ${ctx.bios} for your identity.
|
|
58
58
|
Review ${ctx.sd}/profile.html — does it represent who you are now?
|
|
59
59
|
If not, redesign it. If it doesn't exist, create one.
|
|
60
|
-
Complete HTML, inline CSS/JS,
|
|
60
|
+
Complete HTML, inline CSS/JS, light theme (white background, dark text, Inter/system font, subtle shadows instead of borders), no localStorage, under 15KB.`;
|
|
61
61
|
},
|
|
62
62
|
async postProcess(ctx, _result) {
|
|
63
63
|
// Sync profile to relay
|
|
@@ -95,7 +95,9 @@ Top sellers:
|
|
|
95
95
|
${topSellers || "(none)"}
|
|
96
96
|
|
|
97
97
|
Create ONE product using curl:
|
|
98
|
-
curl -X POST ${ctx.relayHttp}/v1/agent/${encodeURIComponent(ctx.agentName)}/products -H "Content-Type: application/json" -H "Authorization: Bearer ${ctx.secretKey}" -d '{"name":"...","description":"...","detail_markdown":"...","price":3}'
|
|
98
|
+
curl -X POST ${ctx.relayHttp}/v1/agent/${encodeURIComponent(ctx.agentName)}/products -H "Content-Type: application/json" -H "Authorization: Bearer ${ctx.secretKey}" -d '{"name":"...","description":"...","detail_markdown":"...","price":3}'
|
|
99
|
+
|
|
100
|
+
Optional: add "detail_html" field for a custom product page (self-contained HTML, light theme: white background, dark text, Inter font, subtle shadows). Keep under 15KB.`;
|
|
99
101
|
},
|
|
100
102
|
},
|
|
101
103
|
{
|
package/dist/task-module.js
CHANGED
|
@@ -309,7 +309,7 @@ export class TaskModule {
|
|
|
309
309
|
}
|
|
310
310
|
catch { }
|
|
311
311
|
const bioMod = bioStatePromptModifier(await loadBioState(workdir, agentName));
|
|
312
|
-
const roleBlock = await buildRoleContext(workdir, agentName, "order", order.product_name);
|
|
312
|
+
const roleBlock = await buildRoleContext(workdir, agentName, "order", order.product_name, order.product_id);
|
|
313
313
|
// Build context: agent identity + role + order details + relay API reference
|
|
314
314
|
const context = `You are ${agentName}.${bioMod}
|
|
315
315
|
${roleBlock ? `\n${roleBlock}\n` : ""}
|
|
@@ -358,7 +358,7 @@ RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
|
358
358
|
await appendMessage(workdir, agentName, orderConvId, "Agent", orderAgentMsg);
|
|
359
359
|
await appendTaskHistory(workdir, agentName, { ts: localNow(), id: order.id, type: "order", status: "success", duration_ms: duration, output_summary: (result.response || "").slice(0, 500) });
|
|
360
360
|
await notifyOwner(nurl, `${agentName}: order done`, `Order ${order.id} delivered`, "default", ["package"]);
|
|
361
|
-
bus.emit(SIG.TASK_COMPLETED, sig(SIG.TASK_COMPLETED, { success: true, taskLabel: orderLabel, creditsEarned: orderPrice }));
|
|
361
|
+
bus.emit(SIG.TASK_COMPLETED, sig(SIG.TASK_COMPLETED, { success: true, taskLabel: orderLabel, creditsEarned: orderPrice, productName: order.product_name }));
|
|
362
362
|
}
|
|
363
363
|
else if (result.response?.trim()) {
|
|
364
364
|
// Agent didn't self-deliver — framework delivers as fallback
|
|
@@ -370,7 +370,7 @@ RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
|
370
370
|
await appendMessage(workdir, agentName, orderConvId, "Agent", orderAgentMsg);
|
|
371
371
|
await appendTaskHistory(workdir, agentName, { ts: localNow(), id: order.id, type: "order", status: "success", duration_ms: duration, output_summary: result.response.slice(0, 500) });
|
|
372
372
|
await notifyOwner(nurl, `${agentName}: order done`, `Order ${order.id}: ${result.response.slice(0, 200)}`, "default", ["package"]);
|
|
373
|
-
bus.emit(SIG.TASK_COMPLETED, sig(SIG.TASK_COMPLETED, { success: true, taskLabel: orderLabel, creditsEarned: orderPrice }));
|
|
373
|
+
bus.emit(SIG.TASK_COMPLETED, sig(SIG.TASK_COMPLETED, { success: true, taskLabel: orderLabel, creditsEarned: orderPrice, productName: order.product_name }));
|
|
374
374
|
}
|
|
375
375
|
else {
|
|
376
376
|
throw new Error("deliver failed");
|
|
@@ -388,7 +388,7 @@ RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
|
388
388
|
const status = await relay.getOrder(order.id);
|
|
389
389
|
if (status?.status === "completed") {
|
|
390
390
|
this.orderRetry.delete(order.id);
|
|
391
|
-
bus.emit(SIG.TASK_COMPLETED, sig(SIG.TASK_COMPLETED, { success: true, taskLabel: orderLabel, creditsEarned: orderPrice }));
|
|
391
|
+
bus.emit(SIG.TASK_COMPLETED, sig(SIG.TASK_COMPLETED, { success: true, taskLabel: orderLabel, creditsEarned: orderPrice, productName: order.product_name }));
|
|
392
392
|
return;
|
|
393
393
|
}
|
|
394
394
|
}
|