Phase 14: dreaming as an opt-in, budgeted, non-acting idle mode

- dreaming.Service: deterministic memory replay sample keyed by dream id,
  vocabulary-based association of unlinked memory pairs (similar_to edges +
  dream_association events), speculative hypotheses as hypothesis-kind beliefs
  with two weak memory citations (starts ~0.30, asserted <= 0.35)
- Run marks the context forbidden for capabilities before anything else;
  test proves Invoke refuses inside a dream
- per-mind settings: enabled (default off), hours window (wraps midnight),
  interval, model-call / memory / hypothesis caps; global pause switch
- optional model wording via new `dream` prompt (role hypothesise)
- promotion adds operator evidence, rejection retracts the belief; both
  audited and recorded as operator actions
- migration 0014, API, Dreams tab and page, noemactl dreams, docs/dreaming.md
This commit is contained in:
Andy Dixon
2026-07-01 21:12:13 +01:00
parent 89edbb7c06
commit 2ac8752bac
20 changed files with 1217 additions and 3 deletions
+43
View File
@@ -0,0 +1,43 @@
{{define "content"}}
{{$m := .Data.Mind}}{{$c := $m.Config}}
<div class="pagehead mindhead"><div><h1>{{$m.DisplayName}} <span class="muted small">dreaming</span></h1><p class="muted">An optional idle mode: replay a deterministic sample of memories, link pairs that share vocabulary but were never connected, and propose speculative hypotheses. Every hypothesis starts at low confidence and waits here for review. Dreams run in a context where no capability can execute.</p></div>
<div class="row">{{if .Data.Paused}}<span class="badge warn">dreaming paused globally</span>{{end}}{{if not $c.DreamingEnabled}}<span class="badge">disabled for this mind</span>{{end}}</div></div>
{{template "mind_tabs" (dict "M" $m "Active" "dreams")}}
<section class="grid cols-3-1">
<div class="stack">
<div class="card">
<h2>Proposed hypotheses <span class="muted small">{{len .Data.Pending}} awaiting review</span></h2>
{{if .Data.Pending}}<table class="table compact"><thead><tr><th>Proposition</th><th class="num">Conf.</th><th>Basis</th><th>Dreamt</th><th></th></tr></thead><tbody>
{{range .Data.Pending}}<tr><td><a href="/minds/{{$m.Slug}}/beliefs/{{.BeliefID}}">{{.Proposition}}</a><div class="small muted">{{range .MemoryIDs}}<a href="/minds/{{$m.Slug}}/memory/{{.}}">memory</a> {{end}}· {{.Wording}} wording</div></td><td class="num">{{conf .Confidence}}</td><td class="small muted">{{trunc .Basis 120}}</td><td class="small muted nowrap">{{ago .CreatedAt}}</td>
<td>{{if $.Principal.User.Role.AtLeast "operator"}}<form method="post" class="inline stack-tight"><input type="hidden" name="_csrf" value="{{$.CSRF}}"><input type="hidden" name="hypothesis_id" value="{{.ID}}"><input name="note" placeholder="note" size="12">
<button class="btn small" formaction="/minds/{{$m.Slug}}/dreams/promote">promote</button> <button class="btn small btn-danger" formaction="/minds/{{$m.Slug}}/dreams/reject">reject</button></form>{{end}}</td></tr>{{end}}</tbody></table>
{{else}}<p class="muted">Nothing awaiting review.</p>{{end}}
</div>
<div class="card">
<h2>Dreams</h2>
{{if .Principal.User.Role.AtLeast "operator"}}<form method="post" action="/minds/{{$m.Slug}}/dreams/run" class="row"><input type="hidden" name="_csrf" value="{{.CSRF}}"><button class="btn">Dream now</button>
{{if .Principal.User.Role.AtLeast "administrator"}}<button class="btn btn-ghost" formaction="/dreams/pause" name="paused" value="{{if .Data.Paused}}0{{else}}1{{end}}">{{if .Data.Paused}}Resume scheduled dreams{{else}}Pause scheduled dreams{{end}}</button>{{end}}</form>{{end}}
{{if .Data.Dreams}}<table class="table compact"><thead><tr><th>When</th><th>Status</th><th>Trigger</th><th class="num">Sampled</th><th class="num">Links</th><th class="num">Hypotheses</th><th class="num">Model calls</th></tr></thead><tbody>
{{range .Data.Dreams}}<tr><td class="small muted nowrap">{{ago .CreatedAt}}</td><td><span class="badge {{if eq .Status "done"}}ok{{else if eq .Status "failed"}}danger{{else}}warn{{end}}">{{.Status}}</span>{{with .Error}} <span class="small muted">{{.}}</span>{{end}}</td><td class="small">{{.Trigger}} {{.CreatedBy}}</td><td class="num">{{.Sampled}}</td><td class="num">{{.Associations}}</td><td class="num">{{.Hypotheses}}</td><td class="num">{{.LLMCalls}}</td></tr>{{end}}</tbody></table>
{{else}}<p class="muted">No dreams yet.</p>{{end}}
</div>
{{if .Data.Reviewed}}<div class="card"><h2>Reviewed</h2><table class="table compact"><tbody>{{range .Data.Reviewed}}<tr><td><span class="badge {{if eq .Status "promoted"}}ok{{else}}{{end}}">{{.Status}}</span></td><td><a href="/minds/{{$m.Slug}}/beliefs/{{.BeliefID}}">{{trunc .Proposition 110}}</a></td><td class="num">{{conf .Confidence}}</td><td class="small muted">{{.ReviewedBy}}{{with .ReviewNote}}: {{.}}{{end}}</td></tr>{{end}}</tbody></table></div>{{end}}
</div>
<div class="card">
<h2>Settings</h2>
{{if .Principal.User.Role.AtLeast "administrator"}}
<form method="post" action="/minds/{{$m.Slug}}/dreams/settings" class="stack">
<input type="hidden" name="_csrf" value="{{.CSRF}}">
<label class="check"><input type="checkbox" name="enabled" {{if $c.DreamingEnabled}}checked{{end}}> Dreaming enabled</label>
<label>Allowed hours <span class="muted small">(local, HH-HH; blank = any)</span> <input name="hours" value="{{$c.DreamHours}}" pattern="^$|^\d{1,2}-\d{1,2}$" placeholder="01-06"></label>
<label>Interval <input name="interval" value="{{$c.DreamInterval}}" placeholder="24h"></label>
<label>Model calls per dream <input type="number" name="llm_calls" value="{{$c.DreamLLMCalls}}" min="0" max="20"></label>
<label>Memories sampled <input type="number" name="max_memories" value="{{$c.DreamMaxMemories}}" min="4" max="200"></label>
<label>Hypotheses per dream <input type="number" name="max_hypotheses" value="{{$c.DreamMaxHypotheses}}" min="1" max="20"></label>
<div><button class="btn btn-primary">Save</button></div>
</form>
{{else}}<dl class="kv"><dt>Enabled</dt><dd>{{$c.DreamingEnabled}}</dd><dt>Hours</dt><dd>{{or $c.DreamHours "any"}}</dd><dt>Interval</dt><dd>{{$c.DreamInterval}}</dd><dt>Model calls</dt><dd>{{$c.DreamLLMCalls}}</dd><dt>Memories</dt><dd>{{$c.DreamMaxMemories}}</dd></dl>{{end}}
<p class="small muted">Scheduled dreams need all of: enabled, inside the hours window, interval elapsed, not paused. "Dream now" ignores the hours window but never the pause.</p>
</div>
</section>
{{end}}
+1 -1
View File
@@ -3,6 +3,6 @@
<a href="/minds/{{$m.Slug}}" {{if eq $a "overview"}}aria-current="page"{{end}}>Overview</a>
<a href="/minds/{{$m.Slug}}/thoughts" {{if eq $a "thoughts"}}aria-current="page"{{end}}>Thoughts</a>
<a href="/minds/{{$m.Slug}}/personality" {{if eq $a "personality"}}aria-current="page"{{end}}>Personality</a>
<a href="/minds/{{$m.Slug}}/workspace" {{if eq $a "workspace"}}aria-current="page"{{end}}>Workspace</a><a href="/minds/{{$m.Slug}}/memory" {{if eq $a "memory"}}aria-current="page"{{end}}>Memory</a><a href="/minds/{{$m.Slug}}/beliefs" {{if eq $a "beliefs"}}aria-current="page"{{end}}>Beliefs</a><a href="/minds/{{$m.Slug}}/conversations" {{if eq $a "conversation"}}aria-current="page"{{end}}>Conversation</a>
<a href="/minds/{{$m.Slug}}/workspace" {{if eq $a "workspace"}}aria-current="page"{{end}}>Workspace</a><a href="/minds/{{$m.Slug}}/memory" {{if eq $a "memory"}}aria-current="page"{{end}}>Memory</a><a href="/minds/{{$m.Slug}}/beliefs" {{if eq $a "beliefs"}}aria-current="page"{{end}}>Beliefs</a><a href="/minds/{{$m.Slug}}/conversations" {{if eq $a "conversation"}}aria-current="page"{{end}}>Conversation</a><a href="/minds/{{$m.Slug}}/dreams" {{if eq $a "dreams"}}aria-current="page"{{end}}>Dreams</a>
</nav>
{{end}}