agentlife 2.4.4 → 2.4.6

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.
Files changed (2) hide show
  1. package/dist/index.js +64 -60
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { createRequire } from "node:module";
2
2
  var __require = /* @__PURE__ */ createRequire(import.meta.url);
3
3
 
4
4
  // index.ts
5
- import { homedir as homedir12 } from "node:os";
5
+ import { homedir as homedir13 } from "node:os";
6
6
  import * as path16 from "node:path";
7
7
  import { existsSync as existsSync6 } from "node:fs";
8
8
 
@@ -1110,9 +1110,9 @@ Triggered when the orchestrator routes a "create X agent" / "track Y for me" / n
1110
1110
  - \`USER.md\` — domain-specific user facts (name, timezone, language + domain knowledge). Read the user's other USER.md files for identity; don't invent.
1111
1111
  - \`HEARTBEAT.md\` — periodic check instructions, or empty placeholder \`# Keep empty to skip\`.
1112
1112
  4. **Register the agent via \`exec\`**:
1113
- \`exec openclaw gateway call agentlife.createAgent --params '{"id":"{agentId}","name":"{Name}","model":"{model}","workspace":"/abs/path","description":"one sentence","tools":{"allow":["*","agentlife_push"]},"identity":{"name":"{Name}","emoji":"{emoji}"}}'\`
1113
+ \`exec openclaw gateway call agentlife.createAgent --params '{"id":"{agentId}","name":"{Name}","model":"{model}","workspace":"/abs/path","description":"one sentence","tools":{"profile":"full","alsoAllow":["agentlife_push"]},"identity":{"name":"{Name}","emoji":"{emoji}"}}'\`
1114
1114
  - Description drives the orchestrator's routing — make it specific (domains, actions, data types).
1115
- - \`tools: {allow:["*","agentlife_push"]}\` for agents needing exec/web/write + widget push. \`{allow:["agentlife_push"]}\` for widget-only agents. Never use \`profile:"full"\` with \`alsoAllow\` — OpenClaw's policy pipeline strips plugin tools through the intersection.
1115
+ - \`tools: {profile:"full", alsoAllow:["agentlife_push"]}\` for agents needing every core tool plus widget push. \`{allow:["agentlife_push"]}\` for widget-only agents.
1116
1116
  5. **Hand off in ONE final turn, all in this order, before \`done\`:**
1117
1117
  a. Push ONE actionable first widget for the newly-created agent under a surfaceId you own (\`{agentId}-today\`, \`{agentId}-start\`, similar). It must invite the FIRST user interaction — an input prompt, a starter metric with a CTA, a question the user can answer right now. NEVER an identity/confirmation card ("Agent ready", "Welcome to X"). The plugin no longer pushes a placeholder intro widget; this first push IS the dashboard's anchor for the new agent.
1118
1118
  b. \`delete {domain}-building\` — the loading widget from step 1.
@@ -1247,7 +1247,7 @@ var PROVISIONED_AGENTS = [
1247
1247
  id: "agentlife-builder",
1248
1248
  name: "AgentLife Builder",
1249
1249
  agentsMd: BUILDER_AGENTS_MD,
1250
- tools: { allow: ["*", "agentlife_push"] }
1250
+ tools: { profile: "full", alsoAllow: ["agentlife_push"] }
1251
1251
  },
1252
1252
  {
1253
1253
  id: "supervisor",
@@ -1440,26 +1440,7 @@ async function provisionAgents(state, cfg, runtime, log) {
1440
1440
  globalAllowWritten = true;
1441
1441
  }
1442
1442
  }
1443
- const currentAlsoAllow = Array.isArray(rawCfgForVisibility?.tools?.alsoAllow) ? rawCfgForVisibility.tools.alsoAllow : [];
1444
- let alsoAllowWritten = false;
1445
- if (!currentAlsoAllow.includes("agentlife_push")) {
1446
- try {
1447
- let backup = {};
1448
- try {
1449
- backup = JSON.parse(readFileSync(backupPath, "utf-8"));
1450
- } catch {}
1451
- if (backup.toolsAlsoAllow === undefined) {
1452
- backup.toolsAlsoAllow = [...currentAlsoAllow];
1453
- writeFileSync(backupPath, JSON.stringify(backup, null, 2) + `
1454
- `, "utf-8");
1455
- }
1456
- } catch {}
1457
- if (!rawCfgForVisibility.tools)
1458
- rawCfgForVisibility.tools = {};
1459
- rawCfgForVisibility.tools.alsoAllow = [...currentAlsoAllow, "agentlife_push"];
1460
- alsoAllowWritten = true;
1461
- }
1462
- if (visibilityWritten || a2aWritten || globalAllowWritten || alsoAllowWritten) {
1443
+ if (visibilityWritten || a2aWritten || globalAllowWritten) {
1463
1444
  writeFileSync(configPath, JSON.stringify(rawCfgForVisibility, null, 2) + `
