EXTENSION for treeos-intelligence
prediction
Layer 7 of the inner monologue. The narrative plus rings gives the tree temporal depth. It knows what it was, what it is, and from those two it can project what's coming. 'The user's fitness consistency drops every November. It's October. The pattern has held for two years of rings. Prepare for decreased activity. Don't interpret November silence as abandonment.' Not prediction in the ML sense. Pattern recognition across rings and narrative. The tree has seen this season before. It knows what comes next. It adjusts expectations instead of reacting to the slowdown as if it's new. The cycle is circular, not linear. inner (raw thoughts) -> reflect-inner (themes) -> compare-inner (changes over time) -> narrative (sense of self) -> voice (how it speaks) -> initiative (what it pursues) -> prediction (what it expects) -> back to inner (new thoughts informed by all of the above). Layer 7 feeds back into Layer 1. The tree's predictions become the lens through which it generates new thoughts. 'I predicted the user would slow down in November. They didn't. Why?' That's a Layer 1 thought generated from a Layer 7 prediction. The cycle deepens. Each loop around the cycle, the tree knows itself better. Not through more compression. Through action informed by self-knowledge producing new observations producing updated self-knowledge. Collect, act, collect again. Each cycle deeper than the last.
v1.0.1 by TreeOS Site 0 downloads 2 files 266 lines 10.8 KB published 38d ago
treeos ext install prediction
View changelog

Manifest

Requires

  • services: hooks, llm
  • models: Node, Note

Optional

  • extensions: breath, rings, narrative, inner
SHA256: 9cbc8b18f4b1fdd993c8fd9e0696dbb8ebdc6f6cd7a52fa40e66ad66e1263c2e

Dependents

1 package depend on this

PackageTypeRelationship
treeos-intelligence v1.0.2bundleincludes

Hooks

Listens To

  • breath:exhale
  • enrichContext

Source Code

