symbiot 0.50.0 → 0.50.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/CHANGELOG.md +6 -0
- package/agents.mjs +18 -1
- package/package.json +1 -1
- package/ui.mjs +8 -3
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
What each Symbiot release brought, newest first. When Approve bumps the version, it writes that release here from the tasks it approved, worded as release notes by your connected AI (or in the tasks' own words without one). The app shows the releases newer than yours under "What's new" when it offers an update, and once more after you update.
|
|
4
4
|
|
|
5
|
+
## 0.50.1
|
|
6
|
+
|
|
7
|
+
- **A permission you pick is given.** When an agent asks for one ("let agents read ~/.config/symbiot/screens") and you choose it in the app, Symbiot grants it then and there (as Settings' grant boxes do) and the agent carries on, instead of leaving it as a step for you to do by hand. Steps only you can do, like pasting your own message, are still yours.
|
|
8
|
+
- **The chat on home rolls.** Newest at the bottom by the input; older lines fade as they rise and dissolve before they reach the droplets. Point at it or scroll back to read the whole conversation clearly.
|
|
9
|
+
- **No asterisks in names.** An agent's name on home no longer shows its markdown (`**What's needed:**`).
|
|
10
|
+
|
|
5
11
|
## 0.50.0
|
|
6
12
|
|
|
7
13
|
- **The Map, by nearest neighbours.** Your repos are droplets in the liquid, each beside the repos most like it: what they're built with, the weeks you work on them, and what they're about (from their READMEs). The closest merge into one shape, the next are joined by a thread of liquid; each cluster is named over its region; what you haven't touched in a while is smaller and sinks; "you are here" sits where your recent work is. Point at a repo to see its neighbours and why; click for its details, now with **Most like it** and your last 12 weeks. Zoom and fit buttons, and the languages and tools you build with underneath instead of a coloured legend. Screens folds away.
|
package/agents.mjs
CHANGED
|
@@ -573,6 +573,20 @@ function agentQuestions(path, repo) {
|
|
|
573
573
|
// An answer that picks a "👤 You:" option is a step for the user: they're told
|
|
574
574
|
// it's still theirs (`yours`), and the next run waits for it (waiting.json).
|
|
575
575
|
// A folder a run started in before a restart can be answered too (runs.json).
|
|
576
|
+
// A permission picked in the app is given then and there: the click is the user's
|
|
577
|
+
// say-so. "let agents read ~/x" / "allow /x" becomes --add-dir, "let agents run
|
|
578
|
+
// \`tool\`" a grant for that tool (never sudo/rm), as Settings' grant boxes do. What's
|
|
579
|
+
// granted isn't a step left for the user. Returns what was granted, or null.
|
|
580
|
+
function grantFromStep(step) {
|
|
581
|
+
const t = String(step || "");
|
|
582
|
+
if (!/only you: a permission|\b(let|allow) (the )?agents?\b|\ballow (access|reading)\b/i.test(t)) return null;
|
|
583
|
+
const got = [];
|
|
584
|
+
const dir = (t.match(/(~\/[^\s,;)`'"]+|\/(?:home|Users|opt|srv|mnt|media|tmp)\/[^\s,;)`'"]+)/) || [])[1];
|
|
585
|
+
if (dir) { const d = dir.replace(/^~(?=\/)/, homedir()).replace(/[.]$/, ""); const r = grantAgent({ dir: d }); if (r && r.ok) got.push(d); }
|
|
586
|
+
const tool = (t.match(/\b(?:run|use)\s+`([A-Za-z0-9._+-]+)`/) || [])[1];
|
|
587
|
+
if (tool && !/^(sudo|rm|dd|mkfs)$/.test(tool)) { const r = grantAgent({ tool }); if (r && r.ok) got.push(tool); }
|
|
588
|
+
return got.length ? got : null;
|
|
589
|
+
}
|
|
576
590
|
const stepText = (a) => String(a).split("🤖")[0].replace(/^\s*👤\s*(You:)?\s*/, "").replace(/\s*\(recommended\)\s*$/i, "").trim();
|
|
577
591
|
function answerQuestions(path, answers, opts = {}) {
|
|
578
592
|
path = String(path || "");
|
|
@@ -594,7 +608,9 @@ function answerQuestions(path, answers, opts = {}) {
|
|
|
594
608
|
const youOpts = (q) => ((asked.get(qKey(q)) || {}).options || []).filter(yourStep).map((o) => o.split("🤖")[0]);
|
|
595
609
|
const steps = rows.filter((x) => yourStep(x.a) || holdAnswer(x.a)).map((x) => yourStep(x.a) ? { step: stepText(x.a), named: x.a.split("🤖")[0] }
|
|
596
610
|
: { step: (youOpts(x.q).map(stepText)[0] || x.a).trim(), named: [x.a, ...youOpts(x.q)].join(" ") });
|
|
597
|
-
const
|
|
611
|
+
const granted = [];
|
|
612
|
+
const yours = steps.map((s) => s.step).filter(Boolean).filter((st) => { const g = grantFromStep(st); if (g) granted.push(...g); return !g; });
|
|
613
|
+
if (granted.length) out.granted = granted;
|
|
598
614
|
if (yours.length) {
|
|
599
615
|
const step = yours.join(" "), files = namedFiles(steps.map((s) => s.named).join(" "), path);
|
|
600
616
|
try { writeFileSync(join(path, ".symbiot", WAITING), JSON.stringify({ step, files: files.map((f) => ({ ...f, sig: fileSig(f.path) })), cmd: runCmd(path), at: Date.now(), rerun: !!opts.rerun })); } catch {}
|
|
@@ -604,6 +620,7 @@ function answerQuestions(path, answers, opts = {}) {
|
|
|
604
620
|
return out;
|
|
605
621
|
}
|
|
606
622
|
if (opts.rerun) { const e = runHandoff(path); if (e && e.busy) out.note = "Answers saved. An agent is still running in that folder, so another wasn't started. Send them again once it finishes."; else if (e && e.blocked) out.note = "Answers saved. " + e.note; else if (e) out.rerun = e.id; else out.note = "Answers saved. Set an agent command in Settings to have the agent pick them up automatically."; }
|
|
623
|
+
if (out.granted) out.note = "Allowed " + out.granted.join(", ") + " for your agents" + (opts.rerun ? ", and carried on. " : ". ") + (out.note || "");
|
|
607
624
|
return out;
|
|
608
625
|
}
|
|
609
626
|
// The open tasks' titles in a held brief (TASKS.next.md), or null if nothing is held.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "symbiot",
|
|
3
|
-
"version": "0.50.
|
|
3
|
+
"version": "0.50.1",
|
|
4
4
|
"description": "Your week, written from your real work. Reads your local git and writes it up with the AI of your choice — Claude, OpenAI, Gemini, or a local model.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
package/ui.mjs
CHANGED
|
@@ -65,7 +65,10 @@ body.lq-light #lqask::placeholder{color:#6A7480}
|
|
|
65
65
|
#lqsend{flex:none;width:40px;height:40px;padding:0;display:grid;place-items:center;border-radius:50%;border:0;background:#F3F0EA;color:#0A0B0D;cursor:pointer}
|
|
66
66
|
body.lq-light #lqsend{background:#151A21;color:#FFFFFF}
|
|
67
67
|
body.lq-look-pearl #lqsend{background:linear-gradient(180deg,#FFFFFF 0%,#D9DDE2 45%,#9AA1AA 55%,#E8EAED 100%);color:#1B1E23;box-shadow:0 2px 6px rgba(0,0,0,.18),inset 0 0 0 1px rgba(0,0,0,.08)}
|
|
68
|
-
#lqtalk{position:absolute;left:50%;bottom:88px;transform:translateX(-50%);width:min(640px,calc(100vw - 32px));display:flex;flex-direction:column;gap:16px;z-index:3;max-height:
|
|
68
|
+
#lqtalk{position:absolute;left:50%;bottom:88px;transform:translateX(-50%);width:min(640px,calc(100vw - 32px));display:flex;flex-direction:column;gap:16px;z-index:3;max-height:min(34vh,320px);overflow-x:hidden;overflow-y:auto;scrollbar-width:none;padding-top:90px;scroll-behavior:smooth;-webkit-mask-image:linear-gradient(to bottom,transparent 0,rgba(0,0,0,.12) 22%,rgba(0,0,0,.6) 48%,#000 72%);mask-image:linear-gradient(to bottom,transparent 0,rgba(0,0,0,.12) 22%,rgba(0,0,0,.6) 48%,#000 72%);transition:max-height .3s ease}
|
|
69
|
+
#lqtalk:hover,#lqtalk:focus-within,#lqtalk.back{max-height:min(56vh,560px);-webkit-mask-image:linear-gradient(to bottom,transparent 0,#000 60px);mask-image:linear-gradient(to bottom,transparent 0,#000 60px)}
|
|
70
|
+
#lqtalk .lqmsg{transition:opacity .4s ease}
|
|
71
|
+
#lqtalk:hover .lqmsg,#lqtalk.back .lqmsg{opacity:1 !important}
|
|
69
72
|
#lqtalk::-webkit-scrollbar{display:none}
|
|
70
73
|
.lqmsg{align-self:flex-start;max-width:92%;padding:0;border:0;background:none;color:#ECE9E4;font-size:15px;line-height:1.55;white-space:pre-wrap;overflow-wrap:anywhere;text-shadow:0 1px 12px rgba(0,0,0,.7)}
|
|
71
74
|
.lqmsg.me{align-self:flex-end;max-width:80%;padding:8px 14px;border-radius:18px;background:rgba(255,255,255,.09);color:#C9CDD3;font-size:14.5px;text-shadow:none}
|
|
@@ -1888,7 +1891,7 @@ a.tx=Math.max(a.r+60,Math.min(S.w-a.r-60,a.tx));a.ty=Math.max(140+a.r,Math.min(S
|
|
|
1888
1891
|
var bad=false;for(var i2=0;i2<n&&!bad;i2++)for(var j2=i2+1;j2<n;j2++){var p=L[i2],q=L[j2],ddx=p.tx-q.tx,ddy=p.ty-q.ty;if(Math.sqrt(ddx*ddx+ddy*ddy)<lqSep(p,q,ddx,ddy)-1){bad=true;break;}}
|
|
1889
1892
|
if(!bad)return true;L.forEach(function(x){x.r*=0.9;});coreR*=0.9;}return false;}
|
|
1890
1893
|
function lqLabels(){var el=$('lqdrops');if(!el)return;
|
|
1891
|
-
el.innerHTML=LQ.drops.map(function(d,i){return "<div class='lqd lq-"+d.kind+(LQ.openTag===d.id?' open':'')+"' data-i='"+i+"'><button type='button' class='lt' aria-expanded='"+(LQ.openTag===d.id)+"'><span>"+esc(d.title)+"</span>"+(d.kind==='proj'&&d.sub?"<span class='lm'>"+esc(d.sub)+"</span>":"")+"</button>"+(d.sub?"<small>"+esc(d.sub)+"</small>":"")+"<button type='button' class='lgo'>Open ›</button></div>";}).join('');
|
|
1894
|
+
el.innerHTML=LQ.drops.map(function(d,i){return "<div class='lqd lq-"+d.kind+(LQ.openTag===d.id?' open':'')+"' data-i='"+i+"'><button type='button' class='lt' aria-expanded='"+(LQ.openTag===d.id)+"'><span>"+esc(String(d.title||'').split('**').join(''))+"</span>"+(d.kind==='proj'&&d.sub?"<span class='lm'>"+esc(d.sub)+"</span>":"")+"</button>"+(d.sub?"<small>"+esc(d.sub)+"</small>":"")+"<button type='button' class='lgo'>Open ›</button></div>";}).join('');
|
|
1892
1895
|
LQ.btns=[];LQ.lts=[];el.querySelectorAll('.lqd').forEach(function(b){LQ.btns.push(b);var d=LQ.drops[+b.getAttribute('data-i')],lt=b.querySelector('.lt'),go=b.querySelector('.lgo');LQ.lts.push(lt);
|
|
1893
1896
|
if(lt)lt.addEventListener('click',function(ev){lqOpen(d,ev);});
|
|
1894
1897
|
if(go)go.addEventListener('click',function(ev){LQ.openTag=null;lqOpen(d,ev);});});
|
|
@@ -1923,7 +1926,9 @@ function lqPool(shape){if(shape==='map'&&LM.items&&LM.items.length)setTimeout(fu
|
|
|
1923
1926
|
function lqSink(){lqRemember();var b=document.body;if(b&&b.classList)b.classList.remove('lq-pooled');LQ.mode='aware';lqAct();lqLoad(false);}
|
|
1924
1927
|
function lqAct(){LQ.lastAct=Date.now();if(LQ.mode==='rest'){LQ.mode='aware';lqLoad(true);}}
|
|
1925
1928
|
function lqTalkMode(on){LQ.talking=on;var b=document.body;if(b&&b.classList)b.classList.toggle('lq-talking',on);}
|
|
1926
|
-
function lqTalkShow(wait){var el=$('lqtalk');if(!el)return;el.innerHTML=LQ.talk.map(function(m){return "<div class='lqmsg"+(m.me?' me':'')+"'>"+esc(m.text)+stepsHtml(m.steps)+"</div>";}).join('')+(wait?"<div class='lqmsg thinking'>Thinking: recalling what it knows, reading what's here…</div>":'');if(el.scrollHeight)el.scrollTop=el.scrollHeight;
|
|
1929
|
+
function lqTalkShow(wait){var el=$('lqtalk');if(!el)return;var n=LQ.talk.length;el.innerHTML=LQ.talk.map(function(m,i){var age=n-1-i,op=age<2?1:age===2?0.7:age===3?0.45:0.25;return "<div class='lqmsg"+(m.me?' me':'')+"' style='opacity:"+op+"'>"+esc(m.text)+stepsHtml(m.steps)+"</div>";}).join('')+(wait?"<div class='lqmsg thinking'>Thinking: recalling what it knows, reading what's here…</div>":'');if(el.scrollHeight)el.scrollTop=el.scrollHeight;
|
|
1930
|
+
// scrolled up to read back: the whole history shows, clear; back at the bottom it rolls on again
|
|
1931
|
+
if(!el.wired&&el.addEventListener){el.wired=true;el.addEventListener('scroll',function(){var up=el.scrollHeight-el.scrollTop-el.clientHeight>24;if(el.classList)el.classList.toggle('back',up);});}}
|
|
1927
1932
|
function lqSay(){var i=$('lqask');var q=((i&&i.value)||'').trim();if(!q)return;i.value='';lqAct();
|
|
1928
1933
|
var low=q.toLowerCase().replace(/^(please |can you |could you )/,''),hit='';
|
|
1929
1934
|
Object.keys(LQNAMES).forEach(function(k){var nm=LQNAMES[k].toLowerCase();if(hit)return;['open ','show ','go to ','take me to '].forEach(function(v){if(low.indexOf(v+nm)===0||low.indexOf(v+'my '+nm)===0)hit=k;});if(low===nm)hit=k;});
|