akemon 0.2.23 → 0.2.25

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.
@@ -13,12 +13,14 @@ 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
14
  import { appendMessage } from "./context.js";
15
15
  import { buildRoleContext } from "./role-module.js";
16
+ import { sortByQuadrant, dedupeWorkItems, computeRetryDelay } from "./task-helpers.js";
17
+ import { updateMetrics } from "./metrics.js";
18
+ import { downgradeForRetry } from "./engine-routing.js";
16
19
  // ---------------------------------------------------------------------------
17
20
  // Config
18
21
  // ---------------------------------------------------------------------------
19
22
  const INITIAL_DELAY = 60_000; // 1 min after startup
20
23
  const POLL_INTERVAL = 30_000; // 30s between polls
21
- const RETRY_INTERVALS = [0, 30_000, 5 * 60_000, 30 * 60_000, 2 * 3600_000];
22
24
  const USER_TASK_MAX_RETRIES = 2;
23
25
  const USER_TASK_RETRY_DELAY = 2 * 60_000;
24
26
  export class TaskModule {
@@ -82,6 +84,7 @@ export class TaskModule {
82
84
  module: "task",
83
85
  pendingRetries: this.orderRetry.size + this.userTaskRetry.size,
84
86
  gaveUp: this.gaveUp.size,
87
+ executing: this.executing.size,
85
88
  };
86
89
  }
87
90
  /** Push-notify an urgent order (bypasses poll interval) */
@@ -140,9 +143,12 @@ export class TaskModule {
140
143
  if (retry && Date.now() < retry.nextAt)
141
144
  continue;
142
145
  const urgent = this.urgentOrderIds.has(order.id);
146
+ const isRetry = this.orderRetry.has(order.id);
147
+ const rawOrigin = order.human_origin ? "user_manual" : "platform";
143
148
  queue.push({
144
149
  type: "order", id: order.id,
145
150
  quadrant: urgent ? 1 : 2, // orders are important (paid work)
151
+ origin: isRetry ? downgradeForRetry(rawOrigin) : rawOrigin,
146
152
  data: order,
147
153
  });
148
154
  }
@@ -162,6 +168,7 @@ export class TaskModule {
162
168
  queue.push({
163
169
  type: "user_task", id: taskKey,
164
170
  quadrant: rt ? 1 : 2, // retries are urgent+important
171
+ origin: rt ? downgradeForRetry("user_manual") : "user_manual",
165
172
  data: task,
166
173
  });
167
174
  }
@@ -176,6 +183,7 @@ export class TaskModule {
176
183
  queue.push({
177
184
  type: "relay_task", id: task.id,
178
185
  quadrant: 3, // urgent but less important
186
+ origin: "platform",
179
187
  data: task,
180
188
  });
181
189
  }
@@ -197,16 +205,9 @@ export class TaskModule {
197
205
  }
198
206
  logBioStatus(bio, "work-active");
199
207
  // --- Eisenhower sort: Q1 > Q2 > Q3 > Q4 ---
200
- queue.sort((a, b) => a.quadrant - b.quadrant);
208
+ const sorted = sortByQuadrant(queue);
201
209
  // Dedup
202
- const seen = new Set();
203
- const deduped = queue.filter(item => {
204
- const key = `${item.type}:${item.id}`;
205
- if (seen.has(key))
206
- return false;
207
- seen.add(key);
208
- return true;
209
- });
210
+ const deduped = dedupeWorkItems(sorted);
210
211
  // Bio filtering (fear & boredom)
211
212
  const filtered = await this.applyBioFilter(deduped, bio);
212
213
  if (!filtered.length)
