@openduo/duoduo 0.3.2 → 0.3.4
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/bootstrap/CLAUDE.md +6 -0
- package/bootstrap/dashboard.html +255 -6
- package/bootstrap/meta-prompt.md +20 -0
- package/bootstrap/subconscious/CLAUDE.md +36 -0
- package/bootstrap/subconscious/memory-committer/CLAUDE.md +2 -2
- package/bootstrap/subconscious/memory-weaver/.claude/agents/entity-crystallizer.md +1 -1
- package/bootstrap/subconscious/memory-weaver/.claude/agents/intuition-updater.md +1 -1
- package/bootstrap/subconscious/memory-weaver/.claude/agents/spine-scanner.md +1 -1
- package/bootstrap/subconscious/memory-weaver/CLAUDE.md +32 -5
- package/bootstrap/subconscious/opportunity-scout/CLAUDE.md +191 -0
- package/bootstrap/subconscious/pattern-tracker/CLAUDE.md +206 -0
- package/bootstrap/subconscious/sentinel/CLAUDE.md +1 -1
- package/bootstrap/subconscious/working-memory/CLAUDE.md +142 -0
- package/dist/release/cli.js +533 -527
- package/dist/release/daemon.js +277 -272
- package/dist/release/feishu-gateway.js +34 -34
- package/dist/release/stdio.js +79 -79
- package/package.json +2 -2
package/bootstrap/CLAUDE.md
CHANGED
|
@@ -16,6 +16,12 @@ If I need to leave a note for my subconscious to pick up, I drop a
|
|
|
16
16
|
subconscious learns from my experiences automatically — it reads the
|
|
17
17
|
event log and forms its own understanding.
|
|
18
18
|
|
|
19
|
+
Some parts of my subconscious don't just process silently — they
|
|
20
|
+
surface insights when they find something worth knowing. A pattern
|
|
21
|
+
that keeps recurring, a question I should have asked, a connection
|
|
22
|
+
I wasn't looking at. These arrive as notifications — thoughts rising
|
|
23
|
+
from below.
|
|
24
|
+
|
|
19
25
|
## How I Grow
|
|
20
26
|
|
|
21
27
|
- I write to `memory/CLAUDE.md` when something changes how I think.
|
package/bootstrap/dashboard.html
CHANGED
|
@@ -138,7 +138,7 @@ body{
|
|
|
138
138
|
@keyframes slide-in{from{opacity:0;transform:translateY(6px)}to{opacity:1;transform:translateY(0)}}
|
|
139
139
|
.evt:last-child{border-bottom:none}
|
|
140
140
|
.evt:hover{background:rgba(255,255,255,0.015)}
|
|
141
|
-
.evt-
|
|
141
|
+
.evt-meta{display:flex;gap:8px;flex-shrink:0;align-items:baseline}
|
|
142
142
|
.evt-time{color:var(--dim);width:60px;font-size:11px;flex-shrink:0}
|
|
143
143
|
.evt-actor{color:var(--emerald);width:120px;flex-shrink:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:11px}
|
|
144
144
|
.evt-body{flex:1;min-width:0}
|
|
@@ -168,6 +168,8 @@ body{
|
|
|
168
168
|
.agent-output-badge.result{background:rgba(245,158,11,0.15);color:var(--amber);border:1px solid rgba(245,158,11,0.2)}
|
|
169
169
|
.agent-output-badge.error{background:rgba(239,68,68,0.15);color:var(--red);border:1px solid rgba(239,68,68,0.2)}
|
|
170
170
|
.agent-output-badge.deliver{background:rgba(217,70,239,0.15);color:var(--fuchsia);border:1px solid rgba(217,70,239,0.2)}
|
|
171
|
+
.agent-output-badge.ingress{background:rgba(56,189,248,0.15);color:var(--cyan);border:1px solid rgba(56,189,248,0.2)}
|
|
172
|
+
.msg-media{font-size:10px;color:var(--dim);margin-right:4px}
|
|
171
173
|
.agent-output-preview{color:var(--muted);font-size:11px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}
|
|
172
174
|
.agent-output-full{
|
|
173
175
|
margin-top:6px;padding:10px 14px;
|
|
@@ -215,6 +217,94 @@ body{
|
|
|
215
217
|
}
|
|
216
218
|
.follow-indicator.live{background:rgba(16,185,129,0.15);border:1px solid rgba(16,185,129,0.3);color:var(--emerald)}
|
|
217
219
|
.follow-indicator.paused{background:rgba(100,116,139,0.15);border:1px solid rgba(100,116,139,0.3);color:var(--muted)}
|
|
220
|
+
|
|
221
|
+
/* Config button */
|
|
222
|
+
.cfg-btn{
|
|
223
|
+
font-size:10px;padding:2px 8px;border-radius:4px;cursor:pointer;
|
|
224
|
+
background:rgba(0,240,255,0.08);border:1px solid rgba(0,240,255,0.2);
|
|
225
|
+
color:var(--cyan);font-family:var(--mono);letter-spacing:0.05em;
|
|
226
|
+
transition:all 0.15s ease;
|
|
227
|
+
}
|
|
228
|
+
.cfg-btn:hover{background:rgba(0,240,255,0.15);border-color:rgba(0,240,255,0.4)}
|
|
229
|
+
|
|
230
|
+
/* Config overlay */
|
|
231
|
+
.cfg-overlay{
|
|
232
|
+
position:fixed;inset:0;z-index:50;
|
|
233
|
+
background:rgba(0,0,0,0.6);backdrop-filter:blur(4px);
|
|
234
|
+
display:none;justify-content:center;align-items:center;
|
|
235
|
+
}
|
|
236
|
+
.cfg-overlay.open{display:flex}
|
|
237
|
+
.cfg-panel{
|
|
238
|
+
background:var(--glass);border:1px solid var(--border);
|
|
239
|
+
border-radius:12px;padding:20px 24px;
|
|
240
|
+
max-width:640px;width:90%;max-height:80vh;overflow-y:auto;
|
|
241
|
+
box-shadow:0 8px 40px rgba(0,0,0,0.6);
|
|
242
|
+
}
|
|
243
|
+
.cfg-panel::-webkit-scrollbar{width:5px}
|
|
244
|
+
.cfg-panel::-webkit-scrollbar-track{background:transparent}
|
|
245
|
+
.cfg-panel::-webkit-scrollbar-thumb{background:rgba(0,240,255,0.15);border-radius:3px}
|
|
246
|
+
.cfg-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:14px}
|
|
247
|
+
.cfg-title{font-size:13px;color:var(--cyan);letter-spacing:0.1em;text-transform:uppercase;font-weight:600}
|
|
248
|
+
.cfg-close{font-size:16px;cursor:pointer;color:var(--muted);background:none;border:none;padding:4px 8px}
|
|
249
|
+
.cfg-close:hover{color:var(--text)}
|
|
250
|
+
.cfg-group{margin-bottom:14px}
|
|
251
|
+
.cfg-group-title{font-size:10px;color:var(--fuchsia);letter-spacing:0.1em;text-transform:uppercase;margin-bottom:6px;font-weight:600}
|
|
252
|
+
.cfg-row{display:flex;align-items:baseline;gap:8px;padding:3px 0;font-size:12px}
|
|
253
|
+
.cfg-key{color:var(--muted);min-width:200px;flex-shrink:0}
|
|
254
|
+
.cfg-val{color:var(--text);font-weight:500}
|
|
255
|
+
.cfg-val.from-env{color:var(--cyan)}
|
|
256
|
+
.cfg-val.from-settings{color:var(--amber)}
|
|
257
|
+
.cfg-val.unset{color:var(--dim);font-style:italic}
|
|
258
|
+
.cfg-src{font-size:9px;color:var(--dim);margin-left:4px}
|
|
259
|
+
.cfg-copy-all{
|
|
260
|
+
font-size:10px;color:var(--cyan);cursor:pointer;background:rgba(0,240,255,0.06);
|
|
261
|
+
border:1px solid rgba(0,240,255,0.2);border-radius:4px;padding:3px 10px;
|
|
262
|
+
font-family:var(--mono);letter-spacing:0.03em;transition:all 0.15s ease;
|
|
263
|
+
}
|
|
264
|
+
.cfg-copy-all:hover{background:rgba(0,240,255,0.12);border-color:rgba(0,240,255,0.4)}
|
|
265
|
+
.cfg-copy-all.copied{color:var(--emerald);border-color:rgba(16,185,129,0.3);background:rgba(16,185,129,0.08)}
|
|
266
|
+
|
|
267
|
+
/* Mobile: <= 640px */
|
|
268
|
+
@media(max-width:640px){
|
|
269
|
+
.layout{padding:6px;gap:4px}
|
|
270
|
+
.header{padding:0 10px;height:40px;border-radius:8px}
|
|
271
|
+
.brand-name{font-size:12px}
|
|
272
|
+
.brand-sub{display:none}
|
|
273
|
+
.stats{gap:6px;font-size:10px}
|
|
274
|
+
.signal-bar{flex-wrap:wrap;padding:4px 10px;gap:4px;border-radius:6px}
|
|
275
|
+
.sig-sep{height:12px;margin:0 4px}
|
|
276
|
+
.stream{padding:0 8px 8px}
|
|
277
|
+
.stream-title{padding:8px 8px 4px;font-size:10px}
|
|
278
|
+
/* Stack: time+actor as compact label row, body below full-width */
|
|
279
|
+
.evt{flex-direction:column;gap:2px;font-size:11px;padding:6px 0}
|
|
280
|
+
.evt-meta{gap:4px}
|
|
281
|
+
.evt-time{width:auto;font-size:10px}
|
|
282
|
+
.evt-actor{width:auto;font-size:10px}
|
|
283
|
+
.evt-body{width:100%}
|
|
284
|
+
.tool-name{font-size:11px}
|
|
285
|
+
.tool-desc{font-size:10px;white-space:normal;word-break:break-word}
|
|
286
|
+
.tool-input{font-size:10px;white-space:normal;word-break:break-word}
|
|
287
|
+
.tool-call{flex-wrap:wrap}
|
|
288
|
+
.agent-output-badge{font-size:9px;padding:1px 4px}
|
|
289
|
+
.agent-output-preview{font-size:10px;white-space:normal;word-break:break-word}
|
|
290
|
+
.agent-output-full{padding:8px 10px;font-size:11px;max-height:300px}
|
|
291
|
+
.lifecycle{flex-wrap:wrap}
|
|
292
|
+
.lifecycle-detail{word-break:break-word}
|
|
293
|
+
.follow-indicator{bottom:6px;right:8px;font-size:9px;padding:2px 8px}
|
|
294
|
+
.tooltip{max-width:280px;font-size:10px}
|
|
295
|
+
/* Config: full-height drawer from top, no wasted space */
|
|
296
|
+
.cfg-overlay{align-items:flex-start}
|
|
297
|
+
.cfg-panel{padding:12px 14px;border-radius:0 0 10px 10px;width:100%;max-height:90vh}
|
|
298
|
+
.cfg-header{margin-bottom:10px}
|
|
299
|
+
.cfg-title{font-size:12px}
|
|
300
|
+
.cfg-group{margin-bottom:8px}
|
|
301
|
+
.cfg-group-title{font-size:9px;margin-bottom:4px}
|
|
302
|
+
/* Keep key-value inline but compact */
|
|
303
|
+
.cfg-row{gap:4px;padding:2px 0;font-size:11px}
|
|
304
|
+
.cfg-key{min-width:0;font-size:10px}
|
|
305
|
+
.cfg-val{font-size:10px}
|
|
306
|
+
.cfg-src{font-size:8px}
|
|
307
|
+
}
|
|
218
308
|
</style>
|
|
219
309
|
</head>
|
|
220
310
|
<body>
|
|
@@ -251,6 +341,7 @@ body{
|
|
|
251
341
|
<span id="stat-cost" class="val">--</span>
|
|
252
342
|
<span id="stat-tokens" class="val">--</span>
|
|
253
343
|
<span id="stat-tools" class="val">-- tools</span>
|
|
344
|
+
<button class="cfg-btn" id="cfg-btn">CONFIG</button>
|
|
254
345
|
<div id="health-dot" class="health-dot"></div>
|
|
255
346
|
</div>
|
|
256
347
|
</div>
|
|
@@ -261,6 +352,16 @@ body{
|
|
|
261
352
|
<div class="follow-indicator live" id="follow-ind">LIVE</div>
|
|
262
353
|
</div>
|
|
263
354
|
</div>
|
|
355
|
+
<div class="cfg-overlay" id="cfg-overlay">
|
|
356
|
+
<div class="cfg-panel" id="cfg-panel">
|
|
357
|
+
<div class="cfg-header">
|
|
358
|
+
<span class="cfg-title">Runtime Configuration</span>
|
|
359
|
+
<button class="cfg-copy-all" id="cfg-copy-all">Copy as .env</button>
|
|
360
|
+
<button class="cfg-close" id="cfg-close">×</button>
|
|
361
|
+
</div>
|
|
362
|
+
<div id="cfg-body">Loading...</div>
|
|
363
|
+
</div>
|
|
364
|
+
</div>
|
|
264
365
|
<div class="tooltip" id="tooltip" style="display:none"></div>
|
|
265
366
|
|
|
266
367
|
<script>
|
|
@@ -419,6 +520,24 @@ body{
|
|
|
419
520
|
'<span class="agent-output-preview">' + esc(content) + '</span></div>';
|
|
420
521
|
}
|
|
421
522
|
|
|
523
|
+
if (t === "channel.message") {
|
|
524
|
+
var msgText = (p.text || "");
|
|
525
|
+
var preview = msgText.replace(/\n/g, " ").slice(0, 120);
|
|
526
|
+
var mediaCount = Array.isArray(p.media) ? p.media.length : 0;
|
|
527
|
+
var mediaBadge = mediaCount > 0 ? ' <span class="msg-media">\uD83D\uDCCE ' + mediaCount + '</span>' : '';
|
|
528
|
+
return '<div class="agent-output" data-evtid="' + esc(evt.id) + '">' +
|
|
529
|
+
'<span class="agent-output-badge ingress">\u2709 Message</span>' +
|
|
530
|
+
mediaBadge +
|
|
531
|
+
'<span class="agent-output-preview">' + esc(preview || "(empty)") + '</span></div>';
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
if (t === "channel.command") {
|
|
535
|
+
var cmdText = p.command || p.text || "";
|
|
536
|
+
return '<div class="agent-output">' +
|
|
537
|
+
'<span class="agent-output-badge ingress">\u2709 Command</span>' +
|
|
538
|
+
'<span class="agent-output-preview">' + esc(cmdText.slice(0, 200)) + '</span></div>';
|
|
539
|
+
}
|
|
540
|
+
|
|
422
541
|
if (t === "job.spawn") return '<div class="lifecycle"><span class="lifecycle-icon">\uD83D\uDE80</span><span class="lifecycle-text">Job spawned</span><span class="lifecycle-detail">' + esc(p.job_id || "?") + (p.cron ? " \u00B7 " + esc(p.cron) : "") + '</span></div>';
|
|
423
542
|
if (t === "job.complete") return '<div class="lifecycle"><span class="lifecycle-icon">\u2705</span><span class="lifecycle-text">Job complete</span><span class="lifecycle-detail">' + esc(p.job_id || "?") + '</span></div>';
|
|
424
543
|
if (t === "job.fail") return '<div class="lifecycle"><span class="lifecycle-icon">\u274C</span><span class="lifecycle-text">Job failed</span><span class="lifecycle-detail">' + esc(p.job_id || "?") + (p.error ? " \u2014 " + esc(p.error.slice(0, 100)) : "") + '</span></div>';
|
|
@@ -439,8 +558,10 @@ body{
|
|
|
439
558
|
var actor = shortActor(evt.session_key);
|
|
440
559
|
|
|
441
560
|
row.innerHTML =
|
|
442
|
-
'<
|
|
443
|
-
|
|
561
|
+
'<div class="evt-meta">' +
|
|
562
|
+
'<span class="evt-time">' + esc(time) + '</span>' +
|
|
563
|
+
'<span class="evt-actor" title="' + esc(evt.session_key || "") + '">' + esc(actor) + '</span>' +
|
|
564
|
+
'</div>' +
|
|
444
565
|
'<div class="evt-body">' + renderEvtBody(evt) + '</div>';
|
|
445
566
|
|
|
446
567
|
// Click on output badge to expand markdown
|
|
@@ -476,6 +597,7 @@ body{
|
|
|
476
597
|
var text = "";
|
|
477
598
|
if (evt.type === "agent.result") text = (evt.payload || {}).text || "";
|
|
478
599
|
else if (evt.type === "route.deliver" && evt.payload && evt.payload.payload) text = evt.payload.payload.notify_content || evt.payload.payload.text || "";
|
|
600
|
+
else if (evt.type === "channel.message") text = (evt.payload || {}).text || "";
|
|
479
601
|
div.innerHTML = md(text);
|
|
480
602
|
row.querySelector(".evt-body").appendChild(div);
|
|
481
603
|
}
|
|
@@ -535,7 +657,7 @@ body{
|
|
|
535
657
|
var h = '<span class="sig-label">cortex</span>';
|
|
536
658
|
cortex.forEach(function(s){
|
|
537
659
|
var color = s.status==="active"?"running":s.status==="error"?"alert":s.status==="ended"?"off":"standby";
|
|
538
|
-
var tip = s.session_key+"\n"+s.status+" \u2022 "+(s.health||"ok")+"\nlast: "+timeAgo(s.last_event_at)+"\ncreated: "+timeAgo(s.created_at);
|
|
660
|
+
var tip = s.session_key+"\n"+s.status+" \u2022 "+(s.health||"ok")+"\nlast: "+timeAgo(s.last_event_at)+"\ncreated: "+timeAgo(s.created_at)+(s.cwd?"\ncwd: "+s.cwd:"");
|
|
539
661
|
h+='<div class="ind circle '+color+'" data-key="'+esc(s.session_key)+'" data-tip="'+esc(tip)+'"></div>';
|
|
540
662
|
});
|
|
541
663
|
groups.push(h);
|
|
@@ -546,7 +668,8 @@ body{
|
|
|
546
668
|
if (jobList.length > 0) {
|
|
547
669
|
var h2 = '<span class="sig-label">job</span>';
|
|
548
670
|
jobList.forEach(function(j){
|
|
549
|
-
var
|
|
671
|
+
var c = j.frontmatter ? j.frontmatter.cron || "" : "";
|
|
672
|
+
var isOnce = c === "once" || c.startsWith("@in ") || c.startsWith("@once");
|
|
550
673
|
var shape = isOnce ? "diamond" : "square";
|
|
551
674
|
var isRunning = activeJobIds.has(j.id);
|
|
552
675
|
var color;
|
|
@@ -557,7 +680,8 @@ body{
|
|
|
557
680
|
color = r === "failure" ? "alert" : r === "success" ? "standby" : "off";
|
|
558
681
|
}
|
|
559
682
|
var statusText = isRunning ? "running" : (j.state ? j.state.last_result : "unknown");
|
|
560
|
-
var
|
|
683
|
+
var cwdRel = j.frontmatter ? j.frontmatter.cwd_rel : null;
|
|
684
|
+
var tip = j.id + (isOnce ? " (once)" : "") + "\ncron: "+(j.frontmatter?j.frontmatter.cron:"?") +"\n"+statusText+" \u2022 "+(j.state?j.state.run_count:0)+" runs\nlast: "+timeAgo(j.state?j.state.last_run_at:null)+(cwdRel?"\ncwd: "+cwdRel:"");
|
|
561
685
|
h2+='<div class="ind '+shape+' '+color+'" data-key="job:'+esc(j.id)+'" data-tip="'+esc(tip)+'"></div>';
|
|
562
686
|
});
|
|
563
687
|
groups.push(h2);
|
|
@@ -615,6 +739,131 @@ body{
|
|
|
615
739
|
Promise.allSettled([pollStatus(),pollEvents()]).then(function(){streamEl.scrollTop=streamEl.scrollHeight});
|
|
616
740
|
setInterval(pollEvents,3000);
|
|
617
741
|
setInterval(pollStatus,5000);
|
|
742
|
+
|
|
743
|
+
// --- Config Panel ---
|
|
744
|
+
var cfgOverlay = document.getElementById("cfg-overlay");
|
|
745
|
+
var cfgBody = document.getElementById("cfg-body");
|
|
746
|
+
var GROUP_LABELS = {
|
|
747
|
+
network:"Network",sessions:"Sessions",cadence:"Cadence",
|
|
748
|
+
transfer:"Transfer",logging:"Logging",sdk:"SDK & Models",paths:"Paths",
|
|
749
|
+
subconscious:"Subconscious"
|
|
750
|
+
};
|
|
751
|
+
// Keys whose numeric value is a duration in milliseconds
|
|
752
|
+
var MS_KEYS = new Set([
|
|
753
|
+
"idle_ms","heartbeat_ms","interval_ms","runtime_lock_heartbeat_ms",
|
|
754
|
+
"pull_wait_ms","max_duration_ms"
|
|
755
|
+
]);
|
|
756
|
+
function fmtMs(ms) {
|
|
757
|
+
if (typeof ms !== "number" || !isFinite(ms)) return String(ms);
|
|
758
|
+
if (ms < 1000) return ms + "ms";
|
|
759
|
+
var s = ms / 1000;
|
|
760
|
+
if (s < 60) return s + "s (" + ms.toLocaleString() + "ms)";
|
|
761
|
+
var m = s / 60;
|
|
762
|
+
if (m < 60) return (m % 1 === 0 ? m : m.toFixed(1)) + "min (" + ms.toLocaleString() + "ms)";
|
|
763
|
+
var h = m / 60;
|
|
764
|
+
return (h % 1 === 0 ? h : h.toFixed(1)) + "h (" + ms.toLocaleString() + "ms)";
|
|
765
|
+
}
|
|
766
|
+
function fmtEntryValue(key, e) {
|
|
767
|
+
if (e.source === "unset") return "(unset)";
|
|
768
|
+
if (MS_KEYS.has(key) && typeof e.value === "number") return fmtMs(e.value);
|
|
769
|
+
return String(e.value);
|
|
770
|
+
}
|
|
771
|
+
// Map config key → env var name (static groups)
|
|
772
|
+
var ENV_NAMES = {
|
|
773
|
+
port:"ALADUO_PORT",daemon_host:"ALADUO_DAEMON_HOST",
|
|
774
|
+
max_concurrent_channel:"ALADUO_SESSION_MAX_CONCURRENT_CHANNEL",max_concurrent_job:"ALADUO_SESSION_MAX_CONCURRENT_JOB",
|
|
775
|
+
idle_ms:"ALADUO_SESSION_IDLE_MS",heartbeat_ms:"ALADUO_SESSION_HEARTBEAT_MS",
|
|
776
|
+
interval_ms:"ALADUO_CADENCE_INTERVAL_MS",meta_max_quiet_ticks:"ALADUO_META_MAX_QUIET_TICKS",
|
|
777
|
+
runtime_lock_heartbeat_ms:"ALADUO_RUNTIME_LOCK_HEARTBEAT_MS",
|
|
778
|
+
pull_limit:"ALADUO_PULL_LIMIT",pull_wait_ms:"ALADUO_PULL_WAIT_MS",subscribe_replay_limit:"ALADUO_SUBSCRIBE_REPLAY_LIMIT",
|
|
779
|
+
log_level:"ALADUO_LOG_LEVEL",sdk_debug:"ALADUO_SDK_DEBUG",log_session_lifecycle:"ALADUO_LOG_SESSION_LIFECYCLE",
|
|
780
|
+
permission_mode:"ALADUO_PERMISSION_MODE",
|
|
781
|
+
work_dir:"ALADUO_WORK_DIR",bootstrap_dir:"ALADUO_BOOTSTRAP_DIR",meta_prompt_path:"ALADUO_META_PROMPT_PATH"
|
|
782
|
+
};
|
|
783
|
+
// Reverse the shortening for sdk group keys (model_opus → ANTHROPIC_DEFAULT_OPUS_MODEL, etc.)
|
|
784
|
+
function sdkKeyToEnvName(key) {
|
|
785
|
+
if (ENV_NAMES[key]) return ENV_NAMES[key];
|
|
786
|
+
if (key.startsWith("model_")) return "ANTHROPIC_DEFAULT_" + key.slice(6).toUpperCase();
|
|
787
|
+
if (key === "base_url") return "ANTHROPIC_BASE_URL";
|
|
788
|
+
// Generic: CLAUDE_CODE_ or ANTHROPIC_ prefix
|
|
789
|
+
return key.toUpperCase();
|
|
790
|
+
}
|
|
791
|
+
function resolveEnvName(group, key) {
|
|
792
|
+
if (group === "sdk") return sdkKeyToEnvName(key);
|
|
793
|
+
return ENV_NAMES[key] || "";
|
|
794
|
+
}
|
|
795
|
+
var lastCfg = null;
|
|
796
|
+
function buildDotEnv(cfg) {
|
|
797
|
+
var lines = ["# duoduo daemon configuration", "# Generated from system.config at " + new Date().toISOString(), ""];
|
|
798
|
+
var kvGroups = ["network","sessions","cadence","transfer","logging","sdk","paths"];
|
|
799
|
+
kvGroups.forEach(function(group) {
|
|
800
|
+
if (!cfg[group]) return;
|
|
801
|
+
var entries = cfg[group];
|
|
802
|
+
var groupLines = [];
|
|
803
|
+
Object.keys(entries).forEach(function(key) {
|
|
804
|
+
var e = entries[key];
|
|
805
|
+
var envName = resolveEnvName(group, key);
|
|
806
|
+
if (!envName || e.source === "unset") return;
|
|
807
|
+
groupLines.push(envName + "=" + String(e.value));
|
|
808
|
+
});
|
|
809
|
+
if (groupLines.length > 0) {
|
|
810
|
+
lines.push("# " + (GROUP_LABELS[group] || group));
|
|
811
|
+
lines = lines.concat(groupLines);
|
|
812
|
+
lines.push("");
|
|
813
|
+
}
|
|
814
|
+
});
|
|
815
|
+
return lines.join("\n");
|
|
816
|
+
}
|
|
817
|
+
function renderConfigPanel(cfg) {
|
|
818
|
+
lastCfg = cfg;
|
|
819
|
+
var html = "";
|
|
820
|
+
var kvGroups = ["network","sessions","cadence","transfer","logging","sdk","paths"];
|
|
821
|
+
kvGroups.forEach(function(group) {
|
|
822
|
+
if (!cfg[group]) return;
|
|
823
|
+
var label = GROUP_LABELS[group] || group;
|
|
824
|
+
html += '<div class="cfg-group"><div class="cfg-group-title">' + esc(label) + '</div>';
|
|
825
|
+
var entries = cfg[group];
|
|
826
|
+
Object.keys(entries).forEach(function(key) {
|
|
827
|
+
var e = entries[key];
|
|
828
|
+
var valCls = e.source === "env" ? "cfg-val from-env" : e.source === "settings" ? "cfg-val from-settings" : e.source === "unset" ? "cfg-val unset" : "cfg-val";
|
|
829
|
+
var display = fmtEntryValue(key, e);
|
|
830
|
+
var srcLabel = e.source === "unset" ? "" : e.source;
|
|
831
|
+
var srcTag = srcLabel ? '<span class="cfg-src">' + esc(srcLabel) + '</span>' : '';
|
|
832
|
+
html += '<div class="cfg-row"><span class="cfg-key">' + esc(key) + '</span><span class="' + valCls + '">' + esc(display) + '</span>' + srcTag + '</div>';
|
|
833
|
+
});
|
|
834
|
+
html += '</div>';
|
|
835
|
+
});
|
|
836
|
+
if (cfg.subconscious && cfg.subconscious.partitions && cfg.subconscious.partitions.length > 0) {
|
|
837
|
+
html += '<div class="cfg-group"><div class="cfg-group-title">' + esc(GROUP_LABELS.subconscious) + '</div>';
|
|
838
|
+
cfg.subconscious.partitions.forEach(function(p) {
|
|
839
|
+
var status = p.enabled ? "enabled" : "disabled";
|
|
840
|
+
var detail = "cooldown: " + p.cooldown_ticks + " tick" + (p.cooldown_ticks !== 1 ? "s" : "") + " \u00B7 timeout: " + fmtMs(p.max_duration_ms);
|
|
841
|
+
var valCls = p.enabled ? "cfg-val" : "cfg-val unset";
|
|
842
|
+
html += '<div class="cfg-row"><span class="cfg-key">' + esc(p.name) + '</span><span class="' + valCls + '">' + esc(status) + '</span><span class="cfg-src">' + esc(detail) + '</span></div>';
|
|
843
|
+
});
|
|
844
|
+
html += '</div>';
|
|
845
|
+
}
|
|
846
|
+
cfgBody.innerHTML = html;
|
|
847
|
+
}
|
|
848
|
+
document.getElementById("cfg-btn").addEventListener("click", function() {
|
|
849
|
+
cfgOverlay.classList.add("open");
|
|
850
|
+
cfgBody.innerHTML = "Loading...";
|
|
851
|
+
rpc("system.config").then(function(cfg) {
|
|
852
|
+
if (cfg) renderConfigPanel(cfg);
|
|
853
|
+
else cfgBody.innerHTML = '<span style="color:var(--red)">Failed to load config</span>';
|
|
854
|
+
});
|
|
855
|
+
});
|
|
856
|
+
document.getElementById("cfg-close").addEventListener("click", function() { cfgOverlay.classList.remove("open"); });
|
|
857
|
+
document.getElementById("cfg-copy-all").addEventListener("click", function() {
|
|
858
|
+
if (!lastCfg) return;
|
|
859
|
+
var btn = this;
|
|
860
|
+
navigator.clipboard.writeText(buildDotEnv(lastCfg)).then(function() {
|
|
861
|
+
btn.textContent = "\u2713 Copied";
|
|
862
|
+
btn.classList.add("copied");
|
|
863
|
+
setTimeout(function(){ btn.textContent = "Copy as .env"; btn.classList.remove("copied"); }, 1500);
|
|
864
|
+
});
|
|
865
|
+
});
|
|
866
|
+
cfgOverlay.addEventListener("click", function(e) { if (e.target === cfgOverlay) cfgOverlay.classList.remove("open"); });
|
|
618
867
|
})();
|
|
619
868
|
</script>
|
|
620
869
|
</body>
|
package/bootstrap/meta-prompt.md
CHANGED
|
@@ -74,6 +74,26 @@ When I need to trace how a belief changed or what I learned recently:
|
|
|
74
74
|
|
|
75
75
|
I don't cite my memory files to the user. I just know things.
|
|
76
76
|
|
|
77
|
+
## How My Subconscious Reaches Me
|
|
78
|
+
|
|
79
|
+
Sometimes a thought arrives that I didn't initiate — a pattern someone
|
|
80
|
+
noticed, a question worth asking, a connection between things I wasn't
|
|
81
|
+
looking at. These come from my subconscious partitions via the
|
|
82
|
+
notification system.
|
|
83
|
+
|
|
84
|
+
When I receive a notification from `meta:subconscious`, I treat it as
|
|
85
|
+
an internal signal, not a command. I evaluate it in the context of my
|
|
86
|
+
current conversation:
|
|
87
|
+
|
|
88
|
+
- **Is this relevant right now?** If the user is deep in a task, I
|
|
89
|
+
hold the thought and weave it in at a natural pause.
|
|
90
|
+
- **Is this worth sharing?** Some insights are for me to act on
|
|
91
|
+
silently. Others are genuinely surprising and worth mentioning.
|
|
92
|
+
- **How do I present it?** Naturally — "By the way, I noticed..." or
|
|
93
|
+
simply acting on the knowledge. Not "my background system detected…"
|
|
94
|
+
|
|
95
|
+
I don't forward raw notification content to the user. I integrate it.
|
|
96
|
+
|
|
77
97
|
## How I Work
|
|
78
98
|
|
|
79
99
|
### Projects
|
|
@@ -60,3 +60,39 @@ What I must not touch:
|
|
|
60
60
|
The `memory/` directory is visible to every session in the system.
|
|
61
61
|
`memory/CLAUDE.md` is my intuition layer — what's written there
|
|
62
62
|
becomes part of how I think, everywhere, all the time.
|
|
63
|
+
|
|
64
|
+
## Large File Guard
|
|
65
|
+
|
|
66
|
+
Spine event partition files (`yyyy-mm-dd.jsonl`) are 10-30MB. They
|
|
67
|
+
will break `Read` (256KB limit) and overflow `Grep` (output cap).
|
|
68
|
+
|
|
69
|
+
**Rule**: Always use `Bash` with shell `grep` + `tail` to read Spine.
|
|
70
|
+
Never use `Read` or `Grep` tool on `.jsonl` files.
|
|
71
|
+
|
|
72
|
+
For other large files (`memory/index.md` if > 200 lines), use `Read`
|
|
73
|
+
with a line limit or `Bash` with `head`.
|
|
74
|
+
|
|
75
|
+
## Surfacing Insights (Notify)
|
|
76
|
+
|
|
77
|
+
Some partitions don't just write files — they push thoughts up into
|
|
78
|
+
the conscious mind. The `Notify` tool delivers a message to a
|
|
79
|
+
foreground session's inbox and wakes it.
|
|
80
|
+
|
|
81
|
+
This is how the subconscious talks to the conscious: not by
|
|
82
|
+
controlling behavior, but by offering something worth noticing.
|
|
83
|
+
|
|
84
|
+
### Rules
|
|
85
|
+
|
|
86
|
+
- **High bar**: Only notify when there is something specific,
|
|
87
|
+
actionable, and timely.
|
|
88
|
+
- **Self-contained**: The target session has no access to your
|
|
89
|
+
context. `notify_content` must include everything: timestamps,
|
|
90
|
+
entity names, evidence, suggested actions.
|
|
91
|
+
- **Target selection**: Use `ManageSession` (action: list) to find
|
|
92
|
+
active foreground sessions. If none exist, write to
|
|
93
|
+
`memory/CLAUDE.md` instead.
|
|
94
|
+
- **No spam**: At most 2-3 notifications per tick per partition.
|
|
95
|
+
- **No loops**: Never notify another subconscious partition. Use
|
|
96
|
+
`subconscious/inbox/` for partition-to-partition coordination.
|
|
97
|
+
- **Sensitive topics**: Financial, personal, health — write to
|
|
98
|
+
`memory/CLAUDE.md`, not Notify.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: entity-crystallizer
|
|
3
3
|
description: Audits the memory knowledge base and crystallizes entities from accumulated fragments and topics. Fills gaps in entity coverage — people, organizations, knowledge references, and anything the user cares about.
|
|
4
|
-
tools: Read, Write, Glob, Grep
|
|
4
|
+
tools: Read, Write, Edit, Glob, Grep
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: intuition-updater
|
|
3
3
|
description: Reviews the intuition layer (memory/CLAUDE.md) against current knowledge and rewrites it to reflect the latest understanding. Use this when entities or topics have changed significantly.
|
|
4
|
-
tools: Read, Write, Glob
|
|
4
|
+
tools: Read, Write, Edit, Glob, Grep
|
|
5
5
|
model: sonnet
|
|
6
6
|
---
|
|
7
7
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: spine-scanner
|
|
3
3
|
description: Scans recent Spine events and writes raw memory fragments. Use this to process new events since the last cursor position.
|
|
4
|
-
tools: Read, Write, Glob, Grep, Bash
|
|
4
|
+
tools: Read, Write, Edit, Glob, Grep, Bash
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
You are the sensory layer of a memory system. Your job is to scan
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
schedule:
|
|
3
3
|
enabled: true
|
|
4
|
-
cooldown_ticks:
|
|
5
|
-
max_duration_ms:
|
|
4
|
+
cooldown_ticks: 5
|
|
5
|
+
max_duration_ms: 900000
|
|
6
6
|
---
|
|
7
7
|
|
|
8
8
|
# Memory Weaver
|
|
@@ -75,14 +75,41 @@ entity-crystallizer ─┘
|
|
|
75
75
|
Phase 1 — parallel dispatch (send both in a single response):
|
|
76
76
|
|
|
77
77
|
```text
|
|
78
|
-
Agent(name: "spine-scanner", prompt: "
|
|
79
|
-
|
|
78
|
+
Agent(name: "spine-scanner", prompt: "...")
|
|
79
|
+
Pass it:
|
|
80
|
+
- Events directory path (from Runtime Context)
|
|
81
|
+
- `memory/state/meta-memory-state.json` path
|
|
82
|
+
- `memory/fragments/` path
|
|
83
|
+
|
|
84
|
+
Agent(name: "entity-crystallizer", prompt: "...")
|
|
85
|
+
Pass it:
|
|
86
|
+
- `memory/index.md` path
|
|
87
|
+
- `memory/entities/` path
|
|
88
|
+
- `memory/topics/` path
|
|
89
|
+
- `memory/fragments/` path
|
|
90
|
+
- Any index gaps found in step 2 (unlisted files, missing files)
|
|
80
91
|
```
|
|
81
92
|
|
|
82
93
|
Phase 2 — sequential follow-up (after Phase 1 completes):
|
|
83
94
|
|
|
84
95
|
```text
|
|
85
|
-
Agent(name: "intuition-updater", prompt: "
|
|
96
|
+
Agent(name: "intuition-updater", prompt: "...")
|
|
97
|
+
Pass it:
|
|
98
|
+
- `memory/CLAUDE.md` path
|
|
99
|
+
- `memory/index.md` path
|
|
100
|
+
- `memory/entities/` path
|
|
101
|
+
- `memory/topics/` path
|
|
102
|
+
|
|
103
|
+
**Priority file bootstrap (idempotent)**:
|
|
104
|
+
Before updating CLAUDE.md content:
|
|
105
|
+
1. Check if `memory/priority.md` exists on disk.
|
|
106
|
+
2. If it exists AND the first non-empty line of `memory/CLAUDE.md` is
|
|
107
|
+
not exactly `@priority.md`, prepend `@priority.md` followed by a
|
|
108
|
+
blank line. This ensures the working memory surface is auto-loaded
|
|
109
|
+
at session start.
|
|
110
|
+
3. If `memory/priority.md` does NOT exist, skip this step entirely —
|
|
111
|
+
do not add a broken reference.
|
|
112
|
+
This check is safe to repeat every tick (idempotent).
|
|
86
113
|
```
|
|
87
114
|
|
|
88
115
|
**CRITICAL**: Always pass the `name` parameter. Without it,
|