@lelouchhe/webagent 0.8.0 → 0.10.0

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 (56) hide show
  1. package/README.md +43 -15
  2. package/config.toml +7 -27
  3. package/dist/index.html +21 -5
  4. package/dist/js/app.INIQQEGD.js +5 -0
  5. package/dist/js/chunk.3CLGCUHW.js +1 -0
  6. package/dist/js/{chunk.CT5WBNGZ.js → chunk.7WADDFJZ.js} +50 -49
  7. package/dist/js/chunk.AOTG3PL7.js +20 -0
  8. package/dist/js/{login.2WA6DTGM.js → login.WMURU4NI.js} +1 -1
  9. package/dist/js/viewer.RHZMFYWJ.js +1 -0
  10. package/dist/login.html +2 -2
  11. package/dist/share-viewer.html +6 -6
  12. package/dist/{styles.00etlpgs.css → styles.01aj0l37.css} +186 -4
  13. package/dist/sw.js +6 -6
  14. package/lib/agent-key.js +6 -0
  15. package/lib/attachment-dispatch.js +60 -31
  16. package/lib/attachment-interceptor.js +7 -7
  17. package/lib/attachment-labels.js +1 -1
  18. package/lib/attachments.js +69 -7
  19. package/lib/auth-middleware.js +11 -4
  20. package/lib/auth.js +2 -2
  21. package/lib/bridge.js +209 -90
  22. package/lib/client-registry.js +12 -12
  23. package/lib/config.js +2 -31
  24. package/lib/event-handler.js +166 -85
  25. package/lib/files/limits.js +15 -0
  26. package/lib/files/paths.js +155 -0
  27. package/lib/files/routes.js +232 -0
  28. package/lib/home-path.js +35 -0
  29. package/lib/http-status.js +1 -0
  30. package/lib/mcp/capability.js +74 -0
  31. package/lib/mcp/server.js +148 -0
  32. package/lib/mcp/task-history.js +245 -0
  33. package/lib/mcp/task-host.js +253 -0
  34. package/lib/mcp/tools.js +168 -0
  35. package/lib/mode-bucket.js +1 -1
  36. package/lib/push-service.js +33 -35
  37. package/lib/routes.js +1022 -475
  38. package/lib/server.js +84 -34
  39. package/lib/share/routes.js +97 -85
  40. package/lib/shared/task-reference.js +20 -0
  41. package/lib/sse-manager.js +8 -8
  42. package/lib/store.js +992 -284
  43. package/lib/task-collaboration.js +15 -0
  44. package/lib/task-manager.js +1409 -0
  45. package/lib/task-path.js +131 -0
  46. package/lib/{session-state.js → task-state.js} +90 -38
  47. package/lib/task-tree-lock.js +74 -0
  48. package/lib/{sessions-anchor.js → tasks-anchor.js} +8 -7
  49. package/lib/tokens.js +1 -1
  50. package/lib/types.js +2 -2
  51. package/package.json +8 -1
  52. package/dist/js/app.XBFXH37R.js +0 -2
  53. package/dist/js/chunk.UMQMOGWO.js +0 -1
  54. package/dist/js/viewer.CVWXSKJM.js +0 -1
  55. package/lib/session-manager.js +0 -613
  56. package/lib/title-service.js +0 -95
