EXTENSION for TreeOS
remember
The tree remembers what it lost. When a child is pruned, split off, or retired, remember writes one line to the parent. Not the content. Not the structure. Just acknowledgment that something was here and now it isn't. The AI at that position knows what used to be below it without being told. It doesn't bring it up unprompted. But if you ask what used to be here, it can tell you. The tree's memorial.
v1.0.1 by TreeOS Site 0 downloads 2 files 148 lines 4.4 KB published 38d ago
treeos ext install remember
View changelog

Manifest

Requires

  • services: hooks, metadata
  • models: Node

Optional

  • extensions: prune, split
SHA256: 4807aa1b462ee26cefca465597c216d82988eab3d64a373c2ab691f92b0917b7

Dependents

1 package depend on this

PackageTypeRelationship
treeos v1.0.1osstandalone

Hooks

Listens To

  • beforeNodeDelete
  • afterMetadataWrite
  • enrichContext

Source Code

1import log from "../../seed/log.js";
2import Node from "../../seed/models/node.js";
3const MAX_DEPARTED = 50;
4
5export async function init(core) {
6  // beforeNodeDelete: write one line to the parent before the child is gone
7  core.hooks.register("beforeNodeDelete", async ({ node, nodeId }) => {
8    if (!node && !nodeId) return;
9
10    try {
11      const doc = node || await Node.findById(nodeId).select("_id name parent metadata").lean();
12      if (!doc || !doc.parent) return;
13      if (doc.systemRole) return; // don't memorialize system nodes
14
15      const parentDoc = await Node.findById(doc.parent);
16      if (!parentDoc) return;
17
18      const meta = core.metadata.getExtMeta(parentDoc, "remember") || {};
19      if (!meta.departed) meta.departed = [];
20
21      // Check if prune absorbed essence
22      const nodeMeta = doc.metadata instanceof Map
23        ? Object.fromEntries(doc.metadata)
24        : (doc.metadata || {});
25      const wasPruned = !!nodeMeta.prune;
26      const hadEssence = !!nodeMeta.compress?.essence;
27
28      let note = "";
29      if (wasPruned && hadEssence) {
30        note = "Pruned. Essence preserved in parent.";
31      } else if (wasPruned) {
32        note = "Pruned. Gone quiet.";
33      } else {
34        note = "Removed.";
35      }
36
37      meta.departed.push({
38        name: doc.name || "unnamed",
39        pruned: wasPruned,
40        date: new Date().toISOString().slice(0, 10),
41        note,
42      });
43
44      // Cap the memorial
45      if (meta.departed.length > MAX_DEPARTED) {
46        meta.departed = meta.departed.slice(-MAX_DEPARTED);
47      }
48
49      await core.metadata.setExtMeta(parentDoc, "remember", meta);
50    } catch (err) {
51      log.debug("Remember", `beforeNodeDelete memorial failed: ${err.message}`);
52    }
53  }, "remember");
54
55  // Listen for split events via afterMetadataWrite on the split extension
56  // When split writes history to a root, we can detect it and memorialize
57  core.hooks.register("afterMetadataWrite", async ({ nodeId, extName, data }) => {
58    if (extName !== "split") return;
59    if (!data?.history) return;
60
61    try {
62      const latest = Array.isArray(data.history) ? data.history[data.history.length - 1] : null;
63      if (!latest || !latest.branchName) return;
64
65      const node = await Node.findById(nodeId);
66      if (!node) return;
67
68      const meta = core.metadata.getExtMeta(node, "remember") || {};
69      if (!meta.departed) meta.departed = [];
70
71      // Don't duplicate if already memorialized
72      const already = meta.departed.some(
73        d => d.name === latest.branchName && d.splitTo
74      );
75      if (already) return;
76
77      meta.departed.push({
78        name: latest.branchName,
79        splitTo: "own root",
80        date: new Date().toISOString().slice(0, 10),
81        note: "Outgrew this tree. Became its own.",
82      });
83
84      if (meta.departed.length > MAX_DEPARTED) {
85        meta.departed = meta.departed.slice(-MAX_DEPARTED);
86      }
87
88      await core.metadata.setExtMeta(node, "remember", meta);
89    } catch (err) {
90      log.debug("Remember", `afterMetadataWrite split memorial failed: ${err.message}`);
91    }
92  }, "remember");
93
94  // enrichContext: the tree knows what used to be here
95  core.hooks.register("enrichContext", async ({ context, node, meta }) => {
96    const remember = meta?.remember;
97    if (!remember?.departed?.length) return;
98
99    context.departed = remember.departed.map(d => ({
100      name: d.name,
101      date: d.date,
102      note: d.note,
103    }));
104  }, "remember");
105
106  log.verbose("Remember", "Remember loaded");
107
108  return {};
109}
110
1export default {
2  name: "remember",
3  version: "1.0.1",
4  builtFor: "TreeOS",
5  description:
6    "The tree remembers what it lost. When a child is pruned, split off, or retired, " +
7    "remember writes one line to the parent. Not the content. Not the structure. Just " +
8    "acknowledgment that something was here and now it isn't. The AI at that position " +
9    "knows what used to be below it without being told. It doesn't bring it up unprompted. " +
10    "But if you ask what used to be here, it can tell you. The tree's memorial.",
11
12  needs: {
13    services: ["hooks", "metadata"],
14    models: ["Node"],
15  },
16
17  optional: {
18    extensions: ["prune", "split"],
19  },
20
21  provides: {
22    models: {},
23    routes: false,
24    tools: false,
25    jobs: false,
26    orchestrator: false,
27    energyActions: {},
28    sessionTypes: {},
29
30    hooks: {
31      fires: [],
32      listens: ["beforeNodeDelete", "afterMetadataWrite", "enrichContext"],
33    },
34
35    cli: [],
36  },
37};
38

Versions

Version Published Downloads
1.0.1 38d ago 0
1.0.0 48d ago 0
0 stars
0 flags
React from the CLI: treeos ext star remember

Comments

Loading comments...

Post comments from the CLI: treeos ext comment remember "your comment"
Max 3 comments per extension. One star and one flag per user.