@chatpanel/events 0.354.0 → 0.358.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/access-request.js CHANGED
@@ -1 +1 @@
1
- import{EventError as W}from"./event.js";import{SECRET_PATHS as z}from"./sandbox-policy.js";import{egressAllowed as N}from"./policy.js";const p=e=>!!e&&typeof e=="object"&&!Array.isArray(e),d=(e,n)=>{if(!e)throw new W("SHAPE",n)},O=e=>[...new Set(e)],f=Object.freeze(["path","host"]),v=Object.freeze(["once","session","always"]),x=Object.freeze(["read","write"]),E=Object.freeze(["refused","ahead"]),H=Object.freeze(["grant","deny","ask"]),R=500,u=e=>{let n=String(e||"").trim();for(;n.length>1&&(n.endsWith("/")||n.endsWith("\\"));)n=n.slice(0,-1);return n},w=(e,n)=>n&&(e==="~"||e.startsWith("~/")||e.startsWith("~\\"))?n+e.slice(1):e;function g(e,n){const t=u(e),s=u(n);return!t||!s?!1:t===s||t.startsWith(`${s}/`)||t.startsWith(`${s}\\`)}function C(e,n=""){const t=u(w(u(e),n));return z.some(s=>g(t,u(s))||g(t,u(w(s,n))))}function P(e,n=""){const t=u(w(u(e),n));if(!t||t==="/"||t==="~"||/^[a-z]:$/i.test(t)||/^[a-z]:\\?$/i.test(t)||n&&t===u(n))return!0;const s=t.split(/[/\\]/).filter(Boolean).length;return t.startsWith("/")&&s<2}function b(e){d(p(e),"access request: must be an object");const n=e.what;d(p(n),"access request: what { kind, value, mode } required");const t=String(n.kind||"");d(f.includes(t),`access request: kind must be one of ${f.join(", ")}`);const s=t==="path"?u(n.value):String(n.value||"").trim().toLowerCase();d(s.length>0&&s.length<1024&&!s.includes("\0"),`access request: not a ${t}: ${JSON.stringify(n.value)}`);const i=n.mode==null?"write":String(n.mode);d(x.includes(i),`access request: mode must be one of ${x.join(", ")}`);const a=e.scope==null?"session":String(e.scope);d(v.includes(a),`access request: scope must be one of ${v.join(", ")}`);const o=e.asked==null?"refused":String(e.asked);return d(E.includes(o),`access request: asked must be one of ${E.join(", ")}`),{id:String(e.id||""),at:Number(e.at)||0,commandId:String(e.commandId||""),engine:String(e.engine||""),agentId:String(e.agentId||""),runId:String(e.runId||""),what:{kind:t,value:s,mode:i},why:String(e.why||"").slice(0,R),scope:a,asked:o,state:"pending"}}function D(e,{home:n=""}={}){d(p(e),"delegation: must be an object");const t=e.what;d(p(t),"delegation: what { kind, under | host, mode } required");const s=String(t.kind||"");d(f.includes(s),`delegation: kind must be one of ${f.join(", ")}`);const i=t.mode==null?"write":String(t.mode);d(x.includes(i),`delegation: mode must be one of ${x.join(", ")}`);const a=e.scope==null?"session":String(e.scope);d(a==="once"||a==="session","delegation: scope must be once or session — a standing grant is a person's decision, not a rule's");const o={id:String(e.id||""),label:String(e.label||"").slice(0,120),enabled:e.enabled!==!1,what:{kind:s,mode:i},scope:a};if(s==="path"){const r=u(t.under);d(r.length>0,"delegation: what.under is required — a rule without a root is not a bounded rule"),d(!C(r,n),`delegation: ${r} is a secret path and can never be granted`),d(!P(r,n),`delegation: ${r} is too broad to delegate — name the project folder, not the disk`),o.what.under=r}else{const r=String(t.host||t.under||"").trim().toLowerCase();d(/^(\*\.)?[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i.test(r)&&r!=="*",`delegation: not a host pattern: ${JSON.stringify(t.host)}`),o.what.host=r}return o.ttlMs=Math.max(0,Math.min(Number(e.ttlMs)||0,365*24*3600*1e3)),o.maxPerRun=e.maxPerRun==null?0:Math.max(0,Math.min(Number(e.maxPerRun)||0,1e3)),o.agents=O((Array.isArray(e.agents)?e.agents:[]).map(r=>String(r).trim()).filter(Boolean)),o.expiresAt=Number(e.expiresAt)||0,o.createdBy=String(e.createdBy||""),o}function L(e,n,{home:t=""}={}){if(!e?.enabled||e.what.kind!==n.what.kind||e.what.mode==="read"&&n.what.mode==="write"||e.agents.length&&!e.agents.includes(n.engine)&&!e.agents.includes(n.agentId))return!1;if(e.what.kind==="path")return g(w(n.what.value,t),w(e.what.under,t));const s=n.what.value;return e.what.host.startsWith("*.")?s===e.what.host.slice(2)||s.endsWith(e.what.host.slice(1)):s===e.what.host}const k=Object.freeze({ALLOW:"allow",ALWAYS:"allow_always",DENY:"deny"});function M(e){return e===k.ALWAYS?"always":"session"}function I(e,{home:n=""}={}){d(p(e),"access grant: must be an object");const t=e.what;d(p(t),"access grant: what required");const s=String(t.kind||"");d(f.includes(s),`access grant: kind must be one of ${f.join(", ")}`);const i=s==="path"?u(t.value):String(t.value||"").trim().toLowerCase();d(i.length>0,"access grant: what.value required"),d(!(s==="path"&&C(i,n)),`access grant: ${i} is a secret path and can never be granted`);const a=String(e.scope||"session");return d(v.includes(a),`access grant: scope must be one of ${v.join(", ")}`),{id:String(e.id||""),what:{kind:s,value:i,mode:t.mode==="read"?"read":"write"},scope:a,allow:e.allow!==!1,at:Number(e.at)||0,expiresAt:Number(e.expiresAt)||0,by:String(e.by||""),label:String(e.label||"").slice(0,200)}}function q(e,n,t,{now:s=0,newId:i=()=>"",home:a=""}={}){const o=b(n);if(t===k.DENY)return[...y(e,a),I({id:i(),what:o.what,scope:"session",allow:!1,at:s,by:"person"},{home:a})];const r=M(t),c=I({id:i(),what:o.what,scope:r,allow:!0,at:s,by:"person",label:o.what.value},{home:a}),S=[];for(const h of y(e,a)){if(h.allow&&j(h,o,{home:a,now:s})&&$(h.scope)>=$(r))return y(e,a);h.allow===c.allow&&h.what.kind===c.what.kind&&$(h.scope)<=$(r)&&j(c,{what:h.what,engine:o.engine},{home:a})||S.push(h)}return[...S,c]}const $=e=>e==="always"?2:e==="session"?1:0,y=(e,n="")=>(Array.isArray(e)?e:[]).map(t=>{try{return I(t,{home:n})}catch{return null}}).filter(Boolean);function j(e,n,{home:t="",now:s=0}={}){if(!e||e.expiresAt&&s&&s>e.expiresAt||e.what.kind!==n.what.kind||e.allow&&e.what.mode==="read"&&n.what.mode==="write")return!1;if(e.what.kind==="path")return g(w(n.what.value,t),w(e.what.value,t));const i=n.what.value;return e.what.value.startsWith("*.")?i===e.what.value.slice(2)||i.endsWith(e.what.value.slice(1)):i===e.what.value}function _(e,n,{home:t="",now:s=0}={}){const i=b(n),a=y(e,t).filter(r=>j(r,i,{home:t,now:s}));if(!a.length)return null;const o=a.find(r=>r.allow);return o?{verdict:"grant",by:{kind:"person",id:o.by},ruleId:o.id,reason:o.scope==="always"?"you allowed this folder":"you allowed this earlier in this chat",scope:o.scope,expiresAt:o.expiresAt||0}:{verdict:"deny",by:{kind:"person",id:a[0].by},ruleId:a[0].id,reason:"you denied this earlier in this chat",scope:"session"}}function F(e,n,{home:t=""}={}){return y(e,t).filter(s=>s.id!==String(n))}function G(e,{now:n=0,home:t=""}={}){return y(e,t).filter(s=>s.allow&&s.scope==="always"&&!(s.expiresAt&&n&&n>s.expiresAt))}const m=(e,n,t={})=>({verdict:e,by:n,ruleId:"",reason:"",...t});function J({request:e,delegations:n=[],policy:t=null,now:s=0,usedPerRule:i={},home:a="",canAsk:o=!0,grants:r=[]}={}){const c=b(e);if(c.what.kind==="path"&&C(c.what.value,a))return m("deny",{kind:"floor"},{reason:`${c.what.value} holds credentials or ChatPanel's own store, and is never grantable`});if(t&&c.what.kind==="host"&&!N(t,c.what.value))return m("deny",{kind:"policy"},{reason:`your organisation does not allow ${c.what.value}`});if(t&&c.what.kind==="path"&&Array.isArray(t.access?.deny)){const A=t.access.deny.find(l=>g(w(c.what.value,a),w(u(l),a)));if(A)return m("deny",{kind:"policy"},{reason:`your organisation does not allow access under ${A}`})}const S=_(r,c,{home:a,now:s});if(S)return S;const h=t?.access?.delegation!=="never";if(h)for(const A of n){let l;try{l=D(A,{home:a})}catch{continue}if(L(l,c,{home:a})&&!(l.expiresAt&&s&&s>l.expiresAt)&&!(l.maxPerRun&&(i[l.id]||0)>=l.maxPerRun))return m("grant",{kind:"delegation",id:l.createdBy||""},{ruleId:l.id,reason:l.label?`allowed by your rule “${l.label}”`:"allowed by a rule you set",scope:l.scope,expiresAt:l.ttlMs&&s?s+l.ttlMs:0})}return o?m("ask",{kind:"person"},{scope:c.scope}):m("deny",{kind:"floor"},{reason:h?"nothing here can approve this, and an unanswered request is a denial":"your organisation requires a person to approve this, and this run has nobody to ask"})}function K(e={},{newId:n=()=>"",now:t=0}={}){const s=e.path?"path":"host";return b({id:e.id||n()||"",at:e.at||t||0,commandId:e.commandId||"",engine:e.engine||"",agentId:e.agentId||"",runId:e.runId||"",what:{kind:s,value:s==="path"?e.path:e.host,mode:e.mode||"write"},why:e.why||"",scope:"session",asked:"refused"})}function V(e,{home:n=""}={}){const t=b(e),s=t.engine||t.agentId||"This agent",i=c=>n&&g(c,u(n))?`~${c.slice(u(n).length)}`:c,a=t.what.kind==="path",o=a?i(t.what.value):t.what.value,r=t.what.mode==="read"?"read":a?"write in":"reach";return{title:`${s} wants to ${r} ${a&&o.split(/[/\\]/).filter(Boolean).pop()||o}`,detail:o,kind:t.what.kind,why:t.why,answers:[{id:k.ALLOW,label:"Allow",hint:"for the rest of this chat"},{id:k.ALWAYS,label:`Always allow ${o}`,hint:"revoke any time in Settings › Runtime"},{id:k.DENY,label:"Deny",hint:""}]}}function X(e,n){const t=b(e),s=(t.what.kind==="path",t.what.value),i=t.engine||t.agentId||"the agent",a=t.what.mode==="read"?"read":"write in";if(n?.verdict==="grant"){const o=n.by?.kind==="delegation"?n.reason||"allowed by a rule you set":"allowed by you";return`${i} asked to ${a} ${s} — ${o}`}return n?.verdict==="deny"?`${i} asked to ${a} ${s} — denied${n.reason?` (${n.reason})`:""}`:`${i} is asking to ${a} ${s}`}export{k as ACCESS_ANSWERS,E as ACCESS_ASKED,f as ACCESS_KINDS,x as ACCESS_MODES,v as ACCESS_SCOPES,H as ACCESS_VERDICTS,R as WHY_MAX,K as accessRequestFromRefusal,J as decideAccess,L as delegationCovers,V as describeAccessCard,X as describeDecision,j as grantCovers,C as isSecretPath,P as isTooBroadRoot,I as normalizeAccessGrant,b as normalizeAccessRequest,D as normalizeDelegation,g as pathInside,_ as recallAccess,q as rememberAccess,F as revokeAccess,M as scopeForAnswer,G as standingGrants};
1
+ import{EventError as j}from"./event.js";import{SECRET_PATHS as z}from"./sandbox-policy.js";import{egressAllowed as N}from"./policy.js";const p=e=>!!e&&typeof e=="object"&&!Array.isArray(e),c=(e,n)=>{if(!e)throw new j("SHAPE",n)},R=e=>[...new Set(e)],f=Object.freeze(["path","host"]),v=Object.freeze(["once","session","always"]),x=Object.freeze(["read","write"]),I=Object.freeze(["refused","ahead"]),H=Object.freeze(["grant","deny","ask"]),O=500,u=e=>{let n=String(e||"").trim();for(;n.length>1&&(n.endsWith("/")||n.endsWith("\\"));)n=n.slice(0,-1);return n},w=(e,n)=>n&&(e==="~"||e.startsWith("~/")||e.startsWith("~\\"))?n+e.slice(1):e;function g(e,n){const t=u(e),s=u(n);return!t||!s?!1:t===s||t.startsWith(`${s}/`)||t.startsWith(`${s}\\`)}function E(e,n=""){const t=u(w(u(e),n));return z.some(s=>g(t,u(s))||g(t,u(w(s,n))))}function M(e,n=""){const t=u(w(u(e),n));if(!t||t==="/"||t==="~"||/^[a-z]:$/i.test(t)||/^[a-z]:\\?$/i.test(t)||n&&t===u(n))return!0;const s=t.split(/[/\\]/).filter(Boolean).length;return t.startsWith("/")&&s<2}function b(e){c(p(e),"access request: must be an object");const n=e.what;c(p(n),"access request: what { kind, value, mode } required");const t=String(n.kind||"");c(f.includes(t),`access request: kind must be one of ${f.join(", ")}`);const s=t==="path"?u(n.value):String(n.value||"").trim().toLowerCase();c(s.length>0&&s.length<1024&&!s.includes("\0"),`access request: not a ${t}: ${JSON.stringify(n.value)}`);const o=n.mode==null?"write":String(n.mode);c(x.includes(o),`access request: mode must be one of ${x.join(", ")}`);const a=e.scope==null?"session":String(e.scope);c(v.includes(a),`access request: scope must be one of ${v.join(", ")}`);const i=e.asked==null?"refused":String(e.asked);return c(I.includes(i),`access request: asked must be one of ${I.join(", ")}`),{id:String(e.id||""),at:Number(e.at)||0,commandId:String(e.commandId||""),engine:String(e.engine||""),agentId:String(e.agentId||""),runId:String(e.runId||""),what:{kind:t,value:s,mode:o},why:String(e.why||"").slice(0,O),scope:a,asked:i,state:"pending"}}function P(e,{home:n=""}={}){c(p(e),"delegation: must be an object");const t=e.what;c(p(t),"delegation: what { kind, under | host, mode } required");const s=String(t.kind||"");c(f.includes(s),`delegation: kind must be one of ${f.join(", ")}`);const o=t.mode==null?"write":String(t.mode);c(x.includes(o),`delegation: mode must be one of ${x.join(", ")}`);const a=e.scope==null?"session":String(e.scope);c(a==="once"||a==="session","delegation: scope must be once or session — a standing grant is a person's decision, not a rule's");const i={id:String(e.id||""),label:String(e.label||"").slice(0,120),enabled:e.enabled!==!1,what:{kind:s,mode:o},scope:a};if(s==="path"){const r=u(t.under);c(r.length>0,"delegation: what.under is required — a rule without a root is not a bounded rule"),c(!E(r,n),`delegation: ${r} is a secret path and can never be granted`),c(!M(r,n),`delegation: ${r} is too broad to delegate — name the project folder, not the disk`),i.what.under=r}else{const r=String(t.host||t.under||"").trim().toLowerCase();c(/^(\*\.)?[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i.test(r)&&r!=="*",`delegation: not a host pattern: ${JSON.stringify(t.host)}`),i.what.host=r}return i.ttlMs=Math.max(0,Math.min(Number(e.ttlMs)||0,365*24*3600*1e3)),i.maxPerRun=e.maxPerRun==null?0:Math.max(0,Math.min(Number(e.maxPerRun)||0,1e3)),i.agents=R((Array.isArray(e.agents)?e.agents:[]).map(r=>String(r).trim()).filter(Boolean)),i.expiresAt=Number(e.expiresAt)||0,i.createdBy=String(e.createdBy||""),i}function _(e,n,{home:t=""}={}){if(!e?.enabled||e.what.kind!==n.what.kind||e.what.mode==="read"&&n.what.mode==="write"||e.agents.length&&!e.agents.includes(n.engine)&&!e.agents.includes(n.agentId))return!1;if(e.what.kind==="path")return g(w(n.what.value,t),w(e.what.under,t));const s=n.what.value;return e.what.host.startsWith("*.")?s===e.what.host.slice(2)||s.endsWith(e.what.host.slice(1)):s===e.what.host}const k=Object.freeze({ALLOW:"allow",ALWAYS:"allow_always",DENY:"deny"});function D(e){return e===k.ALWAYS?"always":"session"}function C(e,{home:n=""}={}){c(p(e),"access grant: must be an object");const t=e.what;c(p(t),"access grant: what required");const s=String(t.kind||"");c(f.includes(s),`access grant: kind must be one of ${f.join(", ")}`);const o=s==="path"?u(t.value):String(t.value||"").trim().toLowerCase();c(o.length>0,"access grant: what.value required"),c(!(s==="path"&&E(o,n)),`access grant: ${o} is a secret path and can never be granted`);const a=String(e.scope||"session");return c(v.includes(a),`access grant: scope must be one of ${v.join(", ")}`),{id:String(e.id||""),what:{kind:s,value:o,mode:t.mode==="read"?"read":"write"},scope:a,allow:e.allow!==!1,at:Number(e.at)||0,expiresAt:Number(e.expiresAt)||0,by:String(e.by||""),label:String(e.label||"").slice(0,200)}}function F(e,n,t,{now:s=0,newId:o=()=>"",home:a=""}={}){const i=b(n);if(t===k.DENY)return[...y(e,a),C({id:o(),what:i.what,scope:"session",allow:!1,at:s,by:"person"},{home:a})];const r=D(t),d=C({id:o(),what:i.what,scope:r,allow:!0,at:s,by:"person",label:i.what.value},{home:a}),m=[];for(const h of y(e,a)){if(h.allow&&W(h,i,{home:a,now:s})&&$(h.scope)>=$(r))return y(e,a);h.allow===d.allow&&h.what.kind===d.what.kind&&$(h.scope)<=$(r)&&W(d,{what:h.what,engine:i.engine},{home:a})||m.push(h)}return[...m,d]}const $=e=>e==="always"?2:e==="session"?1:0,y=(e,n="")=>(Array.isArray(e)?e:[]).map(t=>{try{return C(t,{home:n})}catch{return null}}).filter(Boolean);function W(e,n,{home:t="",now:s=0}={}){if(!e||e.expiresAt&&s&&s>e.expiresAt||e.what.kind!==n.what.kind||e.allow&&e.what.mode==="read"&&n.what.mode==="write")return!1;if(e.what.kind==="path")return g(w(n.what.value,t),w(e.what.value,t));const o=n.what.value;return e.what.value.startsWith("*.")?o===e.what.value.slice(2)||o.endsWith(e.what.value.slice(1)):o===e.what.value}function L(e,n,{home:t="",now:s=0}={}){const o=b(n),a=y(e,t).filter(r=>W(r,o,{home:t,now:s}));if(!a.length)return null;const i=a.find(r=>r.allow);return i?{verdict:"grant",by:{kind:"person",id:i.by},ruleId:i.id,reason:i.scope==="always"?"you allowed this folder":"you allowed this earlier in this chat",scope:i.scope,expiresAt:i.expiresAt||0}:{verdict:"deny",by:{kind:"person",id:a[0].by},ruleId:a[0].id,reason:"you denied this earlier in this chat",scope:"session"}}function G(e,n,{home:t=""}={}){return y(e,t).filter(s=>s.id!==String(n))}function J(e,{now:n=0,home:t=""}={}){return y(e,t).filter(s=>s.allow&&s.scope==="always"&&!(s.expiresAt&&n&&n>s.expiresAt))}const S=(e,n,t={})=>({verdict:e,by:n,ruleId:"",reason:"",...t});function K({request:e,delegations:n=[],policy:t=null,now:s=0,usedPerRule:o={},home:a="",canAsk:i=!0,grants:r=[]}={}){const d=b(e);if(d.what.kind==="path"&&E(d.what.value,a))return S("deny",{kind:"floor"},{reason:`${d.what.value} holds credentials or ChatPanel's own store, and is never grantable`});if(t&&d.what.kind==="host"&&!N(t,d.what.value))return S("deny",{kind:"policy"},{reason:`your organisation does not allow ${d.what.value}`});if(t&&d.what.kind==="path"&&Array.isArray(t.access?.deny)){const A=t.access.deny.find(l=>g(w(d.what.value,a),w(u(l),a)));if(A)return S("deny",{kind:"policy"},{reason:`your organisation does not allow access under ${A}`})}const m=L(r,d,{home:a,now:s});if(m)return m;const h=t?.access?.delegation!=="never";if(h)for(const A of n){let l;try{l=P(A,{home:a})}catch{continue}if(_(l,d,{home:a})&&!(l.expiresAt&&s&&s>l.expiresAt)&&!(l.maxPerRun&&(o[l.id]||0)>=l.maxPerRun))return S("grant",{kind:"delegation",id:l.createdBy||""},{ruleId:l.id,reason:l.label?`allowed by your rule “${l.label}”`:"allowed by a rule you set",scope:l.scope,expiresAt:l.ttlMs&&s?s+l.ttlMs:0})}return i?S("ask",{kind:"person"},{scope:d.scope}):S("deny",{kind:"floor"},{reason:h?"nothing here can approve this, and an unanswered request is a denial":"your organisation requires a person to approve this, and this run has nobody to ask"})}function V(e={},{newId:n=()=>"",now:t=0}={}){const s=e.path?"path":"host";return b({id:e.id||n()||"",at:e.at||t||0,commandId:e.commandId||"",engine:e.engine||"",agentId:e.agentId||"",runId:e.runId||"",what:{kind:s,value:s==="path"?e.path:e.host,mode:e.mode||"write"},why:e.why||"",scope:"session",asked:"refused"})}function X(e={}){const n=p(e.input)?e.input:{},t=String(e.tool||"");let o=((/^(Write|Edit|MultiEdit|NotebookEdit)$/.test(t)?String(n.file_path||n.notebook_path||""):"")||String(e.path||"")).trim();if(!o)for(const a of Array.isArray(e.rules)?e.rules:[]){if(!/^(Read|Edit|Write|MultiEdit|NotebookEdit)$/.test(String(a?.tool||"")))continue;const i=String(a.content||"").trim().replace(/\/\*\*$|\/\*$/,"");if(i.startsWith("//")){o=i.slice(1);break}if(i==="~"||i.startsWith("~/")){o=i;break}}return o&&!o.includes("\0")?o:null}function Q(e,{home:n=""}={}){const t=b(e),s=t.engine||t.agentId||"This agent",o=d=>n&&g(d,u(n))?`~${d.slice(u(n).length)}`:d,a=t.what.kind==="path",i=a?o(t.what.value):t.what.value,r=t.what.mode==="read"?"read":a?"write in":"reach";return{title:`${s} wants to ${r} ${a&&i.split(/[/\\]/).filter(Boolean).pop()||i}`,detail:i,kind:t.what.kind,why:t.why,answers:[{id:k.ALLOW,label:"Allow",hint:"for the rest of this chat"},{id:k.ALWAYS,label:`Always allow ${i}`,hint:"revoke any time in Settings › Runtime"},{id:k.DENY,label:"Deny",hint:""}]}}function U(e,n){const t=b(e),s=(t.what.kind==="path",t.what.value),o=t.engine||t.agentId||"the agent",a=t.what.mode==="read"?"read":"write in";if(n?.verdict==="grant"){const i=n.by?.kind==="delegation"?n.reason||"allowed by a rule you set":"allowed by you";return`${o} asked to ${a} ${s} — ${i}`}return n?.verdict==="deny"?`${o} asked to ${a} ${s} — denied${n.reason?` (${n.reason})`:""}`:`${o} is asking to ${a} ${s}`}export{k as ACCESS_ANSWERS,I as ACCESS_ASKED,f as ACCESS_KINDS,x as ACCESS_MODES,v as ACCESS_SCOPES,H as ACCESS_VERDICTS,O as WHY_MAX,V as accessRequestFromRefusal,K as decideAccess,_ as delegationCovers,Q as describeAccessCard,U as describeDecision,W as grantCovers,E as isSecretPath,M as isTooBroadRoot,C as normalizeAccessGrant,b as normalizeAccessRequest,P as normalizeDelegation,g as pathInside,X as permissionAccessTarget,L as recallAccess,F as rememberAccess,G as revokeAccess,D as scopeForAnswer,J as standingGrants};
@@ -1,2 +1,2 @@
1
- const $="permission_request",p=Object.freeze({ALLOW:"allow",ALLOW_CHAT:"allow_chat",DENY:"deny"}),W=600*1e3,_="permission_prompt";function N(){return{name:_,description:"Chap-internal: the person answers the agent's permission prompt. Not for the model.",parameters:{type:"object",properties:{tool:{type:"string"},input:{type:"object"},description:{type:"string"},path:{type:"string"},rules:{type:"array"}}}}}const T=t=>t===_,u=t=>t!==null&&typeof t=="object"&&!Array.isArray(t),a=t=>typeof t=="string"?t:t==null?"":String(t);function k(t={}){const e=u(t)?t:{},o=a(e.tool_name||e.toolName||e.tool),n=[];for(const s of Array.isArray(e.permission_suggestions)?e.permission_suggestions:[])if(!(!u(s)||s.type!=="addRules"||s.behavior==="deny"))for(const r of Array.isArray(s.rules)?s.rules:[]){if(!u(r))continue;const c=a(r.toolName||o),i=a(r.ruleContent);c&&!n.some(f=>f.tool===c&&f.content===i)&&n.push({tool:c,content:i})}return{tool:o,displayName:a(e.display_name||e.displayName)||o,input:u(e.input)?e.input:{},description:a(e.description),path:a(e.blocked_path||e.path)||null,rules:n,toolUseId:a(e.tool_use_id||e.toolUseId)||null}}function g(t={}){const e=Array.isArray(t.rules)?t.rules.filter(o=>o&&o.tool):[];return e.length?e.map(o=>o.content?`${o.tool}(${o.content})`:o.tool):t.tool?[String(t.tool)]:[]}function L(t={}){const e=a(t.tool),o=u(t.input)?t.input:{},n=(l,d=240)=>l.length>d?`${l.slice(0,d-1)}…`:l;let s,r;if(e==="Bash")s="Run a command",r=n(a(o.command).trim()||a(t.description)||"a shell command");else if(e==="Edit"||e==="MultiEdit"||e==="NotebookEdit")s="Edit a file",r=a(o.file_path||o.notebook_path||t.path)||"a file";else if(e==="Write"){s="Write a file";const l=a(o.file_path||t.path)||"a file",d=a(o.content).length;r=d?`${l} (${d.toLocaleString()} chars)`:l}else if(/^mcp__/.test(e))s="Use a connected server",r=e.replace(/^mcp__/,"").replace(/__/," › ");else{s=`Use ${t.displayName||e||"a tool"}`;let l="";try{l=JSON.stringify(o)}catch{l=""}r=a(t.description)||(l&&l!=="{}"?n(l,200):e)}const c=g(t),i=Array.isArray(t.rules)?t.rules.filter(l=>l?.content):[],f=i.length&&i.some(l=>l.content!==r)?i.map(l=>l.content).join(", "):c.length&&!i.length?`every ${e} call`:null;return{title:s,detail:r,scope:f,description:a(t.description)||null}}function h(t,e=""){return t===p.ALLOW?{behavior:"allow",scope:"once"}:t===p.ALLOW_CHAT?{behavior:"allow",scope:"chat"}:{behavior:"deny",scope:"once",...e?{message:String(e)}:{}}}function w(t){return u(t)&&t.behavior==="allow"}function E(t,e){const o=new Set(Array.isArray(t)?t:[]);for(const n of g(e))o.add(n);return[...o].sort()}const b="*";function O(t,e){const o=new Set(Array.isArray(t)?t:[]);if(o.has(b))return h(p.ALLOW);const n=g(e);return n.length&&n.every(s=>o.has(s))?h(p.ALLOW):null}function R(t,e,o={}){const n=u(o)?o:{},s=w(e);let r;if(s){if(r={behavior:"allow",updatedInput:u(n.input)?n.input:{}},e.scope==="chat"){const c=(Array.isArray(n.permission_suggestions)?n.permission_suggestions:[]).filter(i=>u(i)&&i.type==="addRules"&&i.behavior!=="deny"&&Array.isArray(i.rules)&&i.rules.length).map(i=>({type:"addRules",rules:i.rules,behavior:"allow",destination:"session"}));c.length&&(r.updatedPermissions=c)}}else r={behavior:"deny",message:a(e?.message)||"The user declined this action in Chap. Continue without it, or explain what it was for and ask."};return{type:"control_response",response:{subtype:"success",request_id:String(t),response:r}}}function C(t,e){const{detail:o}=L(t),n=w(e),s=n&&e.scope==="chat"?" for this chat":"";return`${n?"Allowed":"Declined"}${s}: ${o}`}function x(t){return t==="Bash"?"code":/^(Edit|MultiEdit|NotebookEdit)$/.test(String(t))?"pencil":t==="Write"?"file":/^mcp__/.test(String(t))?"plug":"bot"}function P({memory:t=new Map}={}){const e=t;async function o(n,{convId:s=null,confirm:r,agentName:c="The agent"}={}){const i=O(await e.get(s)||[],n);if(i)return i;const{title:f,detail:l,scope:d,description:y}=L(n),S=[l,y&&y!==l?y:"",d?`“Allow for this chat” also covers: ${d}`:""].filter(Boolean).join(`
2
- `);let m;try{if(typeof r!="function")throw new Error("no card");m=await r(S,{title:`${c} asks — ${f.toLowerCase()}?`,iconName:x(n.tool),scopeLabel:"Allow for this chat",allLabel:"Allow all in this chat"})}catch(A){return h(p.DENY,`Chap could not show the permission card (${A?.message||A}).`)}return m==="all"?(await e.set(s,[b]),h(p.ALLOW_CHAT)):m==="site"||m==="always"?(await e.set(s,E(await e.get(s)||[],n)),h(p.ALLOW_CHAT)):h(m==="allow"?p.ALLOW:p.DENY)}return{ask:o,forget(n){e.delete(n)},standing(n){return e.get(n)||[]},provider({convId:n=null,confirm:s,agentName:r="The agent"}={}){return{id:"permission",specs:[N()],execute:async(c,i)=>JSON.stringify(await o(i&&typeof i=="object"?i:{},{convId:n,confirm:s,agentName:r}))}}}}export{b as ALLOW_ALL,p as PERMISSION_ANSWERS,$ as PERMISSION_EVENT,W as PERMISSION_TIMEOUT_MS,_ as PERMISSION_TOOL_NAME,R as claudePermissionResponse,P as createPermissionDesk,L as describePermission,T as isPermissionTool,w as permissionAllowed,h as permissionAnswer,x as permissionIcon,g as permissionKeys,C as permissionLine,k as permissionRequestFromClaude,N as permissionToolSpec,O as recallPermission,E as rememberPermission};
1
+ const T="permission_request",u=Object.freeze({ALLOW:"allow",ALLOW_CHAT:"allow_chat",DENY:"deny"}),W=600*1e3,w="permission_prompt";function E(){return{name:w,description:"Chap-internal: the person answers the agent's permission prompt. Not for the model.",parameters:{type:"object",properties:{tool:{type:"string"},input:{type:"object"},description:{type:"string"},path:{type:"string"},rules:{type:"array"}}}}}const k=t=>t===w,p=t=>t!==null&&typeof t=="object"&&!Array.isArray(t),a=t=>typeof t=="string"?t:t==null?"":String(t);function R(t={}){const e=p(t)?t:{},o=a(e.tool_name||e.toolName||e.tool),s=[];for(const n of Array.isArray(e.permission_suggestions)?e.permission_suggestions:[])if(!(!p(n)||n.type!=="addRules"||n.behavior==="deny"))for(const l of Array.isArray(n.rules)?n.rules:[]){if(!p(l))continue;const c=a(l.toolName||o),i=a(l.ruleContent);c&&!s.some(f=>f.tool===c&&f.content===i)&&s.push({tool:c,content:i})}return{tool:o,displayName:a(e.display_name||e.displayName)||o,input:p(e.input)?e.input:{},description:a(e.description),path:a(e.blocked_path||e.path)||null,rules:s,toolUseId:a(e.tool_use_id||e.toolUseId)||null}}function A(t={}){const e=Array.isArray(t.rules)?t.rules.filter(o=>o&&o.tool):[];return e.length?e.map(o=>o.content?`${o.tool}(${o.content})`:o.tool):t.tool?[String(t.tool)]:[]}function L(t={}){const e=a(t.tool),o=p(t.input)?t.input:{},s=(r,d=240)=>r.length>d?`${r.slice(0,d-1)}…`:r;let n,l;if(e==="Bash")n="Run a command",l=s(a(o.command).trim()||a(t.description)||"a shell command");else if(e==="Edit"||e==="MultiEdit"||e==="NotebookEdit")n="Edit a file",l=a(o.file_path||o.notebook_path||t.path)||"a file";else if(e==="Write"){n="Write a file";const r=a(o.file_path||t.path)||"a file",d=a(o.content).length;l=d?`${r} (${d.toLocaleString()} chars)`:r}else if(/^mcp__/.test(e))n="Use a connected server",l=e.replace(/^mcp__/,"").replace(/__/," › ");else{n=`Use ${t.displayName||e||"a tool"}`;let r="";try{r=JSON.stringify(o)}catch{r=""}l=a(t.description)||(r&&r!=="{}"?s(r,200):e)}const c=A(t),i=Array.isArray(t.rules)?t.rules.filter(r=>r?.content):[],f=i.length&&i.some(r=>r.content!==l)?i.map(r=>r.content).join(", "):c.length&&!i.length?`every ${e} call`:null,h=p(t.access)?a(t.access.folder):"",g=h?`This works in ${h}, outside the folders it may change. Allowing it opens ${h} to it for the rest of this chat.`:null;return{title:n,detail:l,scope:f,description:a(t.description)||null,access:g}}function m(t,e=""){return t===u.ALLOW?{behavior:"allow",scope:"once"}:t===u.ALLOW_CHAT?{behavior:"allow",scope:"chat"}:{behavior:"deny",scope:"once",...e?{message:String(e)}:{}}}function b(t){return p(t)&&t.behavior==="allow"}function O(t,e){const o=new Set(Array.isArray(t)?t:[]);for(const s of A(e))o.add(s);return[...o].sort()}const S="*";function x(t,e){const o=new Set(Array.isArray(t)?t:[]);if(o.has(S))return m(u.ALLOW);const s=A(e);return s.length&&s.every(n=>o.has(n))?m(u.ALLOW):null}function C(t,e,o={}){const s=p(o)?o:{},n=b(e);let l;if(n){if(l={behavior:"allow",updatedInput:p(s.input)?s.input:{}},e.scope==="chat"){const c=(Array.isArray(s.permission_suggestions)?s.permission_suggestions:[]).filter(i=>p(i)&&i.type==="addRules"&&i.behavior!=="deny"&&Array.isArray(i.rules)&&i.rules.length).map(i=>({type:"addRules",rules:i.rules,behavior:"allow",destination:"session"}));c.length&&(l.updatedPermissions=c)}}else l={behavior:"deny",message:a(e?.message)||"The user declined this action in Chap. Continue without it, or explain what it was for and ask."};return{type:"control_response",response:{subtype:"success",request_id:String(t),response:l}}}function P(t,e){const{detail:o}=L(t),s=b(e),n=s&&e.scope==="chat"?" for this chat":"";return`${s?"Allowed":"Declined"}${n}: ${o}`}function $(t){return t==="Bash"?"code":/^(Edit|MultiEdit|NotebookEdit)$/.test(String(t))?"pencil":t==="Write"?"file":/^mcp__/.test(String(t))?"plug":"bot"}function I({memory:t=new Map}={}){const e=t;async function o(s,{convId:n=null,confirm:l,agentName:c="The agent"}={}){const i=p(s.access)?null:x(await e.get(n)||[],s);if(i)return i;const{title:f,detail:h,scope:g,description:r,access:d}=L(s),N=[d,h,r&&r!==h?r:"",g?`“Allow for this chat” also covers: ${g}`:""].filter(Boolean).join(`
2
+ `);let y;try{if(typeof l!="function")throw new Error("no card");y=await l(N,{title:`${c} asks — ${f.toLowerCase()}?`,iconName:$(s.tool),scopeLabel:"Allow for this chat",allLabel:"Allow all in this chat"})}catch(_){return m(u.DENY,`Chap could not show the permission card (${_?.message||_}).`)}return y==="all"?(await e.set(n,[S]),m(u.ALLOW_CHAT)):y==="site"||y==="always"?(await e.set(n,O(await e.get(n)||[],s)),m(u.ALLOW_CHAT)):m(y==="allow"?u.ALLOW:u.DENY)}return{ask:o,forget(s){e.delete(s)},standing(s){return e.get(s)||[]},provider({convId:s=null,confirm:n,agentName:l="The agent"}={}){return{id:"permission",specs:[E()],execute:async(c,i)=>JSON.stringify(await o(i&&typeof i=="object"?i:{},{convId:s,confirm:n,agentName:l}))}}}}export{S as ALLOW_ALL,u as PERMISSION_ANSWERS,T as PERMISSION_EVENT,W as PERMISSION_TIMEOUT_MS,w as PERMISSION_TOOL_NAME,C as claudePermissionResponse,I as createPermissionDesk,L as describePermission,k as isPermissionTool,b as permissionAllowed,m as permissionAnswer,$ as permissionIcon,A as permissionKeys,P as permissionLine,R as permissionRequestFromClaude,E as permissionToolSpec,x as recallPermission,O as rememberPermission};
package/agent-run-hint.js CHANGED
@@ -1 +1 @@
1
- const r=["default","acceptEdits","bypassPermissions"];function u(t,n){const e=Array.isArray(t?.agents)?t.agents:[],i=String(n||"");return i&&e.find(s=>s&&(s.bridgeAgent===i||s.id===i))||null}function l(t){if(!t||typeof t!="object")return null;const n={},e=String(t.permissionMode||"");r.includes(e)&&e!=="default"&&(n.permissionMode=e);const i=String(t.workingDir||"").trim();i&&/^([A-Za-z]:[\\/]|\/|~)/.test(i)&&(n.workingDir=i);const s=String(t.sandboxHosts||"").split(/[\s,;]+/).map(o=>o.trim().toLowerCase()).filter(Boolean);return s.length&&(n.sandbox={hosts:[...new Set(s)]}),t.outsideSandbox===!0&&(n.sandbox={...n.sandbox||{},outside:!0}),Object.keys(n).length?n:null}export{u as agentCardFor,l as runHintFor};
1
+ import{SIGN_IN_NAMES as g}from"./sandbox-grant.js";const l=["default","acceptEdits","bypassPermissions"];function f(n,t){const e=Array.isArray(n?.agents)?n.agents:[],i=String(t||"");return i&&e.find(s=>s&&(s.bridgeAgent===i||s.id===i))||null}function d(n){if(!n||typeof n!="object")return null;const t={},e=String(n.permissionMode||"");l.includes(e)&&e!=="default"&&(t.permissionMode=e);const i=String(n.workingDir||"").trim();i&&/^([A-Za-z]:[\\/]|\/|~)/.test(i)&&(t.workingDir=i);const s=String(n.sandboxHosts||"").split(/[\s,;]+/).map(o=>o.trim().toLowerCase()).filter(Boolean);s.length&&(t.sandbox={hosts:[...new Set(s)]}),n.outsideSandbox===!0&&(t.sandbox={...t.sandbox||{},outside:!0});const r=(Array.isArray(n.signIn)?n.signIn:String(n.signIn||"").split(/[\s,;]+/)).map(o=>String(o).trim().toLowerCase()).filter(o=>g.includes(o));return r.length&&(t.sandbox={...t.sandbox||{},signIn:[...new Set(r)]}),Object.keys(t).length?t:null}export{f as agentCardFor,d as runHintFor};
package/date-filter.js ADDED
@@ -0,0 +1 @@
1
+ import{resolveSince as O}from"./omni.js";const p=e=>Number.isFinite(Number(e))?Number(e):0,F=e=>String(e).padStart(2,"0");function h(e){const t=new Date(e);return new Date(t.getFullYear(),t.getMonth(),t.getDate()).getTime()}function l(e,t){const n=new Date(e);return new Date(n.getFullYear(),n.getMonth(),n.getDate()+t).getTime()}function y(e){const t=new Date(e);return`${t.getFullYear()}-${F(t.getMonth()+1)}-${F(t.getDate())}`}const $=Object.freeze([Object.freeze({id:"today",label:"Today"}),Object.freeze({id:"yesterday",label:"Yesterday"}),Object.freeze({id:"7d",label:"Last 7 days"}),Object.freeze({id:"30d",label:"Last 30 days"}),Object.freeze({id:"month",label:"This month"})]),f=(e,t)=>({from:e||0,to:t||0});function Y(e){const t=/^(\d{4})(?:-(\d{1,2})(?:-(\d{1,2}))?)?$/.exec(e);if(!t)return null;const n=Number(t[1]);if(t[2]===void 0)return f(new Date(n,0,1).getTime(),new Date(n+1,0,1).getTime());const r=Number(t[2])-1;if(r<0||r>11)return null;if(t[3]===void 0)return f(new Date(n,r,1).getTime(),new Date(n,r+1,1).getTime());const o=Number(t[3]),a=new Date(n,r,o);return a.getMonth()!==r||a.getDate()!==o?null:f(a.getTime(),new Date(n,r,o+1).getTime())}function b(e,t){const n=String(e||"").trim().toLowerCase();if(!n)return null;const r=h(t);if(n==="today")return f(r,l(r,1));if(n==="yesterday")return f(l(r,-1),r);if(n==="month"){const a=new Date(t);return f(new Date(a.getFullYear(),a.getMonth(),1).getTime(),l(r,1))}if(n==="year")return f(new Date(new Date(t).getFullYear(),0,1).getTime(),l(r,1));const o=/^(\d{1,4})([dw])$/.exec(n);if(o){const a=Number(o[1])*(o[2]==="w"?7:1);return a>0?f(l(r,1-a),l(r,1)):null}return Y(n)}function L(e,{now:t=Date.now()}={}){const n=String(e||"").trim();if(!n.includes(".."))return b(n,t);const[r,o,a]=n.split("..");if(a!==void 0||!r&&!o)return null;const u=r?b(r,t):null,s=o?b(o,t):null;if(r&&!u||o&&!s)return null;const i=f(u?u.from:0,s?s.to:0);return i.from&&i.to&&i.to<=i.from?f(s.from,u.to):i}function S(e,t){const n=String(e||"").trim().toLowerCase(),r=n==="today"||n==="yesterday"?b(n,t):Y(n);return r?r.from:O(n,t)}function w(e,t){return e?t?f(Math.max(e.from,t.from),e.to&&t.to?Math.min(e.to,t.to):e.to||t.to):e:t}function j(e={},{now:t=Date.now()}={}){let n=null;if(e.date&&(n=w(n,L(e.date,{now:t}))),e.since){const r=S(e.since,t);r&&(n=w(n,f(r,0)))}if(e.before){const r=S(e.before,t);r&&(n=w(n,f(0,r)))}return n}const E=/(^|\s)(date|since|before):("[^"]*"|[^\s"]+)/giu;function x(e,{now:t=Date.now()}={}){const n=[];let r=null,o=String(e??"").replace(E,(a,u,s,i)=>{const c=s.toLowerCase(),m=i.replace(/^"|"$/g,""),g=j({[c]:m},{now:t});return g?(r=w(r,g),n.push({key:c,value:m}),u||""):a});return o=o.replace(/\s+/g," ").trim(),{range:r,text:o,terms:n}}function v(e,t){return x(e,t).terms.length>0}function M(e){return!!e&&!!e.from&&e.to===l(e.from,1)}function z(e){if(!e)return"";if(typeof e=="string")return $.some(r=>r.id===e)?`date:${e}`:"";const t=p(e.from),n=p(e.to);return!t&&!n?"":M({from:t,to:n})?`date:${y(t)}`:`date:${t?y(t):""}..${n?y(n-1):""}`}function I(e,t,n){const{text:r}=x(e,n);return[z(t),r].filter(Boolean).join(" ")}function C(e,t){return!t||!t.from&&!t.to?!0:(Array.isArray(e)?e:[e]).some(r=>{const o=p(r);return o>0&&(!t.from||o>=t.from)&&(!t.to||o<t.to)})}const k=e=>[e?.createdAt,e?.updatedAt];function W(e,t,n=k){const r=Array.isArray(e)?e:[];return!t||!t.from&&!t.to?r:r.filter(o=>C(n(o),t))}const T=["January","February","March","April","May","June","July","August","September","October","November","December"],A=["Su","Mo","Tu","We","Th","Fr","Sa"];function d(e,t,n){const r=new Date(e),o=`${T[r.getMonth()].slice(0,3)} ${r.getDate()}`;return n||r.getFullYear()!==new Date(t).getFullYear()?`${o}, ${r.getFullYear()}`:o}function N(e,{now:t=Date.now()}={}){if(!e||!e.from&&!e.to)return"";const n=h(t);if(M(e))return e.from===n?"Today":e.from===l(n,-1)?"Yesterday":d(e.from,t);if(!e.to)return`Since ${d(e.from,t)}`;const r=e.to-1;if(!e.from)return`Until ${d(r,t)}`;const o=new Date(e.from),a=new Date(r);if(o.getFullYear()!==a.getFullYear())return`${d(e.from,t,!0)} – ${d(r,t,!0)}`;if(o.getMonth()===a.getMonth()){const u=o.getFullYear()!==new Date(t).getFullYear()?`, ${o.getFullYear()}`:"";return`${T[o.getMonth()].slice(0,3)} ${o.getDate()} – ${a.getDate()}${u}`}return`${d(e.from,t)} – ${d(r,t)}`}function B(e,{now:t=Date.now()}={}){const{range:n,terms:r}=x(e,{now:t});if(!n)return"";if(r.length===1&&r[0].key==="date"){const o=$.find(a=>a.id===r[0].value.toLowerCase());if(o)return o.label}return N(n,{now:t})}function J(e){const{terms:t}=x(e);return t.length!==1||t[0].key!=="date"?"":$.find(n=>n.id===t[0].value.toLowerCase())?.id||""}function _(e,t){const n=h(t),r=f(n,l(n,1));return M(e)?e.from===n?null:n<e.from?f(n,e.to):f(e.from,r.to):r}function K({year:e,month:t},n){const r=new Date(e,t+n,1);return{year:r.getFullYear(),month:r.getMonth()}}function P(e,t,{weekStart:n=0,now:r=Date.now()}={}){const o=new Date(e,t,1),a=(o.getDay()-n+7)%7,u=h(r),s=[];for(let i=0;i<6;i+=1){const c=[];for(let m=0;m<7;m+=1){const g=new Date(e,t,1-a+i*7+m),D=g.getTime();c.push({ts:D,key:y(D),day:g.getDate(),inMonth:g.getMonth()===o.getMonth(),today:D===u,future:D>u})}s.push(c)}return{year:o.getFullYear(),month:o.getMonth(),label:`${T[o.getMonth()]} ${o.getFullYear()}`,weekdays:A.map((i,c)=>A[(c+n)%7]),weeks:s}}function G(e,t=k){const n=new Map;for(const r of Array.isArray(e)?e:[]){const o=new Set;for(const a of[].concat(t(r)??[])){const u=p(a);u>0&&o.add(y(u))}for(const a of o)n.set(a,(n.get(a)||0)+1)}return n}function H(e,t,{limit:n=3,now:r=Date.now()}={}){if(!t||!t.from&&!t.to||!(e instanceof Map))return[];const o=[];for(const[a,u]of e){const s=/^(\d{4})-(\d{2})-(\d{2})$/.exec(a);if(!s||!u)continue;const i=new Date(Number(s[1]),Number(s[2])-1,Number(s[3])).getTime(),c=t.from&&i<t.from,m=t.to&&i>=t.to;!c&&!m||o.push({key:a,ts:i,count:u,away:c?t.from-i:i-t.to+1})}return o.sort((a,u)=>a.away-u.away).slice(0,Math.max(0,n)).sort((a,u)=>a.ts-u.ts).map(({key:a,ts:u,count:s})=>({key:a,ts:u,count:s,label:N(f(u,l(u,1)),{now:r})}))}function Q(e){try{const t=new Intl.Locale(e||new Intl.DateTimeFormat().resolvedOptions().locale),n=(typeof t.getWeekInfo=="function"?t.getWeekInfo():t.weekInfo)?.firstDay;if(n>=1&&n<=7)return n%7}catch{}return 0}export{$ as DATE_PRESETS,J as activePreset,l as addDays,B as dateFilterLabel,j as dateRangeOf,G as dayCounts,y as dayKey,N as describeDateRange,W as filterByDate,z as formatDateTerm,v as hasDateTerms,M as isSingleDay,Q as localeWeekStart,C as matchesDateRange,P as monthGrid,H as nearbyDays,x as parseDateQuery,_ as pickDay,L as resolveDateValue,I as setDateTerm,K as shiftMonth,h as startOfDay};
package/feature-map.js CHANGED
@@ -1 +1 @@
1
- const s=["chat","privacy","notes","knowledge","meetings","voice","automation","sync","runtime","browser"],t=[{id:"chat.streaming-turn",surface:"chat",title:"Streaming turns",what:"A turn streams into the thread through its tool rounds, with a stop.",shared:["turn-loop.js","tool-round.js"]},{id:"chat.toolset",surface:"chat",title:"Per-turn toolset",what:"The tools a turn may call, chosen per target and policy.",shared:["toolset.js","tool-dispatch.js"]},{id:"chat.slash-skills",surface:"chat",title:"Skills as /commands",what:"The user’s skills and the packages the bridge found, offered as slash commands in every composer.",shared:["slash-commands.js","skill-vars.js"]},{id:"chat.slash-builtins",surface:"chat",title:"Built-in /commands",what:"/search and /history as commands, not tools the model may or may not call.",shared:["slash-commands.js"]},{id:"chat.bug-report",surface:"chat",title:"Report a problem",what:"/bug and About → Report a problem: a prefilled GitHub issue with whitelisted, scrubbed diagnostics, checked against existing issues first.",shared:["bug-report.js"]},{id:"chat.suggestions",surface:"chat",title:"Empty-chat suggestions",what:"Prompt suggestions on an empty thread, from the page or the recent history.",shared:[]},{id:"chat.attachments",surface:"chat",title:"Attachments",what:"Text, PDF and by-reference files attached to a turn.",shared:["context-attachments.js","attached-paths.js","pdf-layout.js"]},{id:"chat.html-artifacts",surface:"chat",title:"HTML artifacts",what:"An HTML reply rendered in a sandbox, never in the panel.",shared:[]},{id:"chat.diagram-artifacts",surface:"chat",title:"Diagram artifacts",what:"A mermaid block becomes a Diagram | Code card with zoom and copy.",shared:[]},{id:"chat.model-routing",surface:"chat",title:"Auto model routing",what:"An Auto target picks a candidate per message and fails over on error.",shared:["model-candidates.js","route-strategies.js","route-decider.js","failover.js"]},{id:"chat.history-rag",surface:"chat",title:"History in the turn",what:"Past chats, notes and meetings retrieved into a turn automatically, not only on request.",shared:["sources-retrieval.js","text-search.js"]},{id:"chat.history-search-tool",surface:"chat",title:"history_search tool",what:"The model searches the local history and reads one record in full.",shared:["sources.js"]},{id:"chat.sideline",surface:"chat",title:"Sideline",what:"A side conversation beside the thread, on the same context.",shared:["sideline.js"]},{id:"chat.fast-path",surface:"chat",title:"Fast path",what:"A turn answered without the model when a local answer exists.",shared:["fast-path.js"]},{id:"chat.capability-tools",surface:"chat",title:"decide / rerank tools",what:"The model hands a judgement or an ordering to a small local model mid-turn.",shared:["capability-tools.js","capability-providers.js"]},{id:"chat.presence",surface:"chat",title:"Situational presence",what:"What the client knows about the moment (time, place, page) stated to the model as facts.",shared:["presence.js"]},{id:"chat.usage",surface:"chat",title:"Usage meter",what:"Tokens and cost per turn and per day.",shared:["usage.js","usage-pricing.js"]},{id:"chat.web-search",surface:"chat",title:"Web search",what:"Web search as a tool, client-side or through the gateway.",shared:["web-search.js","web-search-tool.js"]},{id:"chat.weather",surface:"chat",title:"Weather",what:"Weather as a tool.",shared:["weather.js","weather-tool.js"]},{id:"chat.event-log",surface:"chat",title:"Event log",what:"What happened in a turn, in order, with where the time went.",shared:["event.js","trajectory.js"]},{id:"chat.teams",surface:"chat",title:"Agent teams",what:"Several agents on one task with a board, a plan and a trail.",shared:["team.js","team-run.js"]},{id:"chat.mcp",surface:"chat",title:"MCP servers",what:"MCP servers as tools.",shared:["mcp-client.js","mcp-manager.js"]},{id:"chat.mcp-oauth",surface:"chat",title:"MCP sign-in",what:"Signing in to an MCP server that asks for OAuth.",shared:["mcp-oauth.js","mcp-signin.js"]},{id:"chat.omni-search",surface:"chat",title:"Omni search",what:"One palette searching notes, chats and meetings at once.",shared:["omni.js"]},{id:"chat.memory",surface:"chat",title:"Memory",what:"Facts remembered across conversations, and the tool that writes them.",shared:["memory.js","memory-tool.js"]},{id:"chat.tool-result-shield",surface:"chat",title:"Tool-result shield",what:"A tool result is redacted before the model sees it.",shared:["tool-result.js"]},{id:"chat.read-aloud",surface:"chat",title:"Read aloud",what:"A reply spoken as it streams.",shared:["speak-stream.js"]},{id:"chat.structured-output",surface:"chat",title:"Typed answers",what:"A model call that expects a shape goes through one schema.",shared:["structured.js"]},{id:"chat.media-transcript",surface:"chat",title:"Video transcripts",what:"“Summarise this video” from a caption track.",shared:["media-transcript.js"]},{id:"privacy.redaction",surface:"privacy",title:"On-device redaction",what:"PII redacted before a cloud model and restored on the way back.",shared:["redaction-tokens.js","redaction-dictionary.js"]},{id:"privacy.redaction-preview",surface:"privacy",title:"Show what the model sees",what:"The redacted text, by type, before it leaves.",shared:[]},{id:"privacy.access-log",surface:"privacy",title:"Access log",what:"What left this device, on the record.",shared:["observability.js"]},{id:"privacy.vault",surface:"privacy",title:"Vault",what:"Records kept encrypted at rest.",shared:["vault.js"]},{id:"privacy.encrypted-backup",surface:"privacy",title:"Encrypted backup",what:"A backup sealed with the user’s key.",shared:["backup-envelope.js"]},{id:"notes.editor",surface:"notes",title:"Live-preview editor",what:"The note editor with regions, follow-tail and rich paste.",shared:[]},{id:"notes.images",surface:"notes",title:"Images in a note",what:"A picture pasted or dropped into a note, stored beside it and placed by reference.",shared:["note-images.js"]},{id:"notes.cowriter",surface:"notes",title:"Co-writer",what:"A writer swarm working the note, routed per section.",shared:["cowriter.js","cowriter-router.js"]},{id:"notes.fact-check",surface:"notes",title:"Fact check",what:"Claims in a note checked against sources.",shared:["cowriter-factcheck.js"]},{id:"notes.research",surface:"notes",title:"Note research",what:"Research gathered for a note from the web and the history.",shared:["note-research.js"]},{id:"notes.graph",surface:"notes",title:"Note graph",what:"Links and mentions between notes as a graph.",shared:["note-graph.js"]},{id:"notes.tags",surface:"notes",title:"Tags",what:"Tags on notes, with tag queries in search.",shared:["tags.js"]},{id:"knowledge.briefs",surface:"knowledge",title:"Briefs",what:"Briefs read with first-class citations.",shared:["knowledge.js"]},{id:"knowledge.brief-curation",surface:"knowledge",title:"Brief curation",what:"Briefs derived from the sources — the curator.",shared:["knowledge-derive.js","synthesis.js"]},{id:"knowledge.topics",surface:"knowledge",title:"Topics",what:"Topics extracted from chats, notes and meetings, and the index they build.",shared:[]},{id:"knowledge.corpus",surface:"knowledge",title:"Corpus overview",what:"What the local corpus holds, by kind and by time.",shared:[]},{id:"meetings.live-transcript",surface:"meetings",title:"Live transcript",what:"A meeting transcribed as it happens, kept as a record.",shared:["meeting-shape.js","meeting-text.js"]},{id:"meetings.charts",surface:"meetings",title:"Meeting charts",what:"Who spoke, how much, when.",shared:[]},{id:"meetings.insights",surface:"meetings",title:"Meeting insights",what:"Decisions, actions and the moments that need you, from the transcript.",shared:["meeting-insights.js","meeting-analyzers.js"]},{id:"meetings.monitors",surface:"meetings",title:"Monitors",what:"A profile watching meetings for what matters to it.",shared:["monitor-profile.js"]},{id:"meetings.transcript-import",surface:"meetings",title:"Transcript import",what:"A transcript pasted from Zoom, Teams or Meet becomes a meeting record.",shared:[]},{id:"meetings.speaker-names",surface:"meetings",title:"Speaker names",what:"Who “Speaker 1” is: named in the transcript or by typing “Speaker 1 is Sarah” in the chat, applied on every read and to every later line.",shared:["meeting-speakers.js","meeting-speakers-typed.js"]},{id:"meetings.voice-commands",surface:"meetings",title:"Spoken commands",what:"Commands spoken during a meeting, matched from the transcript.",shared:["voice-intents.js"]},{id:"meetings.audio-scribe",surface:"meetings",title:"Audio scribe",what:"A meeting with no captions, a desktop app, a playing video or a conversation in the room, transcribed locally from what is playing and the microphone as separate channels — a mic held by one person is You, everything else is diarized.",shared:["voice-channels.js","voice-scribe.js","speaker-cluster.js"]},{id:"voice.dictation",surface:"voice",title:"Dictation",what:"Speak into any composer.",shared:["voice-vad.js"]},{id:"voice.mode",surface:"voice",title:"Voice mode",what:"A spoken conversation with barge-in.",shared:["voice-loop.js"]},{id:"voice.tts",surface:"voice",title:"Speech",what:"Text to speech with the chosen voice, including the user’s own.",shared:["voice-speaker.js","voice-models.js"]},{id:"automation.jobs",surface:"automation",title:"Jobs",what:"Scheduled and triggered work that runs without a turn.",shared:["job.js","schedule.js"]},{id:"automation.text-triggers",surface:"automation",title:"Text triggers",what:"A phrase arriving anywhere, matched against the user’s jobs.",shared:[]},{id:"automation.alert-sound",surface:"automation",title:"Timer sound",what:"A timer that goes off makes a sound, not only a notification.",shared:[]},{id:"automation.widgets",surface:"automation",title:"Widgets",what:"Small HTML plugins the user or an agent wrote, hosted in a frame.",shared:["widget.js"]},{id:"automation.projects",surface:"automation",title:"Projects",what:"A folder an agent works in, with its runs.",shared:["project.js","project-run.js"]},{id:"automation.skills",surface:"automation",title:"Skills",what:"Skills written by the user or found on the machine.",shared:["skill-manifest.js","skill-runtime.js","skill-sources.js"]},{id:"automation.recipes",surface:"automation",title:"Recipes",what:"Saved multi-step prompts.",shared:["recipe.js"]},{id:"automation.channels",surface:"automation",title:"Channels",what:"Telegram and WhatsApp surfaces to the agents.",shared:[]},{id:"automation.channel-pairing-qr",surface:"automation",title:"Pairing QR",what:"The channel pairing link as a QR code, read from another device.",shared:[]},{id:"sync.auto-backup",surface:"sync",title:"Auto backup",what:"A backup written on a schedule.",shared:["backup-records.js"]},{id:"sync.warm",surface:"sync",title:"Warm sync",what:"Records pulled from and pushed to the gateway.",shared:["sync-plan.js"]},{id:"sync.prefs",surface:"sync",title:"Settings travel",what:"Every setting shared between the clients, live.",shared:["client-prefs.js"]},{id:"sync.link",surface:"sync",title:"Link your phone",what:"A phone paired by QR reaches this computer’s models, agents and knowledge through the end-to-end encrypted relay.",shared:["link-connect.js","link-protocol.js","link-noise.js","qr.js"]},{id:"sync.drive",surface:"sync",title:"Google Drive backup",what:"Backups to and from the user’s Drive.",shared:["drive-backup.js"]},{id:"runtime.model-place",surface:"runtime",title:"One model place",what:"Every model of every type — chat, vision, embeddings, speech, entity recognition, decisions, ranking — added by type and runtime, listed in one place with where it runs and which jobs may use it.",shared:["model-roles.js","capability-uses.js","model-entries.js"]},{id:"runtime.model-fusion",surface:"runtime",title:"Model fusion",what:"Several models as one — a union for recall (the entity detector with companions), a draft + target for speed (a server drafting with a small model or the model’s MTP head), a fallback for reliability (the next member answers when one is down) — shown with their members in the model place.",shared:["model-fusion.js"]},{id:"runtime.license",surface:"runtime",title:"Licence",what:"Pro and Team verified offline against the signed token.",shared:["entitlement.js"]},{id:"runtime.free-caps",surface:"runtime",title:"Free caps",what:"The Free limits on the workspace surfaces, asked the same way everywhere.",shared:["entitlement.js"]},{id:"runtime.managed",surface:"runtime",title:"Managed mode",what:"The organisation’s configuration, pushed by IT.",shared:[]},{id:"runtime.updates",surface:"runtime",title:"Updates",what:"The client, the gateway and the bridge know when a newer version exists.",shared:[]},{id:"runtime.bridge-health",surface:"runtime",title:"Runtime health",what:"The bridge and the gateway watched, with what to do when one is down.",shared:[]},{id:"runtime.local-pairing",surface:"runtime",title:"Local pairing",what:"This client paired with the local servers by token or pin.",shared:[]},{id:"runtime.model-roles",surface:"runtime",title:"What runs where",what:"Every model in use by job, where it runs, a jump to swap it.",shared:["model-roles.js"]},{id:"browser.page-actions",surface:"browser",title:"Page actions",what:"The agent acts on the open page.",shared:[]},{id:"browser.page-tools",surface:"browser",title:"Page tools",what:"The open page read, matched and calibrated as a tool.",shared:[]},{id:"browser.canvas",surface:"browser",title:"Canvas adapters",what:"Web apps (docs, sheets, whiteboards) driven through their canvas.",shared:[]},{id:"browser.in-browser-model",surface:"browser",title:"In-browser model",what:"A model run in the browser on WebGPU.",shared:[]},{id:"browser.floating-launcher",surface:"browser",title:"Floating launcher",what:"The button on web pages that opens the panel.",shared:[]}];function r(e){return e?t.filter(a=>a.surface===e):t.slice()}function i(){return t.map(e=>e.id)}export{t as FEATURES,s as SURFACES,i as featureIds,r as listFeatures};
1
+ const s=["chat","privacy","notes","knowledge","meetings","voice","automation","sync","runtime","browser"],t=[{id:"chat.streaming-turn",surface:"chat",title:"Streaming turns",what:"A turn streams into the thread through its tool rounds, with a stop.",shared:["turn-loop.js","tool-round.js"]},{id:"chat.toolset",surface:"chat",title:"Per-turn toolset",what:"The tools a turn may call, chosen per target and policy.",shared:["toolset.js","tool-dispatch.js"]},{id:"chat.slash-skills",surface:"chat",title:"Skills as /commands",what:"The user’s skills and the packages the bridge found, offered as slash commands in every composer.",shared:["slash-commands.js","skill-vars.js"]},{id:"chat.slash-builtins",surface:"chat",title:"Built-in /commands",what:"/search and /history as commands, not tools the model may or may not call.",shared:["slash-commands.js"]},{id:"chat.bug-report",surface:"chat",title:"Report a problem",what:"/bug and About → Report a problem: a prefilled GitHub issue with whitelisted, scrubbed diagnostics, checked against existing issues first.",shared:["bug-report.js"]},{id:"chat.suggestions",surface:"chat",title:"Empty-chat suggestions",what:"Prompt suggestions on an empty thread, from the page or the recent history.",shared:[]},{id:"chat.attachments",surface:"chat",title:"Attachments",what:"Text, PDF and by-reference files attached to a turn.",shared:["context-attachments.js","attached-paths.js","pdf-layout.js"]},{id:"chat.html-artifacts",surface:"chat",title:"HTML artifacts",what:"An HTML reply rendered in a sandbox, never in the panel.",shared:[]},{id:"chat.diagram-artifacts",surface:"chat",title:"Diagram artifacts",what:"A mermaid block becomes a Diagram | Code card with zoom and copy.",shared:[]},{id:"chat.model-routing",surface:"chat",title:"Auto model routing",what:"An Auto target picks a candidate per message and fails over on error.",shared:["model-candidates.js","route-strategies.js","route-decider.js","failover.js"]},{id:"chat.history-rag",surface:"chat",title:"History in the turn",what:"Past chats, notes and meetings retrieved into a turn automatically, not only on request.",shared:["sources-retrieval.js","text-search.js"]},{id:"chat.history-search-tool",surface:"chat",title:"history_search tool",what:"The model searches the local history and reads one record in full.",shared:["sources.js"]},{id:"chat.sideline",surface:"chat",title:"Sideline",what:"A side conversation beside the thread, on the same context.",shared:["sideline.js"]},{id:"chat.fast-path",surface:"chat",title:"Fast path",what:"A turn answered without the model when a local answer exists.",shared:["fast-path.js"]},{id:"chat.capability-tools",surface:"chat",title:"decide / rerank tools",what:"The model hands a judgement or an ordering to a small local model mid-turn.",shared:["capability-tools.js","capability-providers.js"]},{id:"chat.presence",surface:"chat",title:"Situational presence",what:"What the client knows about the moment (time, place, page) stated to the model as facts.",shared:["presence.js"]},{id:"chat.usage",surface:"chat",title:"Usage meter",what:"Tokens and cost per turn and per day.",shared:["usage.js","usage-pricing.js"]},{id:"chat.web-search",surface:"chat",title:"Web search",what:"Web search as a tool, client-side or through the gateway.",shared:["web-search.js","web-search-tool.js"]},{id:"chat.weather",surface:"chat",title:"Weather",what:"Weather as a tool.",shared:["weather.js","weather-tool.js"]},{id:"chat.event-log",surface:"chat",title:"Event log",what:"What happened in a turn, in order, with where the time went.",shared:["event.js","trajectory.js"]},{id:"chat.teams",surface:"chat",title:"Agent teams",what:"Several agents on one task with a board, a plan and a trail.",shared:["team.js","team-run.js"]},{id:"chat.mcp",surface:"chat",title:"MCP servers",what:"MCP servers as tools.",shared:["mcp-client.js","mcp-manager.js"]},{id:"chat.mcp-oauth",surface:"chat",title:"MCP sign-in",what:"Signing in to an MCP server that asks for OAuth.",shared:["mcp-oauth.js","mcp-signin.js"]},{id:"chat.omni-search",surface:"chat",title:"Omni search",what:"One palette searching notes, chats and meetings at once.",shared:["omni.js"]},{id:"chat.date-filter",surface:"chat",title:"Calendar filter",what:"A calendar on every list and search of chats, notes and meetings: pick a day or a span, or type date:2026-09-30, and only what was made or changed then is listed.",shared:["date-filter.js"]},{id:"chat.memory",surface:"chat",title:"Memory",what:"Facts remembered across conversations, and the tool that writes them.",shared:["memory.js","memory-tool.js"]},{id:"chat.tool-result-shield",surface:"chat",title:"Tool-result shield",what:"A tool result is redacted before the model sees it.",shared:["tool-result.js"]},{id:"chat.read-aloud",surface:"chat",title:"Read aloud",what:"A reply spoken as it streams.",shared:["speak-stream.js"]},{id:"chat.structured-output",surface:"chat",title:"Typed answers",what:"A model call that expects a shape goes through one schema.",shared:["structured.js"]},{id:"chat.media-transcript",surface:"chat",title:"Video transcripts",what:"“Summarise this video” from a caption track.",shared:["media-transcript.js"]},{id:"privacy.redaction",surface:"privacy",title:"On-device redaction",what:"PII redacted before a cloud model and restored on the way back.",shared:["redaction-tokens.js","redaction-dictionary.js"]},{id:"privacy.redaction-preview",surface:"privacy",title:"Show what the model sees",what:"The redacted text, by type, before it leaves.",shared:[]},{id:"privacy.access-log",surface:"privacy",title:"Access log",what:"What left this device, on the record.",shared:["observability.js"]},{id:"privacy.vault",surface:"privacy",title:"Vault",what:"Records kept encrypted at rest.",shared:["vault.js"]},{id:"privacy.encrypted-backup",surface:"privacy",title:"Encrypted backup",what:"A backup sealed with the user’s key.",shared:["backup-envelope.js"]},{id:"notes.editor",surface:"notes",title:"Live-preview editor",what:"The note editor with regions, follow-tail and rich paste.",shared:[]},{id:"notes.images",surface:"notes",title:"Images in a note",what:"A picture pasted or dropped into a note, stored beside it and placed by reference.",shared:["note-images.js"]},{id:"notes.cowriter",surface:"notes",title:"Co-writer",what:"A writer swarm working the note, routed per section.",shared:["cowriter.js","cowriter-router.js"]},{id:"notes.fact-check",surface:"notes",title:"Fact check",what:"Claims in a note checked against sources.",shared:["cowriter-factcheck.js"]},{id:"notes.research",surface:"notes",title:"Note research",what:"Research gathered for a note from the web and the history.",shared:["note-research.js"]},{id:"notes.graph",surface:"notes",title:"Note graph",what:"Links and mentions between notes as a graph.",shared:["note-graph.js"]},{id:"notes.tags",surface:"notes",title:"Tags",what:"Tags on notes, with tag queries in search.",shared:["tags.js"]},{id:"knowledge.briefs",surface:"knowledge",title:"Briefs",what:"Briefs read with first-class citations.",shared:["knowledge.js"]},{id:"knowledge.brief-curation",surface:"knowledge",title:"Brief curation",what:"Briefs derived from the sources — the curator.",shared:["knowledge-derive.js","synthesis.js"]},{id:"knowledge.topics",surface:"knowledge",title:"Topics",what:"Topics extracted from chats, notes and meetings, and the index they build.",shared:[]},{id:"knowledge.corpus",surface:"knowledge",title:"Corpus overview",what:"What the local corpus holds, by kind and by time.",shared:[]},{id:"meetings.live-transcript",surface:"meetings",title:"Live transcript",what:"A meeting transcribed as it happens, kept as a record.",shared:["meeting-shape.js","meeting-text.js"]},{id:"meetings.charts",surface:"meetings",title:"Meeting charts",what:"Who spoke, how much, when.",shared:[]},{id:"meetings.insights",surface:"meetings",title:"Meeting insights",what:"Decisions, actions and the moments that need you, from the transcript.",shared:["meeting-insights.js","meeting-analyzers.js"]},{id:"meetings.monitors",surface:"meetings",title:"Monitors",what:"A profile watching meetings for what matters to it.",shared:["monitor-profile.js"]},{id:"meetings.transcript-import",surface:"meetings",title:"Transcript import",what:"A transcript pasted from Zoom, Teams or Meet becomes a meeting record.",shared:[]},{id:"meetings.speaker-names",surface:"meetings",title:"Speaker names",what:"Who “Speaker 1” is: named in the transcript or by typing “Speaker 1 is Sarah” in the chat, applied on every read and to every later line.",shared:["meeting-speakers.js","meeting-speakers-typed.js"]},{id:"meetings.voice-commands",surface:"meetings",title:"Spoken commands",what:"Commands spoken during a meeting, matched from the transcript.",shared:["voice-intents.js"]},{id:"meetings.audio-scribe",surface:"meetings",title:"Audio scribe",what:"A meeting with no captions, a desktop app, a playing video or a conversation in the room, transcribed locally from what is playing and the microphone as separate channels — a mic held by one person is You, everything else is diarized.",shared:["voice-channels.js","voice-scribe.js","speaker-cluster.js"]},{id:"voice.dictation",surface:"voice",title:"Dictation",what:"Speak into any composer.",shared:["voice-vad.js"]},{id:"voice.mode",surface:"voice",title:"Voice mode",what:"A spoken conversation with barge-in.",shared:["voice-loop.js"]},{id:"voice.tts",surface:"voice",title:"Speech",what:"Text to speech with the chosen voice, including the user’s own.",shared:["voice-speaker.js","voice-models.js"]},{id:"automation.jobs",surface:"automation",title:"Jobs",what:"Scheduled and triggered work that runs without a turn.",shared:["job.js","schedule.js"]},{id:"automation.text-triggers",surface:"automation",title:"Text triggers",what:"A phrase arriving anywhere, matched against the user’s jobs.",shared:[]},{id:"automation.alert-sound",surface:"automation",title:"Timer sound",what:"A timer that goes off makes a sound, not only a notification.",shared:[]},{id:"automation.widgets",surface:"automation",title:"Widgets",what:"Small HTML plugins the user or an agent wrote, hosted in a frame.",shared:["widget.js"]},{id:"automation.projects",surface:"automation",title:"Projects",what:"A folder an agent works in, with its runs.",shared:["project.js","project-run.js"]},{id:"automation.skills",surface:"automation",title:"Skills",what:"Skills written by the user or found on the machine.",shared:["skill-manifest.js","skill-runtime.js","skill-sources.js"]},{id:"automation.recipes",surface:"automation",title:"Recipes",what:"Saved multi-step prompts.",shared:["recipe.js"]},{id:"automation.channels",surface:"automation",title:"Channels",what:"Telegram and WhatsApp surfaces to the agents.",shared:[]},{id:"automation.channel-pairing-qr",surface:"automation",title:"Pairing QR",what:"The channel pairing link as a QR code, read from another device.",shared:[]},{id:"sync.auto-backup",surface:"sync",title:"Auto backup",what:"A backup written on a schedule.",shared:["backup-records.js"]},{id:"sync.warm",surface:"sync",title:"Warm sync",what:"Records pulled from and pushed to the gateway.",shared:["sync-plan.js"]},{id:"sync.prefs",surface:"sync",title:"Settings travel",what:"Every setting shared between the clients, live.",shared:["client-prefs.js"]},{id:"sync.link",surface:"sync",title:"Link your phone",what:"A phone paired by QR reaches this computer’s models, agents and knowledge through the end-to-end encrypted relay.",shared:["link-connect.js","link-protocol.js","link-noise.js","qr.js"]},{id:"sync.drive",surface:"sync",title:"Google Drive backup",what:"Backups to and from the user’s Drive.",shared:["drive-backup.js"]},{id:"runtime.model-place",surface:"runtime",title:"One model place",what:"Every model of every type — chat, vision, embeddings, speech, entity recognition, decisions, ranking — added by type and runtime, listed in one place with where it runs and which jobs may use it.",shared:["model-roles.js","capability-uses.js","model-entries.js"]},{id:"runtime.model-fusion",surface:"runtime",title:"Model fusion",what:"Several models as one — a union for recall (the entity detector with companions), a draft + target for speed (a server drafting with a small model or the model’s MTP head), a fallback for reliability (the next member answers when one is down) — shown with their members in the model place.",shared:["model-fusion.js"]},{id:"runtime.license",surface:"runtime",title:"Licence",what:"Pro and Team verified offline against the signed token.",shared:["entitlement.js"]},{id:"runtime.free-caps",surface:"runtime",title:"Free caps",what:"The Free limits on the workspace surfaces, asked the same way everywhere.",shared:["entitlement.js"]},{id:"runtime.managed",surface:"runtime",title:"Managed mode",what:"The organisation’s configuration, pushed by IT.",shared:[]},{id:"runtime.updates",surface:"runtime",title:"Updates",what:"The client, the gateway and the bridge know when a newer version exists.",shared:[]},{id:"runtime.bridge-health",surface:"runtime",title:"Runtime health",what:"The bridge and the gateway watched, with what to do when one is down.",shared:[]},{id:"runtime.local-pairing",surface:"runtime",title:"Local pairing",what:"This client paired with the local servers by token or pin.",shared:[]},{id:"runtime.model-roles",surface:"runtime",title:"What runs where",what:"Every model in use by job, where it runs, a jump to swap it.",shared:["model-roles.js"]},{id:"browser.page-actions",surface:"browser",title:"Page actions",what:"The agent acts on the open page.",shared:[]},{id:"browser.page-tools",surface:"browser",title:"Page tools",what:"The open page read, matched and calibrated as a tool.",shared:[]},{id:"browser.canvas",surface:"browser",title:"Canvas adapters",what:"Web apps (docs, sheets, whiteboards) driven through their canvas.",shared:[]},{id:"browser.in-browser-model",surface:"browser",title:"In-browser model",what:"A model run in the browser on WebGPU.",shared:[]},{id:"browser.floating-launcher",surface:"browser",title:"Floating launcher",what:"The button on web pages that opens the panel.",shared:[]}];function r(e){return e?t.filter(a=>a.surface===e):t.slice()}function i(){return t.map(e=>e.id)}export{t as FEATURES,s as SURFACES,i as featureIds,r as listFeatures};
package/harness-cards.js CHANGED
@@ -1 +1 @@
1
- import{EventError as a}from"./event.js";const l=Object.freeze({claude:{label:"Claude Code",vendor:"Anthropic",hosts:["api.anthropic.com"],repoint:"limited",how:"ANTHROPIC_BASE_URL (+ ANTHROPIC_AUTH_TOKEN) to an Anthropic-compatible endpoint — this gateway’s /v1/messages qualifies",dirs:{flag:"--add-dir",repeat:!0},defaultModel:"cloud",session:{resume:["--resume","{id}"],off:["--no-session-persistence"],name:["--name","{name}"]}},codex:{label:"Codex",vendor:"OpenAI",hosts:["api.openai.com","chatgpt.com"],repoint:"limited",how:"OPENAI_BASE_URL, or a model_provider in ~/.codex/config.toml pointing at an OpenAI-compatible endpoint",dirs:{flag:"--add-dir",repeat:!0},defaultModel:"cloud",ownSandbox:"computer use (controlling the browser and other apps)",session:{resume:["resume","{id}"],off:["--ephemeral"]}},antigravity:{label:"Antigravity",vendor:"Google",hosts:["*.googleapis.com"],repoint:"no",how:"",dirs:null,defaultModel:"cloud"},kiro:{label:"Kiro",vendor:"AWS",hosts:["*.amazonaws.com"],repoint:"no",how:"",dirs:null,defaultModel:"cloud"},copilot:{label:"GitHub Copilot",vendor:"GitHub",hosts:["api.githubcopilot.com","*.github.com"],repoint:"no",how:"",dirs:{flag:"--add-dir",repeat:!0},defaultModel:"cloud",session:{resume:["--session-id","{id}"],off:null,name:["--name","{name}"]}},deepseek:{label:"DeepSeek Harness",vendor:"DeepSeek",hosts:["api.deepseek.com"],repoint:"limited",how:"its base URL setting, to an OpenAI-compatible endpoint",dirs:null,defaultModel:"cloud"},opencode:{label:"OpenCode",vendor:null,hosts:[],repoint:"yes",how:"a provider in its config — any OpenAI-compatible endpoint, Ollama, LM Studio, or this gateway",dirs:null,defaultModel:"configurable",session:{resume:["--session","{id}"],off:null,name:["--title","{name}"]}},pi:{label:"Pi",vendor:null,hosts:[],repoint:"yes",how:"a provider in its config — any OpenAI-compatible endpoint, or this gateway",dirs:null,defaultModel:"configurable"},hermes:{label:"Hermes",vendor:null,hosts:[],repoint:"yes",how:"a provider in its config — a local model is a first-class choice",dirs:null,defaultModel:"configurable"}}),p=n=>String(n||"").toLowerCase().replace(/[^a-z]/g,""),d={claudecode:"claude",githubcopilot:"copilot",copilotcli:"copilot",deepseekharness:"deepseek",gemini:"antigravity",geminicli:"antigravity"};function s(n){const o=p(n);if(l[o])return{id:o,...l[o]};if(d[o])return{id:d[o],...l[d[o]]};const e=Object.entries(l).find(([i,t])=>o.includes(i)||o.includes(p(t.label)));return e?{id:e[0],...e[1]}:null}function m(n,{repointed:o=null}={}){const e=s(n);return e?o?{egress:"configurable",where:`a coding agent · model at ${o}`,note:`Re-pointed by you; ${e.how||"its own setting"}.`}:e.repoint==="yes"?{egress:"configurable",where:"a coding agent · model of your choosing",note:`Its model is a setting of its own — ${e.how}. Until you set one, whatever it defaults to.`}:e.repoint==="limited"?{egress:"internet",where:`a coding agent · model at ${e.vendor}`,note:`The conversation goes to ${e.vendor} with your own login. It can be re-pointed: ${e.how}.`}:{egress:"internet",where:`a coding agent · model at ${e.vendor}`,note:`The conversation goes to ${e.vendor} with your own login; this agent cannot be pointed elsewhere.`}:{egress:"internet",where:"a coding agent · its own model, elsewhere",note:"No card for this agent — assume its model is a vendor’s until you know otherwise."}}function u(n){if(n==null)return null;if(typeof n=="string")return u({flag:n,repeat:!0});if(typeof n!="object"||Array.isArray(n))throw new a("SHAPE","dirs: must be { flag, repeat } or { flag, separator }");const o=String(n.flag||"").trim();if(!/^--?[A-Za-z][\w-]*$/.test(o))throw new a("SHAPE",`dirs: not a flag: ${JSON.stringify(n.flag)}`);const e=n.separator==null?null:String(n.separator);if(e!=null&&(e.length===0||/\s/.test(e)))throw new a("SHAPE","dirs: separator must be a non-empty run of non-space characters");return e!=null?{flag:o,separator:e}:{flag:o,repeat:!0}}function h({harness:n,roots:o=[],dirs:e}={}){const i=(Array.isArray(o)?o:[]).map(r=>String(r||"").trim()).filter(Boolean);if(!i.length)return[];const t=u(e!==void 0?e:s(n)?.dirs??null);return t?t.separator?[t.flag,i.join(t.separator)]:i.flatMap(r=>[t.flag,r]):[]}function w(n,o){try{return!!u(o!==void 0?o:s(n)?.dirs??null)}catch{return!1}}const f=/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;function A({harness:n,mode:o,id:e="",name:i=""}={}){const t=s(n)?.session;if(!t)return[];if(o==="off")return Array.isArray(t.off)?[...t.off]:[];if(o==="name"){const r=String(i??"").replace(/\s+/g," ").trim().slice(0,120);return!r||r.startsWith("-")||!Array.isArray(t.name)?[]:t.name.map(c=>c==="{name}"?r:c)}if(o!=="resume"||!Array.isArray(t.resume))return[];if(!f.test(String(e)))throw new a("SHAPE",`harness session: ${JSON.stringify(String(e).slice(0,40))} is not a session id`);return t.resume.map(r=>r==="{id}"?String(e):r)}const y=n=>Array.isArray(s(n)?.session?.resume);export{l as HARNESS_CARDS,s as harnessCard,h as harnessDirArgs,m as harnessEgress,y as harnessResumes,A as harnessSessionArgs,w as harnessTakesDirs,u as normalizeDirsSpec};
1
+ import{EventError as a}from"./event.js";const l=Object.freeze({claude:{label:"Claude Code",vendor:"Anthropic",hosts:["api.anthropic.com"],repoint:"limited",how:"ANTHROPIC_BASE_URL (+ ANTHROPIC_AUTH_TOKEN) to an Anthropic-compatible endpoint — this gateway’s /v1/messages qualifies",dirs:{flag:"--add-dir",repeat:!0},defaultModel:"cloud",session:{resume:["--resume","{id}"],off:["--no-session-persistence"],name:["--name","{name}"]},envFrom:["~/.claude/settings.json","/etc/claude-code/managed-settings.json","/Library/Application Support/ClaudeCode/managed-settings.json"]},codex:{label:"Codex",vendor:"OpenAI",hosts:["api.openai.com","chatgpt.com"],repoint:"limited",how:"OPENAI_BASE_URL, or a model_provider in ~/.codex/config.toml pointing at an OpenAI-compatible endpoint",dirs:{flag:"--add-dir",repeat:!0},defaultModel:"cloud",ownSandbox:"computer use (controlling the browser and other apps)",session:{resume:["resume","{id}"],off:["--ephemeral"]}},antigravity:{label:"Antigravity",vendor:"Google",hosts:["*.googleapis.com"],repoint:"no",how:"",dirs:null,defaultModel:"cloud"},kiro:{label:"Kiro",vendor:"AWS",hosts:["*.amazonaws.com"],repoint:"no",how:"",dirs:null,defaultModel:"cloud"},copilot:{label:"GitHub Copilot",vendor:"GitHub",hosts:["api.githubcopilot.com","*.github.com"],repoint:"no",how:"",dirs:{flag:"--add-dir",repeat:!0},defaultModel:"cloud",session:{resume:["--session-id","{id}"],off:null,name:["--name","{name}"]}},deepseek:{label:"DeepSeek Harness",vendor:"DeepSeek",hosts:["api.deepseek.com"],repoint:"limited",how:"its base URL setting, to an OpenAI-compatible endpoint",dirs:null,defaultModel:"cloud"},opencode:{label:"OpenCode",vendor:null,hosts:[],repoint:"yes",how:"a provider in its config — any OpenAI-compatible endpoint, Ollama, LM Studio, or this gateway",dirs:null,defaultModel:"configurable",session:{resume:["--session","{id}"],off:null,name:["--title","{name}"]}},pi:{label:"Pi",vendor:null,hosts:[],repoint:"yes",how:"a provider in its config — any OpenAI-compatible endpoint, or this gateway",dirs:null,defaultModel:"configurable"},hermes:{label:"Hermes",vendor:null,hosts:[],repoint:"yes",how:"a provider in its config — a local model is a first-class choice",dirs:null,defaultModel:"configurable"}}),p=o=>String(o||"").toLowerCase().replace(/[^a-z]/g,""),d={claudecode:"claude",githubcopilot:"copilot",copilotcli:"copilot",deepseekharness:"deepseek",gemini:"antigravity",geminicli:"antigravity"};function s(o){const n=p(o);if(l[n])return{id:n,...l[n]};if(d[n])return{id:d[n],...l[d[n]]};const e=Object.entries(l).find(([i,t])=>n.includes(i)||n.includes(p(t.label)));return e?{id:e[0],...e[1]}:null}function m(o,{repointed:n=null}={}){const e=s(o);return e?n?{egress:"configurable",where:`a coding agent · model at ${n}`,note:`Re-pointed by you; ${e.how||"its own setting"}.`}:e.repoint==="yes"?{egress:"configurable",where:"a coding agent · model of your choosing",note:`Its model is a setting of its own — ${e.how}. Until you set one, whatever it defaults to.`}:e.repoint==="limited"?{egress:"internet",where:`a coding agent · model at ${e.vendor}`,note:`The conversation goes to ${e.vendor} with your own login. It can be re-pointed: ${e.how}.`}:{egress:"internet",where:`a coding agent · model at ${e.vendor}`,note:`The conversation goes to ${e.vendor} with your own login; this agent cannot be pointed elsewhere.`}:{egress:"internet",where:"a coding agent · its own model, elsewhere",note:"No card for this agent — assume its model is a vendor’s until you know otherwise."}}function u(o){if(o==null)return null;if(typeof o=="string")return u({flag:o,repeat:!0});if(typeof o!="object"||Array.isArray(o))throw new a("SHAPE","dirs: must be { flag, repeat } or { flag, separator }");const n=String(o.flag||"").trim();if(!/^--?[A-Za-z][\w-]*$/.test(n))throw new a("SHAPE",`dirs: not a flag: ${JSON.stringify(o.flag)}`);const e=o.separator==null?null:String(o.separator);if(e!=null&&(e.length===0||/\s/.test(e)))throw new a("SHAPE","dirs: separator must be a non-empty run of non-space characters");return e!=null?{flag:n,separator:e}:{flag:n,repeat:!0}}function h({harness:o,roots:n=[],dirs:e}={}){const i=(Array.isArray(n)?n:[]).map(r=>String(r||"").trim()).filter(Boolean);if(!i.length)return[];const t=u(e!==void 0?e:s(o)?.dirs??null);return t?t.separator?[t.flag,i.join(t.separator)]:i.flatMap(r=>[t.flag,r]):[]}function w(o,n){try{return!!u(n!==void 0?n:s(o)?.dirs??null)}catch{return!1}}const f=/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/;function A({harness:o,mode:n,id:e="",name:i=""}={}){const t=s(o)?.session;if(!t)return[];if(n==="off")return Array.isArray(t.off)?[...t.off]:[];if(n==="name"){const r=String(i??"").replace(/\s+/g," ").trim().slice(0,120);return!r||r.startsWith("-")||!Array.isArray(t.name)?[]:t.name.map(c=>c==="{name}"?r:c)}if(n!=="resume"||!Array.isArray(t.resume))return[];if(!f.test(String(e)))throw new a("SHAPE",`harness session: ${JSON.stringify(String(e).slice(0,40))} is not a session id`);return t.resume.map(r=>r==="{id}"?String(e):r)}const y=o=>Array.isArray(s(o)?.session?.resume);export{l as HARNESS_CARDS,s as harnessCard,h as harnessDirArgs,m as harnessEgress,y as harnessResumes,A as harnessSessionArgs,w as harnessTakesDirs,u as normalizeDirsSpec};