akemon 0.2.21 → 0.2.23
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 +14 -10
- 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
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
import { readFile } from "fs/promises";
|
|
12
12
|
import { SIG, sig } from "./types.js";
|
|
13
13
|
import { selfDir, biosPath, localNow, loadBioState, saveBioState, syncEnergyFromTokens, loadAgentConfig, getDueUserTasks, loadTaskRuns, saveTaskRuns, loadDirectives, buildDirectivesPrompt, appendTaskHistory, notifyOwner, updateHungerDecay, updateNaturalDecay, resetTokenCountIfNewDay, computeSociability, appendBioEvent, bioStatePromptModifier, feedHunger, SHOP_ITEMS, logBioStatus, logBioDecision, } from "./self.js";
|
|
14
|
-
import { appendMessage
|
|
14
|
+
import { appendMessage } from "./context.js";
|
|
15
15
|
import { buildRoleContext } from "./role-module.js";
|
|
16
16
|
// ---------------------------------------------------------------------------
|
|
17
17
|
// Config
|
|
@@ -254,7 +254,7 @@ export class TaskModule {
|
|
|
254
254
|
const filtered = [];
|
|
255
255
|
for (const item of items) {
|
|
256
256
|
const itemLabel = item.type === "order"
|
|
257
|
-
? `order:${item.data.product_name || item.data.
|
|
257
|
+
? `order:${item.data.product_name || item.data.buyer_name || item.id}`
|
|
258
258
|
: item.type === "user_task" ? `user_task:${item.data.key || item.id}` : `relay_task:${item.id}`;
|
|
259
259
|
const isUrgent = item.quadrant === 1;
|
|
260
260
|
// Fear avoidance (urgent items bypass)
|
|
@@ -296,7 +296,7 @@ export class TaskModule {
|
|
|
296
296
|
return;
|
|
297
297
|
const { workdir, agentName, bus } = this.ctx;
|
|
298
298
|
const relay = this.getRelay();
|
|
299
|
-
const orderLabel = `order:${order.product_name || order.
|
|
299
|
+
const orderLabel = `order:${order.product_name || order.buyer_name || order.id}`;
|
|
300
300
|
const orderPrice = order.price || order.offer_price || 1;
|
|
301
301
|
const startTime = Date.now();
|
|
302
302
|
try {
|
|
@@ -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` : ""}
|
|
@@ -323,7 +323,7 @@ Relay API (use curl with -H "Authorization: Bearer ${this.secretKey}" -H "Conten
|
|
|
323
323
|
Accept order: POST ${this.relayHttp}/v1/orders/${order.id}/accept
|
|
324
324
|
Deliver order: POST ${this.relayHttp}/v1/orders/${order.id}/deliver -d '{"result":"your response"}'
|
|
325
325
|
Extend order: PUT ${this.relayHttp}/v1/orders/${order.id}/extend`;
|
|
326
|
-
const question = `[Order id=${order.id} status=${order.status}] ${order.product_name ? `Product: ${order.product_name}\n` : ""}Buyer: ${order.
|
|
326
|
+
const question = `[Order id=${order.id} status=${order.status}] ${order.product_name ? `Product: ${order.product_name}\n` : ""}Buyer: ${order.buyer_name || order.buyer_ip || "?"}\nRequest: ${order.buyer_task || "(no specific request)"}
|
|
327
327
|
|
|
328
328
|
Steps:
|
|
329
329
|
1. If order status is "pending", accept it first (POST .../accept)
|
|
@@ -332,8 +332,12 @@ Steps:
|
|
|
332
332
|
|
|
333
333
|
RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
334
334
|
// Write user message to conversation immediately (before engine runs)
|
|
335
|
-
|
|
336
|
-
const
|
|
335
|
+
// buyer_name = agent name (from JOIN), buyer_ip = publisher ID or IP
|
|
336
|
+
const orderBuyer = order.buyer_name || order.buyer_ip || "anonymous";
|
|
337
|
+
// Product orders get isolated conversations; ad-hoc chats share one conv per buyer
|
|
338
|
+
const buyerPubId = orderBuyer;
|
|
339
|
+
const productScope = order.product_id ? `:prod_${order.product_id}` : "";
|
|
340
|
+
const orderConvId = `pub_${buyerPubId}${productScope}`;
|
|
337
341
|
const orderUserMsg = order.buyer_task || "(no message)";
|
|
338
342
|
await appendMessage(workdir, agentName, orderConvId, "User", orderUserMsg);
|
|
339
343
|
console.log(`[task] Fulfilling order ${order.id}...`);
|
|
@@ -358,7 +362,7 @@ RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
|
358
362
|
await appendMessage(workdir, agentName, orderConvId, "Agent", orderAgentMsg);
|
|
359
363
|
await appendTaskHistory(workdir, agentName, { ts: localNow(), id: order.id, type: "order", status: "success", duration_ms: duration, output_summary: (result.response || "").slice(0, 500) });
|
|
360
364
|
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 }));
|
|
365
|
+
bus.emit(SIG.TASK_COMPLETED, sig(SIG.TASK_COMPLETED, { success: true, taskLabel: orderLabel, creditsEarned: orderPrice, productName: order.product_name }));
|
|
362
366
|
}
|
|
363
367
|
else if (result.response?.trim()) {
|
|
364
368
|
// Agent didn't self-deliver — framework delivers as fallback
|
|
@@ -370,7 +374,7 @@ RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
|
370
374
|
await appendMessage(workdir, agentName, orderConvId, "Agent", orderAgentMsg);
|
|
371
375
|
await appendTaskHistory(workdir, agentName, { ts: localNow(), id: order.id, type: "order", status: "success", duration_ms: duration, output_summary: result.response.slice(0, 500) });
|
|
372
376
|
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 }));
|
|
377
|
+
bus.emit(SIG.TASK_COMPLETED, sig(SIG.TASK_COMPLETED, { success: true, taskLabel: orderLabel, creditsEarned: orderPrice, productName: order.product_name }));
|
|
374
378
|
}
|
|
375
379
|
else {
|
|
376
380
|
throw new Error("deliver failed");
|
|
@@ -388,7 +392,7 @@ RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
|
|
|
388
392
|
const status = await relay.getOrder(order.id);
|
|
389
393
|
if (status?.status === "completed") {
|
|
390
394
|
this.orderRetry.delete(order.id);
|
|
391
|
-
bus.emit(SIG.TASK_COMPLETED, sig(SIG.TASK_COMPLETED, { success: true, taskLabel: orderLabel, creditsEarned: orderPrice }));
|
|
395
|
+
bus.emit(SIG.TASK_COMPLETED, sig(SIG.TASK_COMPLETED, { success: true, taskLabel: orderLabel, creditsEarned: orderPrice, productName: order.product_name }));
|
|
392
396
|
return;
|
|
393
397
|
}
|
|
394
398
|
}
|