dsh-thoughtdag 0.4.10 → 0.4.12
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/README.md +7 -0
- package/dist-app/assets/{canonical-BZIQSZy0.js → canonical-DccXDqkX.js} +2 -2
- package/dist-app/assets/{canvas-record-D7Ns1Kpg.js → canvas-record-Dw4tjwhH.js} +1 -1
- package/dist-app/assets/{claude-code-session-0K23DXD3.js → claude-code-session-ON4i33o-.js} +1 -1
- package/dist-app/assets/{codex-session-Cs0gAA_-.js → codex-session-BWQTXLRK.js} +1 -1
- package/dist-app/assets/{dsh-session-Bl9dP5PX.js → dsh-session-DoRNUaQq.js} +1 -1
- package/dist-app/assets/{experiment-loop-GwbyJMFI.js → experiment-loop-BuZFR0si.js} +1 -1
- package/dist-app/assets/index-2pggDGtC.css +1 -0
- package/dist-app/assets/{index-CZpsihFG.js → index-CTQSx_k0.js} +3 -3
- package/dist-app/assets/{index-B5j_QAmX.js → index-DD461-fW.js} +1 -1
- package/dist-app/assets/{index-DWm1onYm.js → index-DaODh24h.js} +1 -1
- package/dist-app/assets/index-DwwxoOcO.js +788 -0
- package/dist-app/assets/{live-mirror-DXHCi_Hf.js → live-mirror-D3u92mbO.js} +2 -2
- package/dist-app/assets/{pi-session-D-6Qneax.js → pi-session-IbIlOArr.js} +1 -1
- package/dist-app/assets/{sensitive-scan-BiyGrjXf.js → sensitive-scan-C6YN1rzZ.js} +1 -1
- package/dist-app/assets/{session-handoff-IiyUUNcD.js → session-handoff-Fpl3jes7.js} +2 -2
- package/dist-app/assets/{shared-BUQtzATG.js → shared-DCtLTsEN.js} +1 -1
- package/dist-app/assets/{turndown-plugin-gfm.cjs-BOHTxH4C.js → turndown-plugin-gfm.cjs-CqvfCrbV.js} +1 -1
- package/dist-app/assets/{update-check-BojzO74W.js → update-check-B1kgBEyl.js} +1 -1
- package/dist-app/index.html +2 -2
- package/lib/client.js +3 -2
- package/lib/index.js +59 -11
- package/lib/runtime/agents/README.md +8 -6
- package/lib/runtime/agents/claude.cjs +303 -0
- package/lib/runtime/agents/codex.cjs +93 -15
- package/lib/runtime/agents/http.cjs +1 -0
- package/lib/runtime/agents/pi.cjs +21 -6
- package/package.json +1 -1
- package/dist-app/assets/index-BBqrNNdn.js +0 -787
- package/dist-app/assets/index-BfIl0Oie.css +0 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-
|
|
2
|
-
import{_ as l,i as
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/index-DwwxoOcO.js","assets/index-2pggDGtC.css","assets/experiment-loop-BuZFR0si.js","assets/index-CTQSx_k0.js","assets/canonical-DccXDqkX.js"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{_ as l,i as b,t as p,a as h,f as R}from"./index-DwwxoOcO.js";function K(d,S){return d.find(y=>(y.rel.split("/").pop()??y.rel).includes(S))}let j=!1,I=null;function H(){I?.()}function z(){const d=window.desktopSessions;if(!d||j)return;j=!0,d.watchStart();let S=!1,y=null;const g=async s=>{if(S){y=s;return}S=!0;try{window.dispatchEvent(new CustomEvent("td:sessions-changed"));const{useProjects:a,subscribedSessionIds:i}=await l(async()=>{const{useProjects:w,subscribedSessionIds:E}=await import("./index-DwwxoOcO.js").then(D=>D.H);return{useProjects:w,subscribedSessionIds:E}},__vite__mapDeps([0,1])),{activeId:c,projects:o}=a.getState(),m=o.find(w=>w.id===c),t=await d.head(s.rootKey,s.rel,262144).catch(()=>"");if(!t)return;const e=b(t);if(!e)return;const f=!!m&&i(m).includes(e);let r=null;if(!f){if(o.some(v=>i(v).includes(e)))return;const{useStore:E}=await l(async()=>{const{useStore:v}=await import("./index-DwwxoOcO.js").then(A=>A.G);return{useStore:v}},__vite__mapDeps([0,1]));if(!E.getState().nodes.some(v=>v.data.importSource?.sessionId===e)){const{parseAnchor:v}=await l(async()=>{const{parseAnchor:k}=await import("./experiment-loop-BuZFR0si.js");return{parseAnchor:k}},__vite__mapDeps([2,0,1])),A=v(t);if(!A||!o.some(k=>k.id===A.project))return;if(r=A.project,r!==c){p("info",h("atlas.mountPending"),9e3);return}}}const{streamRunnerConversation:n}=await l(async()=>{const{streamRunnerConversation:w}=await import("./index-CTQSx_k0.js");return{streamRunnerConversation:w}},__vite__mapDeps([3,0,1])),{importOrAppendConversation:u,shellSessionReader:_}=await l(async()=>{const{importOrAppendConversation:w,shellSessionReader:E}=await import("./canonical-DccXDqkX.js");return{importOrAppendConversation:w,shellSessionReader:E}},__vite__mapDeps([4,0,1])),V=await n(_(s.rootKey,s.rel)).catch(()=>null),P=await u(V);P?.kind==="appended"?p("success",R(h("atlas.liveAppended"),{n:P.turns}),8e3):P?.kind==="mounted"&&p("success",R(h(P.mode==="continue"?"atlas.mountedChapter":"atlas.mountedBranch"),{n:P.turns}),9e3)}finally{if(S=!1,y){const a=y;y=null,g(a)}}};d.onSessionsChanged(s=>{g(s)});const C=async s=>{if(!s)return;const{useStore:a}=await l(async()=>{const{useStore:o}=await import("./index-DwwxoOcO.js").then(m=>m.G);return{useStore:o}},__vite__mapDeps([0,1])),{useUiStore:i}=await l(async()=>{const{useUiStore:o}=await import("./index-DwwxoOcO.js").then(m=>m.D);return{useUiStore:o}},__vite__mapDeps([0,1])),c=a.getState().nodes.find(o=>o.data.importSource?.itemIds?.includes(s));c&&i.getState().setArrivalFocusNodeId(c.id)},T=async(s,a)=>{const{useProjects:i,switchProject:c}=await l(async()=>{const{useProjects:e,switchProject:f}=await import("./index-DwwxoOcO.js").then(r=>r.H);return{useProjects:e,switchProject:f}},__vite__mapDeps([0,1])),o=i.getState().projects,m=o.filter(e=>e.name===s),t=o.find(e=>e.id===s)??(m.length===1?m[0]:void 0);if(!t){p("error",h("handoff.deeplinkMiss"));return}if(await c(t.id),a){const{useStore:e}=await l(async()=>{const{useStore:r}=await import("./index-DwwxoOcO.js").then(n=>n.G);return{useStore:r}},__vite__mapDeps([0,1])),{useUiStore:f}=await l(async()=>{const{useUiStore:r}=await import("./index-DwwxoOcO.js").then(n=>n.D);return{useUiStore:r}},__vite__mapDeps([0,1]));e.getState().nodes.some(r=>r.id===a)&&f.getState().setArrivalFocusNodeId(a)}},O=async s=>{let a="",i="";try{const n=new URL(s),u=n.searchParams.get("canvas");if(u){await T(u,n.searchParams.get("node")??"");return}a=n.searchParams.get("session")??"",i=n.searchParams.get("turn")??""}catch{return}if(!/^[A-Za-z0-9_-]{8,}$/.test(a))return;/^[A-Za-z0-9_-]*$/.test(i)||(i="");const c=await d.roots().catch(()=>[]);let o=null;for(const n of c){const u=await d.list(n.key).catch(()=>[]),_=K(u,a);if(_){o={rootKey:n.key,rel:_.rel};break}}if(!o){const n=[];for(const u of c)for(const _ of await d.list(u.key).catch(()=>[]))n.push({rootKey:u.key,rel:_.rel,mtime:_.mtime});n.sort((u,_)=>_.mtime-u.mtime);for(const u of n.slice(0,40)){const _=await d.head(u.rootKey,u.rel,262144).catch(()=>"");if(_&&b(_)===a){o=u;break}}}if(!o){p("error",h("handoff.deeplinkMiss"));return}const{streamRunnerConversation:m}=await l(async()=>{const{streamRunnerConversation:n}=await import("./index-CTQSx_k0.js");return{streamRunnerConversation:n}},__vite__mapDeps([3,0,1])),{importOrAppendConversation:t,shellSessionReader:e}=await l(async()=>{const{importOrAppendConversation:n,shellSessionReader:u}=await import("./canonical-DccXDqkX.js");return{importOrAppendConversation:n,shellSessionReader:u}},__vite__mapDeps([4,0,1])),f=await m(e(o.rootKey,o.rel)).catch(()=>null),r=await t(f);r?r.kind==="appended"?p("success",R(h("atlas.liveAppended"),{n:r.turns}),8e3):r.kind==="mounted"?p("success",R(h(r.mode==="continue"?"atlas.mountedChapter":"atlas.mountedBranch"),{n:r.turns}),9e3):r.kind==="imported"&&p("success",R(h("toast.importedChats"),{n:1,m:r.nodeCount}),8e3):p("error",h("handoff.notASession")),r&&i&&await C(i)};d.onDeepLink?.(s=>{O(s)}),(async()=>{const{bootProjects:s}=await l(async()=>{const{bootProjects:i}=await import("./index-DwwxoOcO.js").then(c=>c.H);return{bootProjects:i}},__vite__mapDeps([0,1]));await s();const a=await d.pendingDeepLink?.().catch(()=>null);a&&O(a)})();const L="thoughtdag.atlas.sweepmark";I=async()=>{const{bootProjects:s}=await l(async()=>{const{bootProjects:t}=await import("./index-DwwxoOcO.js").then(e=>e.H);return{bootProjects:t}},__vite__mapDeps([0,1]));await s();const a=await d.roots().catch(()=>[]),i=[];for(const t of a){const e=await d.list(t.key).catch(()=>[]);for(const f of e)i.push({rootKey:t.key,rel:f.rel,mtime:f.mtime,size:f.size})}let c={};try{c=JSON.parse(localStorage.getItem(L)??"{}")}catch{}const o=Object.keys(c).length===0;i.sort((t,e)=>e.mtime-t.mtime);const m=i.filter((t,e)=>{const f=`${t.mtime}|${t.size}`;return c[`${t.rootKey}|${t.rel}`]===f?!1:o?e<30:!0});for(const t of m)await g({rootKey:t.rootKey,rel:t.rel});for(const t of i)c[`${t.rootKey}|${t.rel}`]=`${t.mtime}|${t.size}`;localStorage.setItem(L,JSON.stringify(c))},I()}export{K as pickSessionFile,z as startLiveMirror,H as sweepRecentSessions};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{v as w,B as x,o as T}from"./index-
|
|
1
|
+
import{v as w,B as x,o as T}from"./index-DwwxoOcO.js";import{t as A,a as I,e as l,d as C,T as L,A as h,g as m,f as O,s as S}from"./shared-DCtLTsEN.js";const f=r=>typeof r=="string"?r:Array.isArray(r)?r.filter(t=>t&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(`
|
|
2
2
|
`):"";function b(r,t){const s=t&&typeof t=="object"?t:{},e=I(r),n=typeof s.path=="string"&&s.path&&(e==="read"||e==="edit"||e==="write")?[s.path]:[];let o;if(e==="read"&&(typeof s.offset=="number"||typeof s.limit=="number")){const c=Math.max(1,typeof s.offset=="number"?s.offset:1),d=typeof s.limit=="number"?c+Math.max(0,s.limit)-1:c;o={lines:[c,Math.max(c,d)]}}if(e==="write"&&typeof s.content=="string")return{...l(`${s.path??""}
|
|
3
3
|
|
|
4
4
|
${s.content}`,h),paths:n};if(e==="edit"){const c=Array.isArray(s.edits)?s.edits:typeof s.newText=="string"?[{oldText:s.oldText,newText:s.newText}]:[];if(c.length){const d=c.map(i=>`${s.path??""}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{f as c,a,w as g}from"./index-
|
|
1
|
+
import{f as c,a,w as g}from"./index-DwwxoOcO.js";const k=[{kind:"sk-… key",re:/\bsk-[A-Za-z0-9_-]{16,}\b/g},{kind:"GitHub token",re:/\bgh[pousr]_[A-Za-z0-9]{20,}\b/g},{kind:"AWS access key",re:/\bAKIA[0-9A-Z]{16}\b/g},{kind:"Slack token",re:/\bxox[baprs]-[A-Za-z0-9-]{10,}\b/g},{kind:"Google API key",re:/\bAIza[0-9A-Za-z_-]{35}\b/g},{kind:"JWT",re:/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{6,}\b/g},{kind:"private key block",re:/-----BEGIN [A-Z ]*PRIVATE KEY-----/g},{kind:"token assignment",re:/\b(?:API_KEY|APIKEY|SECRET|TOKEN|PASSWORD|ACCESS_KEY|AUTH_TOKEN)[A-Z_]*\s*[=:]\s*['"]?[A-Za-z0-9_/+-]{16,}/g},{kind:"HuggingFace token",re:/\bhf_[A-Za-z0-9]{30,}\b/g},{kind:"npm token",re:/\bnpm_[A-Za-z0-9]{30,}\b/g}],f=t=>t.length<=10?`${t.slice(0,4)}…`:`${t.slice(0,6)}…${t.slice(-2)}`;function o(t,e,s){if(t)for(const{kind:i,re:n}of k){n.lastIndex=0;const r=n.exec(t);if(r&&s.push({where:e,kind:i,sample:f(r[0])}),s.length>=20)return}}function l(t){const e=[];for(const s of t){const i=(s.data.question||s.data.summaries?.[0]||s.id).replace(/\s+/g," ").slice(0,28);o(s.data.question,i,e);for(const n of s.data.responses??[])o(n,i,e);for(const n of s.data.attachments??[])n.type.startsWith("image/")||o(n.content,`${i} · ${n.name}`,e);if(e.length>=20)break}return e}async function b(t){const e=l(t);if(e.length===0)return!0;const s=e.slice(0,5).map(n=>`· ${n.where} — ${n.kind}(${n.sample})`).join(`
|
|
2
2
|
`),i=e.length>5?`
|
|
3
3
|
${c(a("sensitive.more"),{n:e.length-5})}`:"";return g({title:a("sensitive.title"),message:`${c(a("sensitive.body"),{n:e.length})}
|
|
4
4
|
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/canonical-
|
|
2
|
-
import{t as n,a as e,_ as p,f as a,u as h}from"./index-
|
|
1
|
+
const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/canonical-DccXDqkX.js","assets/index-DwwxoOcO.js","assets/index-2pggDGtC.css"])))=>i.map(i=>d[i]);
|
|
2
|
+
import{t as n,a as e,_ as p,f as a,u as h}from"./index-DwwxoOcO.js";const l=/^http:\/\/(127\.0\.0\.1|localhost|\[::1\])(:\d+)?\//i;async function w(){const s=window.location.hash,r=s.match(/[#&]import-url=([^&]+)/);if(!r)return;const c=/[#&]mode=harvest\b/.test(s);history.replaceState(null,"",window.location.pathname+window.location.search);const i=decodeURIComponent(r[1]);if(!l.test(i)){n("error",e("handoff.notLoopback"),9e3);return}try{const o=await fetch(i);if(!o.ok)throw new Error(`HTTP ${o.status}`);const d=await o.text(),{importOrAppendSession:f}=await p(async()=>{const{importOrAppendSession:u}=await import("./canonical-DccXDqkX.js");return{importOrAppendSession:u}},__vite__mapDeps([0,1,2])),t=await f(d);if(!t)throw new Error(e("handoff.notASession"));t.kind==="appended"?n("success",a(e("atlas.appended"),{n:t.turns}),9e3):t.kind==="mounted"?n("success",a(e(t.mode==="continue"?"atlas.mountedChapter":"atlas.mountedBranch"),{n:t.turns}),12e3):t.kind==="opened"?n("info",e("atlas.upToDate"),6e3):n("info",a(e("handoff.saved"),{n:t.nodeCount}),12e3,{label:e("handoff.backupAction"),run:()=>h.getState().setBackupDialogOpen(!0)})}catch(o){n("error",a(e("handoff.failed"),{msg:o instanceof Error?o.message:String(o)}),12e3)}}export{w as consumeSessionHandoff};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{v as m,o as p,B as h,C as g}from"./index-
|
|
1
|
+
import{v as m,o as p,B as h,C as g}from"./index-DwwxoOcO.js";const _=[[/^(Read|NotebookRead|read_file|view_image)$/i,"read"],[/^(Write|write_file)$/i,"write"],[/^(Edit|MultiEdit|NotebookEdit|apply_patch)$/i,"edit"],[/^(Bash|BashOutput|Shell|exec_command|local_shell|write_stdin)$/i,"run"],[/^(Grep|Glob|LS|Search|WebSearch|web_search)$/i,"search"],[/^(WebFetch|Fetch)$/i,"fetch"],[/^(Task|Agent)$/i,"agent"]];function y(e){return _.find(([t])=>t.test(e))?.[1]??"other"}const x=800,O=32e3,S=4e3,I=(e,t)=>e.length>t?{text:`${e.slice(0,t)}
|
|
2
2
|
…[truncated, ${e.length} chars total]`,truncated:!0}:{text:e,truncated:!1},$=["<command-name>/thoughtdag</command-name>","[[thoughtdag:command]]"];function M(e){return e.filter(t=>!$.some(n=>t.question.includes(n)))}function w(e){e.width=460}function b(e){const t=g(e.data.response,90);t&&(e.data.summaries=[t])}function L(e){return e.tools.map(t=>({id:p(),name:`tool: ${t.name}${t.truncated?" (truncated)":""}`,type:"text/plain",size:t.call.length+t.result.length,content:`[call] ${t.call}
|
|
3
3
|
[result]
|
|
4
4
|
${t.result}`,...t.paths?.length?{paths:t.paths}:{},...t.op?{op:t.op}:{}}))}function k(e,t,n,s,i){if(e.length===0)return null;const r=[],l=[];let u={id:s.id};for(const o of e){const a=m(o.question||"(tool-only turn)",o.response,!1);a.data.importSource={runner:n,sessionId:t,itemIds:o.itemIds,...i?{cwd:i}:{}},a.data.source={question:a.data.question,response:a.data.response},a.data.attachments=L(o),b(a),r.length===0&&(a.data.isBranch=!0),r.push(a),l.push({id:p(),source:u.id,target:a.id,type:"smoothstep"}),u=a}const c=h(r,l.filter(o=>o.source!==s.id)),f=Math.min(...c.map(o=>o.position.x)),d=Math.min(...c.map(o=>o.position.y));for(const o of c)o.position={x:o.position.x-f+s.x+560,y:o.position.y-d+s.y};return{nodes:c,edges:l,turnCount:e.length}}function A(e){const t=e??{},n={};typeof t.url=="string"&&/^https?:\/\//.test(t.url)&&(n.url=t.url);const s={};if(typeof t.pages=="string"&&t.pages.trim()&&(s.pages=t.pages.trim()),typeof t.offset=="number"||typeof t.limit=="number"){const i=Math.max(1,typeof t.offset=="number"?t.offset:1),r=typeof t.limit=="number"?i+Math.max(0,t.limit)-1:i;s.lines=[i,Math.max(i,r)]}return Object.keys(s).length&&(n.locator=s),n}function C(e){const t=e??{},n=t.file_path??t.notebook_path;return typeof n=="string"&&n?[n]:[]}function q(e,t){const n=t??{},s=y(e);return s==="write"&&typeof n.content=="string"?`${n.file_path??""}
|
package/dist-app/assets/{turndown-plugin-gfm.cjs-BOHTxH4C.js → turndown-plugin-gfm.cjs-CqvfCrbV.js}
RENAMED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{A as W}from"./index-
|
|
1
|
+
import{A as W}from"./index-DwwxoOcO.js";function Y(d,p){for(var g=0;g<p.length;g++){const s=p[g];if(typeof s!="string"&&!Array.isArray(s)){for(const c in s)if(c!=="default"&&!(c in d)){const o=Object.getOwnPropertyDescriptor(s,c);o&&Object.defineProperty(d,c,o.get?o:{enumerable:!0,get:()=>s[c]})}}}return Object.freeze(Object.defineProperty(d,Symbol.toStringTag,{value:"Module"}))}var u={},E;function J(){if(E)return u;E=1,Object.defineProperty(u,"__esModule",{value:!0});var d=/highlight-(?:text|source)-([a-z0-9]+)/;function p(t){t.addRule("highlightedCodeBlock",{filter:function(e){var r=e.firstChild;return e.nodeName==="DIV"&&d.test(e.className)&&r&&r.nodeName==="PRE"},replacement:function(e,r,n){var l=r.className||"",i=(l.match(d)||[null,""])[1];return`
|
|
2
2
|
|
|
3
3
|
`+n.fence+i+`
|
|
4
4
|
`+r.firstChild.textContent+`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{t as u,a as n,f}from"./index-
|
|
1
|
+
import{t as u,a as n,f}from"./index-DwwxoOcO.js";const h=15*6e4;let i=!1;const d=new URLSearchParams(window.location.search),s=d.get("dv"),p=d.get("su")==="1",w=30*6e4;let c=!1,r=0;async function a(){if(!(!s||p||c)&&!(Date.now()-r<w)){r=Date.now();try{const t=await fetch("https://api.github.com/repos/chenxiachan/thoughtdag/releases?per_page=1");if(!t.ok)return;const o=(await t.json())?.[0]?.tag_name?.replace(/^v/,"");if(!o||o===s)return;c=!0,u("info",f(n("update.desktopAvailable"),{v:o}),0,{label:n("update.desktopDownload"),run:()=>window.open("https://chenxiachan.github.io/thoughtdag/#download","_blank")})}catch{}}}function v(){for(const t of document.querySelectorAll("script[src]")){const e=t.src.match(/assets\/index-[^/?#]+\.js/);if(e)return e[0]}return null}async function m(){try{const t=await fetch(`${window.location.pathname}?u=${Date.now()}`,{cache:"no-store"});if(!t.ok)return null;const e=(await t.text()).match(/assets\/index-[^"'?#]+\.js/);return e?e[0]:null}catch{return null}}async function l(){if(i)return;const t=v();if(!t)return;const e=await m();e&&e!==t&&(i=!0,u("info",n("update.available"),0,{label:n("update.refresh"),run:()=>window.location.reload()}))}function b(){document.addEventListener("visibilitychange",()=>{document.visibilityState==="visible"&&(l(),a())}),setInterval(()=>{l(),a()},h),s&&setTimeout(()=>{a()},5e3)}export{b as bootUpdateCheck};
|
package/dist-app/index.html
CHANGED
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
<meta name="twitter:title" content="ThoughtDAG — Your thinking deserves a map" />
|
|
23
23
|
<meta name="twitter:description" content="Wires are the context: an infinite canvas where LLM conversations grow into an editable thought graph. No signup; the example canvas needs no key." />
|
|
24
24
|
<meta name="twitter:image" content="https://app.thoughtdag.workers.dev/og-twitter.jpg" />
|
|
25
|
-
<script type="module" crossorigin src="/thoughtdag/assets/index-
|
|
26
|
-
<link rel="stylesheet" crossorigin href="/thoughtdag/assets/index-
|
|
25
|
+
<script type="module" crossorigin src="/thoughtdag/assets/index-DwwxoOcO.js"></script>
|
|
26
|
+
<link rel="stylesheet" crossorigin href="/thoughtdag/assets/index-2pggDGtC.css">
|
|
27
27
|
</head>
|
|
28
28
|
<body>
|
|
29
29
|
<div id="root"></div>
|
package/lib/client.js
CHANGED
|
@@ -48,7 +48,7 @@ window.__ModuleLoader__.load({
|
|
|
48
48
|
mapBtn.classList.toggle('active', map)
|
|
49
49
|
mapBtn.setAttribute('aria-pressed', String(map))
|
|
50
50
|
}
|
|
51
|
-
const close = () => { overlay.hidden = true; setView(false) }
|
|
51
|
+
const close = () => { overlay.hidden = true; setView(false); send('td:view', { shown: false }) }
|
|
52
52
|
const send = (type, payload) => frame.contentWindow?.postMessage({ source: 'dsh-thoughtdag', type, ...payload }, location.origin)
|
|
53
53
|
|
|
54
54
|
const syncCurrent = () => {
|
|
@@ -64,8 +64,9 @@ window.__ModuleLoader__.load({
|
|
|
64
64
|
// box and shows nothing when revealed
|
|
65
65
|
if (!frame.src) frame.src = frame.dataset.src + (pluginVersion ? (frame.dataset.src.includes('?') ? '&' : '?') + 'dv=' + encodeURIComponent(pluginVersion) : '')
|
|
66
66
|
syncCurrent()
|
|
67
|
+
send('td:view', { shown: true })
|
|
67
68
|
// let the SPA boot, then re-sync so its listener is ready
|
|
68
|
-
window.setTimeout(syncCurrent, 400)
|
|
69
|
+
window.setTimeout(() => { syncCurrent(); send('td:view', { shown: true }) }, 400)
|
|
69
70
|
})
|
|
70
71
|
dialogBtn.addEventListener('click', close)
|
|
71
72
|
|
package/lib/index.js
CHANGED
|
@@ -218,6 +218,14 @@ function sessionToJsonl(session) {
|
|
|
218
218
|
return lines.join('\n')
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
+
/** The events after `since`, as lines — the tail a reader appends to the
|
|
222
|
+
* log it already holds, instead of fetching the whole log again. */
|
|
223
|
+
function eventsJsonlSince(session, since) {
|
|
224
|
+
const lines = []
|
|
225
|
+
for (const event of allEvents(session)) if (typeof event.seq === 'number' && event.seq > since) lines.push(JSON.stringify(event))
|
|
226
|
+
return lines.join('\n')
|
|
227
|
+
}
|
|
228
|
+
|
|
221
229
|
// ── durable-session bridge (disk) ──────────────────────────────────────
|
|
222
230
|
// DSH persists each session as session.jsonl.zstd under
|
|
223
231
|
// $DSH_HOME/sessions/<encoded-cwd>/<session-id>/. The zstd log is a
|
|
@@ -306,6 +314,18 @@ async function findSessionFiles(dshHome) {
|
|
|
306
314
|
* header names the id+cwd; session/title arrives right after in live logs.
|
|
307
315
|
* Falls back to the session id when the frame boundary cuts early. */
|
|
308
316
|
const HEAD_READ_BYTES = 256 * 1024 // covers the first frames: header + title + opening turns
|
|
317
|
+
// a session's head is decoded once per (path, size, mtime): the listing is
|
|
318
|
+
// polled, and a poll must not decompress sixty files it already knows
|
|
319
|
+
const headCache = new Map()
|
|
320
|
+
async function cachedHead(f) {
|
|
321
|
+
const key = `${f.log}|${f.size}|${f.mtime}`
|
|
322
|
+
const hit = headCache.get(key)
|
|
323
|
+
if (hit) return hit
|
|
324
|
+
const head = await sessionTitleFromHead(f.log)
|
|
325
|
+
if (headCache.size > 4000) headCache.clear()
|
|
326
|
+
headCache.set(key, head)
|
|
327
|
+
return head
|
|
328
|
+
}
|
|
309
329
|
async function sessionTitleFromHead(logPath) {
|
|
310
330
|
try {
|
|
311
331
|
const fh = await open(logPath, 'r')
|
|
@@ -412,9 +432,13 @@ function fileInRoot(rootKey, rel) {
|
|
|
412
432
|
}
|
|
413
433
|
|
|
414
434
|
/** Every session file under a root, two-to-five levels deep like the desktop's walk. */
|
|
435
|
+
const listCache = new Map() // rootKey → { at, files }: a walk is reused for a few seconds
|
|
436
|
+
const LIST_CACHE_MS = 5000
|
|
415
437
|
async function listRoot(rootKey) {
|
|
416
438
|
const root = FILE_ROOTS[rootKey]
|
|
417
439
|
if (!root) throw new HttpError(404, 'no such root')
|
|
440
|
+
const hit = listCache.get(rootKey)
|
|
441
|
+
if (hit && Date.now() - hit.at < LIST_CACHE_MS) return hit.files
|
|
418
442
|
const out = []
|
|
419
443
|
const walk = async (dir, depth) => {
|
|
420
444
|
if (depth > 5) return
|
|
@@ -429,6 +453,7 @@ async function listRoot(rootKey) {
|
|
|
429
453
|
}
|
|
430
454
|
}
|
|
431
455
|
await walk(root, 0)
|
|
456
|
+
listCache.set(rootKey, { at: Date.now(), files: out })
|
|
432
457
|
return out
|
|
433
458
|
}
|
|
434
459
|
|
|
@@ -639,21 +664,38 @@ function installApprovalAnswerer(ctx) {
|
|
|
639
664
|
}, true)
|
|
640
665
|
}
|
|
641
666
|
|
|
642
|
-
/** Run one question through the harness's agent loop
|
|
643
|
-
*
|
|
667
|
+
/** Run one question through the harness's agent loop, continuing a mirrored
|
|
668
|
+
* session, forking a dsh mirror at the parent's exact turn, or creating a
|
|
669
|
+
* fresh session. Report progress as SSE-style frames via `emit`; resolves
|
|
670
|
+
* when the turn ends. */
|
|
644
671
|
async function runAgentTurn(ctx, body, emit, isClosed, { answerApprovals = true } = {}) {
|
|
645
672
|
const { question, context } = compileForAgent(body)
|
|
646
|
-
//
|
|
647
|
-
//
|
|
673
|
+
// A parent that is a current ledger tail continues in place. A historical
|
|
674
|
+
// dsh mirror forks at the exact turn named by its user-message id, so later
|
|
675
|
+
// turns (including sibling work) cannot leak into the branch. Otherwise a
|
|
676
|
+
// fresh session receives the compiled canvas context.
|
|
648
677
|
const continueId = typeof body?.harness?.session === 'string' && body.harness.session ? body.harness.session : null
|
|
649
|
-
|
|
678
|
+
const forkId = typeof body?.harness?.forkSession === 'string' && body.harness.forkSession ? body.harness.forkSession : null
|
|
679
|
+
const forkAnchor = typeof body?.harness?.forkAnchor === 'string' && body.harness.forkAnchor ? body.harness.forkAnchor : null
|
|
680
|
+
let sessionId = null
|
|
681
|
+
let forkedFrom = null
|
|
650
682
|
if (continueId && (liveSession(ctx, continueId) || (await eventsOfSession(ctx, continueId)) !== null)) {
|
|
651
683
|
sessionId = continueId
|
|
652
|
-
} else {
|
|
684
|
+
} else if (forkId && forkAnchor) {
|
|
685
|
+
const events = await eventsOfSession(ctx, forkId)
|
|
686
|
+
if (events !== null) {
|
|
687
|
+
const turn = turnsOf(events).find(t => t.userMessageId === forkAnchor)
|
|
688
|
+
if (!turn) throw new HttpError(400, 'no such fork anchor: ' + forkAnchor)
|
|
689
|
+
if (turn.endSeq === null) throw new HttpError(409, 'turn ' + turn.turn + ' is still open; a fork needs a completed turn')
|
|
690
|
+
sessionId = (await ctx.sessionController.fork({ sessionId: forkId, atSeq: turn.endSeq })).sessionId
|
|
691
|
+
forkedFrom = forkId
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
if (!sessionId) {
|
|
653
695
|
const cwd = typeof body?.harness?.cwd === 'string' && body.harness.cwd ? { cwd: body.harness.cwd } : {}
|
|
654
696
|
sessionId = (await ctx.sessionController.create({ ...cwd })).sessionId
|
|
655
697
|
}
|
|
656
|
-
emit({ harnessSession: sessionId, continued: sessionId === continueId })
|
|
698
|
+
emit({ harnessSession: sessionId, continued: sessionId === continueId, ...(forkedFrom ? { forkedFrom } : {}) })
|
|
657
699
|
// a streaming caller can show and answer approvals; a one-shot caller
|
|
658
700
|
// (/claude) cannot, so its requests fall through to the harness's own panel
|
|
659
701
|
const turnEntry = { emit, isClosed, calls: new Map() }
|
|
@@ -699,7 +741,8 @@ async function runAgentTurn(ctx, body, emit, isClosed, { answerApprovals = true
|
|
|
699
741
|
}
|
|
700
742
|
})
|
|
701
743
|
try {
|
|
702
|
-
const
|
|
744
|
+
const inheritsSessionContext = (continueId && sessionId === continueId) || forkedFrom
|
|
745
|
+
const injectContext = inheritsSessionContext ? (typeof body?.harness?.extraContext === 'string' ? body.harness.extraContext : '') : context
|
|
703
746
|
if (injectContext) agent.inject({ id: 'td-' + randomUUID(), role: 'user', content: [{ type: 'text', text: injectContext }], source: SOURCE })
|
|
704
747
|
const promptContent = [{ type: 'text', text: question }]
|
|
705
748
|
for (const img of Array.isArray(body?.images) ? body.images : []) {
|
|
@@ -844,7 +887,7 @@ export async function apply(ctx, config) {
|
|
|
844
887
|
const files = await findSessionFiles(dshHome)
|
|
845
888
|
const sessions = []
|
|
846
889
|
for (const f of files) {
|
|
847
|
-
const head = await
|
|
890
|
+
const head = await cachedHead(f)
|
|
848
891
|
sessions.push({
|
|
849
892
|
id: head.id ?? f.sessionDir,
|
|
850
893
|
title: head.title,
|
|
@@ -880,7 +923,11 @@ export async function apply(ctx, config) {
|
|
|
880
923
|
const id = decodeURIComponent(one[1])
|
|
881
924
|
const session = liveSession(ctx, id)
|
|
882
925
|
if (session === undefined) return sendJson(res, 404, { error: 'no such session' })
|
|
883
|
-
if (one[2] === '/log')
|
|
926
|
+
if (one[2] === '/log') {
|
|
927
|
+
const since = url.searchParams.get('since')
|
|
928
|
+
if (since !== null && since !== '') return sendFile(res, 'application/x-ndjson; charset=utf-8', eventsJsonlSince(session, Number(since)))
|
|
929
|
+
return sendFile(res, 'application/x-ndjson; charset=utf-8', sessionToJsonl(session))
|
|
930
|
+
}
|
|
884
931
|
return sendJson(res, 200, { session: sessionSummary(session) })
|
|
885
932
|
}
|
|
886
933
|
if (path === '/why/status' && req.method === 'GET') return sendJson(res, 200, whyStatus)
|
|
@@ -891,12 +938,13 @@ export async function apply(ctx, config) {
|
|
|
891
938
|
for (const [key, p] of Object.entries(FILE_ROOTS)) roots.push({ key, path: p, builtin: true, exists: await stat(p).then(st => st.isDirectory()).catch(() => false) })
|
|
892
939
|
return sendJson(res, 200, { roots })
|
|
893
940
|
}
|
|
894
|
-
const rootRoute = /^\/roots\/([a-z-]+)\/(list|head|read|range)$/.exec(path)
|
|
941
|
+
const rootRoute = /^\/roots\/([a-z-]+)\/(list|head|read|range|stat)$/.exec(path)
|
|
895
942
|
if (rootRoute !== null && req.method === 'GET') {
|
|
896
943
|
const key = rootRoute[1]
|
|
897
944
|
if (rootRoute[2] === 'list') return sendJson(res, 200, { files: await listRoot(key) })
|
|
898
945
|
const rel = url.searchParams.get('rel') ?? ''
|
|
899
946
|
const abs = fileInRoot(key, rel)
|
|
947
|
+
if (rootRoute[2] === 'stat') { const st = await stat(abs).catch(() => null); return st ? sendJson(res, 200, { size: st.size, mtime: st.mtimeMs }) : sendJson(res, 404, { error: 'no such file' }) }
|
|
900
948
|
if (rootRoute[2] === 'head') return sendFile(res, 'text/plain; charset=utf-8', await headOfFile(abs, Number(url.searchParams.get('bytes')) || 16384).catch(() => ''))
|
|
901
949
|
if (rootRoute[2] === 'read') {
|
|
902
950
|
const st = await stat(abs).catch(() => null)
|
|
@@ -1,24 +1,26 @@
|
|
|
1
1
|
# Agent runtimes
|
|
2
2
|
|
|
3
|
-
One file per runtime (CommonJS, `.cjs`, so it loads the same under any package type): `pi.cjs` over `pi --mode rpc`, `codex.cjs` over `codex app-server
|
|
3
|
+
One file per runtime (CommonJS, `.cjs`, so it loads the same under any package type): `pi.cjs` over `pi --mode rpc`, `codex.cjs` over `codex app-server`, `claude.cjs` over `claude -p` with stream-json both ways. Each exports a factory returning the same surface:
|
|
4
4
|
|
|
5
5
|
- `available()` → where the binary is, or null
|
|
6
|
-
- `models()` → `{ installed, models: [{ provider, id, name, reasoning, vision }], default }`
|
|
7
|
-
- `run(request, onEvent)` → run id at once; events follow, tagged with it
|
|
6
|
+
- `models()` → `{ installed, models: [{ provider, id, name, reasoning, vision, efforts, defaultEffort }], default }` — `efforts` are the effort levels the CLI accepts for that model, in its own words, read from the CLI (empty when it has none); `defaultEffort` its own default when it says
|
|
7
|
+
- `run(request, onEvent)` → run id at once; events follow, tagged with it. The request names the runtime, an absolute `cwd`, one `prompt`, optional `images`, a `model` from that runtime's own list, a session to continue (`sessionPath`, plus `forkEntryId` to branch it), and optionally `effort`: one of the levels the runtime itself listed for that model, in its own words (Codex from `model/list`, Claude Code and Pi from their `--help`), which it passes on as is (`--effort`, the turn's `effort`, `set_thinking_level`). A word the runtime did not list is dropped: the CLI's own setting stands.
|
|
8
8
|
- `abort(runId)`, `answer(runId, requestId, { confirmed } | { value } | { cancelled: true })`, `shutdown()`
|
|
9
9
|
|
|
10
10
|
A run emits exactly these event kinds (the renderer reads nothing else):
|
|
11
11
|
|
|
12
12
|
| kind | meaning |
|
|
13
13
|
|---|---|
|
|
14
|
-
| `session` | which session file this run writes to, and where (cwd) |
|
|
14
|
+
| `session` | which session file this run writes to, and where (cwd); `model.resolved` names the real model when the run used an alias (Claude Code's `opus` → `claude-opus-5`); `effort` is the level the turn actually ran at, read back from the runtime's own record (Pi's state, Codex's rollout `turn_context`, Claude Code's transcript entries), never assumed |
|
|
15
15
|
| `message_update` with a text or thinking delta | the answer or the reasoning growing |
|
|
16
16
|
| `tool_execution_start` / `tool_execution_end` | a tool call, for the live trace |
|
|
17
|
-
| `question` | the agent asks the person: confirm / select / input / editor |
|
|
18
|
-
| `question_answered` | the person's answer went back (record) |
|
|
17
|
+
| `question` | the agent asks the person: confirm / select / input / editor. A confirm may carry `rule`, an opaque key naming what "allow for this conversation" would cover (Claude Code: its own permission suggestion; Codex: the command, or all file changes); the answer `{ confirmed: true, scope: 'session' }` allows it for good — the runtime tells the CLI where it can (Claude Code `updatedPermissions` scoped to the session, Codex `acceptForSession`) and the canvas keeps the rule on the record, sending every such rule back as `allowRules` on later runs of this conversation |
|
|
18
|
+
| `question_answered` | the person's answer went back (record); with `auto: true` the runtime decided from a standing rule of this conversation and nobody was asked — the record is made from its `title`/`message`/`rule` |
|
|
19
19
|
| `fs_changes` | what the shell saw change on disk (produced here, not by the agent) |
|
|
20
20
|
| `run_end` / `run_error` | the turn is over |
|
|
21
21
|
|
|
22
|
+
Nothing starts at launch. A runtime's process (Pi's rpc, Codex's app-server, a Pi catalog read) is started by the first request that needs it and retires after ten minutes without one (`TD_AGENT_IDLE_MS`); Claude Code runs one process per turn. A launch that never opens the agent group never starts a CLI.
|
|
23
|
+
|
|
22
24
|
The finished turn is adopted from the session file by the atlas's adapter for that
|
|
23
25
|
runtime; the stream is for showing, the file is the truth. Anything a runtime does
|
|
24
26
|
beyond these kinds stays inside its own file.
|