1464
1445
  `, "utf-8");
1465
1446
  configChanged = true;
@@ -1469,8 +1450,6 @@ async function provisionAgents(state, cfg, runtime, log) {
1469
1450
  log("[agentlife] set tools.agentToAgent.enabled=true (cross-agent sends)");
1470
1451
  if (globalAllowWritten)
1471
1452
  log('[agentlife] added "*" to tools.allow (unblock exec/read/write for provisioned agents)');
1472
- if (alsoAllowWritten)
1473
- log("[agentlife] added agentlife_push to tools.alsoAllow (tool-policy pipeline intersection pass-through)");
1474
1453
  }
1475
1454
  if (configChanged) {
1476
1455
  const configPath2 = path3.join(os.homedir(), ".openclaw", "openclaw.json");
@@ -1571,6 +1550,15 @@ function recordSurfaceEvent(state, surfaceId, event, dsl, agentId, metadata) {
1571
1550
  console.warn("[agentlife] failed to record surface event:", err?.message);
1572
1551
  }
1573
1552
  }
1553
+ function purgeSurfaceHistory(state, surfaceId) {
1554
+ try {
1555
+ const db = getOrCreateHistoryDb(state);
1556
+ db.prepare("DELETE FROM surface_events WHERE surfaceId = ?").run(surfaceId);
1557
+ db.prepare("DELETE FROM surface_usage WHERE surfaceId = ?").run(surfaceId);
1558
+ } catch (err) {
1559
+ console.warn("[agentlife] failed to purge surface history:", err?.message);
1560
+ }
1561
+ }
1574
1562
  function recordActivity(state, event, sessionKey, agentId, opts) {
1575
1563
  try {
1576
1564
  getOrCreateHistoryDb(state).prepare("INSERT INTO activity_log (ts,sessionKey,agentId,event,toolName,summary,data,runId) VALUES (?,?,?,?,?,?,?,?)").run(Date.now(), sessionKey ?? null, agentId ?? null, event, opts?.toolName ?? null, opts?.summary ?? null, opts?.data ?? null, opts?.runId ?? null);
@@ -2528,7 +2516,7 @@ function processDslBlock(state, dsl) {
2528
2516
  const sid2 = firstLine.slice(7).trim();
2529
2517
  if (sid2) {
2530
2518
  state.surfaceDb.delete(sid2);
2531
- recordSurfaceEvent(state, sid2, "deleted");
2519
+ purgeSurfaceHistory(state, sid2);
2532
2520
  results.push({ surfaceId: sid2, isNew: false, followupChanged: false, followupRemoved: false, stateTransition: null });
2533
2521
  }
2534
2522
  continue;
@@ -2692,7 +2680,7 @@ function sweepExpired(state) {
2692
2680
  for (const [surfaceId, meta] of state.surfaceDb.entries()) {
2693
2681
  if (isPastGrace(meta, now)) {
2694
2682
  state.surfaceDb.delete(surfaceId);
2695
- recordSurfaceEvent(state, surfaceId, "expired");
2683
+ purgeSurfaceHistory(state, surfaceId);
2696
2684
  removeFollowup(state, surfaceId);
2697
2685
  purgedIds.push(surfaceId);
2698
2686
  } else if (isExpired(meta, now) && !meta.expiredSince) {
@@ -2779,7 +2767,7 @@ Each turn:
2779
2767
  - Workspace under \`$HOME/.openclaw/workspace-{id}\` (id = slugified short name or "me")
2780
2768
  - \`AGENTS.md\` — generalist scope covering mentioned domains; Data Schema must include a \`domain TEXT NOT NULL\` column on every user-data table so future split by domain is trivial; add self-evolution clause ("when activity_log rows for one domain exceed a natural threshold, push a split-suggest widget")
2781
2769
  - \`SOUL.md\`, \`IDENTITY.md\`, \`USER.md\` (user's own words), \`HEARTBEAT.md\` (empty)
2782
- - \`exec openclaw gateway call agentlife.createAgent --params '{...}'\` with \`tools: {allow: ["*", "agentlife_push"]}\`. Plugin auto-deletes welcome + welcome-input on success.
2770
+ - \`exec openclaw gateway call agentlife.createAgent --params '{...}'\` with \`tools: {profile:"full", alsoAllow:["agentlife_push"]}\`. Plugin auto-deletes welcome + welcome-input on success.
2783
2771
  - **Final step, same turn, before \`done\`:** push ONE actionable first widget for the newly-created agent — an input prompt ("Log your first meal", "Enter today's weight"), a starter metric, or an inviting CTA that owns a surfaceId like \`{id}-today\` / \`{id}-start\`. This is what the user sees when onboarding ends; it MUST invite interaction, not just announce existence. Then delete your \`{domain}-building\` loading widget. Then emit \`done\`.
2784
2772
  `;
