dsh-thoughtdag 0.5.3 → 0.5.5

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.
Files changed (32) hide show
  1. package/dist-app/assets/{canonical-Bu0lc0IS.js → canonical-DyO4cfs8.js} +2 -2
  2. package/dist-app/assets/{canvas-record-CmXFsOEP.js → canvas-record-B106EgJV.js} +1 -1
  3. package/dist-app/assets/claude-code-session-COqNVArs.js +8 -0
  4. package/dist-app/assets/codex-session-CVhXEocv.js +12 -0
  5. package/dist-app/assets/{dsh-session-DcFBMiBo.js → dsh-session-y6pdDHlg.js} +3 -3
  6. package/dist-app/assets/{experiment-loop-DLwE-thH.js → experiment-loop-Bj2wGvJp.js} +1 -1
  7. package/dist-app/assets/{http-bridge-C9WCIcet.js → http-bridge-TYkQm1R3.js} +1 -1
  8. package/dist-app/assets/{index-sZcnNAul.js → index-BZP0zxsA.js} +3 -3
  9. package/dist-app/assets/{index-DdyOBAYQ.js → index-BndAg25p.js} +1 -1
  10. package/dist-app/assets/index-CqebPxzE.css +1 -0
  11. package/dist-app/assets/{index-DIJtOJxk.js → index-D2RofBbL.js} +1 -1
  12. package/dist-app/assets/index-DR0C_9n-.js +912 -0
  13. package/dist-app/assets/pi-session-fSCIsceL.js +14 -0
  14. package/dist-app/assets/{sensitive-scan-brovGPLt.js → sensitive-scan-_-YPfT-P.js} +1 -1
  15. package/dist-app/assets/{session-handoff-rMaxWoB9.js → session-handoff-rVZFB7ll.js} +2 -2
  16. package/dist-app/assets/shared-svBDp9_R.js +10 -0
  17. package/dist-app/assets/{turndown-plugin-gfm.cjs-D9apJuex.js → turndown-plugin-gfm.cjs-DBiLx1SX.js} +1 -1
  18. package/dist-app/assets/{update-check-D1_48OBp.js → update-check-n2hvSdhA.js} +1 -1
  19. package/dist-app/index.html +2 -2
  20. package/lib/runtime/agents/README.md +1 -1
  21. package/lib/runtime/agents/claude.cjs +13 -14
  22. package/lib/runtime/agents/codex.cjs +13 -14
  23. package/lib/runtime/agents/pi.cjs +14 -27
  24. package/lib/runtime/agents/where.cjs +99 -0
  25. package/lib/why.mjs +101 -7
  26. package/package.json +1 -1
  27. package/dist-app/assets/claude-code-session-B0xfEAeh.js +0 -7
  28. package/dist-app/assets/codex-session-DGckD5vR.js +0 -12
  29. package/dist-app/assets/index-8PjAYhti.js +0 -904
  30. package/dist-app/assets/index-Lvtth6pP.css +0 -1
  31. package/dist-app/assets/pi-session-DA_RmEVw.js +0 -14
  32. package/dist-app/assets/shared-DJOVlRHh.js +0 -10