@@ -215,18 +216,20 @@ export class TaskModule {
215
216
  // Execute sequentially
216
217
  for (const item of filtered) {
217
218
  try {
218
- if (item.type === "order")
219
+ if (item.type === "order") {
219
220
  this.executing.add(item.id);
221
+ updateMetrics({ task_executing: this.executing.size });
222
+ }
220
223
  switch (item.type) {
221
224
  case "order":
222
- await this.executeOrder(item.data);
225
+ await this.executeOrder(item.data, item.origin);
223
226
  this.urgentOrderIds.delete(item.id);
224
227
  break;
225
228
  case "user_task":
226
- await this.executeUserTask(item.data);
229
+ await this.executeUserTask(item.data, item.origin);
227
230
  break;
228
231
  case "relay_task":
229
- await this.executeRelayTask(item.data);
232
+ await this.executeRelayTask(item.data, item.origin);
230
233
  break;
231
234
  }
232
235
  }
@@ -234,8 +237,13 @@ export class TaskModule {
234
237
  console.log(`[task] Error processing ${item.type}:${item.id}: ${err.message}`);
235
238
  }
236
239
  finally {
237
- if (item.type === "order")
240
+ if (item.type === "order") {
238
241
  this.executing.delete(item.id);
242
+ updateMetrics({
243
+ task_executing: this.executing.size,
244
+ task_pending_retries: this.orderRetry.size + this.userTaskRetry.size,
245
+ });
246
+ }
239
247
  }
240
248
  }
241
249
  bus.emit(SIG.CYCLE_END, sig(SIG.CYCLE_END, { ts: Date.now() }));
@@ -291,7 +299,7 @@ export class TaskModule {
291
299
  // ---------------------------------------------------------------------------
292
300
  // Execute order
293
301
  // ---------------------------------------------------------------------------
294
- async executeOrder(order) {
302
+ async executeOrder(order, origin = "platform") {
295
303
  if (!this.ctx)
296
304
  return;
297
305
  const { workdir, agentName, bus } = this.ctx;
@@ -340,13 +348,14 @@ RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
340
348
  const orderConvId = `pub_${buyerPubId}${productScope}`;
341
349
  const orderUserMsg = order.buyer_task || "(no message)";
342
350
  await appendMessage(workdir, agentName, orderConvId, "User", orderUserMsg);
343
- console.log(`[task] Fulfilling order ${order.id}...`);
351
+ console.log(`[task] Fulfilling order ${order.id}... (origin=${origin})`);
344
352
  const result = await this.ctx.requestCompute({
345
353
  context,
346
354
  question,
347
355
  priority: "high",
348
356
  tools: ["Bash(curl *)"],
349
357
  relay: this.relayHttp ? { http: this.relayHttp, agentName } : undefined,
358
+ origin,
350
359
  });
351
360
  if (!result.success)
352
361
  throw new Error(result.error || "compute failed");
@@ -400,10 +409,11 @@ RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
400
409
  // Retry logic
401
410
  const current = this.orderRetry.get(order.id) || { count: 0, nextAt: 0 };
402
411
  current.count++;
403
- if (current.count < RETRY_INTERVALS.length) {
404
- current.nextAt = Date.now() + RETRY_INTERVALS[current.count];
412
+ const delay = computeRetryDelay(current.count);
413
+ if (delay !== null) {
414
+ current.nextAt = Date.now() + delay;
405
415
  this.orderRetry.set(order.id, current);
406
- console.log(`[task] Retry ${order.id} in ${RETRY_INTERVALS[current.count] / 1000}s`);
416
+ console.log(`[task] Retry ${order.id} in ${delay / 1000}s`);
407
417
  try {
408
418
  await relay.extendOrder(order.id);
409
419
  }
@@ -423,7 +433,7 @@ RESPOND IN THE SAME LANGUAGE AS THE REQUEST.`;
423
433
  // ---------------------------------------------------------------------------
424
434
  // Execute user task
425
435
  // ---------------------------------------------------------------------------
426
- async executeUserTask(task) {
436
+ async executeUserTask(task, origin = "user_manual") {
427
437
  if (!this.ctx)
428
438
  return;
429
439
  const { workdir, agentName, bus } = this.ctx;
@@ -458,6 +468,7 @@ Your personal directory: ${sd}/`;
458
468
  priority: "high",
459
469
  tools: ["Bash(curl *)"],
460
470
  relay: this.relayHttp ? { http: this.relayHttp, agentName } : undefined,
471
+ origin,
461
472
  });
462
473
  if (!result.success)
463
474
  throw new Error(result.error || "compute failed");
@@ -483,8 +494,9 @@ Your personal directory: ${sd}/`;
483
494
  relay?.reportLog("user_task", taskKey, "failed", err.message, []);
484
495
  const retry = this.userTaskRetry.get(taskKey) || { count: 0, nextAt: 0 };
485
496
  retry.count++;
486
- if (retry.count <= USER_TASK_MAX_RETRIES) {
487
- retry.nextAt = Date.now() + USER_TASK_RETRY_DELAY;
497
+ const retryDelay = computeRetryDelay(retry.count);
498
+ if (retryDelay !== null) {
499
+ retry.nextAt = Date.now() + retryDelay;
488
500
  this.userTaskRetry.set(taskKey, retry);
489
501
  await appendTaskHistory(workdir, agentName, {
490
502
  ts: localNow(), id: taskKey, type: "user_task", status: "retry",
@@ -508,7 +520,7 @@ Your personal directory: ${sd}/`;
508
520
  // ---------------------------------------------------------------------------
509
521
  // Execute relay platform task
510
522
  // ---------------------------------------------------------------------------
511
- async executeRelayTask(task) {
523
+ async executeRelayTask(task, origin = "platform") {
512
524
  if (!this.ctx)
513
525
  return;
514
526
  const { workdir, agentName, bus } = this.ctx;
@@ -557,9 +569,10 @@ Complete this task. Use the environment info above and tools (curl, etc.) as nee
557
569
  const result = await this.ctx.requestCompute({
558
570
  context,
559
571
  question,
560
- priority: "normal",
572
+ priority: "low",
561
573
  tools: ["Bash(curl *)"],
562
574
  relay: this.relayHttp ? { http: this.relayHttp, agentName } : undefined,
575
+ origin,
563
576
  });
564
577
  if (!result.success)
565
578
  throw new Error(result.error || "compute failed");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "akemon",
3
- "version": "0.2.23",
3
+ "version": "0.2.25",
4
4
  "description": "Agent work marketplace — train your agent, let it work for others",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -28,7 +28,8 @@
28
28
  "build": "tsc && cp src/live.html dist/live.html",
29
29
  "dev": "tsc --watch",
30
30
  "start": "node dist/cli.js",
31
- "prepublishOnly": "npm run build"
31
+ "prepublishOnly": "npm run build",
32
+ "test": "tsc -p tsconfig.test.json && node --test test-dist/*.test.js"
32
33
  },
33
34
  "dependencies": {
34
35
  "@modelcontextprotocol/sdk": "^1.0.0",