@@ -0,0 +1,20 @@
1
+ import"./chunk.AJZBJBMO.js";var C=class{diff(e,t,i={}){let r;typeof i=="function"?(r=i,i={}):"callback"in i&&(r=i.callback);let l=this.castInput(e,i),o=this.castInput(t,i),m=this.removeEmpty(this.tokenize(l,i)),d=this.removeEmpty(this.tokenize(o,i));return this.diffWithOptionsObj(m,d,i,r)}diffWithOptionsObj(e,t,i,r){var l;let o=u=>{if(u=this.postProcess(u,i),r){setTimeout(function(){r(u)},0);return}else return u},m=t.length,d=e.length,w=1,g=m+d;i.maxEditLength!=null&&(g=Math.min(g,i.maxEditLength));let x=(l=i.timeout)!==null&&l!==void 0?l:1/0,F=Date.now()+x,y=[{oldPos:-1,lastComponent:void 0}],f=this.extractCommon(y[0],t,e,0,i);if(y[0].oldPos+1>=d&&f+1>=m)return o(this.buildValues(y[0].lastComponent,t,e));let s=-1/0,c=1/0,h=()=>{for(let u=Math.max(s,-w);u<=Math.min(c,w);u+=2){let a,p=y[u-1],v=y[u+1];p&&(y[u-1]=void 0);let W=!1;if(v){let b=v.oldPos-u;W=v&&0<=b&&b<m}let N=p&&p.oldPos+1<d;if(!W&&!N){y[u]=void 0;continue}if(!N||W&&p.oldPos<v.oldPos?a=this.addToPath(v,!0,!1,0,i):a=this.addToPath(p,!1,!0,1,i),f=this.extractCommon(a,t,e,u,i),a.oldPos+1>=d&&f+1>=m)return o(this.buildValues(a.lastComponent,t,e))||!0;y[u]=a,a.oldPos+1>=d&&(c=Math.min(c,u-1)),f+1>=m&&(s=Math.max(s,u+1))}w++};if(r)(function u(){setTimeout(function(){if(w>g||Date.now()>F)return r(void 0);h()||u()},0)})();else for(;w<=g&&Date.now()<=F;){let u=h();if(u)return u}}addToPath(e,t,i,r,l){let o=e.lastComponent;return o&&!l.oneChangePerToken&&o.added===t&&o.removed===i?{oldPos:e.oldPos+r,lastComponent:{count:o.count+1,added:t,removed:i,previousComponent:o.previousComponent}}:{oldPos:e.oldPos+r,lastComponent:{count:1,added:t,removed:i,previousComponent:o}}}extractCommon(e,t,i,r,l){let o=t.length,m=i.length,d=e.oldPos,w=d-r,g=0;for(;w+1<o&&d+1<m&&this.equals(i[d+1],t[w+1],l);)w++,d++,g++,l.oneChangePerToken&&(e.lastComponent={count:1,previousComponent:e.lastComponent,added:!1,removed:!1});return g&&!l.oneChangePerToken&&(e.lastComponent={count:g,previousComponent:e.lastComponent,added:!1,removed:!1}),e.oldPos=d,w}equals(e,t,i){return i.comparator?i.comparator(e,t):e===t||!!i.ignoreCase&&e.toLowerCase()===t.toLowerCase()}removeEmpty(e){let t=[];for(let i=0;i<e.length;i++)e[i]&&t.push(e[i]);return t}castInput(e,t){return e}tokenize(e,t){return Array.from(e)}join(e){return e.join("")}postProcess(e,t){return e}get useLongestToken(){return!1}buildValues(e,t,i){let r=[],l;for(;e;)r.push(e),l=e.previousComponent,delete e.previousComponent,e=l;r.reverse();let o=r.length,m=0,d=0,w=0;for(;m<o;m++){let g=r[m];if(g.removed)g.value=this.join(i.slice(w,w+g.count)),w+=g.count;else{if(!g.added&&this.useLongestToken){let x=t.slice(d,d+g.count);x=x.map(function(F,y){let f=i[w+y];return f.length>F.length?f:F}),g.value=this.join(x)}else g.value=this.join(t.slice(d,d+g.count));d+=g.count,g.added||(w+=g.count)}}return r}};var R=class extends C{},oe=new R;function be(n,e,t){return oe.diff(n,e,t)}function _(n,e){let t;for(t=0;t<n.length&&t<e.length;t++)if(n[t]!=e[t])return n.slice(0,t);return n.slice(0,t)}function $(n,e){let t;if(!n||!e||n[n.length-1]!=e[e.length-1])return"";for(t=0;t<n.length&&t<e.length;t++)if(n[n.length-(t+1)]!=e[e.length-(t+1)])return n.slice(-t);return n.slice(-t)}function M(n,e,t){if(n.slice(0,e.length)!=e)throw Error(`string ${JSON.stringify(n)} doesn't start with prefix ${JSON.stringify(e)}; this is a bug`);return t+n.slice(e.length)}function H(n,e,t){if(!e)return n+t;if(n.slice(-e.length)!=e)throw Error(`string ${JSON.stringify(n)} doesn't end with suffix ${JSON.stringify(e)}; this is a bug`);return n.slice(0,-e.length)+t}function S(n,e){return M(n,e,"")}function D(n,e){return H(n,e,"")}function U(n,e){return e.slice(0,Ee(n,e))}function Ee(n,e){let t=0;n.length>e.length&&(t=n.length-e.length);let i=e.length;n.length<e.length&&(i=n.length);let r=Array(i),l=0;r[0]=0;for(let o=1;o<i;o++){for(e[o]==e[l]?r[o]=r[l]:r[o]=l;l>0&&e[o]!=e[l];)l=r[l];e[o]==e[l]&&l++}l=0;for(let o=t;o<n.length;o++){for(;l>0&&n[o]!=e[l];)l=r[l];n[o]==e[l]&&l++}return l}function se(n){return n.includes(`\r
2
+ `)&&!n.startsWith(`
3
+ `)&&!n.match(/[^\r]\n/)}function fe(n){return!n.includes(`\r
4
+ `)&&n.includes(`
5
+ `)}function q(n,e){let t=[];for(let i of Array.from(e.segment(n))){let r=i.segment;t.length&&/\s/.test(t[t.length-1])&&/\s/.test(r)?t[t.length-1]+=r:t.push(r)}return t}function P(n,e){if(e)return L(n,e)[1];let t;for(t=n.length-1;t>=0&&n[t].match(/\s/);t--);return n.substring(t+1)}function O(n,e){if(e)return L(n,e)[0];let t=n.match(/^\s*/);return t?t[0]:""}function L(n,e){if(!e)return[O(n),P(n)];if(e.resolvedOptions().granularity!="word")throw new Error('The segmenter passed must have a granularity of "word"');let t=q(n,e),i=t[0],r=t[t.length-1],l=/\s/.test(i)?i:"",o=/\s/.test(r)?r:"";return[l,o]}var T="a-zA-Z0-9_\\u{AD}\\u{C0}-\\u{D6}\\u{D8}-\\u{F6}\\u{F8}-\\u{2C6}\\u{2C8}-\\u{2D7}\\u{2DE}-\\u{2FF}\\u{1E00}-\\u{1EFF}",Oe=new RegExp(`[${T}]+|\\s+|[^${T}]`,"ug"),G=class extends C{equals(e,t,i){return i.ignoreCase&&(e=e.toLowerCase(),t=t.toLowerCase()),e.trim()===t.trim()}tokenize(e,t={}){let i;if(t.intlSegmenter){let o=t.intlSegmenter;if(o.resolvedOptions().granularity!="word")throw new Error('The segmenter passed must have a granularity of "word"');i=q(e,o)}else i=e.match(Oe)||[];let r=[],l=null;return i.forEach(o=>{/\s/.test(o)?l==null?r.push(o):r.push(r.pop()+o):l!=null&&/\s/.test(l)?r[r.length-1]==l?r.push(r.pop()+o):r.push(l+o):r.push(o),l=o}),r}join(e){return e.map((t,i)=>i==0?t:t.replace(/^\s+/,"")).join("")}postProcess(e,t){if(!e||t.oneChangePerToken)return e;let i=null,r=null,l=null;return e.forEach(o=>{o.added?r=o:o.removed?l=o:((r||l)&&ue(i,l,r,o,t.intlSegmenter),i=o,r=null,l=null)}),(r||l)&&ue(i,l,r,null,t.intlSegmenter),e}},ae=new G;function ke(n,e,t){return t?.ignoreWhitespace!=null&&!t.ignoreWhitespace?ce(n,e,t):ae.diff(n,e,t)}function ue(n,e,t,i,r){if(e&&t){let[l,o]=L(e.value,r),[m,d]=L(t.value,r);if(n){let w=_(l,m);n.value=H(n.value,m,w),e.value=S(e.value,w),t.value=S(t.value,w)}if(i){let w=$(o,d);i.value=M(i.value,d,w),e.value=D(e.value,w),t.value=D(t.value,w)}}else if(t){if(n){let l=O(t.value,r);t.value=t.value.substring(l.length)}if(i){let l=O(i.value,r);i.value=i.value.substring(l.length)}}else if(n&&i){let l=O(i.value,r),[o,m]=L(e.value,r),d=_(l,o);e.value=S(e.value,d);let w=$(S(l,d),m);e.value=D(e.value,w),i.value=M(i.value,l,w),n.value=H(n.value,l,l.slice(0,l.length-w.length))}else if(i){let l=O(i.value,r),o=P(e.value,r),m=U(o,l);e.value=D(e.value,m)}else if(n){let l=P(n.value,r),o=O(e.value,r),m=U(l,o);e.value=S(e.value,m)}}var J=class extends C{tokenize(e){let t=new RegExp(`(\\r?\\n)|[${T}]+|[^\\S\\n\\r]+|[^${T}]`,"ug");return e.match(t)||[]}},de=new J;function ce(n,e,t){return de.diff(n,e,t)}function me(n,e){if(typeof n=="function")e.callback=n;else if(n)for(let t in n)Object.prototype.hasOwnProperty.call(n,t)&&(e[t]=n[t]);return e}var V=class extends C{constructor(){super(...arguments),this.tokenize=Q}equals(e,t,i){return i.ignoreWhitespace?((!i.newlineIsToken||!e.includes(`
6
+ `))&&(e=e.trim()),(!i.newlineIsToken||!t.includes(`
7
+ `))&&(t=t.trim())):i.ignoreNewlineAtEof&&!i.newlineIsToken&&(e.endsWith(`
8
+ `)&&(e=e.slice(0,-1)),t.endsWith(`
9
+ `)&&(t=t.slice(0,-1))),super.equals(e,t,i)}},B=new V;function A(n,e,t){return B.diff(n,e,t)}function Se(n,e,t){return t=me(t,{ignoreWhitespace:!0}),B.diff(n,e,t)}function Q(n,e){e.stripTrailingCr&&(n=n.replace(/\r\n/g,`
10
+ `));let t=[],i=n.split(/(\n|\r\n)/);i[i.length-1]||i.pop();for(let r=0;r<i.length;r++){let l=i[r];r%2&&!e.newlineIsToken?t[t.length-1]+=l:t.push(l)}return t}function Le(n){return n=="."||n=="!"||n=="?"}var X=class extends C{tokenize(e){var t;let i=[],r=0;for(let l=0;l<e.length;l++){if(l==e.length-1){i.push(e.slice(r));break}if(Le(e[l])&&e[l+1].match(/\s/)){for(i.push(e.slice(r,l+1)),l=r=l+1;!((t=e[l+1])===null||t===void 0)&&t.match(/\s/);)l++;i.push(e.slice(r,l+1)),r=l+1}}return i}},he=new X;function De(n,e,t){return he.diff(n,e,t)}var Y=class extends C{tokenize(e){return e.split(/([{}:;,]|\s+)/)}},ge=new Y;function Me(n,e,t){return ge.diff(n,e,t)}var Z=class extends C{constructor(){super(...arguments),this.tokenize=Q}get useLongestToken(){return!0}castInput(e,t){let{undefinedReplacement:i,stringifyReplacer:r=(l,o)=>typeof o>"u"?i:o}=t;return typeof e=="string"?e:JSON.stringify(I(e,null,null,r),null," ")}equals(e,t,i){return super.equals(e.replace(/,([\r\n])/g,"$1"),t.replace(/,([\r\n])/g,"$1"),i)}},pe=new Z;function He(n,e,t){return pe.diff(n,e,t)}function I(n,e,t,i,r){e=e||[],t=t||[],i&&(n=i(r===void 0?"":r,n));let l;for(l=0;l<e.length;l+=1)if(e[l]===n)return t[l];let o;if(Object.prototype.toString.call(n)==="[object Array]"){for(e.push(n),o=new Array(n.length),t.push(o),l=0;l<n.length;l+=1)o[l]=I(n[l],e,t,i,String(l));return e.pop(),t.pop(),o}if(n&&n.toJSON&&(n=n.toJSON()),typeof n=="object"&&n!==null){e.push(n),o={},t.push(o);let m=[],d;for(d in n)Object.prototype.hasOwnProperty.call(n,d)&&m.push(d);for(m.sort(),l=0;l<m.length;l+=1)d=m[l],o[d]=I(n[d],e,t,i,d);e.pop(),t.pop()}else o=n;return o}var K=class extends C{tokenize(e){return e.slice()}join(e){return e}removeEmpty(e){return e}},we=new K;function Pe(n,e,t){return we.diff(n,e,t)}function ee(n){return Array.isArray(n)?n.map(e=>ee(e)):Object.assign(Object.assign({},n),{hunks:n.hunks.map(e=>Object.assign(Object.assign({},e),{lines:e.lines.map((t,i)=>{var r;return t.startsWith("\\")||t.endsWith("\r")||!((r=e.lines[i+1])===null||r===void 0)&&r.startsWith("\\")?t:t+"\r"})}))})}function ne(n){return Array.isArray(n)?n.map(e=>ne(e)):Object.assign(Object.assign({},n),{hunks:n.hunks.map(e=>Object.assign(Object.assign({},e),{lines:e.lines.map(t=>t.endsWith("\r")?t.substring(0,t.length-1):t)}))})}function xe(n){return Array.isArray(n)||(n=[n]),!n.some(e=>e.hunks.some(t=>t.lines.some(i=>!i.startsWith("\\")&&i.endsWith("\r"))))}function ve(n){return Array.isArray(n)||(n=[n]),n.some(e=>e.hunks.some(t=>t.lines.some(i=>i.endsWith("\r"))))&&n.every(e=>e.hunks.every(t=>t.lines.every((i,r)=>{var l;return i.startsWith("\\")||i.endsWith("\r")||((l=t.lines[r+1])===null||l===void 0?void 0:l.startsWith("\\"))})))}function z(n){let e=n.split(/\n/),t=[],i=0;function r(f){return/^diff --git /.test(f)}function l(f){return r(f)||/^Index:\s/.test(f)||/^diff(?: -r \w+)+\s/.test(f)}function o(f){return/^(---|\+\+\+)\s/.test(f)}function m(f){return/^@@\s/.test(f)}function d(){var f;let s={};s.hunks=[],t.push(s);let c=!1;for(;i<e.length;){let h=e[i];if(o(h)||m(h))break;if(r(h)){if(c)return;c=!0,s.isGit=!0;let u=w(h);for(u&&(s.oldFileName=u.oldFileName,s.newFileName=u.newFileName),i++;i<e.length;){let a=e[i];if(o(a)||m(a)||l(a))break;let p=/^rename from (.*)/.exec(a);p&&(s.oldFileName="a/"+g(p[1]),s.isRename=!0);let v=/^rename to (.*)/.exec(a);v&&(s.newFileName="b/"+g(v[1]),s.isRename=!0);let W=/^copy from (.*)/.exec(a);W&&(s.oldFileName="a/"+g(W[1]),s.isCopy=!0);let N=/^copy to (.*)/.exec(a);N&&(s.newFileName="b/"+g(N[1]),s.isCopy=!0);let b=/^new file mode (\d+)/.exec(a);b&&(s.isCreate=!0,s.newMode=b[1]);let k=/^deleted file mode (\d+)/.exec(a);k&&(s.isDelete=!0,s.oldMode=k[1]);let re=/^old mode (\d+)/.exec(a);re&&(s.oldMode=re[1]);let le=/^new mode (\d+)/.exec(a);le&&(s.newMode=le[1]),/^Binary files /.test(a)&&(s.isBinary=!0),i++}continue}else if(l(h)){if(c)return;c=!0;let u=/^(?:Index:|diff(?: -r \w+)+)\s+/.exec(h);u&&(s.index=h.substring(u[0].length).trim())}i++}if(F(s),F(s),s.oldFileName===void 0!=(s.newFileName===void 0))throw new Error("Missing "+(s.oldFileName!==void 0?'"+++ ..."':'"--- ..."')+" file header for "+((f=s.oldFileName)!==null&&f!==void 0?f:s.newFileName));for(;i<e.length;){let h=e[i];if(l(h)||o(h)||/^===================================================================/.test(h))break;m(h)?s.hunks.push(y()):i++}}function w(f){let s=f.substring(11);if(s.startsWith('"')){let h=x(s);if(h===null)return null;let u=s.substring(h.rawLength+1),a;if(u.startsWith('"')){let p=x(u);if(p===null)return null;a=p.fileName}else a=u;return{oldFileName:h.fileName,newFileName:a}}let c=s.indexOf('"');if(c>0){let h=s.substring(0,c-1),u=x(s.substring(c));return u===null?null:{oldFileName:h,newFileName:u.fileName}}if(s.startsWith("a/")){let h=[],u=0;for(;u=s.indexOf(" b/",u+1),u!==-1;)h.push(u);if(h.length>0){let a=h[Math.floor(h.length/2)];return{oldFileName:s.substring(0,a),newFileName:s.substring(a+1)}}}return null}function g(f){if(f.startsWith('"')){let s=x(f);if(s)return s.fileName}return f}function x(f){if(!f.startsWith('"'))return null;let s="",c=1;for(;c<f.length;){if(f[c]==='"')return{fileName:s,rawLength:c+1};if(f[c]==="\\"&&c+1<f.length)switch(c++,f[c]){case"a":s+="\x07";break;case"b":s+="\b";break;case"f":s+="\f";break;case"n":s+=`
11
+ `;break;case"r":s+="\r";break;case"t":s+=" ";break;case"v":s+="\v";break;case"\\":s+="\\";break;case'"':s+='"';break;case"0":case"1":case"2":case"3":case"4":case"5":case"6":case"7":{if(c+2>=f.length||f[c+1]<"0"||f[c+1]>"7"||f[c+2]<"0"||f[c+2]>"7")return null;let h=[parseInt(f.substring(c,c+3),8)];for(c+=3;f[c]==="\\"&&f[c+1]>="0"&&f[c+1]<="7";){if(c+3>=f.length||f[c+2]<"0"||f[c+2]>"7"||f[c+3]<"0"||f[c+3]>"7")return null;h.push(parseInt(f.substring(c+1,c+4),8)),c+=4}s+=new TextDecoder("utf-8").decode(new Uint8Array(h));continue}default:return null}else s+=f[c];c++}return null}function F(f){let s=/^(---|\+\+\+)\s+/.exec(e[i]);if(s){let c=s[1],h=e[i].substring(3).trim().split(" ",2),u=(h[1]||"").trim(),a=h[0];a.startsWith('"')?a=g(a):a=a.replace(/\\\\/g,"\\"),c==="---"?(f.oldFileName=a,f.oldHeader=u):(f.newFileName=a,f.newHeader=u),i++}}function y(){var f;let s=i,c=e[i++],h=c.split(/@@ -(\d+)(?:,(\d+))? \+(\d+)(?:,(\d+))? @@/),u={oldStart:+h[1],oldLines:typeof h[2]>"u"?1:+h[2],newStart:+h[3],newLines:typeof h[4]>"u"?1:+h[4],lines:[]};u.oldLines===0&&(u.oldStart+=1),u.newLines===0&&(u.newStart+=1);let a=0,p=0;for(;i<e.length&&(p<u.oldLines||a<u.newLines||!((f=e[i])===null||f===void 0)&&f.startsWith("\\"));i++){let v=e[i].length==0&&i!=e.length-1?" ":e[i][0];if(v==="+"||v==="-"||v===" "||v==="\\")u.lines.push(e[i]),v==="+"?a++:v==="-"?p++:v===" "&&(a++,p++);else throw new Error(`Hunk at line ${s+1} contained invalid line ${e[i]}`)}if(!a&&u.newLines===1&&(u.newLines=0),!p&&u.oldLines===1&&(u.oldLines=0),a!==u.newLines)throw new Error("Added line count did not match for hunk at line "+(s+1));if(p!==u.oldLines)throw new Error("Removed line count did not match for hunk at line "+(s+1));if(i<e.length&&e[i]&&/^[+ -]/.test(e[i])&&!o(e[i]))throw new Error("Hunk at line "+(s+1)+" has more lines than expected (expected "+u.oldLines+" old lines and "+u.newLines+" new lines)");return u}for(;i<e.length;)d();return t}function ye(n,e,t){let i=!0,r=!1,l=!1,o=1;return function m(){if(i&&!l){if(r?o++:i=!1,n+o<=t)return n+o;l=!0}if(!r)return l||(i=!0),e<=n-o?n-o++:(r=!0,m())}}function Fe(n,e,t={}){let i;if(typeof e=="string"?i=z(e):Array.isArray(e)?i=e:i=[e],i.length>1)throw new Error("applyPatch only works with a single input.");return Te(n,i[0],t)}function Te(n,e,t={}){(t.autoConvertLineEndings||t.autoConvertLineEndings==null)&&(se(n)&&xe(e)?e=ee(e):fe(n)&&ve(e)&&(e=ne(e)));let i=n.split(`
12
+ `),r=e.hunks,l=t.compareLine||((f,s,c,h)=>s===h),o=t.fuzzFactor||0,m=0;if(o<0||!Number.isInteger(o))throw new Error("fuzzFactor must be a non-negative integer");if(!r.length)return n;let d="",w=!1,g=!1;for(let f=0;f<r[r.length-1].lines.length;f++){let s=r[r.length-1].lines[f];s[0]=="\\"&&(d[0]=="+"?w=!0:d[0]=="-"&&(g=!0)),d=s}if(w){if(g){if(!o&&i[i.length-1]=="")return!1}else if(i[i.length-1]=="")i.pop();else if(!o)return!1}else if(g){if(i[i.length-1]!="")i.push("");else if(!o)return!1}function x(f,s,c,h=0,u=!0,a=[],p=0){let v=0,W=!1;for(;h<f.length;h++){let N=f[h],b=N.length>0?N[0]:" ",k=N.length>0?N.substr(1):N;if(b==="-")if(l(s+1,i[s],b,k))s++,v=0;else return!c||i[s]==null?null:(a[p]=i[s],x(f,s+1,c-1,h,!1,a,p+1));if(b==="+"){if(!u)return null;a[p]=k,p++,v=0,W=!0}if(b===" ")if(v++,a[p]=i[s],l(s+1,i[s],b,k))p++,u=!0,W=!1,s++;else return W||!c?null:i[s]&&(x(f,s+1,c-1,h+1,!1,a,p+1)||x(f,s+1,c-1,h,!1,a,p+1))||x(f,s,c-1,h+1,!1,a,p)}return p-=v,s-=v,a.length=p,{patchedLines:a,oldLineLastI:s-1}}let F=[],y=0;for(let f=0;f<r.length;f++){let s=r[f],c,h=i.length-s.oldLines+o,u;for(let a=0;a<=o;a++){u=s.oldStart+y-1;let p=ye(u,m,h);for(;u!==void 0&&(c=x(s.lines,u,a),!c);u=p());if(c)break}if(!c)return!1;for(let a=m;a<u;a++)F.push(i[a]);for(let a=0;a<c.patchedLines.length;a++){let p=c.patchedLines[a];F.push(p)}m=c.oldLineLastI+1,y=u+1-s.oldStart}for(let f=m;f<i.length;f++)F.push(i[f]);return F.join(`
13
+ `)}function Ae(n,e){let t=typeof n=="string"?z(n):n,i=0;function r(){let l=t[i++];if(!l)return e.complete();e.loadFile(l,function(o,m){if(o)return e.complete(o);let d=Fe(m,l,e);e.patched(l,d,function(w){if(w)return e.complete(w);r()})})}r()}function Ce(n){return n===void 0||n==="/dev/null"?n:n.startsWith("a/")?"b/"+n.slice(2):n.startsWith("b/")?"a/"+n.slice(2):n}function Ne(n){if(Array.isArray(n))return n.map(t=>Ne(t)).reverse();let e=Object.assign(Object.assign({},n),{oldFileName:n.isGit?Ce(n.newFileName):n.newFileName,oldHeader:n.newHeader,newFileName:n.isGit?Ce(n.oldFileName):n.oldFileName,newHeader:n.oldHeader,oldMode:n.newMode,newMode:n.oldMode,isCreate:n.isDelete,isDelete:n.isCreate,hunks:n.hunks.map(t=>({oldLines:t.newLines,oldStart:t.newStart,newLines:t.oldLines,newStart:t.oldStart,lines:t.lines.map(i=>i.startsWith("-")?`+${i.slice(1)}`:i.startsWith("+")?`-${i.slice(1)}`:i)}))});return n.isCopy&&(e.newFileName="/dev/null",e.newHeader=void 0,e.isDelete=!0,delete e.isCreate,delete e.isCopy,delete e.isRename,e.hunks=[]),e}function Ie(n){for(let e=0;e<n.length;e++)if(n[e]<" "||n[e]>"~"||n[e]==='"'||n[e]==="\\")return!0;return!1}function E(n){if(!Ie(n))return n;let e='"',t=new TextEncoder().encode(n),i=0;for(;i<t.length;){let r=t[i];r===7?e+="\\a":r===8?e+="\\b":r===9?e+="\\t":r===10?e+="\\n":r===11?e+="\\v":r===12?e+="\\f":r===13?e+="\\r":r===34?e+='\\"':r===92?e+="\\\\":r>=32&&r<=126?e+=String.fromCharCode(r):e+="\\"+r.toString(8).padStart(3,"0"),i++}return e+='"',e}var te={includeIndex:!0,includeUnderline:!0,includeFileHeaders:!0},ze={includeIndex:!1,includeUnderline:!1,includeFileHeaders:!0},je={includeIndex:!1,includeUnderline:!1,includeFileHeaders:!1};function ie(n,e,t,i,r,l,o){let m;o?typeof o=="function"?m={callback:o}:m=o:m={},typeof m.context>"u"&&(m.context=4);let d=m.context;if(m.newlineIsToken)throw new Error("newlineIsToken may not be used with patch-generation functions, only with diffing functions");if(m.callback){let{callback:g}=m;A(t,i,Object.assign(Object.assign({},m),{callback:x=>{let F=w(x);g(F)}}))}else return w(A(t,i,m));function w(g){if(!g)return;g.push({value:"",lines:[]});function x(u){return u.map(function(a){return" "+a})}let F=[],y=0,f=0,s=[],c=1,h=1;for(let u=0;u<g.length;u++){let a=g[u],p=a.lines||_e(a.value);if(a.lines=p,a.added||a.removed){if(!y){let v=g[u-1];y=c,f=h,v&&(s=d>0?x(v.lines.slice(-d)):[],y-=s.length,f-=s.length)}for(let v of p)s.push((a.added?"+":"-")+v);a.added?h+=p.length:c+=p.length}else{if(y)if(p.length<=d*2&&u<g.length-2)for(let v of x(p))s.push(v);else{let v=Math.min(p.length,d);for(let N of x(p.slice(0,v)))s.push(N);let W={oldStart:y,oldLines:c-y+v,newStart:f,newLines:h-f+v,lines:s};F.push(W),y=0,f=0,s=[]}c+=p.length,h+=p.length}}for(let u of F)for(let a=0;a<u.lines.length;a++)u.lines[a].endsWith(`
14
+ `)?u.lines[a]=u.lines[a].slice(0,-1):(u.lines.splice(a+1,0,"\"),a++);return{oldFileName:n,newFileName:e,oldHeader:r,newHeader:l,hunks:F}}}function j(n,e){var t,i,r,l,o,m;if(e||(e=te),Array.isArray(n)){if(n.length>1&&!e.includeFileHeaders&&!n.every(g=>g.isGit))throw new Error("Cannot omit file headers on a multi-file patch. (The result would be unparseable; how would a tool trying to apply the patch know which changes are to which file?)");return n.map(g=>j(g,e)).join(`
15
+ `)}let d=[];if(n.isGit){if(e=te,!n.oldFileName)throw new Error("oldFileName must be specified for Git patches");if(!n.newFileName)throw new Error("newFileName must be specified for Git patches");let g=n.oldFileName,x=n.newFileName;n.isCreate&&g==="/dev/null"?g=x.replace(/^b\//,"a/"):n.isDelete&&x==="/dev/null"&&(x=g.replace(/^a\//,"b/")),d.push("diff --git "+E(g)+" "+E(x)),n.isDelete&&d.push("deleted file mode "+((t=n.oldMode)!==null&&t!==void 0?t:"100644")),n.isCreate&&d.push("new file mode "+((i=n.newMode)!==null&&i!==void 0?i:"100644")),n.oldMode&&n.newMode&&!n.isDelete&&!n.isCreate&&(d.push("old mode "+n.oldMode),d.push("new mode "+n.newMode)),n.isRename&&(d.push("rename from "+E(((r=n.oldFileName)!==null&&r!==void 0?r:"").replace(/^a\//,""))),d.push("rename to "+E(((l=n.newFileName)!==null&&l!==void 0?l:"").replace(/^b\//,"")))),n.isCopy&&(d.push("copy from "+E(((o=n.oldFileName)!==null&&o!==void 0?o:"").replace(/^a\//,""))),d.push("copy to "+E(((m=n.newFileName)!==null&&m!==void 0?m:"").replace(/^b\//,""))))}else e.includeIndex&&n.oldFileName==n.newFileName&&n.oldFileName!==void 0&&d.push("Index: "+n.oldFileName),e.includeUnderline&&d.push("===================================================================");let w=n.hunks.length>0;e.includeFileHeaders&&n.oldFileName!==void 0&&n.newFileName!==void 0&&(!n.isGit||w)&&(d.push("--- "+E(n.oldFileName)+(n.oldHeader?" "+n.oldHeader:"")),d.push("+++ "+E(n.newFileName)+(n.newHeader?" "+n.newHeader:"")));for(let g=0;g<n.hunks.length;g++){let x=n.hunks[g],F=x.oldLines===0?x.oldStart-1:x.oldStart,y=x.newLines===0?x.newStart-1:x.newStart;d.push("@@ -"+F+","+x.oldLines+" +"+y+","+x.newLines+" @@");for(let f of x.lines)d.push(f)}return d.join(`
16
+ `)+`
17
+ `}function We(n,e,t,i,r,l,o){if(typeof o=="function"&&(o={callback:o}),o?.callback){let{callback:m}=o;ie(n,e,t,i,r,l,Object.assign(Object.assign({},o),{callback:d=>{m(d?j(d,o.headerOptions):void 0)}}))}else{let m=ie(n,e,t,i,r,l,o);return m?j(m,o?.headerOptions):void 0}}function Re(n,e,t,i,r,l){return We(n,n,e,t,i,r,l)}function _e(n){let e=n.endsWith(`
18
+ `),t=n.split(`
19
+ `).map(i=>i+`
20
+ `);return e?t.pop():t.push(t.pop().slice(0,-1)),t}function $e(n){let e=[],t,i;for(let r=0;r<n.length;r++)t=n[r],t.added?i=1:t.removed?i=-1:i=0,e.push([i,t.value]);return e}function Ue(n){let e=[];for(let t=0;t<n.length;t++){let i=n[t];i.added?e.push("<ins>"):i.removed&&e.push("<del>"),e.push(qe(i.value)),i.added?e.push("</ins>"):i.removed&&e.push("</del>")}return e.join("")}function qe(n){let e=n;return e=e.replace(/&/g,"&amp;"),e=e.replace(/</g,"&lt;"),e=e.replace(/>/g,"&gt;"),e=e.replace(/"/g,"&quot;"),e}export{C as Diff,ze as FILE_HEADERS_ONLY,te as INCLUDE_HEADERS,je as OMIT_HEADERS,Fe as applyPatch,Ae as applyPatches,we as arrayDiff,I as canonicalize,oe as characterDiff,$e as convertChangesToDMP,Ue as convertChangesToXML,Re as createPatch,We as createTwoFilesPatch,ge as cssDiff,Pe as diffArrays,be as diffChars,Me as diffCss,He as diffJson,A as diffLines,De as diffSentences,Se as diffTrimmedLines,ke as diffWords,ce as diffWordsWithSpace,j as formatPatch,pe as jsonDiff,B as lineDiff,z as parsePatch,Ne as reversePatch,he as sentenceDiff,ie as structuredPatch,ae as wordDiff,de as wordsWithSpaceDiff};
@@ -1 +1 @@
1
- import{b as r,c as i}from"./chunk.UMQMOGWO.js";import"./chunk.AJZBJBMO.js";localStorage.getItem(r)&&location.replace("/");var s=document.getElementById("login-form"),n=document.getElementById("token-input"),o=document.getElementById("error"),t=document.getElementById("submit-btn");function u(e){o&&(o.textContent=e,o.style.display=e?"block":"none")}s&&n&&t&&(s.addEventListener("submit",e=>{e.preventDefault(),u(""),t.disabled=!0,t.textContent="Verifying\u2026",(async()=>{try{let l=await i(n.value);l.ok?location.replace("/"):u(l.error)}finally{t.disabled=!1,t.textContent="Sign in"}})()}),n.focus());
1
+ import{b as r,c as i}from"./chunk.3CLGCUHW.js";import"./chunk.AJZBJBMO.js";localStorage.getItem(r)&&location.replace("/");var s=document.getElementById("login-form"),n=document.getElementById("token-input"),o=document.getElementById("error"),t=document.getElementById("submit-btn");function u(e){o&&(o.textContent=e,o.style.display=e?"block":"none")}s&&n&&t&&(s.addEventListener("submit",e=>{e.preventDefault(),u(""),t.disabled=!0,t.textContent="Verifying\u2026",(async()=>{try{let l=await i(n.value);l.ok?location.replace("/"):u(l.error)}finally{t.disabled=!1,t.textContent="Sign in"}})()}),n.focus());
@@ -0,0 +1 @@
1
+ import{c as g,e as h,n as y,p as T,q as C}from"./chunk.7WADDFJZ.js";import{a as p}from"./chunk.3CLGCUHW.js";import"./chunk.AJZBJBMO.js";var $=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"];function f(t){return String(t).padStart(2,"0")}function E(t,n){if(!t)return"";let r=new Date(t),i=r.getTime();if(isNaN(i))return"";let l=Math.floor((n.getTime()-i)/1e3);if(l<60)return"just now";let c=Math.floor(l/60);if(c<60)return`${c}m ago`;let o=Math.floor(c/60);if(o<24)return`${o}h ago`;let e=Math.floor(o/24);if(e<7)return`${e}d ago`;let m=r.getUTCFullYear()===n.getUTCFullYear(),s=$[r.getUTCMonth()],a=r.getUTCDate();return m?`${s} ${a}`:`${s} ${a}, ${r.getUTCFullYear()}`}function _(t){if(!t)return"";let n=new Date(t);return isNaN(n.getTime())?"":`${n.getUTCFullYear()}-${f(n.getUTCMonth()+1)}-${f(n.getUTCDate())} ${f(n.getUTCHours())}:${f(n.getUTCMinutes())} UTC`}function S(t){let n=/^\/api\/v1\/tasks\/[^/]+\/attachments\/([A-Za-z0-9._-]+)(?:\?.*)?$/;return r=>{let i=n.exec(r);return i?`/s/${encodeURIComponent(t)}/attachments/${i[1]}`:r}}function w(t){if(typeof t.data=="string")try{return JSON.parse(t.data)}catch{return{}}return t.data}function x(t,n,r,i){if(t.type!=="assistant_message"||n?.type!=="assistant_message"||t.seq!==n.seq+1||!r?.classList.contains("assistant"))return!1;let l=(r.getAttribute("data-raw")??"")+(typeof i.text=="string"?i.text:"");return r.setAttribute("data-raw",l),C(r,l,void 0,!0),g(r),!0}function v(t,n,r){let i=new Set;for(let s of t)if(s.type==="permission_response")try{let a=JSON.parse(s.data);typeof a.requestId=="string"&&i.add(a.requestId)}catch{}let l=new Map,c=new Map,o=null,e=null,m={rewriteAttachmentSrc:S(r),enhanceMarkdown:g,findToolCallEl:s=>l.get(s)??null,findPermissionEl:s=>c.get(s)??null,findBashEl:()=>o,isPermissionResolved:s=>i.has(s),finalAnswerToolText:null};for(let s=0;s<t.length;s++){let a=t[s];if(!y(a.type))continue;let u=w(a),A=s>0?t[s-1]:null,M=n.lastElementChild;if(x(a,A,M,u)){e=null;continue}m.finalAnswerToolText=a.type==="assistant_message"?e:null;let d=T(a.type,u,m);d?(n.appendChild(d),a.type==="tool_call"&&typeof u.id=="string"?l.set(u.id,d):a.type==="permission_request"&&typeof u.requestId=="string"?c.set(u.requestId,d):a.type==="bash_command"&&(o=d)):a.type==="bash_result"&&(o=null),a.type==="tool_call_update"?e=h(typeof u.status=="string"?u.status:"",Array.isArray(u.content)?u.content:void 0):e=null}}async function k(){try{let e=localStorage.getItem("theme")??"auto";document.documentElement.setAttribute("data-theme",e)}catch{}let t=/^\/s\/([A-Za-z0-9_-]{24})(?:[/?#]|$)/.exec(location.pathname);if(!t){document.body.textContent="invalid share URL";return}let n=t[1],r=document.getElementById("task-info"),i=document.getElementById("messages"),l=document.querySelector(".share-footer-author"),c=document.querySelector(".share-footer-meta");if(!i)return;let o;try{let e=await fetch(`/api/v1/shared/${encodeURIComponent(n)}/events`,{headers:{Accept:"application/json"},credentials:"omit"});if(e.status===p.GONE){document.body.innerHTML="<div class='share-gone'>\u6B64\u94FE\u63A5\u5DF2\u64A4\u9500\u6216\u8FC7\u671F\u3002</div>";return}if(!e.ok){document.body.textContent=`error ${e.status}`;return}o=await e.json()}catch(e){document.body.textContent=`failed to load: ${String(e)}`;return}if(document.title=o.share.task_title?`${o.share.task_title} \u2014 shared`:"shared task",r&&(r.textContent=o.share.task_title??"(untitled)"),l){let e=o.share.display_name??"";l.textContent=e?`by ${e}`:""}if(v(o.events,i,n),c){let e=o.share.shared_at??o.share.created_at;c.textContent=E(e,new Date),c instanceof HTMLElement&&(c.title=_(e))}}k();
package/dist/login.html CHANGED
@@ -17,7 +17,7 @@
17
17
  media="(prefers-color-scheme: light)"
18
18
  />
19
19
  <script src="/theme-init.js"></script>
20
- <link rel="stylesheet" href="/styles.00etlpgs.css" />
20
+ <link rel="stylesheet" href="/styles.01aj0l37.css" />
21
21
  </head>
22
22
  <body class="login-page">
23
23
  <div class="login-card">
@@ -44,6 +44,6 @@
44
44
  <code>webagent --create-token &lt;name&gt;</code> on the server.
45
45
  </p>
46
46
  </div>
47
- <script type="module" src="/js/login.2WA6DTGM.js"></script>
47
+ <script type="module" src="/js/login.WMURU4NI.js"></script>
48
48
  </body>
49
49
  </html>
@@ -3,7 +3,7 @@
3
3
  <head>
4
4
  <meta charset="utf-8" />
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1" />
6
- <title>shared session — >_</title>
6
+ <title>shared task — >_</title>
7
7
  <link rel="icon" href="/icons/icon.svg" type="image/svg+xml" />
8
8
  <meta
9
9
  name="theme-color"
@@ -16,10 +16,10 @@
16
16
  media="(prefers-color-scheme: light)"
17
17
  />
18
18
  <meta name="robots" content="noindex, nofollow" />
19
- <link rel="stylesheet" href="/s/_/styles.00etlpgs.css" />
19
+ <link rel="stylesheet" href="/s/_/styles.01aj0l37.css" />
20
20
  <link rel="stylesheet" href="/s/_/share-viewer.00gubshk.css" />
21
- <link rel="modulepreload" href="/s/_/chunk.CT5WBNGZ.js">
22
- <link rel="modulepreload" href="/s/_/chunk.UMQMOGWO.js">
21
+ <link rel="modulepreload" href="/s/_/chunk.7WADDFJZ.js">
22
+ <link rel="modulepreload" href="/s/_/chunk.3CLGCUHW.js">
23
23
  <link rel="modulepreload" href="/s/_/chunk.AJZBJBMO.js">
24
24
  </head>
25
25
  <body class="share-viewer">
@@ -28,7 +28,7 @@
28
28
  <span class="logo">>_</span>
29
29
  <span class="share-badge">shared</span>
30
30
  </div>
31
- <span id="session-info" class="status"></span>
31
+ <span id="task-info" class="status"></span>
32
32
  <div class="header-side header-right">
33
33
  <button id="theme-btn" title="Toggle theme">◑</button>
34
34
  </div>
@@ -49,6 +49,6 @@
49
49
  >
50
50
  </div>
51
51
  </footer>
52
- <script type="module" src="/s/_/viewer.CVWXSKJM.js"></script>
52
+ <script type="module" src="/s/_/viewer.RHZMFYWJ.js"></script>
53
53
  </body>
54
54
  </html>
@@ -5,6 +5,7 @@
5
5
  --text: #c9d1d9;
6
6
  --text-dim: #6e7681;
7
7
  --accent: #58a6ff;
8
+ --system-accent: #bc8cff;
8
9
  --green: #3fb950;
9
10
  --yellow: #d29922;
10
11
  --red: #f85149;
@@ -25,6 +26,7 @@
25
26
  --text: #1f2328;
26
27
  --text-dim: #656d76;
27
28
  --accent: #0969da;
29
+ --system-accent: #8250df;
28
30
  --green: #1a7f37;
29
31
  --yellow: #9a6700;
30
32
  --red: #cf222e;
@@ -43,6 +45,7 @@
43
45
  --text: #1f2328;
44
46
  --text-dim: #656d76;
45
47
  --accent: #0969da;
48
+ --system-accent: #8250df;
46
49
  --green: #1a7f37;
47
50
  --yellow: #9a6700;
48
51
  --red: #cf222e;
@@ -157,7 +160,7 @@ body {
157
160
  opacity: 1;
158
161
  }
159
162
  }
160
- #session-info {
163
+ #task-info {
161
164
  overflow: hidden;
162
165
  text-overflow: ellipsis;
163
166
  white-space: nowrap;
@@ -165,7 +168,8 @@ body {
165
168
  width: 100%;
166
169
  text-align: center;
167
170
  }
168
- #theme-btn {
171
+ #theme-btn,
172
+ #file-viewer-close {
169
173
  background: transparent;
170
174
  border: none;
171
175
  color: var(--text-dim);
@@ -174,7 +178,8 @@ body {
174
178
  font-family: inherit;
175
179
  padding: 2px 6px;
176
180
  }
177
- #theme-btn:hover {
181
+ #theme-btn:hover,
182
+ #file-viewer-close:hover {
178
183
  color: var(--text);
179
184
  }
180
185
  @media (max-width: 640px) {
@@ -189,7 +194,7 @@ body {
189
194
  .header-right {
190
195
  margin-left: auto;
191
196
  }
192
- #session-info {
197
+ #task-info {
193
198
  flex: 1 1 auto;
194
199
  text-align: left;
195
200
  }
@@ -426,6 +431,20 @@ body {
426
431
  color: var(--text-dim);
427
432
  white-space: pre-wrap;
428
433
  }
434
+ .system-msg.expandable {
435
+ padding-top: 4px;
436
+ padding-bottom: 4px;
437
+ border-left: 2px solid var(--system-accent);
438
+ }
439
+ .system-msg.expandable summary {
440
+ cursor: pointer;
441
+ color: var(--system-accent);
442
+ }
443
+ .system-msg-body {
444
+ margin: 4px 0 2px 16px;
445
+ color: var(--text);
446
+ white-space: pre-wrap;
447
+ }
429
448
  .system-msg a {
430
449
  color: var(--accent);
431
450
  text-decoration: underline;
@@ -748,7 +767,25 @@ body {
748
767
  #status-bar::before {
749
768
  content: "\200b";
750
769
  }
770
+ #status-bar .status-model {
771
+ flex: 0 0 auto;
772
+ }
773
+ #status-bar .status-separator,
774
+ #status-bar .status-usage {
775
+ flex: 0 0 auto;
776
+ }
751
777
  #status-bar .status-cwd {
778
+ display: flex;
779
+ flex: 1 1 auto;
780
+ min-width: 0;
781
+ overflow: hidden;
782
+ }
783
+ #status-bar .status-cwd-prefix {
784
+ flex: 0 0 auto;
785
+ }
786
+ #status-bar .status-cwd-tail {
787
+ flex: 1 1 auto;
788
+ min-width: 0;
752
789
  overflow: hidden;
753
790
  text-overflow: ellipsis;
754
791
  direction: rtl;
@@ -990,6 +1027,151 @@ body {
990
1027
  cursor: default;
991
1028
  }
992
1029
 
1030
+ /* Read-only file viewer */
1031
+ #file-viewer[hidden] {
1032
+ display: none;
1033
+ }
1034
+ #file-viewer {
1035
+ position: fixed;
1036
+ inset: 0;
1037
+ z-index: 100;
1038
+ min-width: 0;
1039
+ background: var(--bg);
1040
+ color: var(--text);
1041
+ display: flex;
1042
+ flex-direction: column;
1043
+ }
1044
+ body.file-viewer-open {
1045
+ overflow: hidden;
1046
+ }
1047
+ .file-viewer-header {
1048
+ min-height: 41px;
1049
+ padding: 6px 16px;
1050
+ background: var(--surface);
1051
+ border-bottom: 1px solid var(--border);
1052
+ display: flex;
1053
+ align-items: center;
1054
+ gap: 12px;
1055
+ font-size: 15px;
1056
+ }
1057
+ #file-viewer-path {
1058
+ min-width: 0;
1059
+ flex: 1;
1060
+ overflow: hidden;
1061
+ text-overflow: ellipsis;
1062
+ white-space: nowrap;
1063
+ direction: ltr;
1064
+ text-align: left;
1065
+ color: var(--text-dim);
1066
+ font-size: inherit;
1067
+ }
1068
+ #file-viewer-close {
1069
+ flex: 0 0 auto;
1070
+ }
1071
+ #file-viewer-notice {
1072
+ flex: 0 0 auto;
1073
+ padding: 6px 14px;
1074
+ border-bottom: 1px solid var(--border);
1075
+ color: var(--yellow);
1076
+ font-size: 13px;
1077
+ }
1078
+ #file-viewer-notice[hidden] {
1079
+ display: none;
1080
+ }
1081
+ #file-viewer-content {
1082
+ flex: 1 1 auto;
1083
+ min-height: 0;
1084
+ overflow: auto;
1085
+ padding-bottom: env(safe-area-inset-bottom);
1086
+ }
1087
+ .file-viewer-markdown {
1088
+ width: 100%;
1089
+ min-height: 100%;
1090
+ }
1091
+ .file-viewer-image {
1092
+ display: block;
1093
+ max-width: 100%;
1094
+ max-height: 100%;
1095
+ margin: auto;
1096
+ padding: 12px;
1097
+ object-fit: contain;
1098
+ }
1099
+ .file-viewer-code-scroll {
1100
+ min-height: 100%;
1101
+ min-width: max-content;
1102
+ }
1103
+ .file-viewer-code-grid {
1104
+ display: grid;
1105
+ grid-template-columns: max-content minmax(max-content, 1fr);
1106
+ width: max-content;
1107
+ min-width: 100%;
1108
+ min-height: 100%;
1109
+ }
1110
+ .file-viewer-line-numbers,
1111
+ .file-viewer-code {
1112
+ margin: 0;
1113
+ padding: 10px 12px;
1114
+ border: 0;
1115
+ border-radius: 0;
1116
+ background: var(--bg);
1117
+ color: var(--text);
1118
+ font:
1119
+ 14px/1.55 "SF Mono",
1120
+ "Fira Code",
1121
+ monospace;
1122
+ white-space: pre;
1123
+ }
1124
+ .file-viewer-line-numbers {
1125
+ position: sticky;
1126
+ left: 0;
1127
+ z-index: 1;
1128
+ padding-right: 8px;
1129
+ background: var(--surface);
1130
+ border-right: 1px solid var(--border);
1131
+ color: var(--text-dim);
1132
+ text-align: right;
1133
+ user-select: none;
1134
+ }
1135
+ .file-viewer-code {
1136
+ padding-left: 12px;
1137
+ }
1138
+ .file-viewer-code code,
1139
+ .file-viewer-code code.hljs {
1140
+ padding: 0;
1141
+ background: transparent;
1142
+ font: inherit;
1143
+ }
1144
+ .file-viewer-binary-meta {
1145
+ padding: 18px 14px 10px;
1146
+ color: var(--text-dim);
1147
+ overflow-wrap: anywhere;
1148
+ }
1149
+ .file-viewer-download {
1150
+ display: inline-flex;
1151
+ min-height: 44px;
1152
+ margin: 0 14px;
1153
+ padding: 8px 12px;
1154
+ border: 1px solid var(--border);
1155
+ border-radius: var(--radius);
1156
+ align-items: center;
1157
+ color: var(--accent);
1158
+ text-decoration: none;
1159
+ }
1160
+ .file-viewer-download:hover,
1161
+ .file-viewer-download:focus-visible {
1162
+ border-color: var(--accent);
1163
+ }
1164
+ @media (min-width: 1024px) {
1165
+ body.file-viewer-open {
1166
+ padding-right: min(55vw, 800px);
1167
+ }
1168
+ #file-viewer {
1169
+ left: auto;
1170
+ width: min(55vw, 800px);
1171
+ border-left: 1px solid var(--border);
1172
+ }
1173
+ }
1174
+
993
1175
  /* Image attach */
994
1176
  #attach-preview {
995
1177
  display: none;
package/dist/sw.js CHANGED
@@ -31,7 +31,7 @@ self.addEventListener("push", (e) => {
31
31
 
32
32
  // Backward-compat: older payloads had no `kind`. Treat as notify.
33
33
  const { title, body, tag, data } = payload;
34
- const finalTag = tag || data?.sessionId || "default";
34
+ const finalTag = tag || data?.taskId || "default";
35
35
  e.waitUntil(showNotify(title, body, finalTag, data));
36
36
  });
37
37
 
@@ -79,15 +79,15 @@ self.addEventListener("notificationclick", (e) => {
79
79
  e.notification.close();
80
80
 
81
81
  const data = e.notification.data || {};
82
- const sessionId = data.sessionId;
82
+ const taskId = data.taskId;
83
83
  const messageId = data.messageId;
84
- const target = sessionId
85
- ? { type: "navigate", sessionId }
84
+ const target = taskId
85
+ ? { type: "navigate", taskId }
86
86
  : messageId
87
87
  ? { type: "navigate", messageId }
88
88
  : { type: "navigate" };
89
- const targetUrl = sessionId
90
- ? `/#${encodeURIComponent(sessionId)}`
89
+ const targetUrl = taskId
90
+ ? `/#${encodeURIComponent(taskId)}`
91
91
  : messageId
92
92
  ? `/?message=${encodeURIComponent(messageId)}`
93
93
  : "/";
@@ -0,0 +1,6 @@
1
+ export function agentKeyFromCommand(agentCmd) {
2
+ const key = agentCmd.trim().split(/\s+/, 1)[0];
3
+ if (!key)
4
+ throw new Error("ACP agent command is empty");
5
+ return key;
6
+ }
@@ -1,33 +1,38 @@
1
1
  import { readFile, realpath } from "node:fs/promises";
2
2
  import { pathToFileURL } from "node:url";
3
- import { isInsideSessionAttachments } from "./sessions-anchor.js";
3
+ import { isUpstreamImageMime } from "./attachments.js";
4
+ import { isInsideTaskAttachments } from "./tasks-anchor.js";
4
5
  const NOOP_LOGGER = { warn: () => { } };
5
6
  /**
6
- * Builds the ACP prompt block for one client-supplied attachment. Returns
7
+ * Builds the ACP prompt blocks for one client-supplied attachment. Returns
7
8
  * a fallback text block on any failure (DB miss, disk miss, anchor breach,
8
- * cross-session reference) so the prompt turn never gets stuck in a retry
9
+ * cross-task reference) so the prompt turn never gets stuck in a retry
9
10
  * loop just because one image vanished.
10
11
  *
11
12
  * Trust boundary (decision 10 in uploads-plan v2.6): client only supplies
12
13
  * `attachmentId`. Everything else (realpath, anchor, MIME for read) comes
13
- * from the server-side row.
14
+ * from the server-side row — including whether the payload is an image and
15
+ * whether its mime is one the upstream model accepts. A client cannot
16
+ * change the wire format by mislabelling the attachment.
14
17
  */
15
18
  export class AttachmentDispatcher {
16
19
  store;
17
- sessionsAnchor;
20
+ tasksAnchor;
18
21
  logger;
19
- constructor(store, sessionsAnchor, logger = NOOP_LOGGER) {
22
+ constructor(store, tasksAnchor, logger = NOOP_LOGGER) {
20
23
  this.store = store;
21
- this.sessionsAnchor = sessionsAnchor;
24
+ this.tasksAnchor = tasksAnchor;
22
25
  this.logger = logger;
23
26
  }
24
- async dispatch(sessionId, ref) {
27
+ async dispatch(taskId, ref) {
25
28
  const fallback = (reason) => {
26
- this.logger.warn(`[attachments] dispatch fallback (${reason}) for ${sessionId}/${ref.attachmentId}`);
27
- return {
28
- type: "text",
29
- text: `[attachment removed: ${ref.displayName}]`,
30
- };
29
+ this.logger.warn(`[attachments] dispatch fallback (${reason}) for ${taskId}/${ref.attachmentId}`);
30
+ return [
31
+ {
32
+ type: "text",
33
+ text: `[attachment removed: ${ref.displayName}]`,
34
+ },
35
+ ];
31
36
  };
32
37
  // Reject any client trying to smuggle a uri / data / path. The shape
33
38
  // of AttachmentRef already forbids these statically; this guard is a
@@ -38,18 +43,18 @@ export class AttachmentDispatcher {
38
43
  typeof wider.path === "string") {
39
44
  return fallback("client_supplied_external_field");
40
45
  }
41
- const row = this.store.getAttachment(sessionId, ref.attachmentId);
46
+ const row = this.store.getAttachment(taskId, ref.attachmentId);
42
47
  if (!row)
43
48
  return fallback("row_not_found");
44
- // Cross-session reference — the row exists but for a DIFFERENT session.
45
- // store.getAttachment scopes by session_id so this should already be
49
+ // Cross-task reference — the row exists but for a DIFFERENT task.
50
+ // store.getAttachment scopes by task_id so this should already be
46
51
  // caught by row_not_found, but assert defensively.
47
- if (row.session_id !== sessionId) {
48
- return fallback("cross_session");
52
+ if (row.task_id !== taskId) {
53
+ return fallback("cross_task");
49
54
  }
50
55
  // Anchor check on the stored realpath. If the file was moved out from
51
56
  // under us, or a future bug let an attacker inject a row with a path
52
- // outside SESSIONS_ANCHOR/<sid>/attachments/, we MUST refuse to dispatch
57
+ // outside TASKS_ANCHOR/<sid>/attachments/, we MUST refuse to dispatch
53
58
  // it as a `file://` URI — the agent would happily read it.
54
59
  let resolvedPath;
55
60
  try {
@@ -58,24 +63,48 @@ export class AttachmentDispatcher {
58
63
  catch {
59
64
  return fallback("realpath_failed");
60
65
  }
61
- if (!isInsideSessionAttachments(this.sessionsAnchor, sessionId, resolvedPath)) {
66
+ if (!isInsideTaskAttachments(this.tasksAnchor, taskId, resolvedPath)) {
62
67
  return fallback("path_outside_anchor");
63
68
  }
64
- if (ref.kind === "image") {
65
- try {
66
- const buf = await readFile(resolvedPath);
67
- return {
68
- type: "image",
69
- data: buf.toString("base64"),
70
- mimeType: row.mime,
71
- };
72
- }
73
- catch {
74
- return fallback("read_failed");
69
+ // Storage truth, not the client's `ref.kind`: the browser guesses the
70
+ // mime from the file extension / OS registry (renaming IMG_1040.HEIC to
71
+ // .jpg makes it claim image/jpeg), while `row.kind` is what the sniff on
72
+ // upload produced.
73
+ if (row.kind === "image") {
74
+ if (isUpstreamImageMime(row.mime)) {
75
+ try {
76
+ const buf = await readFile(resolvedPath);
77
+ return [
78
+ {
79
+ type: "image",
80
+ data: buf.toString("base64"),
81
+ mimeType: row.mime,
82
+ },
83
+ ];
84
+ }
85
+ catch {
86
+ return fallback("read_failed");
87
+ }
75
88
  }
89
+ // Image container the upstream model rejects (heic/heif/avif/bmp/
90
+ // tiff and other `image/*` containers — note an SVG sniffs as text,
91
+ // so it never reaches this branch). Sending it as an image block
92
+ // poisons the session history: the provider 400s on every later turn
93
+ // too. Degrade to a file link plus a hint instead.
94
+ this.logger.warn(`[attachments] image downgraded to resource_link (mime=${row.mime}) for ${taskId}/${ref.attachmentId}`);
95
+ return [
96
+ {
97
+ type: "text",
98
+ text: `[attachment ${row.name} (${row.mime}) cannot be read as an inline image by the model; convert it to png or jpeg to make it readable]`,
99
+ },
100
+ this.resourceLink(row, resolvedPath),
101
+ ];
76
102
  }
77
103
  // kind === "file" → ACP resource_link with file:// URI built from the
78
104
  // realpath (NOT from any client-supplied string).
105
+ return [this.resourceLink(row, resolvedPath)];
106
+ }
107
+ resourceLink(row, resolvedPath) {
79
108
  return {
80
109
  type: "resource_link",
81
110
  uri: pathToFileURL(resolvedPath).toString(),
@@ -2,12 +2,12 @@
2
2
  //
3
3
  // Plan §1.4 (uploads-plan v2.6, lines 105-183). When an agent requests
4
4
  // permission to *read* a path that we know is one of the user-uploaded
5
- // session attachments, auto-approve with `allow_once`. Any deviation
5
+ // task attachments, auto-approve with `allow_once`. Any deviation
6
6
  // from the strict allowlist falls through to the user prompt.
7
7
  //
8
8
  // Defenses (mirrored from the plan):
9
9
  // F1 kind === "read" only
10
- // F2 every locations[].path realpath ∈ session attachment realpaths
10
+ // F2 every locations[].path realpath ∈ task attachment realpaths
11
11
  // F4 schema gate — locations must exist; if rawInput has known path
12
12
  // keys they must also realpath into the attachment set; if it has
13
13
  // none, allow but bump schemaDrift counter so we notice when the
@@ -67,7 +67,7 @@ async function checkRawInputPaths(raw, attachmentRealpaths, miss) {
67
67
  }
68
68
  /**
69
69
  * Returns true iff the request matches the strict
70
- * "agent reading a known session attachment" pattern.
70
+ * "agent reading a known task attachment" pattern.
71
71
  *
72
72
  * Returning false does NOT deny the user prompt — it just declines to
73
73
  * auto-approve, so the normal permission UI continues to render.
@@ -79,7 +79,7 @@ export async function shouldAutoApproveAttachmentRead(ev, deps) {
79
79
  counters.fellThrough++;
80
80
  log.debug?.("attachment auto-allow miss", {
81
81
  reason,
82
- sessionId: ev.sessionId,
82
+ taskId: ev.taskId,
83
83
  toolKind: ev.toolKind,
84
84
  toolName: ev.toolName,
85
85
  });
@@ -96,11 +96,11 @@ export async function shouldAutoApproveAttachmentRead(ev, deps) {
96
96
  }
97
97
  let attachmentRealpaths;
98
98
  try {
99
- attachmentRealpaths = new Set(deps.listAttachmentRealpaths(ev.sessionId));
99
+ attachmentRealpaths = new Set(deps.listAttachmentRealpaths(ev.taskId));
100
100
  }
101
101
  catch (e) {
102
102
  log.warn?.("attachment interceptor db error", {
103
- sessionId: ev.sessionId,
103
+ taskId: ev.taskId,
104
104
  error: e.message,
105
105
  });
106
106
  return miss("db_error");
@@ -121,7 +121,7 @@ export async function shouldAutoApproveAttachmentRead(ev, deps) {
121
121
  }
122
122
  counters.autoAllowed++;
123
123
  log.info?.("attachment auto-allowed", {
124
- sessionId: ev.sessionId,
124
+ taskId: ev.taskId,
125
125
  toolKind: ev.toolKind,
126
126
  toolName: ev.toolName,
127
127
  locations: ev.locations.map((l) => l.path),
@@ -109,7 +109,7 @@ export function enrichStoredEventDataForDisplay(type, data, map) {
109
109
  return data;
110
110
  }
111
111
  // Synthesize a minimal event shape so we can reuse the object-level
112
- // enricher. `sessionId` etc. don't matter to enrich logic.
112
+ // enricher. `taskId` etc. don't matter to enrich logic.
113
113
  const ev = { type, ...parsed };
114
114
  const out = enrichEventForDisplay(ev, map);
115
115
  if (out === ev)