@@ -0,0 +1,14 @@
1
+ import{q as x,B as A,n as T}from"./index-DR0C_9n-.js";import{t as w,a as g,f as d,d as b,T as S,A as h,i as m,g as k,s as C,h as L}from"./shared-svBDp9_R.js";const f=i=>typeof i=="string"?i:Array.isArray(i)?i.filter(t=>t&&t.type==="text"&&typeof t.text=="string").map(t=>t.text).join(`
2
+ `):"";function $(i,t){const s=t&&typeof t=="object"?t:{},e=g(i),r=typeof s.path=="string"&&s.path&&(e==="read"||e==="edit"||e==="write")?[s.path]:[];let n;if(e==="read"&&(typeof s.offset=="number"||typeof s.limit=="number")){const o=Math.max(1,typeof s.offset=="number"?s.offset:1),u=typeof s.limit=="number"?o+Math.max(0,s.limit)-1:o;n={lines:[o,Math.max(o,u)]}}if(e==="write"&&typeof s.content=="string")return{...d(`${s.path??""}
3
+
4
+ ${s.content}`,h),paths:r};if(e==="edit"){const o=Array.isArray(s.edits)?s.edits:typeof s.newText=="string"?[{oldText:s.oldText,newText:s.newText}]:[];if(o.length){const u=o.map(a=>`${s.path??""}
5
+ --- old
6
+ ${a.oldText??""}
7
+ +++ new
8
+ ${a.newText??""}`).join(`
9
+
10
+ `);return{...d(u,h),paths:r}}}return e==="run"&&typeof s.command=="string"?{...d(s.command,m),paths:r}:{...d(JSON.stringify(t??{}),m),paths:r,...n?{locator:n}:{}}}const O=i=>{if(!Array.isArray(i))return"";for(let t=i.length-1;t>=0;t--){const s=i[t];if(s?.role==="assistant"){const e=f(s.content).trim();if(e)return e}}return""};function q(i,t,s,e){const r=s;if(Array.isArray(r?.results)&&r.results.length)return r.results.map((u,l)=>({agent:String(u.agent??"agent"),task:String(u.task??""),output:O(u.messages)||String(u.stderr??"").trim(),itemIds:[`${e}#${l}`]}));const n=i&&typeof i=="object"?i:{},c=Array.isArray(n.tasks)&&n.tasks.length?n.tasks:Array.isArray(n.chain)&&n.chain.length?n.chain:n.agent||n.task?[{agent:n.agent,task:n.task}]:[],o=f(t).trim();return c.map((u,l)=>({agent:String(u.agent??"agent"),task:String(u.task??""),output:c.length===1?o:"",itemIds:[`${e}#${l}`]}))}class M{turns=[];pending=new Map;current=null;sessionId=null;cwd=null;firstQuestion=null;feedLine(t){const s=t.trim();if(!s)return;let e;try{e=JSON.parse(s)}catch{return}this.feed(e)}flush(){const t=this.current;t&&(t.question||t.response||t.tools.length)&&this.turns.push(t),this.current=null}feed(t){if(t.type==="session"&&typeof t.id=="string"){this.sessionId||(this.sessionId=t.id),typeof t.cwd=="string"&&!this.cwd&&(this.cwd=t.cwd);return}if(t.type!=="message"){this.current&&typeof t.id=="string"&&this.current.itemIds.push(t.id);return}const s=t.message;if(s){if(s.role==="user"){const e=f(s.content).trim();if(!e)return;this.flush(),this.firstQuestion||(this.firstQuestion=e.split(`
11
+ `)[0].slice(0,80));const r=typeof t.timestamp=="string"?t.timestamp:typeof s.timestamp=="number"?new Date(s.timestamp).toISOString():void 0;this.current={question:e,response:"",itemIds:typeof t.id=="string"?[t.id]:[],tools:[],...typeof t.parentId=="string"?{parentItemId:t.parentId}:{},...r?{at:r}:{}};return}if(s.role==="assistant"){const e=this.current??(this.current={question:"",response:"",itemIds:[],tools:[]});typeof t.id=="string"&&e.itemIds.push(t.id);const r=f(s.content).trim();if(r&&(e.response=e.response?`${e.response}
12
+
13
+ ${r}`:r),Array.isArray(s.content))for(const n of s.content){if(n?.type!=="toolCall"||typeof n.id!="string"||typeof n.name!="string")continue;const c=$(n.name,n.arguments);this.pending.set(n.id,{name:n.name,call:c.text,truncated:c.truncated,op:g(n.name),paths:c.paths,...c.locator?{locator:c.locator}:{},...n.name==="subagent"?{args:n.arguments}:{}})}return}if(s.role==="toolResult"){const e=s.toolCallId;if(!e)return;const r=this.pending.get(e);if(!r)return;const n=this.current??(this.current={question:"",response:"",itemIds:[],tools:[]});typeof t.id=="string"&&n.itemIds.push(t.id);const c=d(f(s.content),S);if(n.tools.push({name:r.name,call:r.call,result:c.text,truncated:r.truncated||c.truncated,op:r.op,nativeCallId:e,...r.paths.length?{paths:r.paths}:{},...r.locator?{locator:r.locator}:{}}),r.name==="subagent"){const o=q(r.args,s.content,s.details,e);o.length&&(n.subagents=[...n.subagents??[],...o])}this.pending.delete(e)}}}finish(){if(this.flush(),!this.sessionId)return null;const t=this.firstQuestion??`session ${this.sessionId.slice(0,8)}`;return{sessionId:this.sessionId,title:t,turns:b(this.turns),...this.cwd?{cwd:this.cwd}:{}}}toConversation(){const t=this.finish();return!t||t.turns.length===0?null:{title:t.title,messageCount:t.turns.length,source:"pi",sessionId:t.sessionId,build:()=>v(t.turns,t.sessionId,t.cwd)}}}function y(i){const t=new M;for(const s of i.split(`
14
+ `))t.feedLine(s);return t}function v(i,t,s){const e=s?{cwd:s}:{},r=[],n=[],c=new Map,o=(l,a)=>n.push({id:T(),source:l.id,target:a.id,type:"smoothstep"});let u=null;for(const l of i){const a=x(l.question||"(tool-only turn)",l.response,u===null);a.data.importSource={runner:"pi",sessionId:t,itemIds:l.itemIds,...e},a.data.source={question:a.data.question,response:a.data.response},a.data.attachments=k(l),C(a),r.push(a);const p=l.parentItemId?c.get(l.parentItemId):void 0;p?o(p,a):u&&o(u,a),L(a,l,r,n,"pi",t,s);for(const I of l.itemIds)c.set(I,a);u=a}return{nodes:A(r,n),edges:n}}function B(i,t){const s=y(i).finish();return s?w(s.turns,s.sessionId,"pi",t,s.cwd):null}function Q(i){return y(i).toConversation()}export{M as PiSessionCollector,v as buildGraphFromTurns,B as piSessionAsBranch,Q as piSessionConversation};
@@ -1,4 +1,4 @@
1
- import{f as c,a,v as g}from"./index-8PjAYhti.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(`
1
+ import{f as c,a,v as g}from"./index-DR0C_9n-.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-Bu0lc0IS.js","assets/index-8PjAYhti.js","assets/index-Lvtth6pP.css"])))=>i.map(i=>d[i]);
2
- import{t as n,a as e,_ as p,f as a,u as h}from"./index-8PjAYhti.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-Bu0lc0IS.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
+ const __vite__mapDeps=(i,m=__vite__mapDeps,d=(m.f||(m.f=["assets/canonical-DyO4cfs8.js","assets/index-DR0C_9n-.js","assets/index-CqebPxzE.css"])))=>i.map(i=>d[i]);
2
+ import{t as n,a as e,_ as p,f as a,u as h}from"./index-DR0C_9n-.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-DyO4cfs8.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};
@@ -0,0 +1,10 @@
1
+ import{q as d,n as p,B as _,C as y}from"./index-DR0C_9n-.js";const I=[[/^(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 $(e){return I.find(([t])=>t.test(e))?.[1]??"other"}const T=800,k=32e3,O=4e3,x=(e,t)=>e.length>t?{text:`${e.slice(0,t)}
2
+ …[truncated, ${e.length} chars total]`,truncated:!0}:{text:e,truncated:!1},b=["<command-name>/thoughtdag</command-name>","[[thoughtdag:command]]"];function M(e){return e.filter(t=>!b.some(s=>t.question.includes(s)))}function q(e){e.width=460}function m(e){const t=y(e.data.response,90);t&&(e.data.summaries=[t])}function h(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
+ [result]
4
+ ${t.result}`,...t.paths?.length?{paths:t.paths}:{},...t.op?{op:t.op}:{}}))}function w(e,t){const s=new Set,i=(n,a)=>{s.add(a);const c=a.turns.flatMap(r=>r.tools);c.length&&(n.tools=c),n.output.trim()||(n.output=a.turns.map(r=>r.response).filter(r=>r.trim()).at(-1)??""),n.itemIds.includes(a.sessionId)||n.itemIds.push(a.sessionId),!n.at&&a.at&&(n.at=a.at)};for(const n of e)for(const a of n.subagents??[]){const c=a.match?.trim();if(!c)continue;const r=t.find(o=>!s.has(o)&&o.firstQuestion.trim()===c);r&&i(a,r)}for(const n of t){if(s.has(n)||!n.at)continue;const c=e.filter(o=>o.at&&o.at<=n.at).at(-1)??e.at(-1);if(!c)continue;const r={agent:n.runner,task:n.firstQuestion,output:"",itemIds:[n.sessionId],at:n.at};i(r,n),(c.subagents??=[]).push(r)}}function S(e,t,s,i,n,a,c){const r=[];for(const o of t.subagents??[]){const f=d(`[${o.agent}] ${o.task}`.trim(),o.output,!1);f.data.isBranch=!0,f.data.importSource={runner:n,sessionId:a,itemIds:o.itemIds,...c?{cwd:c}:{},subagent:{agent:o.agent,of:t.itemIds[0]??""}},f.data.source={question:f.data.question,response:f.data.response},o.tools?.length&&(f.data.attachments=h({question:o.task,response:o.output,itemIds:o.itemIds,tools:o.tools})),m(f),s.push(f),r.push(f),i.push({id:p(),source:e.id,target:f.id,type:"smoothstep",data:{isBranchFromSelection:!0,branchYRatio:.5}})}return r}function A(e,t,s,i,n){if(e.length===0)return null;const a=[],c=[];let r={id:i.id};for(const u of e){const l=d(u.question||"(tool-only turn)",u.response,!1);l.data.importSource={runner:s,sessionId:t,itemIds:u.itemIds,...n?{cwd:n}:{}},l.data.source={question:l.data.question,response:l.data.response},l.data.attachments=h(u),m(l),a.length===0&&(l.data.isBranch=!0),a.push(l),c.push({id:p(),source:r.id,target:l.id,type:"smoothstep"}),S(l,u,a,c,s,t,n),r=l}const o=_(a,c.filter(u=>u.source!==i.id)),f=Math.min(...o.map(u=>u.position.x)),g=Math.min(...o.map(u=>u.position.y));for(const u of o)u.position={x:u.position.x-f+i.x+560,y:u.position.y-g+i.y};return{nodes:o,edges:c,turnCount:e.length}}function B(e){const t=e??{},s={};typeof t.url=="string"&&/^https?:\/\//.test(t.url)&&(s.url=t.url);const i={};if(typeof t.pages=="string"&&t.pages.trim()&&(i.pages=t.pages.trim()),typeof t.offset=="number"||typeof t.limit=="number"){const n=Math.max(1,typeof t.offset=="number"?t.offset:1),a=typeof t.limit=="number"?n+Math.max(0,t.limit)-1:n;i.lines=[n,Math.max(n,a)]}return Object.keys(i).length&&(s.locator=i),s}function C(e){const t=e??{},s=t.file_path??t.notebook_path;return typeof s=="string"&&s?[s]:[]}function R(e,t){const s=t??{},i=$(e);return i==="write"&&typeof s.content=="string"?`${s.file_path??""}
5
+
6
+ ${s.content}`:i==="edit"&&typeof s.new_string=="string"?`${s.file_path??s.notebook_path??""}${s.replace_all?" (replace all)":""}
7
+ --- old
8
+ ${s.old_string??""}
9
+ +++ new
10
+ ${s.new_string}`:JSON.stringify(t??{})}export{k as A,O as T,$ as a,B as b,C as c,M as d,w as e,x as f,h as g,S as h,T as i,q as m,R as r,m as s,A as t};
@@ -1,4 +1,4 @@
1
- import{A as W}from"./index-8PjAYhti.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`
1
+ import{A as W}from"./index-DR0C_9n-.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-8PjAYhti.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};
1
+ import{t as u,a as n,f}from"./index-DR0C_9n-.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};
@@ -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-8PjAYhti.js"></script>
26
- <link rel="stylesheet" crossorigin href="/thoughtdag/assets/index-Lvtth6pP.css">
25
+ <script type="module" crossorigin src="/thoughtdag/assets/index-DR0C_9n-.js"></script>
26
+ <link rel="stylesheet" crossorigin href="/thoughtdag/assets/index-CqebPxzE.css">
27
27
  </head>
28
28
  <body>
29
29
  <div id="root"></div>
@@ -3,7 +3,7 @@
3
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, 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
6
+ - `models()` → `{ installed, models: [{ provider, id, name, reasoning, vision, efforts, defaultEffort }], default }`; when the CLI is nowhere, `{ installed: false, searched: [dirs] }` names every directory the lookup walked (see `where.cjs`: the process PATH, then the login shell's PATH, then the usual homes and Node version managers), and the next ask looks again — `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
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
 
@@ -16,19 +16,13 @@ const { randomUUID } = require('node:crypto');
16
16
  const { snapshotDir, diffSnapshots } = require('./fs-diff.cjs');
17
17
 
18
18
  const fsp = fs.promises;
19
- const CANDIDATE_DIRS = ['/opt/homebrew/bin', '/usr/local/bin', '/usr/bin', path.join(os.homedir(), '.local', 'bin'), path.join(os.homedir(), '.claude', 'local'), path.join(os.homedir(), '.npm-global', 'bin'), path.join(os.homedir(), '.bun', 'bin')];
19
+ const { locate, childEnv: envWith } = require('./where.cjs');
20
+ const EXTRA_HOMES = [path.join(os.homedir(), '.claude', 'local')]; // the CLI's own install location, on top of the usual ones
20
21
 
21
- async function executable(p) { try { await fsp.access(p, fs.constants.X_OK); return true; } catch { return false; } }
22
- async function findClaude() {
23
- const names = process.platform === 'win32' ? ['claude.cmd', 'claude.exe', 'claude'] : ['claude'];
24
- const dirs = [...(process.env.PATH || '').split(path.delimiter), ...CANDIDATE_DIRS].filter(Boolean);
25
- for (const d of dirs) for (const n of names) { const p = path.join(d, n); if (await executable(p)) return p; }
26
- return null;
27
- }
28
- function childEnv() {
29
- const seen = new Set((process.env.PATH || '').split(path.delimiter).filter(Boolean));
30
- return { ...process.env, PATH: [...seen, ...CANDIDATE_DIRS.filter((d) => !seen.has(d))].join(path.delimiter) };
31
- }
22
+ /** The claude binary and the directories walked to find it (see where.cjs). */
23
+ let located = null;
24
+ const findClaude = () => (located ??= locate('claude', EXTRA_HOMES));
25
+ const childEnv = () => envWith(EXTRA_HOMES);
32
26
 
33
27
  /** Claude Code has no model list; its aliases resolve to the newest model
34
28
  * of each family inside the CLI, so they never go stale. The CLI's own
@@ -114,14 +108,19 @@ function createClaudeRuntime({ log } = {}) {
114
108
  const say = log || (() => {});
115
109
  const runs = new Map(); // runId → state
116
110
  let binPromise = null;
117
- const bin = () => { if (!binPromise) binPromise = findClaude(); return binPromise; };
111
+ const bin = () => {
112
+ if (!binPromise) binPromise = findClaude().then((r) => { if (!r.path) { located = null; binPromise = null; } return r.path; });
113
+ return binPromise;
114
+ };
115
+ /** Where the lookup walked, for the picker's "not found" note. */
116
+ const searched = async () => (await findClaude()).searched;
118
117
 
119
118
  return {
120
119
  available: () => bin(),
121
120
 
122
121
  async models() {
123
122
  const b = await bin();
124
- if (!b) return { installed: false, models: [], default: null };
123
+ if (!b) return { installed: false, models: [], default: null, searched: await searched() };
125
124
  const [efforts, defaultEffort] = await Promise.all([effortsOf(b), configuredEffort()]);
126
125
  return { installed: true, models: MODELS.map((m) => ({ ...m, efforts, defaultEffort })), default: 'claude/claude-code/opus' };
127
126
  },
@@ -17,19 +17,13 @@ const { snapshotDir, diffSnapshots } = require('./fs-diff.cjs');
17
17
 
18
18
  const fsp = fs.promises;
19
19
  const RESPONSE_MS = 30 * 1000;
20
- const CANDIDATE_DIRS = ['/opt/homebrew/bin', '/usr/local/bin', '/usr/bin', path.join(os.homedir(), '.bun', 'bin'), path.join(os.homedir(), '.npm-global', 'bin'), path.join(os.homedir(), '.local', 'bin')];
20
+ const { locate, childEnv: envWith } = require('./where.cjs');
21
+ const EXTRA_HOMES = [path.join(os.homedir(), '.codex', 'bin')];
21
22
 
22
- async function executable(p) { try { await fsp.access(p, fs.constants.X_OK); return true; } catch { return false; } }
23
- async function findCodex() {
24
- const names = process.platform === 'win32' ? ['codex.cmd', 'codex.exe', 'codex'] : ['codex'];
25
- const dirs = [...(process.env.PATH || '').split(path.delimiter), ...CANDIDATE_DIRS].filter(Boolean);
26
- for (const d of dirs) for (const n of names) { const p = path.join(d, n); if (await executable(p)) return p; }
27
- return null;
28
- }
29
- function childEnv() {
30
- const seen = new Set((process.env.PATH || '').split(path.delimiter).filter(Boolean));
31
- return { ...process.env, PATH: [...seen, ...CANDIDATE_DIRS.filter((d) => !seen.has(d))].join(path.delimiter) };
32
- }
23
+ /** The codex binary and the directories walked to find it (see where.cjs). */
24
+ let located = null;
25
+ const findCodex = () => (located ??= locate('codex', EXTRA_HOMES));
26
+ const childEnv = () => envWith(EXTRA_HOMES);
33
27
 
34
28
  /** The guard file the canvas writes per working directory, as Codex policy. */
35
29
  async function policyFor(cwd) {
@@ -58,7 +52,12 @@ function createCodexRuntime({ log } = {}) {
58
52
  const byThread = new Map(); // threadId → runId
59
53
  const serverAsks = new Map(); // question id → { rpcId, respond(answer) }
60
54
  let binPromise = null;
61
- const bin = () => { if (!binPromise) binPromise = findCodex(); return binPromise; };
55
+ const bin = () => {
56
+ if (!binPromise) binPromise = findCodex().then((r) => { if (!r.path) { located = null; binPromise = null; } return r.path; });
57
+ return binPromise;
58
+ };
59
+ /** Where the lookup walked, for the picker's "not found" note. */
60
+ const searched = async () => (await findCodex()).searched;
62
61
 
63
62
  const write = (obj) => { try { proc.stdin.write(JSON.stringify(obj) + '\n'); return true; } catch { return false; } };
64
63
  const send = (method, params, timeoutMs = RESPONSE_MS) => new Promise((resolve, reject) => {
@@ -283,7 +282,7 @@ function createCodexRuntime({ log } = {}) {
283
282
 
284
283
  async models() {
285
284
  const b = await bin();
286
- if (!b) return { installed: false, models: [], default: null };
285
+ if (!b) return { installed: false, models: [], default: null, searched: await searched() };
287
286
  await ensureServer();
288
287
  const r = await send('model/list', {});
289
288
  noteEfforts(r);
@@ -23,32 +23,14 @@ const fsp = fs.promises;
23
23
  const GUARD = path.join(__dirname, 'pi-guard.mjs');
24
24
  const IDLE_MS = Number(process.env.TD_AGENT_IDLE_MS) || 10 * 60 * 1000; // a Pi process (session or catalog) retires after this long without a listener
25
25
  const RESPONSE_MS = 30 * 1000;
26
- const CANDIDATE_DIRS = [
27
- '/opt/homebrew/bin', '/usr/local/bin', '/usr/bin',
28
- path.join(os.homedir(), '.bun', 'bin'), path.join(os.homedir(), '.npm-global', 'bin'),
29
- path.join(os.homedir(), '.local', 'bin'), path.join(os.homedir(), '.pi', 'bin'),
30
- ];
26
+ const { locate, childEnv: envWith } = require('./where.cjs');
27
+ const EXTRA_HOMES = [path.join(os.homedir(), '.pi', 'bin')]; // pi's own home, on top of the usual ones (see where.cjs)
31
28
 
32
- async function executable(p) {
33
- try { await fsp.access(p, fs.constants.X_OK); return true; } catch { return false; }
34
- }
35
-
36
- /** The pi binary: PATH first (a terminal launch), then the usual homes
37
- * (a Finder launch carries almost no PATH). */
38
- async function findPi() {
39
- const names = process.platform === 'win32' ? ['pi.cmd', 'pi.exe', 'pi'] : ['pi'];
40
- const dirs = [...(process.env.PATH || '').split(path.delimiter), ...CANDIDATE_DIRS].filter(Boolean);
41
- for (const d of dirs) for (const n of names) { const p = path.join(d, n); if (await executable(p)) return p; }
42
- return null;
43
- }
44
-
45
- /** PATH for the child: the shell's plus the usual homes, so Pi's own bash
46
- * tool finds git, node and friends even under a Finder launch. */
47
- function childEnv() {
48
- const seen = new Set((process.env.PATH || '').split(path.delimiter).filter(Boolean));
49
- const extra = CANDIDATE_DIRS.filter((d) => !seen.has(d));
50
- return { ...process.env, PATH: [...seen, ...extra].join(path.delimiter) };
51
- }
29
+ /** The pi binary and the directories walked to find it (PATH, the login shell's PATH, the usual homes). */
30
+ let located = null;
31
+ const findPi = () => (located ??= locate('pi', EXTRA_HOMES));
32
+ /** PATH for the child: the shell's plus the usual homes, so Pi's own bash tool finds git, node and friends even under a Finder launch. */
33
+ const childEnv = () => envWith(EXTRA_HOMES);
52
34
 
53
35
  class PiProcess {
54
36
  constructor(bin, cwd, opts) {
@@ -191,7 +173,12 @@ function createPiRuntime({ log } = {}) {
191
173
  const runs = new Map(); // runId → { proc, abort() }
192
174
  let binPromise = null;
193
175
 
194
- const bin = () => { if (!binPromise) binPromise = findPi(); return binPromise; };
176
+ const bin = () => {
177
+ if (!binPromise) binPromise = findPi().then((r) => { if (!r.path) { located = null; binPromise = null; } return r.path; });
178
+ return binPromise;
179
+ };
180
+ /** Where the lookup walked, for the picker's "not found" note. */
181
+ const searched = async () => (await findPi()).searched;
195
182
  // Pi's thinking levels in its own words, from its own `--help`
196
183
  // (`--thinking <level> Set thinking level: off, minimal, …`); read once
197
184
  let levelsPromise = null;
@@ -228,7 +215,7 @@ function createPiRuntime({ log } = {}) {
228
215
  * sessionless process in the home directory. */
229
216
  async models() {
230
217
  const b = await bin();
231
- if (!b) return { installed: false, models: [], default: null };
218
+ if (!b) return { installed: false, models: [], default: null, searched: await searched() };
232
219
  const key = '\0catalog';
233
220
  let p = procs.get(key);
234
221
  if (!p || p.dead) { p = new PiProcess(b, os.homedir(), { log: say, noSession: true, onExit: (d) => { if (procs.get(key) === d) procs.delete(key); } }); procs.set(key, p); }
@@ -0,0 +1,99 @@
1
+ // Where a CLI lives when the process that launched us had no PATH to speak of.
2
+ //
3
+ // An app opened from the Finder, the Dock or a Start menu inherits a bare PATH
4
+ // (/usr/bin:/bin on macOS), while the person's CLIs sit wherever their Node
5
+ // version manager put them (nvm, fnm, volta, pnpm, asdf, mise …). A lookup
6
+ // therefore walks three lists in order: the process PATH, the PATH a login
7
+ // shell would give a terminal (asked once, with a timeout), and the usual
8
+ // homes, version managers included. Every lookup returns what it searched,
9
+ // so the picker can say where it looked when a runtime is missing.
10
+ 'use strict';
11
+
12
+ const { execFile } = require('node:child_process');
13
+ const fs = require('node:fs');
14
+ const os = require('node:os');
15
+ const path = require('node:path');
16
+
17
+ const fsp = fs.promises;
18
+ const home = os.homedir();
19
+ const isWin = process.platform === 'win32';
20
+ const LOGIN_SHELL_MS = 4000;
21
+
22
+ /** The login shell's PATH (zsh, bash and fish read their rc files there). Asked once; empty on Windows or when the shell is silent. */
23
+ let loginPathPromise = null;
24
+ function loginPath() {
25
+ if (loginPathPromise) return loginPathPromise;
26
+ loginPathPromise = new Promise((resolve) => {
27
+ if (isWin) return resolve([]);
28
+ const shell = process.env.SHELL || '/bin/zsh';
29
+ const args = /fish$/.test(shell) ? ['-l', '-c', 'string join : $PATH'] : ['-ilc', 'printf "%s" "$PATH"'];
30
+ execFile(shell, args, { timeout: LOGIN_SHELL_MS, env: { ...process.env, TERM: 'dumb' }, maxBuffer: 1 << 16 }, (err, stdout) => {
31
+ if (err) return resolve([]);
32
+ // rc files may print greetings; PATH itself has no newline, so the last line is ours
33
+ const line = String(stdout || '').split('\n').map((s) => s.trim()).filter(Boolean).pop() || '';
34
+ resolve(line.split(path.delimiter).map((d) => d.trim()).filter(Boolean));
35
+ });
36
+ });
37
+ return loginPathPromise;
38
+ }
39
+
40
+ const numeric = (v) => String(v).replace(/^v/, '').split('.').map((n) => parseInt(n, 10) || 0);
41
+ const newestFirst = (a, b) => { const x = numeric(a), y = numeric(b); for (let i = 0; i < 3; i++) if (x[i] !== y[i]) return (y[i] || 0) - (x[i] || 0); return 0; };
42
+ const versionsUnder = (dir, ...sub) => { try { return fs.readdirSync(dir).sort(newestFirst).map((v) => path.join(dir, v, ...sub)); } catch { return []; } };
43
+
44
+ /** Where package managers and Node version managers put global binaries. Newest Node first, so a lookup lands on the version the person uses. */
45
+ function managerDirs() {
46
+ const dirs = [];
47
+ dirs.push(...versionsUnder(path.join(process.env.NVM_DIR || path.join(home, '.nvm'), 'versions', 'node'), 'bin'));
48
+ for (const base of [process.env.FNM_DIR, path.join(home, '.fnm'), path.join(home, '.local', 'share', 'fnm'), path.join(home, 'Library', 'Application Support', 'fnm')].filter(Boolean)) {
49
+ dirs.push(path.join(base, 'aliases', 'default', 'bin'));
50
+ dirs.push(...versionsUnder(path.join(base, 'node-versions'), 'installation', 'bin'));
51
+ }
52
+ dirs.push(
53
+ path.join(home, '.volta', 'bin'), path.join(process.env.N_PREFIX || path.join(home, 'n'), 'bin'), path.join(home, '.nodenv', 'shims'),
54
+ path.join(home, '.asdf', 'shims'), path.join(home, '.local', 'share', 'mise', 'shims'), path.join(home, '.proto', 'shims'),
55
+ path.join(home, 'Library', 'pnpm'), path.join(home, '.local', 'share', 'pnpm'), path.join(home, '.yarn', 'bin'), path.join(home, '.config', 'yarn', 'global', 'node_modules', '.bin'),
56
+ path.join(home, '.deno', 'bin'), path.join(home, '.cargo', 'bin'), path.join(home, 'bin'),
57
+ );
58
+ if (isWin) {
59
+ const { APPDATA, LOCALAPPDATA, ProgramFiles } = process.env;
60
+ if (APPDATA) dirs.push(path.join(APPDATA, 'npm'));
61
+ if (LOCALAPPDATA) dirs.push(path.join(LOCALAPPDATA, 'pnpm'), path.join(LOCALAPPDATA, 'Volta', 'bin'), path.join(LOCALAPPDATA, 'Programs', 'nvm'));
62
+ if (ProgramFiles) dirs.push(path.join(ProgramFiles, 'nodejs'));
63
+ }
64
+ return dirs;
65
+ }
66
+
67
+ const COMMON_HOMES = ['/opt/homebrew/bin', '/usr/local/bin', '/usr/bin', path.join(home, '.bun', 'bin'), path.join(home, '.npm-global', 'bin'), path.join(home, '.local', 'bin')];
68
+
69
+ /** Every directory a lookup walks, in order and without repeats. Sync when the login shell has not answered yet. */
70
+ function dirsSync(extraHomes, login) {
71
+ const seen = new Set(); const out = [];
72
+ const add = (d) => { if (d && !seen.has(d)) { seen.add(d); out.push(d); } };
73
+ (process.env.PATH || '').split(path.delimiter).forEach(add);
74
+ login.forEach(add);
75
+ [...COMMON_HOMES, ...extraHomes, ...managerDirs()].forEach(add);
76
+ return out;
77
+ }
78
+ let loginSeen = [];
79
+ async function searchDirs(extraHomes = []) {
80
+ loginSeen = await loginPath();
81
+ return dirsSync(extraHomes, loginSeen);
82
+ }
83
+
84
+ async function executable(p) { try { await fsp.access(p, fs.constants.X_OK); return true; } catch { return false; } }
85
+
86
+ /** The first executable called `name` (Windows tries .cmd and .exe too): `{ path, searched }`, path null when nowhere. */
87
+ async function locate(name, extraHomes = []) {
88
+ const names = isWin ? [`${name}.cmd`, `${name}.exe`, name] : [name];
89
+ const searched = await searchDirs(extraHomes);
90
+ for (const d of searched) for (const n of names) { const p = path.join(d, n); if (await executable(p)) return { path: p, searched }; }
91
+ return { path: null, searched };
92
+ }
93
+
94
+ /** The process environment with PATH widened the same way, so a CLI's own tools (git, node, bash) resolve under a Finder launch too. */
95
+ function childEnv(extraHomes = []) {
96
+ return { ...process.env, PATH: dirsSync(extraHomes, loginSeen).join(path.delimiter) };
97
+ }
98
+
99
+ module.exports = { locate, childEnv, searchDirs, loginPath };
package/lib/why.mjs CHANGED
@@ -61,8 +61,8 @@ function frameMembers(frame, nodes) {
61
61
  // ../src/lib/constants.ts
62
62
  var API_BASE = define_import_meta_env_default.VITE_API_BASE ?? (define_import_meta_env_default.DEV ? "http://localhost:3001" : "");
63
63
  var LAYOUT_COL_WIDTH = 540;
64
- var LAYOUT_H_GAP = 48;
65
- var LAYOUT_V_GAP = 72;
64
+ var LAYOUT_H_GAP = 64;
65
+ var LAYOUT_V_GAP = 96;
66
66
  var COLLAPSED_LAYOUT_HEIGHT = 240;
67
67
 
68
68
  // ../src/lib/layout.ts
@@ -548,6 +548,47 @@ ${tool.result}`,
548
548
  ...tool.op ? { op: tool.op } : {}
549
549
  }));
550
550
  }
551
+ function adoptSubagentSessions(turns, subs) {
552
+ const used = /* @__PURE__ */ new Set();
553
+ const settle = (run, sub) => {
554
+ used.add(sub);
555
+ const tools = sub.turns.flatMap((t) => t.tools);
556
+ if (tools.length) run.tools = tools;
557
+ if (!run.output.trim()) run.output = sub.turns.map((t) => t.response).filter((r) => r.trim()).at(-1) ?? "";
558
+ if (!run.itemIds.includes(sub.sessionId)) run.itemIds.push(sub.sessionId);
559
+ if (!run.at && sub.at) run.at = sub.at;
560
+ };
561
+ for (const turn of turns) for (const run of turn.subagents ?? []) {
562
+ const key = run.match?.trim();
563
+ if (!key) continue;
564
+ const sub = subs.find((x) => !used.has(x) && x.firstQuestion.trim() === key);
565
+ if (sub) settle(run, sub);
566
+ }
567
+ for (const sub of subs) {
568
+ if (used.has(sub) || !sub.at) continue;
569
+ const before = turns.filter((t) => t.at && t.at <= sub.at);
570
+ const turn = before.at(-1) ?? turns.at(-1);
571
+ if (!turn) continue;
572
+ const run = { agent: sub.runner, task: sub.firstQuestion, output: "", itemIds: [sub.sessionId], at: sub.at };
573
+ settle(run, sub);
574
+ (turn.subagents ??= []).push(run);
575
+ }
576
+ }
577
+ function attachSubagentBranches(parent, turn, nodes, edges, runner, sessionId, cwd) {
578
+ const kids = [];
579
+ for (const run of turn.subagents ?? []) {
580
+ const node = makeNode(`[${run.agent}] ${run.task}`.trim(), run.output, false);
581
+ node.data.isBranch = true;
582
+ node.data.importSource = { runner, sessionId, itemIds: run.itemIds, ...cwd ? { cwd } : {}, subagent: { agent: run.agent, of: turn.itemIds[0] ?? "" } };
583
+ node.data.source = { question: node.data.question, response: node.data.response };
584
+ if (run.tools?.length) node.data.attachments = toolAttachments({ question: run.task, response: run.output, itemIds: run.itemIds, tools: run.tools });
585
+ seedPlaque(node);
586
+ nodes.push(node);
587
+ kids.push(node);
588
+ edges.push({ id: generateId(), source: parent.id, target: node.id, type: "smoothstep", data: { isBranchFromSelection: true, branchYRatio: 0.5 } });
589
+ }
590
+ return kids;
591
+ }
551
592
  function toolScope(input) {
552
593
  const i = input ?? {};
553
594
  const out = {};
@@ -664,6 +705,10 @@ var ClaudeSessionCollector = class {
664
705
  ...reg.url ? { url: reg.url } : {},
665
706
  ...reg.locator ? { locator: reg.locator } : {}
666
707
  });
708
+ if (reg.spawn) {
709
+ const report = clip(resultText(p.content), ARTIFACT_CALL_LIMIT);
710
+ (this.current.subagents ??= []).push({ agent: reg.spawn.agent, task: reg.spawn.task, output: report.text, itemIds: [p.tool_use_id], match: reg.spawn.prompt, ...line.timestamp ? { at: line.timestamp } : {} });
711
+ }
667
712
  this.pendingTools.delete(p.tool_use_id);
668
713
  }
669
714
  }
@@ -708,7 +753,9 @@ ${text}` : text;
708
753
  if (p.type === "tool_use" && p.id && p.name) {
709
754
  const op = toolOpOf(p.name);
710
755
  const call = clip(renderCall(p.name, p.input), op === "write" || op === "edit" ? ARTIFACT_CALL_LIMIT : TOOL_CALL_LIMIT);
711
- this.pendingTools.set(p.id, { name: p.name, call: call.text, paths: toolPaths(p.input), op, ...toolScope(p.input) });
756
+ const inp = p.input && typeof p.input === "object" ? p.input : {};
757
+ const spawn = op === "agent" ? { agent: String(inp.subagent_type ?? "agent"), task: String(inp.description ?? inp.prompt?.split("\n")[0] ?? ""), prompt: String(inp.prompt ?? "") } : void 0;
758
+ this.pendingTools.set(p.id, { name: p.name, call: call.text, paths: toolPaths(p.input), op, ...toolScope(p.input), ...spawn ? { spawn } : {} });
712
759
  }
713
760
  }
714
761
  }
@@ -729,9 +776,17 @@ ${text}` : text;
729
776
  messageCount: s.turns.length,
730
777
  source: "claude-code",
731
778
  sessionId: s.sessionId,
732
- build: () => buildGraphFromTurns(s.turns, s.sessionId, s.cwd)
779
+ build: () => buildGraphFromTurns(s.turns, s.sessionId, s.cwd),
780
+ adopt: (subs) => adoptSubagentSessions(s.turns, subs)
733
781
  };
734
782
  }
783
+ /** This file as a subagent's transcript (a sidechain file), for its parent to adopt. */
784
+ toSubagentSession() {
785
+ const s = this.finish();
786
+ if (!s || s.turns.length === 0 || this.mode !== "sidechain") return null;
787
+ const first = s.turns[0];
788
+ return { runner: "claude-code", sessionId: s.sessionId, firstQuestion: this.firstQuestion ?? first.question, turns: s.turns, ...s.cwd ? { cwd: s.cwd } : {}, ...first.at ? { at: first.at } : {} };
789
+ }
735
790
  };
736
791
  function noteNode(text) {
737
792
  const n = makeNode(text, "", false);
@@ -762,6 +817,7 @@ function buildGraphFromTurns(turns, sessionId, cwd) {
762
817
  seedPlaque(node);
763
818
  node.data.attachments = toolAttachments(turn);
764
819
  nodes.push(node);
820
+ attachSubagentBranches(node, turn, nodes, edges, "claude-code", sessionId, cwd);
765
821
  const parent = turn.parentItemId ? byItem.get(turn.parentItemId) : void 0;
766
822
  if (parent) link(parent, node);
767
823
  else if (prev) link(prev, node);
@@ -799,6 +855,7 @@ var CodexSessionCollector = class {
799
855
  sawItems = false;
800
856
  firstQuestion = null;
801
857
  parentThreadId = null;
858
+ startedAt = null;
802
859
  feedLine(raw) {
803
860
  const t = raw.trim();
804
861
  if (!t) return;
@@ -832,6 +889,7 @@ var CodexSessionCollector = class {
832
889
  this.cwd = p.cwd ?? "";
833
890
  this.day = p.timestamp?.slice(0, 10) ?? "";
834
891
  this.parentThreadId = p.parent_thread_id ?? null;
892
+ this.startedAt = p.timestamp ?? line.timestamp ?? null;
835
893
  return;
836
894
  }
837
895
  if (line.type === "response_item" || line.type === "event_msg") this.sawItems = true;
@@ -931,9 +989,18 @@ ${turn.response}` : turn.response;
931
989
  messageCount: s.turns.length,
932
990
  source: "codex",
933
991
  sessionId: s.sessionId,
934
- build: () => buildGraphFromTurns2(s.turns, s.sessionId, s.cwd)
992
+ build: () => buildGraphFromTurns2(s.turns, s.sessionId, s.cwd),
993
+ adopt: (subs) => adoptSubagentSessions(s.turns, subs)
935
994
  };
936
995
  }
996
+ /** This rollout as a child thread's transcript, for the parent named in its header to adopt. */
997
+ toSubagentSession() {
998
+ const s = this.finish();
999
+ if (!s || s.turns.length === 0 || !s.subagent) return null;
1000
+ const first = s.turns[0];
1001
+ const at = first.at ?? this.startedAt ?? void 0;
1002
+ return { runner: "codex", sessionId: s.sessionId, firstQuestion: this.firstQuestion ?? first.question, turns: s.turns, ...s.cwd ? { cwd: s.cwd } : {}, ...at ? { at } : {} };
1003
+ }
937
1004
  };
938
1005
  function buildGraphFromTurns2(turns, sessionId, cwd) {
939
1006
  const origin = cwd ? { cwd } : {};
@@ -958,6 +1025,7 @@ function buildGraphFromTurns2(turns, sessionId, cwd) {
958
1025
  nodes.push(node);
959
1026
  if (prev) edges.push({ id: generateId(), source: prev.id, target: node.id, type: "smoothstep" });
960
1027
  if (noteToWire) edges.push({ id: generateId(), source: noteToWire.id, target: node.id, type: "smoothstep" });
1028
+ attachSubagentBranches(node, turn, nodes, edges, "codex", sessionId, cwd);
961
1029
  prev = node;
962
1030
  }
963
1031
  return { nodes: autoLayout(nodes, edges), edges };
@@ -1199,6 +1267,27 @@ ${e.newText ?? ""}`).join("\n\n");
1199
1267
  const c = clipText(JSON.stringify(args ?? {}), TOOL_CALL_LIMIT);
1200
1268
  return { ...c, paths, ...locator ? { locator } : {} };
1201
1269
  }
1270
+ var finalOutput = (messages) => {
1271
+ if (!Array.isArray(messages)) return "";
1272
+ for (let i = messages.length - 1; i >= 0; i--) {
1273
+ const m = messages[i];
1274
+ if (m?.role === "assistant") {
1275
+ const text = textOf(m.content).trim();
1276
+ if (text) return text;
1277
+ }
1278
+ }
1279
+ return "";
1280
+ };
1281
+ function subagentRuns(args, content, details, callId) {
1282
+ const d = details;
1283
+ if (Array.isArray(d?.results) && d.results.length) {
1284
+ return d.results.map((r, i) => ({ agent: String(r.agent ?? "agent"), task: String(r.task ?? ""), output: finalOutput(r.messages) || String(r.stderr ?? "").trim(), itemIds: [`${callId}#${i}`] }));
1285
+ }
1286
+ const a = args && typeof args === "object" ? args : {};
1287
+ const asked = Array.isArray(a.tasks) && a.tasks.length ? a.tasks : Array.isArray(a.chain) && a.chain.length ? a.chain : a.agent || a.task ? [{ agent: a.agent, task: a.task }] : [];
1288
+ const text = textOf(content).trim();
1289
+ return asked.map((x, i) => ({ agent: String(x.agent ?? "agent"), task: String(x.task ?? ""), output: asked.length === 1 ? text : "", itemIds: [`${callId}#${i}`] }));
1290
+ }
1202
1291
  var PiSessionCollector = class {
1203
1292
  turns = [];
1204
1293
  pending = /* @__PURE__ */ new Map();
@@ -1261,7 +1350,7 @@ ${text}` : text;
1261
1350
  for (const b of m.content) {
1262
1351
  if (b?.type !== "toolCall" || typeof b.id !== "string" || typeof b.name !== "string") continue;
1263
1352
  const r = renderCall2(b.name, b.arguments);
1264
- this.pending.set(b.id, { name: b.name, call: r.text, truncated: r.truncated, op: toolOpOf(b.name), paths: r.paths, ...r.locator ? { locator: r.locator } : {} });
1353
+ this.pending.set(b.id, { name: b.name, call: r.text, truncated: r.truncated, op: toolOpOf(b.name), paths: r.paths, ...r.locator ? { locator: r.locator } : {}, ...b.name === "subagent" ? { args: b.arguments } : {} });
1265
1354
  }
1266
1355
  }
1267
1356
  return;
@@ -1284,6 +1373,10 @@ ${text}` : text;
1284
1373
  ...reg.paths.length ? { paths: reg.paths } : {},
1285
1374
  ...reg.locator ? { locator: reg.locator } : {}
1286
1375
  });
1376
+ if (reg.name === "subagent") {
1377
+ const runs = subagentRuns(reg.args, m.content, m.details, id);
1378
+ if (runs.length) cur.subagents = [...cur.subagents ?? [], ...runs];
1379
+ }
1287
1380
  this.pending.delete(id);
1288
1381
  }
1289
1382
  }
@@ -1322,6 +1415,7 @@ function buildGraphFromTurns4(turns, sessionId, cwd) {
1322
1415
  const parent = turn.parentItemId ? byItem.get(turn.parentItemId) : void 0;
1323
1416
  if (parent) link(parent, node);
1324
1417
  else if (prev) link(prev, node);
1418
+ attachSubagentBranches(node, turn, nodes, edges, "pi", sessionId, cwd);
1325
1419
  for (const id of turn.itemIds) byItem.set(id, node);
1326
1420
  prev = node;
1327
1421
  }
@@ -3365,7 +3459,7 @@ async function memoriesJson() {
3365
3459
  const facts = await ensureFresh();
3366
3460
  return Object.values(facts.sessions).filter((x) => x.kind === "memory").sort((a, b) => b.mtime - a.mtime).map((x) => ({ id: x.id, runner: x.runner, file: x.file, title: x.title, cwd: x.cwd, entries: x.turns.length, mtime: x.mtime, headings: x.turns.map((t) => t.q) }));
3367
3461
  }
3368
- var CLI_VERSION = "0.2.1";
3462
+ var CLI_VERSION = "0.2.2";
3369
3463
  var MCP_TOOLS = [
3370
3464
  { name: "why_check", description: "Cheap first question before editing a file: does this artifact have any history in local agent sessions? One line; history true/false.", inputSchema: { type: "object", properties: { path: { type: "string", description: "file path (absolute or relative to cwd), URL, or arxiv:<id>" } }, required: ["path"] } },
3371
3465
  { name: "why_file", description: "The turns across local Claude Code, Codex, DeepSeek Harness, Pi and ThoughtDAG sessions that touched a file, URL or paper: when, what changed (\u0394, observed), what was asked, what the answer said about it (\u2248, a candidate explanation, not a verified reason). Each hit carries a deep link.", inputSchema: { type: "object", properties: { path: { type: "string" }, include_read: { type: "boolean", description: "also list turns that only read it (default false)" }, limit: { type: "number", description: "max hits (default 10)" } }, required: ["path"] } },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-thoughtdag",
3
- "version": "0.5.3",
3
+ "version": "0.5.5",
4
4
  "description": "ThoughtDAG for DeepSeek Harness: turn the current DSH session into an editable thought graph on an infinite canvas.",
5
5
  "author": "Xia Chen",
6
6
  "repository": {
@@ -1,7 +0,0 @@
1
- import{q as I,B as g,n as T}from"./index-8PjAYhti.js";import{t as C,a as _,r as A,b as x,c as w,d as L,e as S,s as k,f as v,m as O,T as b,A as q,g as B}from"./shared-DJOVlRHh.js";const M=16e3;function f(o){return typeof o=="string"?o:Array.isArray(o)?o.filter(t=>t.type==="text"&&t.text).map(t=>t.text).join(`
2
- `):""}function N(o){return typeof o=="string"?o:Array.isArray(o)?o.map(t=>t.type==="text"&&t.text?t.text:t.type==="image"?"[image result omitted]":"").filter(Boolean).join(`
3
- `):""}const p=S;class ${turns=[];pendingTools=new Map;current=null;pendingCompaction;sessionId=null;customTitle=null;summary=null;slug=null;firstQuestion=null;cwd=null;mode=null;feedLine(t){const s=t.trim();if(!s)return;let n;try{n=JSON.parse(s)}catch{return}this.feed(n)}flush(){const t=this.current;t&&(t.question||t.response||t.tools.length)&&this.turns.push(t),this.current=null}feed(t){if(t.type==="custom-title"&&t.customTitle&&(this.customTitle=t.customTitle),t.type==="summary"&&t.summary&&!this.summary&&(this.summary=t.summary),t.slug&&!this.slug&&(this.slug=t.slug),t.cwd&&!this.cwd&&(this.cwd=t.cwd),this.mode===null&&(t.type==="user"||t.type==="assistant")&&t.uuid&&(this.mode=t.isSidechain&&t.agentId?"sidechain":"main",this.mode==="sidechain"&&(this.sessionId=t.agentId)),!!t.isSidechain==(this.mode==="sidechain")){if(t.type==="system"&&t.subtype==="compact_boundary"){this.flush();const s=t.compactMetadata;this.pendingCompaction=`[Compaction] The source runner compacted its history here${s?.preTokens?` (${s.preTokens} → ${s.postTokens??"?"} tokens)`:""}. Everything above this point reached later turns only as a summary.`;return}if((t.type==="user"||t.type==="assistant")&&t.uuid&&t.sessionId&&!this.sessionId&&(this.sessionId=t.sessionId),t.type==="user"){const s=t.message?.content;if(Array.isArray(s)){for(const e of s)if(e.type==="tool_result"&&e.tool_use_id){const i=this.pendingTools.get(e.tool_use_id);if(i&&this.current){const a=p(N(e.content),b);this.current.tools.push({name:i.name,call:i.call,result:a.text,truncated:a.truncated,...i.paths.length?{paths:i.paths}:{},op:i.op,nativeCallId:e.tool_use_id,...i.url?{url:i.url}:{},...i.locator?{locator:i.locator}:{}}),this.pendingTools.delete(e.tool_use_id)}}}const n=f(s);if(!n.trim())return;if(/^\s*<task-notification>/.test(n)){if(this.current){const e=p(n,M);this.current.tools.push({name:"Agent",call:"(task notification)",result:e.text,truncated:e.truncated,op:"agent"})}return}this.flush(),this.firstQuestion||(this.firstQuestion=n.trim()),this.current={question:n,response:"",itemIds:t.uuid?[t.uuid]:[],tools:[],parentItemId:t.parentUuid??void 0,...t.timestamp?{at:t.timestamp}:{}},this.pendingCompaction&&(this.current.compactionBefore=this.pendingCompaction,this.pendingCompaction=void 0);return}if(t.type==="assistant"&&this.current){t.uuid&&this.current.itemIds.push(t.uuid);const s=t.message?.content,n=f(s);if(n.trim()&&(this.current.response=this.current.response?`${this.current.response}
4
-
5
- ${n}`:n),Array.isArray(s)){for(const e of s)if(e.type==="tool_use"&&e.id&&e.name){const i=_(e.name),a=p(A(e.name,e.input),i==="write"||i==="edit"?q:B);this.pendingTools.set(e.id,{name:e.name,call:a.text,paths:w(e.input),op:i,...x(e.input)})}}}}}finish(){if(this.flush(),!this.sessionId)return null;const t=this.firstQuestion?.split(`
6
- `).map(n=>n.trim()).find(n=>n&&!n.startsWith("<"))?.slice(0,80)??null,s=this.customTitle??this.summary??t??this.slug??`session ${this.sessionId.slice(0,8)}`;return{sessionId:this.sessionId,title:s,turns:L(this.turns),...this.cwd?{cwd:this.cwd}:{}}}toConversation(){const t=this.finish();return!t||t.turns.length===0?null:{title:t.title,messageCount:t.turns.length,source:"claude-code",sessionId:t.sessionId,build:()=>Q(t.turns,t.sessionId,t.cwd)}}}function y(o){const t=new $;for(const s of o.split(`
7
- `))t.feedLine(s);return t}function F(o){const t=I(o,"",!1);return t.data.stepKind="note",O(t),t}function Q(o,t,s){const n=s?{cwd:s}:{},e=[],i=[];let a=null;const m=new Map,l=(u,c)=>{i.push({id:T(),source:u.id,target:c.id,type:"smoothstep"})};for(const u of o){let c=null;if(u.compactionBefore){const d=F(u.compactionBefore);d.data.importSource={runner:"claude-code",sessionId:t,itemIds:[],...n},e.push(d),c=d}const r=I(u.question,u.response,a===null);r.data.importSource={runner:"claude-code",sessionId:t,itemIds:u.itemIds,...n},r.data.source={question:r.data.question,response:r.data.response},k(r),r.data.attachments=v(u),e.push(r);const h=u.parentItemId?m.get(u.parentItemId):void 0;h?l(h,r):a&&l(a,r),c&&l(c,r);for(const d of u.itemIds)m.set(d,r);a=r}return{nodes:g(e,i),edges:i}}function E(o,t){const s=y(o).finish();return s?C(s.turns,s.sessionId,"claude-code",t,s.cwd):null}function R(o){return y(o).toConversation()}export{$ as ClaudeSessionCollector,E as claudeCodeSessionAsBranch,R as claudeCodeSessionConversation};