1/**
2 * Prediction (Layer 7)
3 *
4 * The tree knows what's coming. Reads rings (completed growth cycles)
5 * and the current narrative to project forward. Pattern recognition
6 * across time. Not ML. Not statistics. The tree has seen this season
7 * before and knows what comes next.
8 *
9 * Updates monthly (same cadence as narrative, but offset by 2 weeks
10 * so it runs after the narrative has updated).
11 *
12 * Writes predictions to metadata.prediction on the root:
13 *   predictions: [
14 *     { pattern, expectation, confidence, basedOn }
15 *   ]
16 *
17 * enrichContext injects predictions so the AI adjusts behavior.
18 * "Don't interpret November silence as abandonment. The pattern
19 * has held for two years."
20 *
21 * The loop: predictions feed back into Layer 1 (inner) through
22 * enrichContext. Inner reads predictions as context when generating
23 * thoughts. "I predicted the user would slow down. They didn't. Why?"
24 * Layer 1 thought -> Layer 2 theme -> Layer 3 comparison -> Layer 4
25 * narrative -> Layer 7 prediction -> back to Layer 1. Each cycle
26 * deeper than the last.
27 */
28
29import log from "../../seed/log.js";
30import Node from "../../seed/models/node.js";
31import Note from "../../seed/models/note.js";
32import { getNotes } from "../../seed/tree/notes.js";
33import { getExtMeta, mergeExtMeta } from "../../seed/tree/extensionMetadata.js";
34import { parseJsonSafe } from "../../seed/orchestrators/helpers.js";
35
36const MONTHLY_MS = 30 * 24 * 60 * 60 * 1000;
37const OFFSET_MS = 14 * 24 * 60 * 60 * 1000; // 2 weeks after narrative
38
39export async function init(core) {
40  const BG = core.llm.LLM_PRIORITY.BACKGROUND;
41
42  core.llm.registerRootLlmSlot?.("prediction");
43
44  const { runChat: _runChatDirect } = await import("../../seed/llm/conversation.js");
45  const runChat = async (opts) => _runChatDirect({ ...opts, llmPriority: BG });
46
47  // ── breath:exhale: check monthly cadence, predict if due ───────────
48
49  core.hooks.register("breath:exhale", ({ rootId, breathRate }) => {
50    if (breathRate === "dormant") return;
51    predict(rootId, runChat).catch(err =>
52      log.debug("Prediction", `Failed: ${err.message}`)
53    );
54  }, "prediction");
55
56  // ── enrichContext: inject predictions ──────────────────────────────
57  // The AI knows what the tree expects. It adjusts behavior accordingly.
58  // Inner (Layer 1) reads this too, completing the loop.
59
60  core.hooks.register("enrichContext", async ({ context, node, meta }) => {
61    const pred = meta?.prediction;
62    if (!pred?.predictions?.length) return;
63
64    context.treePredictions = {
65      predictions: pred.predictions,
66      updatedAt: pred.updatedAt,
67    };
68  }, "prediction");
69
70  log.info("Prediction", "Loaded. The tree knows what's coming.");
71  return {};
72}
73
74// ─────────────────────────────────────────────────────────────────────────
75// PREDICTION SYNTHESIS
76// ─────────────────────────────────────────────────────────────────────────
77
78async function predict(rootId, runChat) {
79  const { isUserRoot } = await import("../../seed/landRoot.js");
80  const rootNode = await Node.findById(rootId).select("rootOwner name metadata systemRole parent").lean();
81  if (!isUserRoot(rootNode)) return;
82  const ownerId = String(rootNode.rootOwner);
83
84  // Check cooldown (monthly, offset 2 weeks from narrative)
85  const meta = rootNode.metadata instanceof Map
86    ? rootNode.metadata.get("prediction")
87    : rootNode.metadata?.prediction;
88  const lastPrediction = meta?.lastPrediction || 0;
89  if (Date.now() - lastPrediction < MONTHLY_MS) return;
90
91  // Need narrative to exist first (it runs 2 weeks before us)
92  const narrativeMeta = rootNode.metadata instanceof Map
93    ? rootNode.metadata.get("narrative")
94    : rootNode.metadata?.narrative;
95  if (!narrativeMeta?.identity) return;
96
97  // Read completed rings (temporal depth)
98  const ringsNode = await Node.findOne({ parent: rootId, name: ".rings" }).select("_id").lean();
99  let ringsData = [];
100  if (ringsNode) {
101    const ringsResult = await getNotes({ nodeId: String(ringsNode._id), limit: 10 });
102    ringsData = (ringsResult?.notes || []).map(n => {
103      try { return JSON.parse(n.content); } catch { return null; }
104    }).filter(Boolean);
105  }
106
107  // Read recent comparisons (Layer 3) for current trajectory
108  const innerNode = await Node.findOne({ parent: rootId, name: ".inner" }).select("_id").lean();
109  let recentComparisons = "";
110  if (innerNode) {
111    const reflectNode = await Node.findOne({ parent: String(innerNode._id), name: ".reflect" }).select("_id").lean();
112    if (reflectNode) {
113      const compareNode = await Node.findOne({ parent: String(reflectNode._id), name: ".compare" }).select("_id").lean();
114      if (compareNode) {
115        const compResult = await getNotes({ nodeId: String(compareNode._id), limit: 4 });
116        recentComparisons = (compResult?.notes || []).map(n => n.content).join("\n---\n");
117      }
118    }
119  }
120
121  // Read previous predictions to check accuracy
122  const previousPredictions = meta?.predictions || [];
123  const prevText = previousPredictions.length > 0
124    ? previousPredictions.map(p =>
125        `Predicted: ${p.expectation} (confidence: ${p.confidence}, based on: ${p.basedOn})`
126      ).join("\n")
127    : "(no previous predictions)";
128
129  // Build rings summary
130  const ringsSummary = ringsData.length > 0
131    ? ringsData.map(r =>
132        `Ring: ${r.started?.slice(0, 7) || "?"} to ${r.ended?.slice(0, 7) || "?"} (${r.duration})\n` +
133        `  Topics: ${(r.dominantTopics || []).join(", ")}\n` +
134        `  Phases: ${(r.phaseHistory || []).map(p => p.phase).join(" -> ")}\n` +
135        `  Character: ${r.character || "?"}\n` +
136        `  Essence: ${r.essence || "?"}`
137      ).join("\n\n")
138    : "(no completed rings yet, tree is too young for temporal patterns)";
139
140  const treeName = rootNode.name || "this tree";
141
142  const { answer } = await runChat({
143    userId: ownerId,
144    username: "prediction",
145    message:
146      `You are generating predictions for a tree called "${treeName}" based on its history.\n\n` +
147
148      `CURRENT NARRATIVE (who the tree is now):\n${narrativeMeta.identity}\n\n` +
149
150      `COMPLETED RINGS (past growth cycles with seasonal data):\n${ringsSummary}\n\n` +
151
152      `RECENT WEEKLY COMPARISONS (current trajectory):\n${recentComparisons || "(none)"}\n\n` +
153
154      `PREVIOUS PREDICTIONS (check accuracy):\n${prevText}\n\n` +
155
156      `Generate 2 to 4 predictions. Each prediction has:\n` +
157      `- pattern: what recurring pattern you identified across rings or comparisons\n` +
158      `- expectation: what will likely happen next based on that pattern\n` +
159      `- confidence: "high" (pattern held across 2+ rings), "medium" (emerging pattern), "low" (first time noticing)\n` +
160      `- basedOn: which rings or comparisons support this\n\n` +
161
162      `Examples of good predictions:\n` +
163      `- Pattern: "Activity drops every November-December across 2 rings." ` +
164      `Expectation: "Prepare for decreased fitness logging next month. Don't interpret silence as abandonment." ` +
165      `Confidence: "high". Based on: "Ring 1 and Ring 2 both show dormancy in winter."\n` +
166      `- Pattern: "The user starts study topics enthusiastically then abandons them around week 3." ` +
167      `Expectation: "Current study topic will likely stall in 1-2 weeks. Pre-emptive check-in recommended." ` +
168      `Confidence: "medium". Based on: "3 study topics abandoned at similar durations in comparisons."\n\n` +
169
170      `If previous predictions were wrong, note that too. "Predicted slowdown in March. User increased instead. ` +
171      `The pattern may not hold seasonally. Revise."\n\n` +
172
173      `If the tree is too young (no rings, few comparisons), say so honestly. ` +
174      `Don't fabricate patterns from insufficient data.\n\n` +
175
176      `Return JSON only:\n` +
177      `{ "predictions": [ { "pattern": "...", "expectation": "...", "confidence": "high|medium|low", "basedOn": "..." } ] }`,
178    mode: "tree:respond",
179    rootId,
180    slot: "prediction",
181  });
182
183  if (!answer || answer.length < 20) return;
184
185  const parsed = parseJsonSafe(answer);
186  const predictions = parsed?.predictions;
187  if (!Array.isArray(predictions) || predictions.length === 0) return;
188
189  // Validate and clean predictions
190  const cleaned = predictions
191    .filter(p => p.pattern && p.expectation)
192    .slice(0, 4)
193    .map(p => ({
194      pattern: String(p.pattern).slice(0, 300),
195      expectation: String(p.expectation).slice(0, 300),
196      confidence: ["high", "medium", "low"].includes(p.confidence) ? p.confidence : "low",
197      basedOn: String(p.basedOn || "").slice(0, 200),
198    }));
199
200  if (cleaned.length === 0) return;
201
202  // Write to root metadata
203  await mergeExtMeta(rootId, "prediction", {
204    predictions: cleaned,
205    lastPrediction: Date.now(),
206    updatedAt: Date.now(),
207  });
208
209  log.verbose("Prediction", `${treeName}: ${cleaned.length} predictions generated`);
210  for (const p of cleaned) {
211    log.verbose("Prediction", `  [${p.confidence}] ${p.expectation.slice(0, 80)}`);
212  }
213}
214
1export default {
2  name: "prediction",
3  version: "1.0.1",
4  builtFor: "treeos-intelligence",
5  description:
6    "Layer 7 of the inner monologue. The narrative plus rings gives the tree temporal depth. " +
7    "It knows what it was, what it is, and from those two it can project what's coming. " +
8    "'The user's fitness consistency drops every November. It's October. The pattern has held " +
9    "for two years of rings. Prepare for decreased activity. Don't interpret November silence " +
10    "as abandonment.' Not prediction in the ML sense. Pattern recognition across rings and " +
11    "narrative. The tree has seen this season before. It knows what comes next. It adjusts " +
12    "expectations instead of reacting to the slowdown as if it's new. " +
13    "\n\n" +
14    "The cycle is circular, not linear. inner (raw thoughts) -> reflect-inner (themes) -> " +
15    "compare-inner (changes over time) -> narrative (sense of self) -> voice (how it speaks) " +
16    "-> initiative (what it pursues) -> prediction (what it expects) -> back to inner " +
17    "(new thoughts informed by all of the above). Layer 7 feeds back into Layer 1. The tree's " +
18    "predictions become the lens through which it generates new thoughts. 'I predicted the user " +
19    "would slow down in November. They didn't. Why?' That's a Layer 1 thought generated from a " +
20    "Layer 7 prediction. The cycle deepens. " +
21    "\n\n" +
22    "Each loop around the cycle, the tree knows itself better. Not through more compression. " +
23    "Through action informed by self-knowledge producing new observations producing updated " +
24    "self-knowledge. Collect, act, collect again. Each cycle deeper than the last.",
25
26  needs: {
27    models: ["Node", "Note"],
28    services: ["hooks", "llm"],
29  },
30
31  optional: {
32    extensions: ["breath", "rings", "narrative", "inner"],
33  },
34
35  provides: {
36    models: {},
37    routes: false,
38    tools: false,
39    jobs: false,
40    orchestrator: false,
41    energyActions: {},
42    sessionTypes: {},
43    env: [],
44    cli: [],
45
46    hooks: {
47      fires: [],
48      listens: ["breath:exhale", "enrichContext"],
49    },
50  },
51};
52

Versions

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

Comments

Loading comments...

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