@tpsdev-ai/flair 0.5.0 → 0.5.1
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/cli.js +204 -2
- package/package.json +3 -1
package/dist/cli.js
CHANGED
|
@@ -1579,6 +1579,198 @@ federation
|
|
|
1579
1579
|
process.exit(1);
|
|
1580
1580
|
}
|
|
1581
1581
|
});
|
|
1582
|
+
// ─── flair rem ───────────────────────────────────────────────────────────────
|
|
1583
|
+
// Memory hygiene and reflection: light (NREM), rapid (REM), restorative (deep).
|
|
1584
|
+
const rem = program.command("rem").description("Memory hygiene and reflection");
|
|
1585
|
+
rem
|
|
1586
|
+
.command("light")
|
|
1587
|
+
.description("NREM — quick cleanup: delete expired, archive old, consolidate candidates")
|
|
1588
|
+
.option("--port <port>", "Harper HTTP port")
|
|
1589
|
+
.option("--agent <id>", "Agent ID (or FLAIR_AGENT_ID env)")
|
|
1590
|
+
.option("--dry-run", "Preview changes without applying them")
|
|
1591
|
+
.action(async (opts) => {
|
|
1592
|
+
const agentId = opts.agent || process.env.FLAIR_AGENT_ID;
|
|
1593
|
+
const dryRun = !!opts.dryRun;
|
|
1594
|
+
console.log(`\n-- rem light${dryRun ? " (dry run)" : ""} --`);
|
|
1595
|
+
if (agentId)
|
|
1596
|
+
console.log(`Agent: ${agentId}`);
|
|
1597
|
+
try {
|
|
1598
|
+
// Step 1: Maintenance — expire + archive
|
|
1599
|
+
const maint = await api("POST", "/MemoryMaintenance", {
|
|
1600
|
+
...(agentId ? { agentId } : {}),
|
|
1601
|
+
dryRun,
|
|
1602
|
+
});
|
|
1603
|
+
if (maint.error) {
|
|
1604
|
+
console.error(`Maintenance error: ${maint.error}`);
|
|
1605
|
+
process.exit(1);
|
|
1606
|
+
}
|
|
1607
|
+
const s = maint.stats ?? {};
|
|
1608
|
+
console.log("\nCleanup");
|
|
1609
|
+
console.log(` Expired (deleted): ${s.expired ?? 0}`);
|
|
1610
|
+
console.log(` Archived (soft): ${s.archived ?? 0}`);
|
|
1611
|
+
console.log(` Total scanned: ${s.total ?? 0}`);
|
|
1612
|
+
if (s.errors)
|
|
1613
|
+
console.log(` Errors: ${s.errors}`);
|
|
1614
|
+
// Step 2: Consolidation candidates
|
|
1615
|
+
if (!agentId) {
|
|
1616
|
+
console.log("\nConsolidation skipped — no agent ID (pass --agent or set FLAIR_AGENT_ID)");
|
|
1617
|
+
return;
|
|
1618
|
+
}
|
|
1619
|
+
const consol = await api("POST", "/ConsolidateMemories", {
|
|
1620
|
+
agentId,
|
|
1621
|
+
scope: "all",
|
|
1622
|
+
});
|
|
1623
|
+
if (consol.error) {
|
|
1624
|
+
console.error(`Consolidation error: ${consol.error}`);
|
|
1625
|
+
process.exit(1);
|
|
1626
|
+
}
|
|
1627
|
+
const candidates = consol.candidates ?? [];
|
|
1628
|
+
const promote = candidates.filter((c) => c.suggestion === "promote");
|
|
1629
|
+
const archive = candidates.filter((c) => c.suggestion === "archive");
|
|
1630
|
+
console.log("\nConsolidation candidates");
|
|
1631
|
+
console.log(` Promote: ${promote.length}`);
|
|
1632
|
+
console.log(` Archive: ${archive.length}`);
|
|
1633
|
+
if (promote.length > 0) {
|
|
1634
|
+
console.log("\n Promote:");
|
|
1635
|
+
for (const c of promote) {
|
|
1636
|
+
console.log(` [${c.memory?.id ?? "?"}] ${c.reason}`);
|
|
1637
|
+
}
|
|
1638
|
+
}
|
|
1639
|
+
if (archive.length > 0) {
|
|
1640
|
+
console.log("\n Archive:");
|
|
1641
|
+
for (const c of archive) {
|
|
1642
|
+
console.log(` [${c.memory?.id ?? "?"}] ${c.reason}`);
|
|
1643
|
+
}
|
|
1644
|
+
}
|
|
1645
|
+
console.log(`\nDone.${dryRun ? " No changes applied (dry run)." : ""}`);
|
|
1646
|
+
}
|
|
1647
|
+
catch (err) {
|
|
1648
|
+
console.error(`Error: ${err.message}`);
|
|
1649
|
+
process.exit(1);
|
|
1650
|
+
}
|
|
1651
|
+
});
|
|
1652
|
+
rem
|
|
1653
|
+
.command("rapid")
|
|
1654
|
+
.description("REM — reflection/learning: generate a structured LLM reflection prompt")
|
|
1655
|
+
.option("--port <port>", "Harper HTTP port")
|
|
1656
|
+
.option("--agent <id>", "Agent ID (or FLAIR_AGENT_ID env)")
|
|
1657
|
+
.option("--focus <type>", "lessons_learned | patterns | decisions | errors", "lessons_learned")
|
|
1658
|
+
.option("--since <date>", "ISO timestamp lower bound (default: 24h ago)")
|
|
1659
|
+
.action(async (opts) => {
|
|
1660
|
+
const agentId = opts.agent || process.env.FLAIR_AGENT_ID;
|
|
1661
|
+
if (!agentId) {
|
|
1662
|
+
console.error("Error: --agent <id> or FLAIR_AGENT_ID env required");
|
|
1663
|
+
process.exit(1);
|
|
1664
|
+
}
|
|
1665
|
+
console.log(`\n-- rem rapid --`);
|
|
1666
|
+
console.log(`Agent: ${agentId} Focus: ${opts.focus}`);
|
|
1667
|
+
try {
|
|
1668
|
+
const body = {
|
|
1669
|
+
agentId,
|
|
1670
|
+
focus: opts.focus,
|
|
1671
|
+
};
|
|
1672
|
+
if (opts.since)
|
|
1673
|
+
body.since = opts.since;
|
|
1674
|
+
const result = await api("POST", "/ReflectMemories", body);
|
|
1675
|
+
if (result.error) {
|
|
1676
|
+
console.error(`Reflection error: ${result.error}`);
|
|
1677
|
+
process.exit(1);
|
|
1678
|
+
}
|
|
1679
|
+
console.log(`\nSource memories: ${result.count ?? 0}`);
|
|
1680
|
+
if (result.suggestedTags?.length) {
|
|
1681
|
+
console.log(`Tags: ${result.suggestedTags.join(", ")}`);
|
|
1682
|
+
}
|
|
1683
|
+
console.log("\n--- Reflection Prompt ---");
|
|
1684
|
+
console.log(result.prompt ?? "(no prompt returned)");
|
|
1685
|
+
console.log("--- End Prompt ---\n");
|
|
1686
|
+
console.log("Feed the prompt above to your LLM, then write insights back with:");
|
|
1687
|
+
console.log(" flair memory add --agent <id> --content <insight> --durability persistent");
|
|
1688
|
+
}
|
|
1689
|
+
catch (err) {
|
|
1690
|
+
console.error(`Error: ${err.message}`);
|
|
1691
|
+
process.exit(1);
|
|
1692
|
+
}
|
|
1693
|
+
});
|
|
1694
|
+
rem
|
|
1695
|
+
.command("restorative")
|
|
1696
|
+
.description("Deep audit: full maintenance + consolidation (olderThan=7d) + reflection")
|
|
1697
|
+
.option("--port <port>", "Harper HTTP port")
|
|
1698
|
+
.option("--agent <id>", "Agent ID (or FLAIR_AGENT_ID env)")
|
|
1699
|
+
.option("--dry-run", "Preview maintenance changes without applying them")
|
|
1700
|
+
.action(async (opts) => {
|
|
1701
|
+
const agentId = opts.agent || process.env.FLAIR_AGENT_ID;
|
|
1702
|
+
const dryRun = !!opts.dryRun;
|
|
1703
|
+
console.log(`\n== rem restorative${dryRun ? " (dry run)" : ""} ==`);
|
|
1704
|
+
if (agentId)
|
|
1705
|
+
console.log(`Agent: ${agentId}`);
|
|
1706
|
+
try {
|
|
1707
|
+
// Step 1: Maintenance
|
|
1708
|
+
console.log("\n[1/3] Maintenance...");
|
|
1709
|
+
const maint = await api("POST", "/MemoryMaintenance", {
|
|
1710
|
+
...(agentId ? { agentId } : {}),
|
|
1711
|
+
dryRun,
|
|
1712
|
+
});
|
|
1713
|
+
if (maint.error) {
|
|
1714
|
+
console.error(`Maintenance error: ${maint.error}`);
|
|
1715
|
+
process.exit(1);
|
|
1716
|
+
}
|
|
1717
|
+
const s = maint.stats ?? {};
|
|
1718
|
+
console.log(` Expired: ${s.expired ?? 0} Archived: ${s.archived ?? 0} Scanned: ${s.total ?? 0}${s.errors ? ` Errors: ${s.errors}` : ""}`);
|
|
1719
|
+
// Step 2: Consolidation (skip if no agentId)
|
|
1720
|
+
if (agentId) {
|
|
1721
|
+
console.log("\n[2/3] Consolidation (scope=all, olderThan=7d)...");
|
|
1722
|
+
const consol = await api("POST", "/ConsolidateMemories", {
|
|
1723
|
+
agentId,
|
|
1724
|
+
scope: "all",
|
|
1725
|
+
olderThan: "7d",
|
|
1726
|
+
});
|
|
1727
|
+
if (consol.error) {
|
|
1728
|
+
console.error(`Consolidation error: ${consol.error}`);
|
|
1729
|
+
process.exit(1);
|
|
1730
|
+
}
|
|
1731
|
+
const candidates = consol.candidates ?? [];
|
|
1732
|
+
const promote = candidates.filter((c) => c.suggestion === "promote");
|
|
1733
|
+
const archive = candidates.filter((c) => c.suggestion === "archive");
|
|
1734
|
+
console.log(` Promote candidates: ${promote.length} Archive candidates: ${archive.length}`);
|
|
1735
|
+
for (const c of promote) {
|
|
1736
|
+
console.log(` promote [${c.memory?.id ?? "?"}] ${c.reason}`);
|
|
1737
|
+
}
|
|
1738
|
+
for (const c of archive) {
|
|
1739
|
+
console.log(` archive [${c.memory?.id ?? "?"}] ${c.reason}`);
|
|
1740
|
+
}
|
|
1741
|
+
}
|
|
1742
|
+
else {
|
|
1743
|
+
console.log("\n[2/3] Consolidation skipped — no agent ID");
|
|
1744
|
+
}
|
|
1745
|
+
// Step 3: Reflection
|
|
1746
|
+
if (agentId) {
|
|
1747
|
+
console.log("\n[3/3] Reflection (scope=all)...");
|
|
1748
|
+
const reflect = await api("POST", "/ReflectMemories", {
|
|
1749
|
+
agentId,
|
|
1750
|
+
scope: "all",
|
|
1751
|
+
});
|
|
1752
|
+
if (reflect.error) {
|
|
1753
|
+
console.error(`Reflection error: ${reflect.error}`);
|
|
1754
|
+
process.exit(1);
|
|
1755
|
+
}
|
|
1756
|
+
console.log(` Source memories: ${reflect.count ?? 0}`);
|
|
1757
|
+
if (reflect.suggestedTags?.length) {
|
|
1758
|
+
console.log(` Tags: ${reflect.suggestedTags.join(", ")}`);
|
|
1759
|
+
}
|
|
1760
|
+
console.log("\n--- Reflection Prompt ---");
|
|
1761
|
+
console.log(reflect.prompt ?? "(no prompt returned)");
|
|
1762
|
+
console.log("--- End Prompt ---");
|
|
1763
|
+
}
|
|
1764
|
+
else {
|
|
1765
|
+
console.log("\n[3/3] Reflection skipped — no agent ID");
|
|
1766
|
+
}
|
|
1767
|
+
console.log(`\nRestorative cycle complete.${dryRun ? " No changes applied (dry run)." : ""}`);
|
|
1768
|
+
}
|
|
1769
|
+
catch (err) {
|
|
1770
|
+
console.error(`Error: ${err.message}`);
|
|
1771
|
+
process.exit(1);
|
|
1772
|
+
}
|
|
1773
|
+
});
|
|
1582
1774
|
// ─── flair status ─────────────────────────────────────────────────────────────
|
|
1583
1775
|
program
|
|
1584
1776
|
.command("status")
|
|
@@ -1592,9 +1784,19 @@ program
|
|
|
1592
1784
|
const baseUrl = opts.url ?? `http://127.0.0.1:${port}`;
|
|
1593
1785
|
let healthy = false;
|
|
1594
1786
|
let healthData = null;
|
|
1595
|
-
// 1. Basic health check
|
|
1787
|
+
// 1. Basic health check — try unauthenticated first, then with admin auth
|
|
1596
1788
|
try {
|
|
1597
|
-
|
|
1789
|
+
let res = await fetch(`${baseUrl}/Health`, { signal: AbortSignal.timeout(5000) });
|
|
1790
|
+
if (!res.ok && res.status === 401) {
|
|
1791
|
+
// Harper requires auth (authorizeLocal: true) — retry with admin credentials
|
|
1792
|
+
const adminPass = process.env.FLAIR_ADMIN_PASS ?? process.env.HDB_ADMIN_PASSWORD;
|
|
1793
|
+
if (adminPass) {
|
|
1794
|
+
res = await fetch(`${baseUrl}/Health`, {
|
|
1795
|
+
headers: { Authorization: `Basic ${Buffer.from(`admin:${adminPass}`).toString("base64")}` },
|
|
1796
|
+
signal: AbortSignal.timeout(5000),
|
|
1797
|
+
});
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1598
1800
|
healthy = res.ok;
|
|
1599
1801
|
}
|
|
1600
1802
|
catch { /* unreachable */ }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tpsdev-ai/flair",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.1",
|
|
4
4
|
"description": "Identity, memory, and soul for AI agents. Cryptographic identity (Ed25519), semantic memory with local embeddings, and persistent personality — all in a single process.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -40,6 +40,7 @@
|
|
|
40
40
|
"build:cli": "tsc -p tsconfig.cli.json --noCheck",
|
|
41
41
|
"prepublishOnly": "npm run build && npm run build:cli",
|
|
42
42
|
"test": "bun test",
|
|
43
|
+
"test:e2e": "playwright test",
|
|
43
44
|
"release": "./scripts/release.sh"
|
|
44
45
|
},
|
|
45
46
|
"publishConfig": {
|
|
@@ -56,6 +57,7 @@
|
|
|
56
57
|
"tweetnacl": "1.0.3"
|
|
57
58
|
},
|
|
58
59
|
"devDependencies": {
|
|
60
|
+
"@playwright/test": "^1.59.1",
|
|
59
61
|
"@types/node": "24.11.0",
|
|
60
62
|
"bun-types": "1.3.11",
|
|
61
63
|
"typescript": "5.9.3"
|