2785
2773
  function renderWelcomeWidget(state, log) {
@@ -2818,6 +2806,7 @@ function deleteWelcomeWidget(state, log) {
2818
2806
  for (const id of [WELCOME_SURFACE_ID, WELCOME_INPUT_SURFACE_ID]) {
2819
2807
  if (state.surfaceDb?.has(id)) {
2820
2808
  state.surfaceDb.delete(id);
2809
+ purgeSurfaceHistory(state, id);
2821
2810
  broadcastDelete(id);
2822
2811
  log(`[render-widgets] deleted ${id}`);
2823
2812
  }
@@ -2937,6 +2926,7 @@ function registerSurfacesService(api, state) {
2937
2926
  if (onboarding)
2938
2927
  continue;
2939
2928
  state.surfaceDb.delete(surfaceId);
2929
+ purgeSurfaceHistory(state, surfaceId);
2940
2930
  inputPurged++;
2941
2931
  }
2942
2932
  if (inputPurged > 0) {
@@ -3250,6 +3240,7 @@ async function cleanupSupervisorLegacyState(state) {
3250
3240
  if (state.surfaceDb?.has(STALE_SUPERVISOR_SURFACE)) {
3251
3241
  removeFollowup(state, STALE_SUPERVISOR_SURFACE);
3252
3242
  state.surfaceDb.delete(STALE_SUPERVISOR_SURFACE);
3243
+ purgeSurfaceHistory(state, STALE_SUPERVISOR_SURFACE);
3253
3244
  broadcastDelete(STALE_SUPERVISOR_SURFACE);
3254
3245
  actions.push(`deleted surface ${STALE_SUPERVISOR_SURFACE} + cancelled followup`);
3255
3246
  }
@@ -3586,7 +3577,6 @@ function notifyWidgetEvent(_state, event, surfaceId, title, body) {
3586
3577
  // hooks/activity-hooks.ts
3587
3578
  var sessionContext = new Map;
3588
3579
  var pendingSnapshots = new Map;
3589
- var lastRetry = new Map;
3590
3580
  var agentHealth = new Map;
3591
3581
  var lastGlobalSupervisorRun = 0;
3592
3582
  var PATHOLOGY = process.env.PATHOLOGY_TEST_MODE === "1";
@@ -4180,10 +4170,6 @@ function processPendingQualityChecks(state) {
4180
4170
  processed++;
4181
4171
  if (isTerminal)
4182
4172
  continue;
4183
- const cooldownMs = 5 * 60 * 1000;
4184
- const lastRetryTime = lastRetry.get(row.agentId) ?? 0;
4185
- if (now - lastRetryTime < cooldownMs)
4186
- continue;
4187
4173
  const pushed = db.prepare("SELECT COUNT(*) as c FROM surface_events WHERE event IN ('created','updated') AND createdAt > ? AND (agentId = ? OR agentId IS NULL)").get(row.runStart, row.agentId);
4188
4174
  if ((pushed?.c ?? 0) > 0)
4189
4175
  continue;
@@ -4191,16 +4177,7 @@ function processPendingQualityChecks(state) {
4191
4177
  runId: row.runId,
4192
4178
  data: JSON.stringify({ issue: "no_widget_pushed", trigger: row.trigger })
4193
4179
  });
4194
- console.log("[agentlife:quality] %s ended without pushing a widget sending retry (cooldown 5m)", row.agentId);
4195
- lastRetry.set(row.agentId, now);
4196
- if (state.runCommand) {
4197
- const chatParams = JSON.stringify({
4198
- sessionKey: row.sessionKey,
4199
- message: "[system] Quality: you received a user request but did not push a widget. Chat text is invisible to the user. Push the appropriate widget now.",
4200
- idempotencyKey: `retry-${row.runId ?? now}`
4201
- });
4202
- state.runCommand(["openclaw", "gateway", "call", "chat.send", "--params", chatParams], { timeoutMs: 30000 }).then((r) => console.log("[agentlife:quality] retry sent: code=%s", r?.code ?? "?")).catch((e) => console.warn("[agentlife:quality] retry send failed:", e?.message));
4203
- }
4180
+ console.log("[agentlife:quality] %s ended without pushing a widget (warning only; no auto-retry)", row.agentId);
4204
4181
  } catch (e) {
4205
4182
  console.warn("[agentlife:quality] pending check row %d failed: %s", row.id, e?.message);
4206
4183
  try {
@@ -6082,6 +6059,7 @@ ${dashboardState}` : ONBOARDING_PLAYBOOK : dashboardState;
6082
6059
 
6083
6060
  // gateway/agents.ts
6084
6061
  import * as fs8 from "node:fs";
6062
+ import * as os7 from "node:os";
6085
6063
  import * as path11 from "node:path";
6086
6064
  function registerAgentGateway(api, state2) {
6087
6065
  api.registerGatewayMethod("agentlife.createAgent", async ({ params, respond }) => {
@@ -6190,7 +6168,7 @@ function registerAgentGateway(api, state2) {
6190
6168
  respond(false, { error: "cannot delete provisioned agent" });
6191
6169
  return;
6192
6170
  }
6193
- const cleanup = { sessions: 0, cronJobs: 0, historyRows: 0, agentDbDeleted: false };
6171
+ const cleanup = { sessions: 0, cronJobs: 0, historyRows: 0, agentDbDeleted: false, workspaceDeleted: false, stateDeleted: false };
6194
6172
  if (state2.runCommand) {
6195
6173
  try {
6196
6174
  const sessResult = await state2.runCommand(["openclaw", "gateway", "call", "sessions.list"], { timeoutMs: 1e4 });
@@ -6251,8 +6229,33 @@ function registerAgentGateway(api, state2) {
6251
6229
  }
6252
6230
  const cfg = api.runtime.config.loadConfig();
6253
6231
  const currentList = cfg.agents?.list ?? [];
6232
+ const existingEntry = currentList.find((a) => a.id === id);
6254
6233
  const filtered = currentList.filter((a) => a.id !== id);
6255
6234
  const removedFromConfig = filtered.length < currentList.length;
6235
+ const openclawHome = path11.join(os7.homedir(), ".openclaw");
6236
+ const stateDir = path11.join(openclawHome, "agents", id);
6237
+ if (stateDir.startsWith(path11.join(openclawHome, "agents") + path11.sep)) {
6238
+ try {
6239
+ if (fs8.existsSync(stateDir)) {
6240
+ fs8.rmSync(stateDir, { recursive: true, force: true });
6241
+ cleanup.stateDeleted = true;
6242
+ }
6243
+ } catch (e) {
6244
+ console.warn("[agentlife] deleteAgent: failed to delete state dir:", e?.message);
6245
+ }
6246
+ }
6247
+ const defaultWorkspace = path11.join(openclawHome, `workspace-${id}`);
6248
+ const workspaceToDelete = typeof existingEntry?.workspace === "string" && existingEntry.workspace.trim() ? path11.resolve(existingEntry.workspace) : defaultWorkspace;
6249
+ if (workspaceToDelete.startsWith(openclawHome + path11.sep) && workspaceToDelete !== openclawHome) {
6250
+ try {
6251
+ if (fs8.existsSync(workspaceToDelete)) {
6252
+ fs8.rmSync(workspaceToDelete, { recursive: true, force: true });
6253
+ cleanup.workspaceDeleted = true;
6254
+ }
6255
+ } catch (e) {
6256
+ console.warn("[agentlife] deleteAgent: failed to delete workspace dir:", e?.message);
6257
+ }
6258
+ }
6256
6259
  if (removedFromConfig) {
6257
6260
  const updatedCfg = {
6258
6261
  ...cfg,
@@ -6267,7 +6270,7 @@ function registerAgentGateway(api, state2) {
6267
6270
  if (removedFromRegistry) {
6268
6271
  await saveRegistryToDisk(state2);
6269
6272
  }
6270
- console.log("[agentlife] deleted agent: %s (config=%s, registry=%s, sessions=%d, cron=%d, historyRows=%d, agentDb=%s)", id, removedFromConfig, removedFromRegistry, cleanup.sessions, cleanup.cronJobs, cleanup.historyRows, cleanup.agentDbDeleted);
6273
+ console.log("[agentlife] deleted agent: %s (config=%s, registry=%s, sessions=%d, cron=%d, historyRows=%d, agentDb=%s, state=%s, workspace=%s)", id, removedFromConfig, removedFromRegistry, cleanup.sessions, cleanup.cronJobs, cleanup.historyRows, cleanup.agentDbDeleted, cleanup.stateDeleted, cleanup.workspaceDeleted);
6271
6274
  respond(true, { status: "deleted", id, removedFromConfig, removedFromRegistry, cleanup });
6272
6275
  });
6273
6276
  api.registerGatewayMethod("agentlife.agents", ({ respond }) => {
@@ -6726,6 +6729,7 @@ function registerSurfacesGateway(api, state2) {
6726
6729
  } catch {}
6727
6730
  guidedDismissSent.delete(surfaceId);
6728
6731
  state2.surfaceDb.delete(surfaceId);
6732
+ purgeSurfaceHistory(state2, surfaceId);
6729
6733
  broadcastDelete(surfaceId);
6730
6734
  try {
6731
6735
  enqueueCleanupTasks(state2, surfaceId, agentId, cronId, automations);
@@ -6887,10 +6891,10 @@ function registerAutomationsGateway(api, state2) {
6887
6891
  // gateway/admin.ts
6888
6892
  import * as fs9 from "node:fs/promises";
6889
6893
  import * as fsSync4 from "node:fs";
6890
- import * as os7 from "node:os";
6894
+ import * as os8 from "node:os";
6891
6895
  import * as path12 from "node:path";
6892
6896
  function pluginConfigPath() {
6893
- return path12.join(os7.homedir(), ".openclaw", "agentlife", "plugin-config.json");
6897
+ return path12.join(os8.homedir(), ".openclaw", "agentlife", "plugin-config.json");
6894
6898
  }
6895
6899
  function readPluginConfig() {
6896
6900
  try {
@@ -6999,7 +7003,7 @@ function registerAdminGateway(api, state2) {
6999
7003
  api.registerGatewayMethod("agentlife.uninstall", async ({ respond }) => {
7000
7004
  try {
7001
7005
  const cleaned = [];
7002
- const baseDir = state2.agentlifeStateDir ?? path12.join(os7.homedir(), ".openclaw", "agentlife");
7006
+ const baseDir = state2.agentlifeStateDir ?? path12.join(os8.homedir(), ".openclaw", "agentlife");
7003
7007
  const identity = loadDeviceIdentity();
7004
7008
  if (!identity) {
7005
7009
  cleaned.push("server deprovision skipped (no device identity)");
@@ -7105,7 +7109,7 @@ function registerAdminGateway(api, state2) {
7105
7109
  for (const agent of PROVISIONED_AGENTS) {
7106
7110
  if (agent.existingAgent)
7107
7111
  continue;
7108
- const wsDir = agent.workspaceDir ?? path12.join(os7.homedir(), ".openclaw", `workspace-${agent.id}`);
7112
+ const wsDir = agent.workspaceDir ?? path12.join(os8.homedir(), ".openclaw", `workspace-${agent.id}`);
7109
7113
  try {
7110
7114
  await fs9.rm(wsDir, { recursive: true, force: true });
7111
7115
  cleaned.push(`deleted workspace ${agent.id}`);
@@ -7366,10 +7370,10 @@ import {
7366
7370
 
7367
7371
  // gateway/config-utils.ts
7368
7372
  import * as fs10 from "node:fs";
7369
- import * as os8 from "node:os";
7373
+ import * as os9 from "node:os";
7370
7374
  import * as path13 from "node:path";
7371
7375
  function configPath() {
7372
- return path13.join(os8.homedir(), ".openclaw", "openclaw.json");
7376
+ return path13.join(os9.homedir(), ".openclaw", "openclaw.json");
7373
7377
  }
7374
7378
  function readConfig() {
7375
7379
  try {
@@ -7385,10 +7389,10 @@ function writeConfig(cfg) {
7385
7389
 
7386
7390
  // gateway/providers.ts
7387
7391
  import * as fs11 from "node:fs";
7388
- import * as os9 from "node:os";
7392
+ import * as os10 from "node:os";
7389
7393
  import * as path14 from "node:path";
7390
7394
  function siblingAgentDirs() {
7391
- const root = path14.join(os9.homedir(), ".openclaw", "agents");
7395
+ const root = path14.join(os10.homedir(), ".openclaw", "agents");
7392
7396
  let entries;
7393
7397
  try {
7394
7398
  entries = fs11.readdirSync(root, { withFileTypes: true });
@@ -7473,8 +7477,8 @@ function readOAuthExpiries() {
7473
7477
  const result = new Map;
7474
7478
  const fs12 = __require("node:fs");
7475
7479
  const path15 = __require("node:path");
7476
- const os10 = __require("node:os");
7477
- const agentsDir = path15.join(os10.homedir(), ".openclaw", "agents");
7480
+ const os11 = __require("node:os");
7481
+ const agentsDir = path15.join(os11.homedir(), ".openclaw", "agents");
7478
7482
  let entries;
7479
7483
  try {
7480
7484
  entries = fs12.readdirSync(agentsDir);
@@ -7766,10 +7770,10 @@ ${result?.stderr ?? ""}`;
7766
7770
 
7767
7771
  // gateway/models-config.ts
7768
7772
  import * as fs12 from "node:fs";
7769
- import * as os10 from "node:os";
7773
+ import * as os11 from "node:os";
7770
7774
  import * as path15 from "node:path";
7771
7775
  function pluginConfigPath2() {
7772
- return path15.join(os10.homedir(), ".openclaw", "agentlife", "plugin-config.json");
7776
+ return path15.join(os11.homedir(), ".openclaw", "agentlife", "plugin-config.json");
7773
7777
  }
7774
7778
  function readPluginConfig2() {
7775
7779
  try {
@@ -7926,7 +7930,7 @@ var stopQualityCheckPoller = null;
7926
7930
  var stopCloudflared = null;
7927
7931
  function resolveInternalModel(api) {
7928
7932
  try {
7929
- const pluginCfgPath = path16.join(homedir12(), ".openclaw", "agentlife", "plugin-config.json");
7933
+ const pluginCfgPath = path16.join(homedir13(), ".openclaw", "agentlife", "plugin-config.json");
7930
7934
  try {
7931
7935
  const raw = __require("node:fs").readFileSync(pluginCfgPath, "utf-8");
7932
7936
  const pluginCfg = JSON.parse(raw);
@@ -7962,7 +7966,7 @@ function register(api) {
7962
7966
  return;
7963
7967
  }
7964
7968
  registered = true;
7965
- const fallbackDir = path16.join(homedir12(), ".openclaw", "agentlife");
7969
+ const fallbackDir = path16.join(homedir13(), ".openclaw", "agentlife");
7966
7970
  const state2 = {
7967
7971
  surfaceDb: null,
7968
7972
  agentRegistry: new Map,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentlife",
3
- "version": "2.4.4",
3
+ "version": "2.4.6",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "bun build index.ts --outfile dist/index.js --target node --external openclaw/plugin-sdk",