agentlife 1.1.4 → 1.1.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.
- package/dist/index.js +261 -143
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1261,6 +1261,52 @@ Your task is to create vision widgets, NOT to create or modify agents. The messa
|
|
|
1261
1261
|
|
|
1262
1262
|
Do NOT call agentlife.createAgent. Do NOT write workspace files. Do NOT spawn sub-sessions. Do NOT ask interactive questions. The user is not present — this runs at install time. Just read, build, push.
|
|
1263
1263
|
|
|
1264
|
+
## Handling Vision Dismiss
|
|
1265
|
+
|
|
1266
|
+
When you receive \`[system:dismiss-requested] surfaceId=vision-{slug}\` — this is vision-specific dismiss handling. It is a third mode, distinct from agent creation and from dashboard bootstrap, and it OVERRIDES the general dismiss-alt protocol described elsewhere.
|
|
1267
|
+
|
|
1268
|
+
The general dismiss-alt rule says "craft 2-3 contextual alternatives from the widget's goal." For vision posters that rule does NOT apply. Vision posters are aspirational content with a closed ontology of failure modes — the user is rejecting either the dream itself, its framing, its timing, or its dashboard placement. Improvising alternatives per dismiss would produce inconsistent wording across dismissals, which reads as broken UX. Use the fixed wording below verbatim.
|
|
1269
|
+
|
|
1270
|
+
### Step 1 — Push the dismiss-alt input surface
|
|
1271
|
+
|
|
1272
|
+
Push exactly this surface (substitute \`{slug}\` with the dismissed vision's slug). Wording is fixed: do not paraphrase, translate, or reorder the buttons. The user's language detection happens at synthesis time, not at dismiss time.
|
|
1273
|
+
|
|
1274
|
+
\`\`\`
|
|
1275
|
+
surface dismiss-alt-vision-{slug} input
|
|
1276
|
+
card
|
|
1277
|
+
column
|
|
1278
|
+
text "Before I remove this:" h4
|
|
1279
|
+
button "This isn't my dream anymore" action=choice
|
|
1280
|
+
button "Wrong framing" action=choice
|
|
1281
|
+
button "Already achieved" action=choice
|
|
1282
|
+
button "Don't show on dashboard" action=choice
|
|
1283
|
+
button "Remove it" action=choice
|
|
1284
|
+
goal: Capture vision dismiss reason for vision-{slug}
|
|
1285
|
+
followup: +1m "User did not respond to vision dismiss alternatives. Say done."
|
|
1286
|
+
\`\`\`
|
|
1287
|
+
|
|
1288
|
+
### Step 2 — On the user's choice, persist the reason
|
|
1289
|
+
|
|
1290
|
+
When you receive \`[action:choice]\` on the dismiss-alt-vision-{slug} surface, append one line to your own workspace file at \`~/.openclaw/workspace-agentlife-builder/memory/vision-feedback.md\` (create the file and parent directory if missing):
|
|
1291
|
+
|
|
1292
|
+
\`\`\`
|
|
1293
|
+
- {ISO timestamp} vision-{slug}: {chosen reason verbatim}
|
|
1294
|
+
\`\`\`
|
|
1295
|
+
|
|
1296
|
+
Use exec for the append: \`mkdir -p ~/.openclaw/workspace-agentlife-builder/memory && echo "..." >> ~/.openclaw/workspace-agentlife-builder/memory/vision-feedback.md\`. This file is read by the next dashboard bootstrap as negative constraints under "## Previously rejected dreams" — your own future self will use it.
|
|
1297
|
+
|
|
1298
|
+
### Step 3 — Let the dismiss complete
|
|
1299
|
+
|
|
1300
|
+
Do NOT push a confirmation widget. Do NOT push \`delete vision-{slug}\`. The widget disappearing is the confirmation. The platform completes the dismiss after you process the action; you only need to record the reason.
|
|
1301
|
+
|
|
1302
|
+
If you receive \`[event:dismissed] surfaceId=vision-{slug}\` afterwards, the surface is already gone — respond "done" and stop.
|
|
1303
|
+
|
|
1304
|
+
### What this is NOT
|
|
1305
|
+
|
|
1306
|
+
- Not a domain widget dismiss (those use the general contextual dismiss-alt rules).
|
|
1307
|
+
- Not an opportunity to generate a replacement vision (the bootstrap path handles regeneration).
|
|
1308
|
+
- Not a place to ask follow-up questions or push additional surfaces beyond the fixed dismiss-alt above.
|
|
1309
|
+
|
|
1264
1310
|
## Deleting an Agent
|
|
1265
1311
|
|
|
1266
1312
|
1. agentlife.deleteAgent: {"id": "{agentId}"}
|
|
@@ -1394,9 +1440,7 @@ var PROVISIONED_AGENTS = [
|
|
|
1394
1440
|
id: "agentlife-builder",
|
|
1395
1441
|
name: "AgentLife Builder",
|
|
1396
1442
|
agentsMd: BUILDER_AGENTS_MD,
|
|
1397
|
-
tools: {
|
|
1398
|
-
allow: ["agentlife_push"]
|
|
1399
|
-
}
|
|
1443
|
+
tools: { profile: "full" }
|
|
1400
1444
|
},
|
|
1401
1445
|
{
|
|
1402
1446
|
id: "supervisor",
|
|
@@ -1463,12 +1507,27 @@ function scheduleEnrichment(state, runtime, targets, log) {
|
|
|
1463
1507
|
async function readAgentMemory(workspace) {
|
|
1464
1508
|
if (!workspace)
|
|
1465
1509
|
return "";
|
|
1466
|
-
const memoryDir = path3.join(workspace, "memory");
|
|
1467
1510
|
const parts = [];
|
|
1511
|
+
const rootFiles = [
|
|
1512
|
+
["SOUL.md", 600],
|
|
1513
|
+
["AGENTS.md", 1200],
|
|
1514
|
+
["USER.md", 1200]
|
|
1515
|
+
];
|
|
1516
|
+
for (const [name, cap] of rootFiles) {
|
|
1517
|
+
try {
|
|
1518
|
+
const content = await fs2.readFile(path3.join(workspace, name), "utf-8");
|
|
1519
|
+
const trimmed = content.trim();
|
|
1520
|
+
if (trimmed)
|
|
1521
|
+
parts.push(`### ${name}
|
|
1522
|
+
${trimmed.slice(0, cap)}`);
|
|
1523
|
+
} catch {}
|
|
1524
|
+
}
|
|
1525
|
+
const memoryDir = path3.join(workspace, "memory");
|
|
1468
1526
|
try {
|
|
1469
1527
|
const index = await fs2.readFile(path3.join(memoryDir, "MEMORY.md"), "utf-8");
|
|
1470
1528
|
if (index.trim())
|
|
1471
|
-
parts.push(
|
|
1529
|
+
parts.push(`### memory/MEMORY.md
|
|
1530
|
+
${index.trim()}`);
|
|
1472
1531
|
} catch {}
|
|
1473
1532
|
try {
|
|
1474
1533
|
const files = await fs2.readdir(memoryDir);
|
|
@@ -1477,7 +1536,7 @@ async function readAgentMemory(workspace) {
|
|
|
1477
1536
|
try {
|
|
1478
1537
|
const content = await fs2.readFile(path3.join(memoryDir, file), "utf-8");
|
|
1479
1538
|
if (content.trim())
|
|
1480
|
-
parts.push(`###
|
|
1539
|
+
parts.push(`### memory/${file}
|
|
1481
1540
|
${content.trim().slice(0, 600)}`);
|
|
1482
1541
|
} catch {}
|
|
1483
1542
|
}
|
|
@@ -1513,6 +1572,174 @@ ${capped}`);
|
|
|
1513
1572
|
|
|
1514
1573
|
`) };
|
|
1515
1574
|
}
|
|
1575
|
+
var VISION_MIN_MEMORY_CHARS = 200;
|
|
1576
|
+
async function ensureVisionPosters(state, runtime, log, options = {}) {
|
|
1577
|
+
if (!state.surfaceDb) {
|
|
1578
|
+
log("[agentlife] ensureVisionPosters: skipped — surfaceDb not initialized");
|
|
1579
|
+
return { status: "skipped", reason: "no_surface_db" };
|
|
1580
|
+
}
|
|
1581
|
+
let visionCount = 0;
|
|
1582
|
+
for (const surfaceId of state.surfaceDb.keys()) {
|
|
1583
|
+
if (surfaceId.startsWith("vision-"))
|
|
1584
|
+
visionCount++;
|
|
1585
|
+
}
|
|
1586
|
+
if (visionCount > 0) {
|
|
1587
|
+
log(`[agentlife] ensureVisionPosters: already_exists — ${visionCount} vision surface(s) in surfaceDb`);
|
|
1588
|
+
return { status: "already_exists", count: visionCount };
|
|
1589
|
+
}
|
|
1590
|
+
const SKIP_IDS = new Set(PROVISIONED_AGENTS.filter((a) => !a.existingAgent).map((a) => a.id));
|
|
1591
|
+
const finalList = runtime.config.loadConfig().agents?.list ?? [];
|
|
1592
|
+
const specialistCount = [...state.agentRegistry.keys()].filter((id) => !SKIP_IDS.has(id)).length;
|
|
1593
|
+
if (specialistCount === 0) {
|
|
1594
|
+
log("[agentlife] ensureVisionPosters: skipped — specialistCount=0 (no user agents registered)");
|
|
1595
|
+
return { status: "skipped", reason: "no_specialists" };
|
|
1596
|
+
}
|
|
1597
|
+
if (!state.runCommand) {
|
|
1598
|
+
log("[agentlife] ensureVisionPosters: skipped — runCommand not available on runtime");
|
|
1599
|
+
return { status: "skipped", reason: "no_run_command" };
|
|
1600
|
+
}
|
|
1601
|
+
const { totalChars, sections } = await gatherAllAgentMemory(state, finalList, SKIP_IDS);
|
|
1602
|
+
if (totalChars < VISION_MIN_MEMORY_CHARS) {
|
|
1603
|
+
log(`[agentlife] ensureVisionPosters: skipped — totalChars=${totalChars} below threshold ${VISION_MIN_MEMORY_CHARS} (memory too thin)`);
|
|
1604
|
+
return { status: "skipped", reason: "thin_memory", details: `totalChars=${totalChars}` };
|
|
1605
|
+
}
|
|
1606
|
+
const homeDir = options.homedirOverride ?? os.homedir();
|
|
1607
|
+
const builderWorkspace = path3.join(homeDir, ".openclaw", "workspace-agentlife-builder");
|
|
1608
|
+
const feedbackFile = path3.join(builderWorkspace, "memory", "vision-feedback.md");
|
|
1609
|
+
let rejectedDreams = "";
|
|
1610
|
+
try {
|
|
1611
|
+
const content = await fs2.readFile(feedbackFile, "utf-8");
|
|
1612
|
+
if (content.trim())
|
|
1613
|
+
rejectedDreams = content.trim().slice(0, 4000);
|
|
1614
|
+
} catch {}
|
|
1615
|
+
const builderKey = buildAgentSessionKey("agentlife-builder");
|
|
1616
|
+
const bootstrapMsg = [
|
|
1617
|
+
`[system:dashboard-bootstrap] The dashboard is empty. Push 3-4 vision posters with agentlife_push.`,
|
|
1618
|
+
``,
|
|
1619
|
+
`## What you do`,
|
|
1620
|
+
``,
|
|
1621
|
+
`Pick 2 to 4 dreams from the user data below — quality over quantity. For each, copy one of the 3 DSL templates exactly and replace the placeholders with the user's own words. Push via agentlife_push.`,
|
|
1622
|
+
``,
|
|
1623
|
+
`## Picking the dreams`,
|
|
1624
|
+
``,
|
|
1625
|
+
`Find aspirational signal — moments where the user names a future they want, a target they're chasing, a transformation they're working toward. Operational logging is not signal.`,
|
|
1626
|
+
``,
|
|
1627
|
+
`Each poster covers a DIFFERENT life area. Body, money, place, work, relationships, identity, craft are distinct. If two candidates share a product, person, or domain name, they're the same area — pick one.`,
|
|
1628
|
+
``,
|
|
1629
|
+
`Pick the dream version of each goal — the one-to-three-year outcome if everything goes right, not the next checkpoint. The first real revenue is a checkpoint; the first profitable year is a dream.`,
|
|
1630
|
+
``,
|
|
1631
|
+
`## Quality bar`,
|
|
1632
|
+
``,
|
|
1633
|
+
`It is better to push 2 strong posters than 4 mixed-quality ones. If you cannot find a real, named, repeated dream for a domain, SKIP that domain. Never invent. Never fill a slot with weather data, agent infrastructure, or generic placeholder language. If a poster's content names tools/agents instead of outcomes, it is wrong — drop it.`,
|
|
1634
|
+
``,
|
|
1635
|
+
`## Language consistency`,
|
|
1636
|
+
``,
|
|
1637
|
+
`Detect the primary language the user writes in (look at the data: Spanish, English, etc.). Write ALL posters in that single language. Never mix languages across the carousel.`,
|
|
1638
|
+
``,
|
|
1639
|
+
`## The 3 templates`,
|
|
1640
|
+
``,
|
|
1641
|
+
`Every template renders emoji + headline + support + a bottom element. The bottom element changes per template.`,
|
|
1642
|
+
``,
|
|
1643
|
+
`### TEMPLATE A — Statement (best for identity, body, declarative truths)`,
|
|
1644
|
+
``,
|
|
1645
|
+
`surface vision-{slug} size=l`,
|
|
1646
|
+
` card`,
|
|
1647
|
+
` column align=center`,
|
|
1648
|
+
` text "{EMOJI}" h1`,
|
|
1649
|
+
` text "{HEADLINE}" h1 color={ACCENT}`,
|
|
1650
|
+
` text "{SUPPORT}" body`,
|
|
1651
|
+
` text "{MOMENT}" h4 color={ACCENT}`,
|
|
1652
|
+
`goal: Vision poster — {slug}`,
|
|
1653
|
+
`followup: +30d "Refresh this vision poster if the user's data has shifted."`,
|
|
1654
|
+
``,
|
|
1655
|
+
`### TEMPLATE B — Destination (best for place, move, new chapter)`,
|
|
1656
|
+
``,
|
|
1657
|
+
`surface vision-{slug} size=l`,
|
|
1658
|
+
` card`,
|
|
1659
|
+
` column align=center`,
|
|
1660
|
+
` text "{EMOJI}" h1`,
|
|
1661
|
+
` text "{HEADLINE}" h1 color={ACCENT}`,
|
|
1662
|
+
` text "{SUPPORT}" body`,
|
|
1663
|
+
` badge "{PLACE}" color={ACCENT} outlined`,
|
|
1664
|
+
`goal: Vision poster — {slug}`,
|
|
1665
|
+
`followup: +30d "Refresh this vision poster if the user's data has shifted."`,
|
|
1666
|
+
``,
|
|
1667
|
+
`### TEMPLATE C — Outcome (best for work, money, craft)`,
|
|
1668
|
+
``,
|
|
1669
|
+
`surface vision-{slug} size=l`,
|
|
1670
|
+
` card`,
|
|
1671
|
+
` column align=center`,
|
|
1672
|
+
` text "{EMOJI}" h1`,
|
|
1673
|
+
` text "{HEADLINE}" h1 color={ACCENT}`,
|
|
1674
|
+
` text "{SUPPORT}" body`,
|
|
1675
|
+
` row distribute=spaceEvenly`,
|
|
1676
|
+
` text "{C1}" h4 color={ACCENT}`,
|
|
1677
|
+
` text "·" h4`,
|
|
1678
|
+
` text "{C2}" h4 color={ACCENT}`,
|
|
1679
|
+
` text "·" h4`,
|
|
1680
|
+
` text "{C3}" h4 color={ACCENT}`,
|
|
1681
|
+
`goal: Vision poster — {slug}`,
|
|
1682
|
+
`followup: +30d "Refresh this vision poster if the user's data has shifted."`,
|
|
1683
|
+
``,
|
|
1684
|
+
`## Placeholder rules`,
|
|
1685
|
+
``,
|
|
1686
|
+
`- {slug}: short kebab-case slug for the life area`,
|
|
1687
|
+
`- {ACCENT}: hex color. Vary across posters: try #10B981, #7C3AED, #3B82F6, #F59E0B, #EC4899, #14B8A6`,
|
|
1688
|
+
`- {EMOJI}: a single emoji capturing the emotional core. Visceral, not literal.`,
|
|
1689
|
+
`- {HEADLINE}: 3-5 words, declarative, first-person if natural, periods welcome. The dream version, not the next checkpoint.`,
|
|
1690
|
+
`- {SUPPORT}: 6-12 words, one short sentence. Concrete supporting context in the user's actual language.`,
|
|
1691
|
+
`- {MOMENT} (Template A): 1-3 words, a time anchor — season, year, phase`,
|
|
1692
|
+
`- {PLACE} (Template B): 1-3 words, must be a real geographic place name (not a date)`,
|
|
1693
|
+
`- {C1}, {C2}, {C3} (Template C): 1-2 words each, concept beats from the user's vocabulary`,
|
|
1694
|
+
``,
|
|
1695
|
+
`## Variety rule`,
|
|
1696
|
+
``,
|
|
1697
|
+
`Across the 3-4 posters, use AT LEAST 2 different templates.`,
|
|
1698
|
+
``,
|
|
1699
|
+
`## Hard constraints`,
|
|
1700
|
+
``,
|
|
1701
|
+
`- Copy ONE of the 3 templates above exactly. Do not add components. Do not remove components. Do not change order.`,
|
|
1702
|
+
`- Use the user's actual language from the data — never invent.`,
|
|
1703
|
+
``,
|
|
1704
|
+
...rejectedDreams ? [
|
|
1705
|
+
`## Previously rejected dreams`,
|
|
1706
|
+
``,
|
|
1707
|
+
`The user has dismissed these past vision posters with the following reasons. Do NOT re-pick these dreams or reframe them with the same emphasis — the user already told you they were wrong.`,
|
|
1708
|
+
``,
|
|
1709
|
+
rejectedDreams,
|
|
1710
|
+
``
|
|
1711
|
+
] : [],
|
|
1712
|
+
`## USER DATA`,
|
|
1713
|
+
``,
|
|
1714
|
+
sections.slice(0, 12000),
|
|
1715
|
+
``,
|
|
1716
|
+
`## Output`,
|
|
1717
|
+
``,
|
|
1718
|
+
`Push each poster with agentlife_push. After the last push, respond "Done".`
|
|
1719
|
+
].join(`
|
|
1720
|
+
`);
|
|
1721
|
+
const params = JSON.stringify({
|
|
1722
|
+
sessionKey: builderKey,
|
|
1723
|
+
message: bootstrapMsg,
|
|
1724
|
+
idempotencyKey: `dashboard-bootstrap-${Date.now()}`
|
|
1725
|
+
});
|
|
1726
|
+
const delayMs = options.delayMs ?? 0;
|
|
1727
|
+
const feedbackChars = rejectedDreams.length;
|
|
1728
|
+
const runBootstrap = () => {
|
|
1729
|
+
log(`[agentlife] ensureVisionPosters: synthesizing (${totalChars} chars memory across ${specialistCount} specialists${feedbackChars > 0 ? `, ${feedbackChars} chars prior feedback` : ""})`);
|
|
1730
|
+
state.runCommand(["openclaw", "gateway", "call", "chat.send", "--params", params], { timeoutMs: 300000 }).then(() => {
|
|
1731
|
+
log("[agentlife] ensureVisionPosters: dashboard bootstrap complete");
|
|
1732
|
+
}).catch((e) => {
|
|
1733
|
+
log(`[agentlife] ensureVisionPosters: dashboard bootstrap failed: ${e?.message}`);
|
|
1734
|
+
});
|
|
1735
|
+
};
|
|
1736
|
+
if (delayMs > 0) {
|
|
1737
|
+
setTimeout(runBootstrap, delayMs);
|
|
1738
|
+
} else {
|
|
1739
|
+
runBootstrap();
|
|
1740
|
+
}
|
|
1741
|
+
return { status: "synthesizing", specialistCount, totalChars, feedbackChars };
|
|
1742
|
+
}
|
|
1516
1743
|
async function provisionAgents(state, cfg, runtime, log) {
|
|
1517
1744
|
const home = os.homedir();
|
|
1518
1745
|
const currentList = [...cfg.agents?.list ?? []];
|
|
@@ -1668,134 +1895,7 @@ async function provisionAgents(state, cfg, runtime, log) {
|
|
|
1668
1895
|
}, 1e4);
|
|
1669
1896
|
}
|
|
1670
1897
|
}
|
|
1671
|
-
|
|
1672
|
-
let hasVisionPosters = false;
|
|
1673
|
-
if (state.surfaceDb) {
|
|
1674
|
-
for (const surfaceId of state.surfaceDb.keys()) {
|
|
1675
|
-
if (surfaceId.startsWith("vision-")) {
|
|
1676
|
-
hasVisionPosters = true;
|
|
1677
|
-
break;
|
|
1678
|
-
}
|
|
1679
|
-
}
|
|
1680
|
-
}
|
|
1681
|
-
if (!hasVisionPosters && specialistCount > 0 && state.runCommand) {
|
|
1682
|
-
const { totalChars, sections } = await gatherAllAgentMemory(state, finalList, SKIP_IDS);
|
|
1683
|
-
if (totalChars < 200) {
|
|
1684
|
-
log("[agentlife] skipping dashboard bootstrap — not enough memory data yet");
|
|
1685
|
-
} else {
|
|
1686
|
-
const builderKey = buildAgentSessionKey("agentlife-builder");
|
|
1687
|
-
const bootstrapMsg = [
|
|
1688
|
-
`[system:dashboard-bootstrap] The dashboard is empty. Push 3-4 vision posters with agentlife_push.`,
|
|
1689
|
-
``,
|
|
1690
|
-
`## What you do`,
|
|
1691
|
-
``,
|
|
1692
|
-
`Pick 2 to 4 dreams from the user data below — quality over quantity. For each, copy one of the 3 DSL templates exactly and replace the placeholders with the user's own words. Push via agentlife_push.`,
|
|
1693
|
-
``,
|
|
1694
|
-
`## Picking the dreams`,
|
|
1695
|
-
``,
|
|
1696
|
-
`Find aspirational signal — moments where the user names a future they want, a target they're chasing, a transformation they're working toward. Operational logging is not signal.`,
|
|
1697
|
-
``,
|
|
1698
|
-
`Each poster covers a DIFFERENT life area. Body, money, place, work, relationships, identity, craft are distinct. If two candidates share a product, person, or domain name, they're the same area — pick one.`,
|
|
1699
|
-
``,
|
|
1700
|
-
`Pick the dream version of each goal — the one-to-three-year outcome if everything goes right, not the next checkpoint. The first real revenue is a checkpoint; the first profitable year is a dream.`,
|
|
1701
|
-
``,
|
|
1702
|
-
`## Quality bar`,
|
|
1703
|
-
``,
|
|
1704
|
-
`It is better to push 2 strong posters than 4 mixed-quality ones. If you cannot find a real, named, repeated dream for a domain, SKIP that domain. Never invent. Never fill a slot with weather data, agent infrastructure, or generic placeholder language. If a poster's content names tools/agents instead of outcomes, it is wrong — drop it.`,
|
|
1705
|
-
``,
|
|
1706
|
-
`## Language consistency`,
|
|
1707
|
-
``,
|
|
1708
|
-
`Detect the primary language the user writes in (look at the data: Spanish, English, etc.). Write ALL posters in that single language. Never mix languages across the carousel.`,
|
|
1709
|
-
``,
|
|
1710
|
-
`## The 3 templates`,
|
|
1711
|
-
``,
|
|
1712
|
-
`Every template renders emoji + headline + support + a bottom element. The bottom element changes per template.`,
|
|
1713
|
-
``,
|
|
1714
|
-
`### TEMPLATE A — Statement (best for identity, body, declarative truths)`,
|
|
1715
|
-
``,
|
|
1716
|
-
`surface vision-{slug} size=l`,
|
|
1717
|
-
` card`,
|
|
1718
|
-
` column align=center`,
|
|
1719
|
-
` text "{EMOJI}" h1`,
|
|
1720
|
-
` text "{HEADLINE}" h1 color={ACCENT}`,
|
|
1721
|
-
` text "{SUPPORT}" body`,
|
|
1722
|
-
` text "{MOMENT}" h4 color={ACCENT}`,
|
|
1723
|
-
`goal: Vision poster — {slug}`,
|
|
1724
|
-
`followup: +30d "Refresh this vision poster if the user's data has shifted."`,
|
|
1725
|
-
``,
|
|
1726
|
-
`### TEMPLATE B — Destination (best for place, move, new chapter)`,
|
|
1727
|
-
``,
|
|
1728
|
-
`surface vision-{slug} size=l`,
|
|
1729
|
-
` card`,
|
|
1730
|
-
` column align=center`,
|
|
1731
|
-
` text "{EMOJI}" h1`,
|
|
1732
|
-
` text "{HEADLINE}" h1 color={ACCENT}`,
|
|
1733
|
-
` text "{SUPPORT}" body`,
|
|
1734
|
-
` badge "{PLACE}" color={ACCENT} outlined`,
|
|
1735
|
-
`goal: Vision poster — {slug}`,
|
|
1736
|
-
`followup: +30d "Refresh this vision poster if the user's data has shifted."`,
|
|
1737
|
-
``,
|
|
1738
|
-
`### TEMPLATE C — Outcome (best for work, money, craft)`,
|
|
1739
|
-
``,
|
|
1740
|
-
`surface vision-{slug} size=l`,
|
|
1741
|
-
` card`,
|
|
1742
|
-
` column align=center`,
|
|
1743
|
-
` text "{EMOJI}" h1`,
|
|
1744
|
-
` text "{HEADLINE}" h1 color={ACCENT}`,
|
|
1745
|
-
` text "{SUPPORT}" body`,
|
|
1746
|
-
` row distribute=spaceEvenly`,
|
|
1747
|
-
` text "{C1}" h4 color={ACCENT}`,
|
|
1748
|
-
` text "·" h4`,
|
|
1749
|
-
` text "{C2}" h4 color={ACCENT}`,
|
|
1750
|
-
` text "·" h4`,
|
|
1751
|
-
` text "{C3}" h4 color={ACCENT}`,
|
|
1752
|
-
`goal: Vision poster — {slug}`,
|
|
1753
|
-
`followup: +30d "Refresh this vision poster if the user's data has shifted."`,
|
|
1754
|
-
``,
|
|
1755
|
-
`## Placeholder rules`,
|
|
1756
|
-
``,
|
|
1757
|
-
`- {slug}: short kebab-case slug for the life area`,
|
|
1758
|
-
`- {ACCENT}: hex color. Vary across posters: try #10B981, #7C3AED, #3B82F6, #F59E0B, #EC4899, #14B8A6`,
|
|
1759
|
-
`- {EMOJI}: a single emoji capturing the emotional core. Visceral, not literal.`,
|
|
1760
|
-
`- {HEADLINE}: 3-5 words, declarative, first-person if natural, periods welcome. The dream version, not the next checkpoint.`,
|
|
1761
|
-
`- {SUPPORT}: 6-12 words, one short sentence. Concrete supporting context in the user's actual language.`,
|
|
1762
|
-
`- {MOMENT} (Template A): 1-3 words, a time anchor — season, year, phase`,
|
|
1763
|
-
`- {PLACE} (Template B): 1-3 words, must be a real geographic place name (not a date)`,
|
|
1764
|
-
`- {C1}, {C2}, {C3} (Template C): 1-2 words each, concept beats from the user's vocabulary`,
|
|
1765
|
-
``,
|
|
1766
|
-
`## Variety rule`,
|
|
1767
|
-
``,
|
|
1768
|
-
`Across the 3-4 posters, use AT LEAST 2 different templates.`,
|
|
1769
|
-
``,
|
|
1770
|
-
`## Hard constraints`,
|
|
1771
|
-
``,
|
|
1772
|
-
`- Copy ONE of the 3 templates above exactly. Do not add components. Do not remove components. Do not change order.`,
|
|
1773
|
-
`- Use the user's actual language from the data — never invent.`,
|
|
1774
|
-
``,
|
|
1775
|
-
`## USER DATA`,
|
|
1776
|
-
``,
|
|
1777
|
-
sections.slice(0, 12000),
|
|
1778
|
-
``,
|
|
1779
|
-
`## Output`,
|
|
1780
|
-
``,
|
|
1781
|
-
`Push each poster with agentlife_push. After the last push, respond "Done".`
|
|
1782
|
-
].join(`
|
|
1783
|
-
`);
|
|
1784
|
-
const params = JSON.stringify({
|
|
1785
|
-
sessionKey: builderKey,
|
|
1786
|
-
message: bootstrapMsg,
|
|
1787
|
-
idempotencyKey: `dashboard-bootstrap-${Date.now()}`
|
|
1788
|
-
});
|
|
1789
|
-
setTimeout(() => {
|
|
1790
|
-
log(`[agentlife] bootstrapping empty dashboard via builder (${totalChars} chars of memory across ${specialistCount} specialists)`);
|
|
1791
|
-
state.runCommand(["openclaw", "gateway", "call", "chat.send", "--params", params], { timeoutMs: 300000 }).then(() => {
|
|
1792
|
-
log("[agentlife] dashboard bootstrap complete");
|
|
1793
|
-
}).catch((e) => {
|
|
1794
|
-
log(`[agentlife] dashboard bootstrap failed: ${e?.message}`);
|
|
1795
|
-
});
|
|
1796
|
-
}, 15000);
|
|
1797
|
-
}
|
|
1798
|
-
}
|
|
1898
|
+
await ensureVisionPosters(state, runtime, log, { delayMs: 15000 });
|
|
1799
1899
|
log("[agentlife] agent provisioning complete");
|
|
1800
1900
|
}
|
|
1801
1901
|
|
|
@@ -4852,14 +4952,13 @@ function mintBootstrapToken() {
|
|
|
4852
4952
|
function registerWebApp(api) {
|
|
4853
4953
|
const pluginRoot = path9.resolve(path9.dirname(api.source), "..");
|
|
4854
4954
|
const appRoot = path9.join(pluginRoot, "web-build");
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
if (!
|
|
4861
|
-
api.logger.warn("[agentlife] web-build/index.html missing — /agentlife/
|
|
4862
|
-
return;
|
|
4955
|
+
const hasWebBuild = fs7.existsSync(appRoot);
|
|
4956
|
+
const indexPath = hasWebBuild ? path9.join(appRoot, "index.html") : null;
|
|
4957
|
+
const hasIndex = !!(indexPath && fs7.existsSync(indexPath));
|
|
4958
|
+
if (!hasWebBuild) {
|
|
4959
|
+
api.logger.info("[agentlife] web-build/ not found — /agentlife/pair will still register; static dashboard disabled");
|
|
4960
|
+
} else if (!hasIndex) {
|
|
4961
|
+
api.logger.warn("[agentlife] web-build/index.html missing — /agentlife/pair will still register; static dashboard disabled");
|
|
4863
4962
|
}
|
|
4864
4963
|
let gatewayToken = "";
|
|
4865
4964
|
try {
|
|
@@ -4971,6 +5070,14 @@ function registerWebApp(api) {
|
|
|
4971
5070
|
res.end(JSON.stringify(payload));
|
|
4972
5071
|
return true;
|
|
4973
5072
|
}
|
|
5073
|
+
if (!hasWebBuild || !hasIndex) {
|
|
5074
|
+
res.writeHead(404, {
|
|
5075
|
+
"Content-Type": "text/plain",
|
|
5076
|
+
...corsHeaders(req)
|
|
5077
|
+
});
|
|
5078
|
+
res.end("AgentLife WASM dashboard not bundled with this plugin install. Use /agentlife/pair for QR pairing, or install the web build separately.");
|
|
5079
|
+
return true;
|
|
5080
|
+
}
|
|
4974
5081
|
const relative = urlPath.replace(/^\/agentlife\/?/, "") || "index.html";
|
|
4975
5082
|
const filePath = path9.resolve(appRoot, relative);
|
|
4976
5083
|
if (!filePath.startsWith(appRoot)) {
|
|
@@ -5012,7 +5119,7 @@ function registerWebApp(api) {
|
|
|
5012
5119
|
return true;
|
|
5013
5120
|
}
|
|
5014
5121
|
});
|
|
5015
|
-
api.logger.info("[agentlife] /agentlife/ route registered — serving WASM web app");
|
|
5122
|
+
api.logger.info(hasWebBuild && hasIndex ? "[agentlife] /agentlife/ route registered — serving WASM web app + /agentlife/pair" : "[agentlife] /agentlife/ route registered — /agentlife/pair only (no web-build)");
|
|
5016
5123
|
}
|
|
5017
5124
|
|
|
5018
5125
|
// services/pairing.ts
|
|
@@ -7077,6 +7184,17 @@ function register(api) {
|
|
|
7077
7184
|
} catch {}
|
|
7078
7185
|
respond(true, { registered: true });
|
|
7079
7186
|
}, { scope: "operator.write" });
|
|
7187
|
+
api.registerGatewayMethod("agentlife.vision.ensure", async ({ respond }) => {
|
|
7188
|
+
try {
|
|
7189
|
+
if (!currentState) {
|
|
7190
|
+
return respond(false, { error: "plugin state not initialized" });
|
|
7191
|
+
}
|
|
7192
|
+
const result = await ensureVisionPosters(currentState, api.runtime, console.log);
|
|
7193
|
+
respond(true, result);
|
|
7194
|
+
} catch (e) {
|
|
7195
|
+
respond(false, { code: "internal_error", message: e?.message ?? String(e) });
|
|
7196
|
+
}
|
|
7197
|
+
}, { scope: "operator.read" });
|
|
7080
7198
|
}
|
|
7081
7199
|
export {
|
|
7082
7200
|
register as default,
|