agentix-cli 0.154.2 → 0.155.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.
- package/dist/{App-6T6NVB7O.js → App-P4MLXD46.js} +2 -2
- package/dist/{ChatApp-QZBJ4NHC.js → ChatApp-DILDQOJL.js} +2 -2
- package/dist/{absorb-eval-EVX7LDXR.js → absorb-eval-DBC7EKU5.js} +2 -2
- package/dist/{agent-F2NT6B25.js → agent-OZ3KPI6Y.js} +2 -2
- package/dist/{board-dashboard-642BO7GV.js → board-dashboard-MWUCBHDN.js} +239 -233
- package/dist/board-dashboard-MWUCBHDN.js.map +1 -0
- package/dist/{chunk-KA2NV7H4.js → chunk-3ZAAUY2N.js} +2 -2
- package/dist/{chunk-743RAKLP.js → chunk-4RHM4XTI.js} +2 -2
- package/dist/{chunk-U4YTTWR6.js → chunk-6CXJAZZD.js} +2 -2
- package/dist/{chunk-AKCEMKQM.js → chunk-ACB6GRSP.js} +2 -2
- package/dist/{chunk-W2RB5XG7.js → chunk-AHZBWCXI.js} +2 -2
- package/dist/chunk-AVAR5KNN.js +36 -0
- package/dist/chunk-AVAR5KNN.js.map +1 -0
- package/dist/{chunk-E76HREC7.js → chunk-CTXHMJLZ.js} +3 -3
- package/dist/{chunk-IA4J3YLB.js → chunk-D65HXPIB.js} +2 -2
- package/dist/{chunk-S3EX64LR.js → chunk-EE3ZYIIB.js} +2 -2
- package/dist/{chunk-VRF4YW46.js → chunk-FTIJQLBX.js} +2 -2
- package/dist/{chunk-YL26FRKJ.js → chunk-GIFUJ3LL.js} +2 -2
- package/dist/chunk-H7DLFJBV.js +575 -0
- package/dist/chunk-H7DLFJBV.js.map +1 -0
- package/dist/{chunk-4CWACHEH.js → chunk-HGF73KFH.js} +2 -2
- package/dist/{chunk-JAML6222.js → chunk-HOVJVXEP.js} +2 -2
- package/dist/{chunk-GRVTLUQ6.js → chunk-IW4AZVJR.js} +2 -2
- package/dist/{chunk-IWWEUXZQ.js → chunk-J3IP3UZX.js} +2 -2
- package/dist/{chunk-67DWBJEO.js → chunk-J432IZXG.js} +333 -336
- package/dist/chunk-J432IZXG.js.map +1 -0
- package/dist/{chunk-37I654UJ.js → chunk-KYRF2FPT.js} +2 -2
- package/dist/{chunk-SD3CC4EX.js → chunk-NJEJU5XN.js} +2 -2
- package/dist/{chunk-CUAS4ITR.js → chunk-PI6CKUUH.js} +2 -2
- package/dist/{chunk-HUN6COOF.js → chunk-Q7LUTCNO.js} +2 -2
- package/dist/chunk-QHRQ3PUX.js +3 -0
- package/dist/chunk-QHRQ3PUX.js.map +1 -0
- package/dist/chunk-SQTL2CLY.js +76 -0
- package/dist/chunk-SQTL2CLY.js.map +1 -0
- package/dist/{chunk-EGTANAFI.js → chunk-SSFQWEPJ.js} +2 -2
- package/dist/{chunk-MLC5INGD.js → chunk-UK6MICKK.js} +5 -5
- package/dist/{chunk-MLC5INGD.js.map → chunk-UK6MICKK.js.map} +1 -1
- package/dist/{chunk-YJNUVAM4.js → chunk-WHMX2D2Q.js} +2 -2
- package/dist/{chunk-ABCQ6QMJ.js → chunk-YDAFHXDE.js} +2 -2
- package/dist/{chunk-GFRAHUAQ.js → chunk-YLAJOO5X.js} +2 -2
- package/dist/{chunk-B75BLZ2M.js → chunk-YXBQSLFV.js} +2 -2
- package/dist/cli.js +1 -1
- package/dist/{client-OCLSGJYQ.js → client-7JINGNNU.js} +2 -2
- package/dist/{config-FZTWI3VG.js → config-S4ZZHOUZ.js} +2 -2
- package/dist/{config-mutate-X5IKMROK.js → config-mutate-U3KKAXVD.js} +2 -2
- package/dist/config-mutator-KKRVNVVV.js +2 -0
- package/dist/{contributions-KHGGVSR6.js → contributions-6F3MYSRR.js} +2 -2
- package/dist/{doctor-KNCRQ3YO.js → doctor-UDZI2OMU.js} +2 -2
- package/dist/{follow-up-tool-TGKEPA5C.js → follow-up-tool-MP6FOIQE.js} +2 -2
- package/dist/{hub-HQUAH5H6.js → hub-TSUVK7PH.js} +2 -2
- package/dist/index.d.ts +362 -61
- package/dist/index.js +1 -1
- package/dist/{program-CYOAZ43O.js → program-CMWF3BQS.js} +234 -232
- package/dist/program-CMWF3BQS.js.map +1 -0
- package/dist/{promote-VD4OVQGL.js → promote-YVOYLTXI.js} +2 -2
- package/dist/{push-bridge-P7HH25VN.js → push-bridge-CFPGPPO7.js} +2 -2
- package/dist/{query-5CPK6G2H.js → query-ZGXXMJ4M.js} +2 -2
- package/dist/{query-settings-35GDM3HU.js → query-settings-TNOGVKBX.js} +2 -2
- package/dist/{registry-JCHKHCRE.js → registry-R2X4YGUK.js} +2 -2
- package/dist/{request-planner-ZIG4YVXC.js → request-planner-QBNGJMF5.js} +2 -2
- package/dist/{review-candidates-LPZWOR2K.js → review-candidates-WCDKCFXR.js} +2 -2
- package/dist/{routing-BLOYDLU4.js → routing-RRRO2GDA.js} +2 -2
- package/dist/schedule-tool-2XZ3I5IL.js +2 -0
- package/dist/{scheduler-BEJRDQR3.js → scheduler-7O73TDCG.js} +2 -2
- package/dist/{seat-NU3UJC6C.js → seat-CCDGNARC.js} +2 -2
- package/dist/{select-IWAFX4XJ.js → select-VPZ4QDHV.js} +2 -2
- package/dist/{set-enabled-JF75TSFU.js → set-enabled-VYSPZXPC.js} +2 -2
- package/dist/{tool-PQBQJWM5.js → tool-K3PZOFC2.js} +2 -2
- package/dist/{tool-WTOOPTGC.js → tool-VPDXU3IZ.js} +2 -2
- package/dist/{wiki-DXG655NP.js → wiki-QA7BDTOR.js} +2 -2
- package/dist/wiki-enrich-ZIZ5QHQ3.js +2 -0
- package/package.json +1 -1
- package/dist/board-dashboard-642BO7GV.js.map +0 -1
- package/dist/chunk-67DWBJEO.js.map +0 -1
- package/dist/chunk-HSCZSNOX.js +0 -533
- package/dist/chunk-HSCZSNOX.js.map +0 -1
- package/dist/chunk-VKEH4IBA.js +0 -76
- package/dist/chunk-VKEH4IBA.js.map +0 -1
- package/dist/chunk-XOUK5ZIQ.js +0 -36
- package/dist/chunk-XOUK5ZIQ.js.map +0 -1
- package/dist/chunk-YMX3QCV7.js +0 -3
- package/dist/chunk-YMX3QCV7.js.map +0 -1
- package/dist/config-mutator-C5BMIBZO.js +0 -2
- package/dist/program-CYOAZ43O.js.map +0 -1
- package/dist/schedule-tool-7KFO3NPD.js +0 -2
- package/dist/wiki-enrich-5VQCT6BT.js +0 -2
- /package/dist/{App-6T6NVB7O.js.map → App-P4MLXD46.js.map} +0 -0
- /package/dist/{ChatApp-QZBJ4NHC.js.map → ChatApp-DILDQOJL.js.map} +0 -0
- /package/dist/{absorb-eval-EVX7LDXR.js.map → absorb-eval-DBC7EKU5.js.map} +0 -0
- /package/dist/{agent-F2NT6B25.js.map → agent-OZ3KPI6Y.js.map} +0 -0
- /package/dist/{chunk-KA2NV7H4.js.map → chunk-3ZAAUY2N.js.map} +0 -0
- /package/dist/{chunk-743RAKLP.js.map → chunk-4RHM4XTI.js.map} +0 -0
- /package/dist/{chunk-U4YTTWR6.js.map → chunk-6CXJAZZD.js.map} +0 -0
- /package/dist/{chunk-AKCEMKQM.js.map → chunk-ACB6GRSP.js.map} +0 -0
- /package/dist/{chunk-W2RB5XG7.js.map → chunk-AHZBWCXI.js.map} +0 -0
- /package/dist/{chunk-E76HREC7.js.map → chunk-CTXHMJLZ.js.map} +0 -0
- /package/dist/{chunk-IA4J3YLB.js.map → chunk-D65HXPIB.js.map} +0 -0
- /package/dist/{chunk-S3EX64LR.js.map → chunk-EE3ZYIIB.js.map} +0 -0
- /package/dist/{chunk-VRF4YW46.js.map → chunk-FTIJQLBX.js.map} +0 -0
- /package/dist/{chunk-YL26FRKJ.js.map → chunk-GIFUJ3LL.js.map} +0 -0
- /package/dist/{chunk-4CWACHEH.js.map → chunk-HGF73KFH.js.map} +0 -0
- /package/dist/{chunk-JAML6222.js.map → chunk-HOVJVXEP.js.map} +0 -0
- /package/dist/{chunk-GRVTLUQ6.js.map → chunk-IW4AZVJR.js.map} +0 -0
- /package/dist/{chunk-IWWEUXZQ.js.map → chunk-J3IP3UZX.js.map} +0 -0
- /package/dist/{chunk-37I654UJ.js.map → chunk-KYRF2FPT.js.map} +0 -0
- /package/dist/{chunk-SD3CC4EX.js.map → chunk-NJEJU5XN.js.map} +0 -0
- /package/dist/{chunk-CUAS4ITR.js.map → chunk-PI6CKUUH.js.map} +0 -0
- /package/dist/{chunk-HUN6COOF.js.map → chunk-Q7LUTCNO.js.map} +0 -0
- /package/dist/{chunk-EGTANAFI.js.map → chunk-SSFQWEPJ.js.map} +0 -0
- /package/dist/{chunk-YJNUVAM4.js.map → chunk-WHMX2D2Q.js.map} +0 -0
- /package/dist/{chunk-ABCQ6QMJ.js.map → chunk-YDAFHXDE.js.map} +0 -0
- /package/dist/{chunk-GFRAHUAQ.js.map → chunk-YLAJOO5X.js.map} +0 -0
- /package/dist/{chunk-B75BLZ2M.js.map → chunk-YXBQSLFV.js.map} +0 -0
- /package/dist/{client-OCLSGJYQ.js.map → client-7JINGNNU.js.map} +0 -0
- /package/dist/{config-FZTWI3VG.js.map → config-S4ZZHOUZ.js.map} +0 -0
- /package/dist/{config-mutate-X5IKMROK.js.map → config-mutate-U3KKAXVD.js.map} +0 -0
- /package/dist/{config-mutator-C5BMIBZO.js.map → config-mutator-KKRVNVVV.js.map} +0 -0
- /package/dist/{contributions-KHGGVSR6.js.map → contributions-6F3MYSRR.js.map} +0 -0
- /package/dist/{doctor-KNCRQ3YO.js.map → doctor-UDZI2OMU.js.map} +0 -0
- /package/dist/{follow-up-tool-TGKEPA5C.js.map → follow-up-tool-MP6FOIQE.js.map} +0 -0
- /package/dist/{hub-HQUAH5H6.js.map → hub-TSUVK7PH.js.map} +0 -0
- /package/dist/{promote-VD4OVQGL.js.map → promote-YVOYLTXI.js.map} +0 -0
- /package/dist/{push-bridge-P7HH25VN.js.map → push-bridge-CFPGPPO7.js.map} +0 -0
- /package/dist/{query-5CPK6G2H.js.map → query-ZGXXMJ4M.js.map} +0 -0
- /package/dist/{query-settings-35GDM3HU.js.map → query-settings-TNOGVKBX.js.map} +0 -0
- /package/dist/{registry-JCHKHCRE.js.map → registry-R2X4YGUK.js.map} +0 -0
- /package/dist/{request-planner-ZIG4YVXC.js.map → request-planner-QBNGJMF5.js.map} +0 -0
- /package/dist/{review-candidates-LPZWOR2K.js.map → review-candidates-WCDKCFXR.js.map} +0 -0
- /package/dist/{routing-BLOYDLU4.js.map → routing-RRRO2GDA.js.map} +0 -0
- /package/dist/{schedule-tool-7KFO3NPD.js.map → schedule-tool-2XZ3I5IL.js.map} +0 -0
- /package/dist/{scheduler-BEJRDQR3.js.map → scheduler-7O73TDCG.js.map} +0 -0
- /package/dist/{seat-NU3UJC6C.js.map → seat-CCDGNARC.js.map} +0 -0
- /package/dist/{select-IWAFX4XJ.js.map → select-VPZ4QDHV.js.map} +0 -0
- /package/dist/{set-enabled-JF75TSFU.js.map → set-enabled-VYSPZXPC.js.map} +0 -0
- /package/dist/{tool-PQBQJWM5.js.map → tool-K3PZOFC2.js.map} +0 -0
- /package/dist/{tool-WTOOPTGC.js.map → tool-VPDXU3IZ.js.map} +0 -0
- /package/dist/{wiki-DXG655NP.js.map → wiki-QA7BDTOR.js.map} +0 -0
- /package/dist/{wiki-enrich-5VQCT6BT.js.map → wiki-enrich-ZIZ5QHQ3.js.map} +0 -0
|
@@ -0,0 +1,575 @@
|
|
|
1
|
+
import{a as Ie}from"./chunk-GTJIKXDB.js";import{A as nn,H as ln,j as q}from"./chunk-AVAR5KNN.js";import{a as w}from"./chunk-7LN4VKDU.js";import{f as sn,i as on,k as Ae,l as Te,n as B,o as M,q as an}from"./chunk-LQS27HSV.js";import{t as en,u as tn}from"./chunk-EE3ZYIIB.js";import{d as Zt,e as Qt}from"./chunk-AQASR6KR.js";import{b as rn}from"./chunk-QHRQ3PUX.js";import{a as Gt,b as Kt,d as Jt,e as Yt}from"./chunk-MNG2AMKZ.js";import{g as qt,h as Bt,i as Ht,k as zt,l as Xt}from"./chunk-ACB6GRSP.js";import{a as jt,c as Vt}from"./chunk-KP7KDLZ2.js";import{X as Ft,Z as re,_ as _e,aa as Re,b as Ee}from"./chunk-GIFUJ3LL.js";import{q as Wt}from"./chunk-AHZBWCXI.js";import{a as Mt,b as Ut}from"./chunk-IW4AZVJR.js";import{Ga as $e,Oa as Lt,Pa as Ot,T as It,U as Ct,V as Nt,W as Dt,Ya as Pt,n as ne,o as At,p as Tt,q as mi}from"./chunk-SQTL2CLY.js";import{a as xe,h as hi}from"./chunk-BRK6M6LA.js";import{f as ve,n as bi}from"./chunk-UIBUZ2N3.js";import{existsSync as yi,readdirSync as wi,readFileSync as Si,statSync as ki}from"fs";import{resolve as xi}from"path";function cn(t,e,n=Date.now(),r=7){let i=r*864e5,s=new Map;for(let o of e){let a=s.get(o.workflowId);a?a.push(o):s.set(o.workflowId,[o])}return t.map(o=>{let a=s.get(o.id)??[],c=a.filter(f=>n-f.at<=i),l=a.filter(f=>n-f.at>i&&n-f.at<=i*2),u=c.filter(f=>f.status==="failed").length,d=a.filter(f=>f.status==="paused").length,p=a.length?Math.max(...a.map(f=>f.at)):null,g=a.length?l.length>0&&c.length===0?"dormant":c.length>0&&u*2>=c.length?"failing":c.length>0?"active":"quiet":"never";return{id:o.id,name:o.name||o.id,state:g,lastRunAt:p,recent:c.length,prior:l.length,failed:u,paused:d}}).sort((o,a)=>{let c={dormant:0,failing:1,active:2,quiet:3,never:4};return c[o.state]-c[a.state]||(a.lastRunAt??0)-(o.lastRunAt??0)})}var ie=new Map;function fl(t,e=1e3){if(!yi(t))return[];let n=wi(t).filter(s=>s.endsWith(".jsonl")).map(s=>{let o=xi(t,s);return{path:o,mtime:ki(o).mtimeMs}}).sort((s,o)=>o.mtime-s.mtime).slice(0,e),r=new Set(n.map(s=>s.path));for(let s of ie.keys())r.has(s)||ie.delete(s);let i=[];for(let{path:s,mtime:o}of n){let a=ie.get(s);if(a&&a.mtime===o){a.summary&&i.push(a.summary);continue}let c=vi(s,o);ie.set(s,{mtime:o,summary:c}),c&&i.push(c)}return i}function vi(t,e){try{let n=Si(t,"utf-8").split(`
|
|
2
|
+
`).filter(Boolean);if(!n.length)return null;let r=JSON.parse(n[0]),i=r.run?.workflowId;if(!i)return null;let s=r.run?.status??"unknown";for(let o=n.length-1;o>=0;o--){let a=JSON.parse(n[o]),c=a.status??a.run?.status;if(c){s=c;break}}return{workflowId:i,status:s,at:e}}catch{return null}}var $i={failStreak:3,disabledDays:30,missedFires:2,graceMs:15*6e4},S={routines:200,summary:160,filters:6,filterValue:60,text:120},Ei=(t,e,n)=>{try{return Wt(t,e,n)}catch{return null}},un=864e5;function yl(t){let e=t.now??Date.now(),n={...$i,...t.thresholds},r=t.nextFire??Ei,i=[];for(let a of t.crons){let c=t.cronRuns.get(a.id)??[],l=a.timezone||"UTC",u=c.map(g=>({at:Date.parse(g.startedAt),status:g.status})),d=c[0],p={id:`cron:${a.id}`,source:"cron",kind:"schedule",name:E(a.id,S.text),trigger:{schedule:E(a.schedule,S.text),timezone:l},agent:a.agent||null,enabled:a.enabled,state:a.enabled?"enabled":"disabled",nextRunAt:a.enabled?Ce(a.nextRun)??Ce(r(a.schedule,new Date(e),l)):null,lastRun:d?{at:d.startedAt,status:d.status,summary:Ci(d.status==="success"?void 0:d.errorSummary,S.summary)}:null,lastSuccessAt:c.find(g=>g.status==="success")?.startedAt??null,consecutiveFailures:dn(u),sampledRuns:c.length,flags:[],attention:null};p.flags=pn(p,u,a.schedule,l,null,e,n,r),i.push(gn(p))}let s=new Map(cn(t.workflows.map(a=>({id:a.id,name:a.title})),t.workflowRuns,e).map(a=>[a.id,a])),o=new Map;for(let a of t.workflowRuns){let c=o.get(a.workflowId);c?c.push(a):o.set(a.workflowId,[a])}for(let a of t.workflows){let c=a.nodes.find(A=>A.type.startsWith("trigger."));if(!c||c.type!=="trigger.cron"&&c.type!=="trigger.hook")continue;let l=c.config??{},u=a.state||"active",d=u==="active",p=(o.get(a.id)??[]).slice().sort((A,ke)=>ke.at-A.at),g=p.map(A=>({at:A.at,status:Ai(A.status)})),f=g[0],m=c.type==="trigger.cron",b=typeof l.spec=="string"?l.spec:"",C=typeof l.timezone=="string"?l.timezone:"UTC",P={id:`workflow:${a.id}`,source:"workflow",kind:m?"schedule":"event",name:E(a.title||a.id,S.text),trigger:m?{schedule:E(b,S.text),timezone:C}:{event:E(typeof l.event=="string"?l.event:"",S.text),filters:Ii(l.filter,a)},agent:Ti(a),enabled:d,state:u,nextRunAt:m&&d&&b?Ce(r(b,new Date(e),C)):null,lastRun:f?{at:new Date(f.at).toISOString(),status:f.status}:null,lastSuccessAt:(()=>{let A=g.find(ke=>ke.status==="success");return A?new Date(A.at).toISOString():null})(),consecutiveFailures:dn(g),sampledRuns:p.length,flags:[],attention:null},te=Date.parse(a.updated||a.created||"");P.flags=m?pn(P,g,b,C,Number.isFinite(te)?te:null,e,n,r):_i(P,s.get(a.id)?.state==="dormant",Number.isFinite(te)?te:null,e,n),i.push(gn(P))}return i.sort(Ri).slice(0,S.routines)}function dn(t){let e=0;for(let n of t)if(!(n.status==="running"||n.status==="paused"||n.status==="unknown"))if(n.status==="failed"||n.status==="timeout")e++;else break;return e}function pn(t,e,n,r,i,s,o,a){let c=[],l=e.length?e[0].at:null;if(!t.enabled){let p=mn(l,i,s,o);return p&&c.push(p),c}if(t.consecutiveFailures>=o.failStreak&&c.push({reason:"failing",detail:`${t.consecutiveFailures} failed attempts in a row`}),l===null)return c.push({reason:"never-ran",detail:"No persisted attempt"}),c;let u=new Date(l),d=null;for(let p=0;p<o.missedFires&&(d=a(n,u,r),!!d);p++)u=d;return d&&s>d.getTime()+o.graceMs&&c.push({reason:"overdue",detail:`No attempt since ${new Date(l).toISOString()}; ${o.missedFires} scheduled fires missed`}),c}function _i(t,e,n,r,i){let s=[],o=t.lastRun?Date.parse(t.lastRun.at):null;if(!t.enabled){let a=mn(o,n,r,i);return a&&s.push(a),s}return t.consecutiveFailures>=i.failStreak&&s.push({reason:"failing",detail:`${t.consecutiveFailures} failed runs in a row`}),o===null?s.push({reason:"never-ran",detail:"No run recorded for this trigger"}):e&&s.push({reason:"dormant",detail:"Fired in the prior week, silent in the last one"}),s}function mn(t,e,n,r){let i=Math.max(t??0,e??0);return i&&n-i<r.disabledDays*un?null:{reason:"disabled-long",detail:i?`Disabled; no activity for ${Math.floor((n-i)/un)} days`:"Disabled and never ran"}}function gn(t){let e=new Set(t.flags.map(n=>n.reason));return t.attention=e.has("failing")?"critical":e.has("overdue")||e.has("dormant")||e.has("never-ran")?"warning":e.has("disabled-long")?"info":null,t}var fn={critical:0,warning:1,info:2};function Ri(t,e){let n=t.attention?fn[t.attention]:3,r=e.attention?fn[e.attention]:3;return n-r||Number(e.enabled)-Number(t.enabled)||t.name.localeCompare(e.name)}function Ai(t){switch(t){case"completed":case"ok":return"success";case"failed":return"failed";case"timeout":return"timeout";case"canceled":return"canceled";case"running":return"running";case"paused":return"paused";default:return"unknown"}}function Ti(t){if(t.ownerAgent)return E(t.ownerAgent,S.text);let e=t.nodes.find(n=>n.type==="agent"&&typeof n.config?.agentId=="string"&&n.config.agentId);return e?E(String(e.config.agentId),S.text):null}function Ii(t,e){let n=[],r=e?.project;if(r&&n.push(E(`project=${r}`,S.filterValue)),t&&typeof t=="object"&&!Array.isArray(t))for(let[i,s]of Object.entries(t)){let o=Array.isArray(s)?s.map(String):s==null?[]:[String(s)];o.length&&n.push(E(`${i}=${o.join(",")}`,S.filterValue))}return n.slice(0,S.filters)}function Ce(t){if(!t)return null;let e=t instanceof Date?t:new Date(t);return Number.isFinite(e.getTime())?e.toISOString():null}function E(t,e){return t.length<=e?t:t.slice(0,e-3)+"..."}function Ci(t,e){return t===void 0?void 0:E(t,e)}import{mkdirSync as Ni,readFileSync as Di,renameSync as Li,writeFileSync as Oi}from"fs";import{resolve as Pi}from"path";function hn(t){return Pi(Te(t),"_state.json")}function x(t){try{let e=JSON.parse(Di(hn(t),"utf-8"));return{snoozed:e&&typeof e.snoozed=="object"&&e.snoozed?e.snoozed:{},...typeof e?.lastDigest=="string"?{lastDigest:e.lastDigest}:{},...e?.popped&&typeof e.popped=="object"?{popped:e.popped}:{},...typeof e?.passAt=="string"?{passAt:e.passAt}:{},...Array.isArray(e?.wanted)?{wanted:e.wanted.filter(n=>typeof n=="string")}:{}}}catch{return{snoozed:{}}}}function H(t,e){Ni(Te(t),{recursive:!0});let n=hn(t),r=`${n}.${process.pid}.tmp`;Oi(r,JSON.stringify(e,null,2)+`
|
|
3
|
+
`),Li(r,n)}function bn(t,e,n,r=Date.now()){let i=x(t),s={};for(let[o,a]of Object.entries(i.snoozed))Date.parse(a)>r&&(s[o]=a);s[e]=n.toISOString(),H(t,{...i,snoozed:s})}function vl(t,e){H(t,{...x(t),lastDigest:e})}function Ne(t,e,n,r=Date.now()){let i=x(t),s=new Set(n),o={};for(let[c,l]of Object.entries(i.popped??{}))s.has(c)&&(o[c]=l);o[e]=new Date(r).toISOString();let a=(i.wanted??[]).filter(c=>c!==e&&s.has(c));H(t,{...i,popped:o,wanted:a})}function yn(t,e){let n=x(t);H(t,{...n,wanted:[...(n.wanted??[]).filter(r=>r!==e),e]})}function $l(t,e=Date.now()){H(t,{...x(t),popped:{},passAt:new Date(e).toISOString()})}import{existsSync as z,readdirSync as Zi,readFileSync as Qi}from"fs";import{resolve as _}from"path";import{randomBytes as kn}from"crypto";import{existsSync as Ui,mkdirSync as Wi,readdirSync as Fi,readFileSync as ji,renameSync as xn,unlinkSync as Vi,writeFileSync as qi}from"fs";import{resolve as vn}from"path";function wn(t){return[...t.account?["--account",t.account]:[],...t.store?["--store",t.store]:[]]}var Mi=t=>t.trim().split(`
|
|
4
|
+
`).pop()?.slice(0,300)||"no output";async function Sn(t,e,n,r=Ie){let i=await r(t.bin,[...wn(t),"send","text","--to",e,"--message",n,"--no-preview"],{timeoutMs:6e4});return i.code===0?{ok:!0}:{ok:!1,error:`wacli send failed: ${Mi(i.stderr||i.stdout)}`}}async function Rl(t,e,n,r,i=Ie){return(await i(t.bin,[...wn(t),"--read-only","media","download","--chat",e,"--id",n,"--output",r],{timeoutMs:12e4})).code===0}var Bi=2e3,Hi=t=>Sn(t.wacli,t.to,t.text);function De(t){return vn(t,".agentx","whatsapp-triage","drafts")}var zi=t=>/^[a-z0-9-]{8,64}$/.test(t),se=(t,e)=>vn(De(t),`${e}.json`);function $n(t,e){Wi(De(t),{recursive:!0});let n=se(t,e.id),r=`${n}.${process.pid}.tmp`;qi(r,JSON.stringify(e,null,2)+`
|
|
5
|
+
`,{mode:384}),xn(r,n)}function Dl(t,e,n=Date.now()){let r=e.text.trim().slice(0,Bi);if(!r)throw new Error("empty draft");let i={...e,text:r,id:`${n.toString(36)}-${kn(4).toString("hex")}`,status:"pending",created_at:new Date(n).toISOString()};return $n(t,i),i}function oe(t,e){if(!zi(e))return null;try{return JSON.parse(ji(se(t,e),"utf-8"))}catch{return null}}function En(t,e){let n=De(t);return Ui(n)?Fi(n).filter(r=>r.endsWith(".json")).map(r=>oe(t,r.slice(0,-5))).filter(r=>!!r&&(!e||r.status===e)).sort((r,i)=>r.created_at.localeCompare(i.created_at)):[]}async function _n(t,e,n,r={}){let i=oe(t,e);if(!i||i.status!=="pending")return{ok:!1,error:`no reply draft "${e}" is waiting`};let s=`${se(t,e)}.${process.pid}.${kn(3).toString("hex")}.claim`;try{xn(se(t,e),s)}catch{return{ok:!1,error:`reply draft "${e}" is already being decided`}}let o={...i,decided_by:r.by??"operator",decided_at:new Date(r.now??Date.now()).toISOString()};if(n==="no")o.status="rejected";else{let a=await(r.send??Hi)(i).catch(c=>({ok:!1,error:String(c?.message??c)}));a.ok?o.status="sent":(o.status="failed",o.error=a.error)}$n(t,o);try{Vi(s)}catch{}return o.status==="failed"?{ok:!1,error:o.error??"send failed"}:{ok:!0,draft:o}}bi();Dt();Dt();var Pl=["done","skipped"],Xi=1e3;function Gi(t){t.exec(`
|
|
6
|
+
CREATE TABLE IF NOT EXISTS request_plans (
|
|
7
|
+
request_id TEXT PRIMARY KEY,
|
|
8
|
+
created_by TEXT NOT NULL,
|
|
9
|
+
created_at INTEGER NOT NULL,
|
|
10
|
+
state TEXT NOT NULL,
|
|
11
|
+
finished_at INTEGER,
|
|
12
|
+
notified_at INTEGER,
|
|
13
|
+
FOREIGN KEY (request_id) REFERENCES requests(id) ON DELETE CASCADE
|
|
14
|
+
);
|
|
15
|
+
CREATE INDEX IF NOT EXISTS idx_request_plans_state ON request_plans(state);
|
|
16
|
+
CREATE TABLE IF NOT EXISTS request_plan_steps (
|
|
17
|
+
request_id TEXT NOT NULL,
|
|
18
|
+
idx INTEGER NOT NULL,
|
|
19
|
+
name TEXT NOT NULL,
|
|
20
|
+
kind TEXT NOT NULL,
|
|
21
|
+
agent_id TEXT NOT NULL,
|
|
22
|
+
done_when TEXT NOT NULL,
|
|
23
|
+
state TEXT NOT NULL,
|
|
24
|
+
needs_approval INTEGER NOT NULL DEFAULT 0,
|
|
25
|
+
approval TEXT NOT NULL DEFAULT 'none',
|
|
26
|
+
card_id TEXT,
|
|
27
|
+
message TEXT,
|
|
28
|
+
to_channel TEXT,
|
|
29
|
+
to_chat TEXT,
|
|
30
|
+
to_account TEXT,
|
|
31
|
+
check_url TEXT,
|
|
32
|
+
check_contains TEXT,
|
|
33
|
+
stall_minutes REAL,
|
|
34
|
+
evidence TEXT,
|
|
35
|
+
note TEXT,
|
|
36
|
+
dispatched_at INTEGER,
|
|
37
|
+
updated_at INTEGER NOT NULL,
|
|
38
|
+
nudges INTEGER NOT NULL DEFAULT 0,
|
|
39
|
+
last_nudge_at INTEGER,
|
|
40
|
+
notified_at INTEGER,
|
|
41
|
+
PRIMARY KEY (request_id, idx),
|
|
42
|
+
FOREIGN KEY (request_id) REFERENCES request_plans(request_id) ON DELETE CASCADE
|
|
43
|
+
);
|
|
44
|
+
CREATE TABLE IF NOT EXISTS request_plan_events (
|
|
45
|
+
request_id TEXT NOT NULL,
|
|
46
|
+
step INTEGER,
|
|
47
|
+
at INTEGER NOT NULL,
|
|
48
|
+
kind TEXT NOT NULL,
|
|
49
|
+
detail TEXT NOT NULL,
|
|
50
|
+
FOREIGN KEY (request_id) REFERENCES request_plans(request_id) ON DELETE CASCADE
|
|
51
|
+
);
|
|
52
|
+
CREATE INDEX IF NOT EXISTS idx_request_plan_events ON request_plan_events(request_id, at);
|
|
53
|
+
`)}function An(t){return!!t.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'request_plans'").get()}function Le(t){return{requestId:t.request_id,createdBy:t.created_by,createdAt:t.created_at,state:t.state,finishedAt:t.finished_at??null,notifiedAt:t.notified_at??null}}function Rn(t){return{requestId:t.request_id,idx:t.idx,name:t.name,kind:t.kind,agentId:t.agent_id,doneWhen:t.done_when,state:t.state,needsApproval:!!t.needs_approval,approval:t.approval,cardId:t.card_id??null,message:t.message??null,toChannel:t.to_channel??null,toChat:t.to_chat??null,toAccount:t.to_account??null,checkUrl:t.check_url??null,checkContains:t.check_contains??null,stallMinutes:t.stall_minutes??null,evidence:t.evidence??null,note:t.note??null,dispatchedAt:t.dispatched_at??null,updatedAt:t.updated_at,nudges:t.nudges,lastNudgeAt:t.last_nudge_at??null,notifiedAt:t.notified_at??null}}var Ki={state:"state",approval:"approval",cardId:"card_id",message:"message",evidence:"evidence",note:"note",dispatchedAt:"dispatched_at",updatedAt:"updated_at",nudges:"nudges",lastNudgeAt:"last_nudge_at",notifiedAt:"notified_at"},ae=class{constructor(e){this.db=e;Gi(e)}create(e,n,r,i){let s=this.db.prepare("INSERT INTO request_plans (request_id, created_by, created_at, state) VALUES (?, ?, ?, 'active')"),o=this.db.prepare(`INSERT INTO request_plan_steps (request_id, idx, name, kind, agent_id, done_when, state, needs_approval, message,
|
|
54
|
+
to_channel, to_chat, to_account, check_url, check_contains, stall_minutes, dispatched_at, updated_at)
|
|
55
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);this.db.transaction(()=>{s.run(e,n,i),r.forEach((a,c)=>{let l=c===0,u=l&&a.agentId===n&&a.kind!=="message";o.run(e,c+1,a.name,a.kind,a.agentId,a.doneWhen,l?"active":"pending",a.needsApproval?1:0,a.message??null,a.toChannel??null,a.toChat??null,a.toAccount??null,a.checkUrl??null,a.checkContains??null,a.stallMinutes??null,u?i:null,i)}),this.event(e,null,"created",`${r.length} steps, written by ${n}`,i)})()}get(e){let n=this.db.prepare("SELECT * FROM request_plans WHERE request_id = ?").get(e);return n?Le(n):null}steps(e){return this.db.prepare("SELECT * FROM request_plan_steps WHERE request_id = ? ORDER BY idx").all(e).map(Rn)}step(e,n){let r=this.db.prepare("SELECT * FROM request_plan_steps WHERE request_id = ? AND idx = ?").get(e,n);return r?Rn(r):null}listActive(){return this.db.prepare("SELECT * FROM request_plans WHERE state = 'active' ORDER BY created_at, rowid").all().map(Le)}updateStep(e,n,r){let i=[],s=[];for(let[o,a]of Object.entries(r)){let c=Ki[o];!c||a===void 0||(i.push(`${c} = ?`),s.push(typeof a=="boolean"?a?1:0:a))}i.length&&this.db.prepare(`UPDATE request_plan_steps SET ${i.join(", ")} WHERE request_id = ? AND idx = ?`).run(...s,e,n)}finish(e,n,r){return this.db.prepare("UPDATE request_plans SET state = ?, finished_at = ? WHERE request_id = ? AND state = 'active'").run(n,r,e).changes>0}markNotified(e,n){this.db.prepare("UPDATE request_plans SET notified_at = ? WHERE request_id = ?").run(n,e)}awaitingSummary(){return this.db.prepare("SELECT * FROM request_plans WHERE state = 'done' AND notified_at IS NULL ORDER BY finished_at").all().map(Le)}event(e,n,r,i,s){this.db.prepare("INSERT INTO request_plan_events (request_id, step, at, kind, detail) VALUES (?, ?, ?, ?, ?)").run(e,n,s,r,i.replace(/\s+/g," ").trim().slice(0,Xi))}events(e,n=200){return this.db.prepare("SELECT request_id, step, at, kind, detail FROM request_plan_events WHERE request_id = ? ORDER BY at, rowid LIMIT ?").all(e,n).map(r=>({requestId:r.request_id,step:r.step??null,at:r.at,kind:r.kind,detail:r.detail}))}},Ml=It;import{existsSync as Ji,readFileSync as Yi}from"fs";Pt();hi();function Tn(t){let e=Mt(t),n={};if(Ji(e))try{n=Lt(JSON.parse(Yi(e,"utf-8")))}catch{n={}}let r=$e.safeParse(n?.requests);return r.success?r.data:$e.parse(void 0)}async function ql(t,e={}){let n=(t.channels??[]).filter(r=>!xe.has(r.toLowerCase().split("@")[0]));return n.length?{success:!1,error:`unknown channel ${n.map(r=>`"${r}"`).join(", ")}: use ${[...xe].join(", ")}`}:Ut(r=>{let i=r.requests??={};for(let s of["enabled","channels","from","staleAfterHours","retentionDays","settleHandedBack"])t[s]!==void 0&&(i[s]=t[s]);if(t.plans){let s=i.plans??={};for(let o of["enabled","stallMinutes","maxNudges","approveKinds","disabledAgents","requireForWork","requireOverMinutes"])t.plans[o]!==void 0&&(s[o]=t.plans[o])}if(t.headlines?.enabled!==void 0&&((i.headlines??={}).enabled=t.headlines.enabled),t.checkers){let s=i.checkers??={};for(let o of["enabled","checkEveryMinutes"])t.checkers[o]!==void 0&&(s[o]=t.checkers[o])}},{configPath:e.configPath,reload:e.reload})}var Nn=["card","schedule","memory","wiki","whatsapp","request"],es=280;function U(t,e=es){let n=t.replace(/\s+/g," ").trim();return n.length>e?`${n.slice(0,e-1)}\u2026`:n}function Oe(t){if(t.configPath)return _(t.configPath);let e=[_(t.root,"agentx.json"),_(t.root,".agentx","config.json")];return e.find(n=>z(n))??e[0]}function Pe(t){return t.wikiDir?_(t.wikiDir):_(t.root,".agentx","wiki")}function ts(t){return M(t.root,"pending").map(ns)}function ns(t){return{key:`card:${t.id}`,kind:"card",title:t.title,ask:t.ask,yes:`tell ${t.raised_by} yes`,no:`tell ${t.raised_by} no`,recommend:t.recommend,if_silent:t.if_silent,expires:t.expires,...t.source?{source:t.source}:{},raised_by:t.raised_by,...t.node?{node:t.node}:{},created_at:t.created_at,...t.choices?{choices:t.choices,more:`agentx approvals popup card:${t.id}`}:{},...t.draft?{draft:t.draft}:{},...t.context||t.draft?{detail:U([t.context,t.draft?`Suggested message: ${t.draft}`:""].filter(Boolean).join(" \xB7 "))}:{}}}function rs(t){let e=Oe(t);if(!z(e))return[];let n;try{n=JSON.parse(Qi(e,"utf-8"))}catch{return[]}let r=new Date(t.now??Date.now()),i=[];for(let[s,o]of Object.entries(n?.crons??{})){let a=o?.approval;if(!a||a.action!=="create"&&a.action!=="delete")continue;let c=o.timezone||"UTC",l=o.schedule?qt(o.schedule):"",u=o.schedule?`${l.charAt(0).toLowerCase()}${l.slice(1)} (${o.schedule})`:"no schedule",d=String(o.prompt??o.command??""),p=a.action==="create";i.push({key:`schedule:${s}`,kind:"schedule",title:p?`New schedule "${s}" for ${o.agent??"an agent"}`:`Delete schedule "${s}"`,ask:p?`Let ${o.agent??"the agent"} run this ${u}?`:`Remove the schedule "${s}" (${u})?`,yes:p?"enable it":"remove it",no:p?"drop the request":"keep the schedule",raised_by:String(a.requestedBy??"unknown"),created_at:String(a.requestedAt??""),detail:U(`${d}${p&&o.schedule?` \xB7 first run ${Ht(Bt(o.schedule,c,r),c)}`:""}`),more:"agentx schedule list"})}return i}function Dn(t){let e=_(t,".agentx","memory");return z(e)?Zi(e).filter(n=>n.endsWith(".jsonl")).map(n=>n.slice(0,-6)).sort():[]}function is(t){let e=Dn(t.root);if(e.length===0)return[];let n=new Ee(t.root),r=[];for(let i of e)for(let s of n.held(i))r.push({key:`memory:${i}/${s.id}`,kind:"memory",title:`Fact ${i} learned from ${s.source.channel}`,ask:`Let ${i} use this fact in its prompts?`,yes:"use it",no:"keep it out for good",raised_by:i,created_at:s.createdAt,detail:U(s.content),more:`agentx memory facts held --agent ${i}`});return r}function ss(t){let e=t.sources.filter(n=>n.failure);return e.length?e.map(n=>`Recurring failure: ${n.agentId}'s ${n.failure.tool} fails with "${n.failure.errorClass}" in ${n.occurrences??1} sessions (${n.failure.runs} runs).`).join(" "):`${t.sources.length} source(s)${t.occurrences>1?`, seen in ${t.occurrences} sessions`:""}.`}function os(t){return Qt(Pe(t),"pending").map(e=>{let n=e.evidence;return{key:`wiki:${e.id}`,kind:"wiki",title:e.article.title,ask:e.replaces?`Update "${e.article.path}" in the shared wiki?`:`Add "${e.article.path}" to the shared wiki?`,yes:"write the article",no:"decline it",raised_by:n.agents.join(", ")||"wiki promote",created_at:e.createdAt,detail:U(`${ss(n)} ${e.article.content}`),more:`agentx wiki proposals show ${e.id}`}})}function as(t){return En(t.root,"pending").map(e=>({key:`whatsapp:${e.id}`,kind:"whatsapp",title:`WhatsApp reply to ${e.chat_name||e.to}`,ask:`Send this to ${e.chat_name||e.to}? "${e.text}"`,yes:"send it on WhatsApp",no:"drop the draft",raised_by:e.agent,created_at:e.created_at,detail:U(`${e.triage}: ${e.summary}`),more:"agentx whatsapp triage log"}))}function X(t){if(t.requests)return t.requests;if(_(t.root)!==process.cwd()||!z(_(t.root,".agentx","db.sqlite")))return null;let e=ve({quiet:!0});if(!e)return null;let n=In.get(e);return!n&&!Ct(e)?null:(n||In.set(e,n=new Nt(e)),n)}var In=new WeakMap;function sc(t){if(t.plans)return t.plans;if(t.requests||_(t.root)!==process.cwd()||!z(_(t.root,".agentx","db.sqlite")))return null;let e=ve({quiet:!0});if(!e||!An(e))return null;let n=Cn.get(e);return n||Cn.set(e,n=new ae(e)),n}var Cn=new WeakMap;function ls(t){let e=X(t);return e?e.listByState("needs_attention").map(n=>({key:`request:${n.id}`,kind:"request",title:q(n,120).headline,ask:`Not finished. Ask ${n.agentId} to pick it up again?`,yes:`hand it back to ${n.agentId}`,no:"drop the request",raised_by:n.agentId,created_at:new Date(n.createdAt).toISOString(),detail:U(`${n.attentionReason??"It needs attention"}. You said (${n.channel}): ${n.text}`),more:`agentx requests show ${n.id}`})):[]}var Ln={card:ts,schedule:rs,memory:is,wiki:os,whatsapp:as,request:ls};function cs(t,e){let n=t.expires?Date.parse(t.expires):1/0,r=e.expires?Date.parse(e.expires):1/0;return n!==r?n-r:(Date.parse(t.created_at)||0)-(Date.parse(e.created_at)||0)}function oc(t,e={}){let n=t.now??Date.now(),{snoozed:r}=x(t.root),i=[],s=[],o=0;for(let a of e.kinds??Nn){let c;try{c=Ln[a](t)}catch(l){s.push({kind:a,error:String(l?.message??l).slice(0,200)});continue}for(let l of c){let u=r[l.key];if(u&&Date.parse(u)>n){if(!e.includeSnoozed){o++;continue}l.snoozed_until=u}i.push(l)}}return i.sort(cs),{items:i,snoozed:o,errors:s}}function us(t){let e=t.indexOf(":");if(e<=0)return null;let n=t.slice(0,e),r=t.slice(e+1);return!Nn.includes(n)||!r?null:{kind:n,ref:r}}function ds(t,e,n){switch(e){case"card":return B(t.root,n)?.status==="pending";case"wiki":return Zt(Pe(t),n)?.status==="pending";case"whatsapp":return oe(t.root,n)?.status==="pending";default:return Ln[e](t).some(r=>r.key===`${e}:${n}`)}}async function On(t,e,n,r={}){let i=us(e);if(!i)return{ok:!1,error:`unknown item "${e}": use a key from \`agentx approvals list\``};let{kind:s,ref:o}=i,a=t.now??Date.now(),c=r.by??"operator";if(n==="later"){if(!ds(t,s,o))return{ok:!1,error:`nothing waiting for "${e}"`};let u=r.laterHours&&r.laterHours>0?r.laterHours:24,d=new Date(a+u*36e5);return bn(t.root,e,d,a),{ok:!0,message:`${e} put off until ${d.toISOString()}`}}let l=n==="yes";switch(s){case"card":{let u=an(t.root,o,l?"yes":"no",{by:c,note:r.note,now:a,choice:r.choice,text:r.text});return u.ok?{ok:!0,message:`${e}: ${u.card.raised_by} will be told ${l?"yes":"no"}${u.card.choice?` (${u.card.choice}${u.card.choice_suggested?", the suggested option":""})`:""}`}:u}case"schedule":{let u=await(l?zt:Xt)(o,{configPath:Oe(t),reload:t.reload});return u.success?{ok:!0,message:u.message}:{ok:!1,error:u.message}}case"memory":{let u=o.indexOf("/");if(u<=0)return{ok:!1,error:"memory keys look like memory:<agent>/<fact-id>"};let d=o.slice(0,u),p=o.slice(u+1);if(!Dn(t.root).includes(d))return{ok:!1,error:`no memory for agent "${d}"`};let g=new Ee(t.root);return g.held(d).some(f=>f.id===p)?(g.review(d,p,l?"approved":"rejected",c),{ok:!0,message:`${e} ${l?"approved: the agent may use it":"rejected: it stays out of prompts"}`}):{ok:!1,error:`fact "${p}" isn't waiting for review`}}case"wiki":{let u=Pe(t),d=l?en(u,o,{by:c,force:r.force,now:a}):tn(u,o,{by:c,reason:r.note,now:a});return d.ok?{ok:!0,message:l?`${d.proposal.article.path} written to the shared wiki`:`${e} rejected`}:d}case"whatsapp":{let u=await _n(t.root,o,l?"yes":"no",{by:c,now:a,send:t.sendWhatsApp});return u.ok?{ok:!0,message:l?`${e}: sent to ${u.draft.chat_name||u.draft.to}`:`${e}: dropped, nothing sent`}:u}case"request":{let u=X(t),d=u?.get(o);return!u||!d||d.state!=="needs_attention"?{ok:!1,error:`nothing waiting for "${e}"`}:l?Tn(Oe(t)).enabled?(u.requestPickup(o,a),{ok:!0,message:`${e}: ${d.agentId} will be asked to pick it up again`}):{ok:!1,error:"Requests are off, so nothing would hand it back. Turn them on (agentx requests settings --enabled on), or answer no to drop it."}:(u.close(o,"dropped",r.note?.trim()||`dropped by ${c}`,a),{ok:!0,message:`${e}: dropped`})}}}var Pn=new Set(["127.0.0.1","::1","::ffff:127.0.0.1"]);function lc(t){return t==="/api/memory"||t.startsWith("/api/memory/")||t==="/approvals"||t.startsWith("/approvals/")||t==="/screen"||t==="/native/notes"||t==="/requests"||t.startsWith("/requests/")||t==="/watchers"||t.startsWith("/watchers/")||t==="/follow-up"||t.startsWith("/follow-up/")||t==="/events/recent"||/^\/agents\/[^/]+\/events$/.test(t)||t==="/a2a/delegations"||t.startsWith("/a2a/delegations/")||t==="/traces"||t.startsWith("/traces/")||t==="/voice/history"||t.startsWith("/voice/history/")||t==="/voice/transcribe"||t==="/voice/speak"||t==="/app-files"||t==="/calls"||t.startsWith("/calls/")||t==="/webrtc/camera"||t.startsWith("/webrtc/camera/")||t==="/mesh/guests"||t.startsWith("/mesh/guests/")||t==="/wiki/agents"||t==="/wiki/entries"||t==="/wiki/articles"||t==="/wiki/article"||t==="/wiki/notes"||t==="/api/wiki/curate"||t==="/api/wiki/curate/restore"||t==="/api/signals/stopped"||t.startsWith("/api/signals/stopped/")||t==="/api/review-gate/reviews"||t==="/owner/told"||t==="/owner/presence"}function cc(t){return t==="/reload"||t==="/api/processes/kill"||t==="/send"||t==="/send/agent"||t==="/send/contact"||t==="/mesh/announce"||t==="/voice/settings"||t==="/voice/preview"||/^\/api\/tasks\/[^/]+\/(cancel|followup)$/.test(t)||/^\/mesh\/guests\/[^/]+\/(pause|resume|end|update)$/.test(t)||/^\/crons\/[^/]+\/enabled$/.test(t)||/^\/channels\/[^/]+\/switch$/.test(t)||/^\/workflow-runs\/[^/]+\/cancel$/.test(t)||t==="/api/signals/stop"||t==="/api/signals/resume"||t==="/api/signals/drop"||t==="/api/review-gate/replay"}function uc(t){return Pn.has(t)}function dc(t){if(t.foreignBrowser)return{allowed:!1,reason:"foreign-browser-origin"};if(t.enforcementDisabled)return{allowed:!0,reason:"disabled"};if(Pn.has(t.remoteAddress))return{allowed:!0,reason:"loopback"};if(t.acceptedTokens.size===0)return{allowed:!0,reason:"no-tokens-configured"};let e=t.authorizationHeader||"",n=e.startsWith("Bearer ")?e.slice(7).trim():"";return n&&t.acceptedTokens.has(n)?{allowed:!0,reason:"token"}:{allowed:!1,reason:"missing-or-invalid-token"}}function pc(t,e=process.env){let n=new Set;e.MESH_TOKEN&&n.add(e.MESH_TOKEN);for(let r of t.mesh?.peers||[])r.token&&n.add(r.token);return n}function gc(t,e,n=process.env){let r=s=>s.replace(/\/+$/,"");return(e===r(t.daemonUrl)?t.token:t.daemons.find(s=>r(s.url)===e)?.token)||n.MESH_TOKEN||void 0}function le(t){return(Array.isArray(t)?t[0]:t)?.trim()??""}function ps(t){try{return new URL(t).host.toLowerCase()}catch{return null}}function gs(t=process.env){return new Set((t.AGENTX_ALLOWED_ORIGINS||"").split(",").map(e=>e.trim().replace(/\/+$/,"").toLowerCase()).filter(Boolean))}function mc(t,e=gs()){let n=le(t.origin);if(n){if(e.has(n.replace(/\/+$/,"").toLowerCase()))return"same-origin";let i=ps(n),s=[le(t.host),le(t["x-forwarded-host"])].filter(Boolean).map(o=>o.toLowerCase());return i&&s.includes(i)?"same-origin":"foreign"}let r=le(t["sec-fetch-site"]).toLowerCase();return r==="cross-site"||r==="same-site"?"foreign":"none"}function hc(t){let e=(t||"GET").toUpperCase();return e!=="GET"&&e!=="HEAD"}import{existsSync as ar,mkdirSync as Vs,readFileSync as qs,renameSync as Bs,writeFileSync as Hs}from"fs";import{homedir as zs}from"os";import{dirname as Xs,join as Gs}from"path";import{execFile as $s,execFileSync as Es}from"child_process";import{execFileSync as fs}from"child_process";import{existsSync as Un,mkdirSync as ms,readdirSync as hs,readFileSync as Wn,renameSync as bs,writeFileSync as ys}from"fs";import{homedir as Fn}from"os";import{join as Ue}from"path";var v="siri:",jn="com.apple.ttsbundle.gryphon-neural_",Vn=t=>!!t&&t.startsWith(jn),ws=["/usr/bin/say","/usr/bin/defaults","/usr/bin/plutil"],Ss=()=>{try{return fs("/usr/bin/defaults",["read","com.apple.Accessibility"],{stdio:"ignore",timeout:5e3}),!0}catch{return!1}},Me;function We(t={}){let e=t.platform!==void 0||t.exists!==void 0||t.prefDomain!==void 0;if(!e&&Me!==void 0)return Me;let{platform:n=process.platform,exists:r=Un,prefDomain:i=Ss}=t,s=n==="darwin"&&ws.every(o=>r(o))&&i();return e||(Me=s),s}var qn=["/System/Library/AssetsV2/com_apple_MobileAsset_Trial_Siri_SiriTextToSpeech/purpose_auto","/System/Library/AssetsV2/com_apple_MobileAsset_UAF_Siri_TextToSpeech/purpose_auto"],Bn={aaron:"male",damon:"male",daniel:"male",pierre:"male",samer:"male",arthur:"male",gordon:"male",martin:"male",nora:"female",simone:"female",marie:"female",soha:"female",martha:"female",catherine:"female",helena:"female",nicky:"female"},Fe=t=>Bn[t.toLowerCase()]??null;function ce(t){let e=/^com\.apple\.ttsbundle\.gryphon-neural_([a-z]+)_([a-z]{2}-[A-Z]{2})_premium$/.exec(t);return e?{name:e[1],locale:e[2]}:null}var ks=/com\.apple\.siri\.tts\.voice\.([a-z]{2})_([A-Z]{2})\.([a-z]+)\.neural\.premium/g;function xs(t){let e=new Map;for(let n of t)for(let[,r,i,s]of n.matchAll(ks)){let o=`${r}-${i}`,a=`${jn}${s}_${o}_premium`;e.has(a)||e.set(a,{id:a,name:s[0].toUpperCase()+s.slice(1),locale:o,quality:"premium",gender:Bn[s]??null,siri:!0})}return[...e.values()]}function ue(t=qn,e={}){if(!We(e))return[];let n=[];for(let r of t){let i;try{i=hs(r)}catch{continue}for(let s of i)try{n.push(Wn(Ue(r,s,"Info.plist"),"latin1"))}catch{}}return xs(n)}var Mn=`#!/bin/sh
|
|
56
|
+
# Written by agentx; shared by the daemon and AgentX Voice. Do not edit.
|
|
57
|
+
# siri-say.sh [voice] < text speak one line. A Siri voice id switches
|
|
58
|
+
# the OS default for it; any other id is
|
|
59
|
+
# passed to say -v.
|
|
60
|
+
# siri-say.sh --stop silence the line speaking, drop the queue.
|
|
61
|
+
# siri-say.sh --restore put back a voice a killed line left switched.
|
|
62
|
+
# AGENTX_SAY_RATE=<words a minute> sets the speed; unset, say's own.
|
|
63
|
+
D=com.apple.Accessibility K=SpokenContentDefaultVoiceSelectionsByLanguage
|
|
64
|
+
dir="$HOME/.agentx/voice" lock="$HOME/.agentx/voice/siri.lock" saved="$HOME/.agentx/voice/siri-saved.plist"
|
|
65
|
+
stops="$dir/stop"
|
|
66
|
+
read_s=\${AGENTX_SAY_READ_S:-5} stale_s=\${AGENTX_SAY_STALE_S:-30} max_s=\${AGENTX_SAY_MAX_S:-300}
|
|
67
|
+
assets=\${AGENTX_SIRI_ASSET_DIRS:-"${qn.join(" ")}"}
|
|
68
|
+
if [ "$1" = --stop ]; then
|
|
69
|
+
mkdir -p "$dir" || exit 1
|
|
70
|
+
# Queued lines see this change and drop. The owner's trap stops say,
|
|
71
|
+
# restores the voice, and frees the lock.
|
|
72
|
+
echo "$$ $(date +%s)" > "$stops"
|
|
73
|
+
owner=$(cat "$lock/pid" 2>/dev/null)
|
|
74
|
+
[ -n "$owner" ] && kill "$owner" 2>/dev/null
|
|
75
|
+
exit 0
|
|
76
|
+
fi
|
|
77
|
+
mode=speak
|
|
78
|
+
[ "$1" = --restore ] && { mode=restore; set --; }
|
|
79
|
+
case "$1" in com.apple.ttsbundle.gryphon-neural_*) siri=$1 voice= ;; *) siri= voice=$1 ;; esac
|
|
80
|
+
rate=\${AGENTX_SAY_RATE:-}
|
|
81
|
+
case "$rate" in *[!0-9]*) rate= ;; esac
|
|
82
|
+
case "$siri" in *[!A-Za-z0-9._-]*) echo "bad voice id" >&2; exit 2;; esac
|
|
83
|
+
# Not macOS, or no pref tools: speak as is and never touch a pref.
|
|
84
|
+
if [ "$(uname)" != Darwin ] || ! command -v defaults >/dev/null || ! command -v plutil >/dev/null; then
|
|
85
|
+
[ "$mode" = restore ] && exit 0
|
|
86
|
+
if [ -n "$voice" ]; then exec say \${rate:+-r "$rate"} -v "$voice"; else exec say \${rate:+-r "$rate"}; fi
|
|
87
|
+
fi
|
|
88
|
+
mkdir -p "$dir" || exit 1
|
|
89
|
+
mine=$(cat "$stops" 2>/dev/null)
|
|
90
|
+
stopped() { [ "$(cat "$stops" 2>/dev/null)" != "$mine" ]; }
|
|
91
|
+
txt=$(mktemp "\${TMPDIR:-/tmp}/agentx-say.XXXXXX") || exit 1
|
|
92
|
+
say_pid= dog=
|
|
93
|
+
restore() {
|
|
94
|
+
[ -f "$saved" ] || return 0
|
|
95
|
+
if [ -s "$saved" ]; then defaults write $D $K "$(cat "$saved")"; else defaults delete $D $K 2>/dev/null; fi
|
|
96
|
+
rm -f "$saved"
|
|
97
|
+
}
|
|
98
|
+
finish() {
|
|
99
|
+
[ -n "$say_pid" ] && kill "$say_pid" 2>/dev/null
|
|
100
|
+
[ -n "$dog" ] && { pkill -P "$dog" 2>/dev/null; kill "$dog" 2>/dev/null; }
|
|
101
|
+
restore; rm -rf "$lock"; rm -f "$txt"; exit "$1"
|
|
102
|
+
}
|
|
103
|
+
drop() { rm -f "$txt"; exit 75; }
|
|
104
|
+
# A Siri voice whose asset macOS purged: switching to it makes the OS
|
|
105
|
+
# speak a fallback of its own (Eloquence Flo), so leave the pref alone.
|
|
106
|
+
if [ -n "$siri" ]; then
|
|
107
|
+
s=\${siri#com.apple.ttsbundle.gryphon-neural_} ; name=\${s%%_*} ; s=\${s#*_} ; loc=$(printf %s "\${s%%_*}" | tr - _)
|
|
108
|
+
found=
|
|
109
|
+
for a in $assets; do
|
|
110
|
+
grep -aqs "com.apple.siri.tts.voice.$loc.$name.neural.premium" "$a"/*/Info.plist && { found=1; break; }
|
|
111
|
+
done
|
|
112
|
+
[ -z "$found" ] && { echo "Siri voice $siri is not installed; speaking with the OS default" >&2; siri=; }
|
|
113
|
+
fi
|
|
114
|
+
# The whole text first, before the lock: a writer that never closes stdin
|
|
115
|
+
# must not hold up every other speaker.
|
|
116
|
+
[ "$mode" = restore ] && exec 0</dev/null
|
|
117
|
+
exec 3<&0 0</dev/null
|
|
118
|
+
cat <&3 > "$txt" & cat_pid=$!
|
|
119
|
+
exec 3<&-
|
|
120
|
+
n=0
|
|
121
|
+
while kill -0 "$cat_pid" 2>/dev/null; do
|
|
122
|
+
if [ $n -ge $((read_s * 10)) ]; then
|
|
123
|
+
kill "$cat_pid" 2>/dev/null; echo "stdin never closed; nothing said" >&2; rm -f "$txt"; exit 3
|
|
124
|
+
fi
|
|
125
|
+
sleep 0.1; n=$((n + 1))
|
|
126
|
+
done
|
|
127
|
+
# One speaker at a time. A lock whose owner died is taken over. A line
|
|
128
|
+
# that waits past stale_s, or that a stop overtook, is dropped unsaid.
|
|
129
|
+
n=0
|
|
130
|
+
while ! mkdir "$lock" 2>/dev/null; do
|
|
131
|
+
stopped && drop
|
|
132
|
+
if [ $n -ge $((stale_s * 10)) ]; then echo "waited \${stale_s}s for the speaker; dropped" >&2; drop; fi
|
|
133
|
+
owner=$(cat "$lock/pid" 2>/dev/null)
|
|
134
|
+
if [ -n "$owner" ] && ! kill -0 "$owner" 2>/dev/null; then rm -rf "$lock"
|
|
135
|
+
elif [ -z "$owner" ] && [ -n "$(find "$lock" -maxdepth 0 -mmin +1 2>/dev/null)" ]; then rm -rf "$lock"
|
|
136
|
+
else sleep 0.1; n=$((n + 1)); fi
|
|
137
|
+
done
|
|
138
|
+
echo $$ > "$lock/pid"
|
|
139
|
+
trap 'finish 143' TERM INT HUP
|
|
140
|
+
stopped && finish 75
|
|
141
|
+
[ "$mode" = restore ] && finish 0
|
|
142
|
+
restore
|
|
143
|
+
if [ -n "$siri" ]; then
|
|
144
|
+
lang=$(defaults read -g AppleLanguages 2>/dev/null | sed -n '2s/^[^A-Za-z]*\\([A-Za-z]*\\).*/\\1/p')
|
|
145
|
+
defaults export $D - 2>/dev/null | plutil -extract $K xml1 -o "$saved.tmp" - 2>/dev/null || : > "$saved.tmp"
|
|
146
|
+
mv "$saved.tmp" "$saved"
|
|
147
|
+
defaults write $D $K -array "\${lang:-en}" "{ _type = \\"Speech.VoiceSelection\\"; _version = 0; voiceId = \\"$siri\\"; }"
|
|
148
|
+
fi
|
|
149
|
+
# A watchdog: no line runs past its length's worth of speech (a hung say
|
|
150
|
+
# held the lock for minutes), capped at max_s.
|
|
151
|
+
limit=$((5 + $(wc -w < "$txt") * 6 / 10))
|
|
152
|
+
[ $limit -gt $max_s ] && limit=$max_s
|
|
153
|
+
if [ -n "$voice" ]; then say \${rate:+-r "$rate"} -v "$voice" -f "$txt" & else say \${rate:+-r "$rate"} -f "$txt" & fi
|
|
154
|
+
say_pid=$!
|
|
155
|
+
( sleep "$limit"; echo "say ran past \${limit}s; stopped" >&2; kill "$say_pid" 2>/dev/null ) & dog=$!
|
|
156
|
+
wait "$say_pid"
|
|
157
|
+
status=$?
|
|
158
|
+
say_pid=
|
|
159
|
+
finish $status
|
|
160
|
+
`,vs=(t=Fn())=>Ue(t,".agentx","voice","siri-say.sh");function Hn(t=Fn()){let e=vs(t);return Un(e)&&Wn(e,"utf8")===Mn||(ms(Ue(t,".agentx","voice"),{recursive:!0}),ys(`${e}.tmp`,Mn,{mode:493}),bs(`${e}.tmp`,e)),e}var zn=`ObjC.import("AVFoundation")
|
|
161
|
+
const vs = $.AVSpeechSynthesisVoice.speechVoices, out = []
|
|
162
|
+
for (let i = 0; i < vs.count; i++) {
|
|
163
|
+
const v = vs.objectAtIndex(i)
|
|
164
|
+
out.push([v.identifier.js, v.name.js, v.language.js, Number(v.quality), Number(v.gender)].join("\\t"))
|
|
165
|
+
}
|
|
166
|
+
out.join("\\n")`,_s=new Set(["Fred","Kathy","Ralph","Junior"]),Rs={Flo:"female",Sandy:"female",Shelley:"female",Grandma:"female",Kathy:"female",Eddy:"male",Reed:"male",Rocko:"male",Grandpa:"male",Fred:"male",Ralph:"male",Junior:"male"};function Xn(t){let e=[];for(let n of t.split(`
|
|
167
|
+
`)){let[r,i,s,o,a]=n.split(" ");!r||!i||!s||r.includes(".siri_")||r.startsWith("com.apple.speech.synthesis.voice.")&&!_s.has(i)||e.push({id:r,name:i,locale:s,quality:o==="3"?"premium":o==="2"?"enhanced":"standard",gender:a==="1"?"male":a==="2"?"female":Rs[i]??null})}return e}var As=10*60*1e3,Ec=60*1e3,W=null,je=!1;function Ve(t=Date.now(),e=As){if(process.platform!=="darwin")return[];if(W)t-W.at>e&&!je&&(je=!0,$s("osascript",["-l","JavaScript","-e",zn],{timeout:1e4},(n,r)=>{je=!1,n||(W={at:Date.now(),voices:[...Xn(String(r)),...ue()]})}));else try{W={at:t,voices:[...Xn(Es("osascript",["-l","JavaScript","-e",zn],{encoding:"utf8",timeout:1e4})),...ue()]}}catch(n){qe(`[voice] could not list system voices: ${String(n?.message??n).split(`
|
|
168
|
+
`)[0]}`),W={at:t,voices:ue()}}return W.voices}var qe=t=>process.stderr.write(t+`
|
|
169
|
+
`),de=new Set;function _c(t){qe=t}function L(t,e){de.has(t)||(de.add(t),qe(e))}function Jn(t){for(let e of de)t(e)&&de.delete(e)}var Yn=()=>{};function Rc(t){Yn=t}function Zn(t){try{Yn(t)}catch(e){L("missing-hook",`[voice] missing-voice check failed: ${e?.message??e}`)}}var Ts={premium:3,enhanced:2,standard:1},Is=new Set(["Grandma","Grandpa"]);function Gn(t){let e=t.id.startsWith("com.apple.voice.")?2:t.id.startsWith("com.apple.eloquence.")&&!Is.has(t.name)?1:0;return Ts[t.quality]*10+e}var F=t=>t.toLowerCase().split(/[-_]/)[0],Kn=t=>t.id.startsWith("com.apple.voice.");function Be(t,e,n,r=new Set){let i=G(t.filter(a=>!a.siri&&F(a.locale)===F(e)),e);if(i.some(Kn)&&(i=i.filter(Kn)),n==="female"||n==="male"){let a=i.filter(c=>c.gender===n);a.length&&(i=a)}if(!i.length)return null;let s=i.filter(a=>a.quality===i[0].quality),o=new Set([...r].map(a=>a.toLowerCase()));return s.find(a=>!o.has(a.name.toLowerCase()))??s[0]}function G(t,e="en"){let n=e.toLowerCase().replace("_","-"),r=F(e),i=t.filter(s=>F(s.locale)===r);return(i.length?i:t).slice().sort((s,o)=>Gn(o)-Gn(s)||+(o.locale.toLowerCase()===n)-+(s.locale.toLowerCase()===n)||s.name.localeCompare(o.name)||s.locale.localeCompare(o.locale))}function K(t,e,n="en"){let r=t.trim();if(!r)return null;let i=e.find(l=>l.id===r);if(i)return i;if(r.toLowerCase().startsWith(v)){let l=r.slice(v.length).trim().toLowerCase(),u=G(e.filter(p=>p.siri&&p.name.toLowerCase()===l),n)[0];if(u||!l)return u??null;let d=K(l,e.filter(p=>F(p.locale)===F(n)),n);return d&&L(`siri:${l}`,`[voice] Siri voice "${l}" is not installed; speaking with ${d.name} (${d.locale}) instead.`),d}e=e.filter(l=>!l.siri);let s=/^(.+?)\s*\((premium|enhanced)\)$/i.exec(r),o=(s?s[1]:r.replace(/\s*\(.*\)\s*$/,"")).trim().toLowerCase(),a=G(e.filter(l=>l.name.toLowerCase()===o),n);return(s?a.find(l=>l.quality===s[2].toLowerCase()):void 0)??a[0]??null}function Qn(t,e,n=new Set){let r=new Map;if(!e.length)return r;let i=new Set([...n].map(o=>o.toLowerCase())),s="female";for(let o of t){let a=o.gender==="female"||o.gender==="male",c=a?o.gender:s,l=e.filter(d=>!i.has(d.name.toLowerCase())),u=l.find(d=>d.gender===c)??(a?e.find(d=>d.gender===c):void 0)??l[0]??e[r.size%e.length];r.set(o.id,u),i.add(u.name.toLowerCase()),a||(s=s==="female"?"male":"female")}return r}At();var He="system",Cs=t=>t.trim().toLowerCase()===He,Ns="21m00Tcm4TlvDq8ikWAM";function ze(t,e,n={},r=Ve()){let i=e[t],s=i?.name||t,o=i?.voice,a=Ge(e,n,r).get(t)??null,c=Ps(t,o?.system,o?.gender,n,r);return{agentId:t,name:s,provider:o?.provider??n.provider??"system",elevenlabsVoiceId:o?.elevenlabsVoiceId||null,systemVoice:a?.id??null,systemVoiceName:a?ge(a):null,systemByLanguage:c,fallback:(n.fallback??"system")==="system",gender:o?.gender??null,style:o?.style||null,intro:o?.intro||Us(s,i?.systemPrompt),...o?.rate?{rate:o.rate}:{},...o?.priority?{priority:o.priority}:{}}}var ge=t=>`${t.name}${t.siri?" (Siri)":t.quality==="standard"?"":` (${t.quality[0].toUpperCase()}${t.quality.slice(1)})`} ${t.locale}`;function Ds(t,e,n,r){if(!e||!n.length)return null;let i=K(e,n,r);return i||(L(`${t}:${e}`,`[voice] ${t}: system voice "${e}" is not installed; using the next choice. Run \`agentx voice list\` to see installed voices.`),Zn(e)),i}function rr(t,e){let n=t.trim();if(n.toLowerCase().startsWith(v))return{locale:e,gender:Fe(n.slice(v.length).trim())};let r=ce(n);return r?{locale:r.locale,gender:Fe(r.name)}:{locale:/^com\.apple\.voice\.[a-z]+\.([a-z]{2}-[A-Z]{2})\./.exec(n)?.[1]??e,gender:null}}var ir=t=>t.toLowerCase().split(/[-_]/)[0],er=(t,e)=>typeof t=="string"?t:t?.[ir(e)];function pe(t,e,n,r){if(e!==void 0)return Cs(e)?null:Ds(t,e,n,r)??void 0}var Xe=(t,e)=>!t||!t.gender||t.gender==="neutral"||!e||e==="neutral"||t.gender===e;function Ls(t,e,n,r=new Set){let i=e.locale??"en",s=new Map,o=[],a=pe("voice.system",er(e.system,i),n,i),c=()=>new Set([...r,...[...s.values()].flatMap(u=>u?[u.name]:[])]);for(let u of t){let d=er(u.system,i),p=pe(u.id,d,n,i),g=p!==void 0?p:d!==void 0&&n.length?Os(u,d,i,a,n,c()):a!==void 0&&Xe(a,u.gender)?a:void 0;g!==void 0?s.set(u.id,g):o.push({id:u.id,gender:u.gender??null})}let l=c();for(let[u,d]of Qn(o,G(n.filter(p=>!p.siri),i),l))s.set(u,d);return s}function Os(t,e,n,r,i,s){for(let a of t.fallbacks??[]){let c=pe(`${t.id}.fallbacks`,a,i,n);if(c!==void 0)return c}if(r!==void 0&&Xe(r,t.gender))return r;let o=rr(e,n);return Be(i,o.locale,t.gender??o.gender,s)??void 0}function Ps(t,e,n,r,i){if(typeof e=="string")return{};let s={},o=(a,c,l)=>{if(!(!c||typeof c=="string"))for(let[u,d]of Object.entries(c)){let p=pe(`${a}.${u}`,d,i,u);p===void 0&&i.length&&(p=Be(i,u,n??rr(d,u).gender)??void 0),p!==void 0&&l(p)&&(s[ir(u)]=p)}};return o("voice.system",r.system,a=>Xe(a,n)),o(t,e,()=>!0),s}function Nc(t,e){let n=e?ne(e):null;if(!n||!(n in t.systemByLanguage))return t;let r=t.systemByLanguage[n];return{...t,systemVoice:r?.id??null,systemVoiceName:r?ge(r):null}}function Ge(t,e={},n=Ve()){let r=Object.entries(t).map(([i,s])=>({id:i,system:s.voice?.system,gender:s.voice?.gender,fallbacks:s.voice?.fallbacks}));return Ls(r,e,n)}function Ke(t,e){let n={provider:t.provider,elevenlabs:Ms(e,t.elevenlabsVoiceId),system:t.systemVoice,fallback:t.fallback},r=Object.entries(t.systemByLanguage??{});return r.length&&(n.languages=Object.fromEntries(r.map(([i,s])=>[i,s?.id??null]))),t.rate&&t.rate!==1&&(n.rate=t.rate),t.priority&&t.priority!=="normal"&&(n.priority=t.priority),n}function Ms(t,e){return t||e||process.env.AGENTX_VOICE_ID||Ns}function Us(t,e){let n=(e??"").split(/(?<=\.)\s/)[0]??"",r=/^You are (.+?)\.?$/i.exec(n.trim());if(!r)return`Hello, this is ${t}.`;let i=r[1].replace(/\s*\([^)]*\)/g,"").trim(),[s,...o]=i.split(/,\s*/),a=o.join(", ");return a&&s.toLowerCase()===t.toLowerCase()?`Hello, this is ${t}, ${a}.`:!/^an? /i.test(i)&&i.toLowerCase().includes(t.toLowerCase())?`Hello, this is ${i}.`:`Hello, this is ${t}, ${i}.`}var Ws=8*60*60*1e3,Fs=500,tr=class{constructor(e=Ws){this.gapMs=e}lastSpoke=new Map;needsIntro(e,n,r=Date.now()){let i=this.lastSpoke.get(nr(e,n));return i===void 0||r-i>this.gapMs}spoke(e,n,r=Date.now()){let i=nr(e,n);this.lastSpoke.delete(i),this.lastSpoke.set(i,r),this.lastSpoke.size>Fs&&this.lastSpoke.delete(this.lastSpoke.keys().next().value)}},nr=(t,e)=>`${t}\0${e}`;function sr(t,e){let n=t.style?` Your speaking manner: ${t.style}.`:"";return e?`[VOICE INTRO] This is your first spoken exchange with this person in a while. Open with one short sentence introducing yourself in your own manner, along the lines of "${t.intro}", then answer.${n}`:"[VOICE CASUAL] You have already introduced yourself in this voice conversation. Do not introduce yourself or greet again; talk casually, like a colleague in the same office."+n}function Dc(t,e,n,r={}){let i=e[t],s=ze(t,e,r),o=(i?.systemPrompt??"").split(/\n\s*\n/)[0].slice(0,600).trim();return{agentId:t,name:s.name,voice:Ke(s),persona:o||`You are ${s.name}.`,introLine:sr(s,n)}}var js="[VOICE MODE] This question arrived by voice and your reply will be spoken aloud by a TTS engine. Answer in two or three short sentences. Plain language, no markdown, no code blocks, no bullet points, no URLs. Speak conversationally.";function Lc(t){let e=t;if(e?.channel!=="voice"||!e.voice||typeof e.voice!="object")return;let n=s=>typeof s=="string"?s.replace(/\s+/g," ").slice(0,200):"",r=n(e.voice.intro),i={agentId:"",name:"",provider:"system",elevenlabsVoiceId:null,systemVoice:null,systemVoiceName:null,systemByLanguage:{},fallback:!0,gender:null,style:n(e.voice.style)||null,intro:r||"Hello."};return`${js}
|
|
170
|
+
${sr(i,e.voice.introduce===!0&&!!r)}`}var Ks="System Settings \u203A Accessibility \u203A Spoken Content \u203A System voice \u203A Manage Voices",lr=(t=zs())=>Gs(t,".agentx","voice","health.json");function Js(t,e,n){let r=t.trim();if(!r||r.toLowerCase()===He)return!0;if(r.toLowerCase().startsWith(v)){let i=r.slice(v.length).trim().toLowerCase();return e.some(s=>s.siri&&s.name.toLowerCase()===i)}return K(r,e,n)!==null}function Ys(t){let e=t.voice?.locale??"en",n=[],r=(i,s)=>{if(typeof s=="string")n.push({owner:i,name:s,locale:e,perLanguage:!1});else for(let[o,a]of Object.entries(s??{}))n.push({owner:i,name:a,locale:o,perLanguage:!0})};r("voice.system",t.voice?.system);for(let[i,s]of Object.entries(t.agents))(s.voice?.provider??t.voice?.provider??"system")!=="system"&&t.voice?.fallback==="none"||r(i,s.voice?.system);return n}function Zs(t,e){if(!e.length)return[];let n=new Map,r;for(let{owner:i,name:s,locale:o,perLanguage:a}of Ys(t)){if(Js(s,e,o))continue;r??=Ge(t.agents,t.voice,e);let c=n.get(s)??{voice:s,agents:[],speaksWith:{}};c.agents.includes(i)||c.agents.push(i);let l=r.get(i);i!=="voice.system"&&!a&&(c.speaksWith[i]=l?ge(l):"the OS default voice"),n.set(s,c)}return[...n.values()]}function Je(t){let e=ce(t);if(e)return`Siri ${e.name[0].toUpperCase()}${e.name.slice(1)} (${e.locale})`;if(t.toLowerCase().startsWith(v)){let n=t.slice(v.length).trim();return`Siri ${n[0]?.toUpperCase()??""}${n.slice(1)}`}return t}function Qs(t){let e=t.map(n=>{let r=n.agents.map(i=>n.speaksWith[i]?`${i} (now ${n.speaksWith[i]})`:i).join(", ");return`\u2022 ${Je(n.voice)}: ${r}`});return{title:t.length===1?"AgentX: a voice is not installed":`AgentX: ${t.length} voices are not installed`,message:`${e.join(`
|
|
171
|
+
`)}
|
|
172
|
+
Reinstall in ${Ks}. Agents switch back on their own once it is installed.`}}function eo(t=lr()){if(!ar(t))return null;try{let e=JSON.parse(qs(t,"utf8"));return e&&Array.isArray(e.missing)?e:null}catch{return null}}function to(t,e){Vs(Xs(t),{recursive:!0}),Hs(`${t}.tmp`,JSON.stringify(e,null,2)+`
|
|
173
|
+
`),Bs(`${t}.tmp`,t)}var or=class{constructor(e){this.deps=e}running=!1;missingNow=!1;hasMissing(){return this.missingNow}async check(e,n,r=new Date){if(this.running||!n.length)return null;this.running=!0;try{let i=this.deps.file??lr(),s=new Map((eo(i)?.missing??[]).map(d=>[d.voice,d])),o=Zs(e,n),a=o.map(d=>({...d,since:s.get(d.voice)?.since??r.toISOString(),...s.get(d.voice)?.notifiedAt?{notifiedAt:s.get(d.voice).notifiedAt}:{}})),c=[...s.keys()].filter(d=>!o.some(p=>p.voice===d));for(let d of c)this.deps.log(`[voice] ${Je(d)} is installed again; its agents speak with it again`);c.length&&Jn(d=>c.some(p=>d===p||d.endsWith(`:${p}`)));let l=a.filter(d=>!d.notifiedAt);if(l.length){let{title:d,message:p}=Qs(l);try{await this.deps.notify(d,p);for(let g of l)g.notifiedAt=r.toISOString();this.deps.log(`[voice] told the owner: ${l.map(g=>Je(g.voice)).join(", ")} not installed`)}catch(g){this.deps.log(`[voice] could not send the missing-voice notice: ${g?.message??g}`)}}this.missingNow=a.length>0;let u={checkedAt:r.toISOString(),missing:a};return(!ar(i)||JSON.stringify(a)!==JSON.stringify([...s.values()]))&&to(i,u),u}finally{this.running=!1}}};import{readFileSync as Ze,writeFileSync as fr,existsSync as Qe,readdirSync as po,statSync as go}from"fs";import{resolve as et,join as fo}from"path";import{existsSync as cr,mkdirSync as no,readFileSync as ro,writeFileSync as ur}from"fs";import{dirname as io,resolve as dr}from"path";import{z as h}from"zod";var so=h.object({type:h.enum(["gitlab","github"]),host:h.string().optional(),project:h.string(),iid:h.number().int().positive(),url:h.string()}),oo=h.object({id:h.string(),title:h.string(),description:h.string().optional(),assignee:h.string().optional(),labels:h.array(h.string()).default([]),milestone:h.string().optional(),status:h.enum(["todo","doing","blocked","done"]).default("todo"),priority:h.number().int().optional(),estimatedSeconds:h.number().int().optional(),source:so.optional(),importedAt:h.string().optional(),createdAt:h.string(),updatedAt:h.string(),blocker:h.string().optional(),doneNote:h.string().optional()}),ao=h.object({version:h.literal(1),items:h.array(oo).default([])}),fe=class{constructor(e){this.path=e}jsonPath(){return dr(process.cwd(),this.path).replace(/\.md$/i,".json")}mdPath(){return dr(process.cwd(),this.path).replace(/\.json$/i,".md")}exists(){return cr(this.jsonPath())}load(){let e=this.jsonPath();if(!cr(e))return{version:1,items:[]};let n=ro(e,"utf-8");try{return ao.parse(JSON.parse(n))}catch(r){throw new Error(`Backlog file invalid (${e}): ${r.message}`)}}save(e){let n=this.jsonPath();no(io(n),{recursive:!0}),ur(n,JSON.stringify(e,null,2)+`
|
|
174
|
+
`),ur(this.mdPath(),this.toMarkdown(e))}list(){return this.load().items}findById(e){return this.list().find(n=>n.id===e)}add(e){let n=this.load();if(n.items.some(s=>s.id===e.id))throw new Error(`backlog item ${e.id} already exists`);let r=new Date().toISOString(),i={...e,labels:e.labels??[],status:e.status??"todo",createdAt:e.createdAt??r,updatedAt:r};return n.items.push(i),this.save(n),i}addMany(e){let n=this.load(),r=new Set(n.items.map(o=>o.id)),i=new Date().toISOString(),s=[];for(let o of e){if(r.has(o.id))continue;let a={...o,labels:o.labels??[],status:o.status??"todo",createdAt:o.createdAt??i,updatedAt:i};n.items.push(a),s.push(a),r.add(a.id)}return s.length&&this.save(n),s}update(e,n){let r=this.load(),i=r.items.findIndex(o=>o.id===e);if(i<0)throw new Error(`backlog item ${e} not found`);let s={...r.items[i],...n,id:r.items[i].id,createdAt:r.items[i].createdAt,updatedAt:new Date().toISOString()};return r.items[i]=s,this.save(r),s}remove(e){let n=this.load(),r=n.items.length;return n.items=n.items.filter(i=>i.id!==e),n.items.length===r?!1:(this.save(n),!0)}toMarkdown(e){let n=["# Backlog","","_Generated from `backlog.json`. Do not edit by hand \u2014 use `agentx backlog` commands._",""],r={doing:[],blocked:[],todo:[],done:[]};for(let s of e.items)r[s.status].push(s);let i=[["doing","Doing"],["blocked","Blocked"],["todo","To Do"],["done","Done"]];for(let[s,o]of i)if(r[s].length){n.push(`## ${o}`,"");for(let a of r[s]){let c=s==="done"?"x":" ",l=a.assignee?`@${a.assignee} `:"",u=a.estimatedSeconds?` [time: ${Math.round(a.estimatedSeconds/60)}m]`:"",d=a.source?` (${a.source.type}: ${a.source.project}#${a.source.iid})`:"";n.push(`- [${c}] ${l}${a.title}${u}${d}`),a.source?.url&&n.push(` - <${a.source.url}>`),a.labels.length&&n.push(` - labels: ${a.labels.join(", ")}`),a.milestone&&n.push(` - milestone: ${a.milestone}`),a.blocker&&n.push(` - BLOCKED: ${a.blocker}`)}n.push("")}return n.join(`
|
|
175
|
+
`)}};function zc(t){return`${t.type}:${t.project}:${t.iid}`}function pr(t,e,n){let r=[];if(e.type==="gitlab"){let i=n.channels.gitlab?.agentMappings??[];for(let s of t){let o=i.find(a=>a.agentId===s);o?.gitlabUsernames?.length?r.push(o.gitlabUsernames[0]):r.push(s)}}else if(e.type==="github"){let i=n.channels.github?.agentMappings??[];for(let s of t){let o=i.find(a=>a.agentId===s);o?.githubUsernames?.length?r.push(o.githubUsernames[0]):r.push(s)}}return r}async function Ye(t,e,n,r=()=>{}){return t.source?lo(t.source,e,n,r):{ok:!0,attempts:[]}}async function lo(t,e,n,r=()=>{}){return t.type==="gitlab"?co(t,e,n,r):t.type==="github"?uo(t,e,n,r):{ok:!1,error:`unknown source.type ${t.type}`,attempts:[]}}async function co(t,e,n,r){let i=n.channels.gitlab,s=i?.token,o=i?.host||(t.host?`https://${t.host}`:void 0);if(!s||!o)return{ok:!1,error:"gitlab token/host not configured",attempts:[]};let a=[],c=async(g,f={})=>fetch(`${o}/api/v4${g}`,{...f,headers:{"PRIVATE-TOKEN":s,"Content-Type":"application/json",...f.headers||{}}}),l=encodeURIComponent(t.project),u;e.assigneeUsernames!==void 0&&(u=(await Promise.all(e.assigneeUsernames.map(async f=>{try{let m=await fetch(`${o}/api/v4/users?username=${encodeURIComponent(f)}`,{headers:{"PRIVATE-TOKEN":s}});if(!m.ok)return null;let b=await m.json();return Array.isArray(b)&&b[0]?.id?b[0].id:null}catch{return null}}))).filter(f=>typeof f=="number"));let d;if(e.milestoneTitle!==void 0)if(e.milestoneTitle===null||e.milestoneTitle==="")d=0;else try{let g=await c(`/projects/${l}/milestones?title=${encodeURIComponent(e.milestoneTitle)}`);if(g.ok){let f=await g.json();Array.isArray(f)&&f[0]?.id&&(d=f[0].id)}}catch(g){r(`[backlog-sync] milestone lookup failed: ${g.message}`)}let p={};if(e.title!==void 0&&(p.title=e.title),e.description!==void 0&&(p.description=e.description),e.labels!==void 0&&(p.labels=e.labels.join(",")),e.addLabels?.length&&(p.add_labels=e.addLabels.join(",")),e.removeLabels?.length&&(p.remove_labels=e.removeLabels.join(",")),u!==void 0&&(p.assignee_ids=u),d!==void 0&&(p.milestone_id=d),e.state&&(p.state_event=e.state==="closed"?"close":"reopen"),Object.keys(p).length)try{let g=await c(`/projects/${l}/issues/${t.iid}`,{method:"PUT",body:JSON.stringify(p)});if(a.push({kind:"PUT issue",ok:g.ok,status:g.status}),!g.ok){let f=await g.text().catch(()=>"");return{ok:!1,attempts:a,error:`PUT issue ${g.status}: ${f.slice(0,200)}`,url:t.url}}}catch(g){return a.push({kind:"PUT issue",ok:!1,error:g.message}),{ok:!1,attempts:a,error:g.message,url:t.url}}if(e.note)try{let g=await c(`/projects/${l}/issues/${t.iid}/notes`,{method:"POST",body:JSON.stringify({body:e.note})});a.push({kind:"POST note",ok:g.ok,status:g.status})}catch(g){a.push({kind:"POST note",ok:!1,error:g.message})}return{ok:a.every(g=>g.ok),attempts:a,url:t.url}}async function uo(t,e,n,r){let s=n.channels.github?.token;if(!s)return{ok:!1,error:"github token not configured",attempts:[]};let o=[],a=async(p,g={})=>fetch(`https://api.github.com${p}`,{...g,headers:{Authorization:`Bearer ${s}`,Accept:"application/vnd.github+json","X-GitHub-Api-Version":"2022-11-28",...g.headers||{}}}),c=t.project,l;if(e.milestoneTitle!==void 0)if(e.milestoneTitle===null||e.milestoneTitle==="")l=null;else try{let p=await a(`/repos/${c}/milestones?state=all&per_page=100`);if(p.ok){let g=await p.json(),f=Array.isArray(g)?g.find(m=>m.title===e.milestoneTitle):null;f?.number&&(l=f.number)}}catch(p){r(`[backlog-sync] github milestone lookup failed: ${p.message}`)}let u;if(e.labels!==void 0)u=e.labels;else if(e.addLabels?.length||e.removeLabels?.length){try{let p=await a(`/repos/${c}/issues/${t.iid}`);if(p.ok){let g=await p.json(),f=Array.isArray(g.labels)?g.labels.map(b=>typeof b=="string"?b:b.name):[],m=new Set([...f,...e.addLabels??[]]);for(let b of e.removeLabels??[])m.delete(b);u=[...m]}}catch(p){r(`[backlog-sync] github label fetch failed: ${p.message}`)}if(u===void 0)return{ok:!1,attempts:o,error:"could not read the issue's labels; nothing written",url:t.url}}let d={};if(e.title!==void 0&&(d.title=e.title),e.description!==void 0&&(d.body=e.description),u!==void 0&&(d.labels=u),e.assigneeUsernames!==void 0&&(d.assignees=e.assigneeUsernames),l!==void 0&&(d.milestone=l),e.state&&(d.state=e.state),Object.keys(d).length)try{let p=await a(`/repos/${c}/issues/${t.iid}`,{method:"PATCH",body:JSON.stringify(d)});if(o.push({kind:"PATCH issue",ok:p.ok,status:p.status}),!p.ok){let g=await p.text().catch(()=>"");return{ok:!1,attempts:o,error:`PATCH issue ${p.status}: ${g.slice(0,200)}`,url:t.url}}}catch(p){return o.push({kind:"PATCH issue",ok:!1,error:p.message}),{ok:!1,attempts:o,error:p.message,url:t.url}}if(e.note)try{let p=await a(`/repos/${c}/issues/${t.iid}/comments`,{method:"POST",body:JSON.stringify({body:e.note})});o.push({kind:"POST comment",ok:p.ok,status:p.status})}catch(p){o.push({kind:"POST comment",ok:!1,error:p.message})}return{ok:o.every(p=>p.ok),attempts:o,url:t.url}}var tt=/^(\s*)-\s*\[( |x|X)\]\s*(.*)$/,me=/@([a-z0-9_-]+)/i,gr=/\[time:\s*([0-9hm\s]+)\]/i;function mo(t){let e=t.match(/(\d+)\s*h/i),n=t.match(/(\d+)\s*m/i);return(e?parseInt(e[1],10)*3600:0)+(n?parseInt(n[1],10)*60:0)}var nt=class{constructor(e,n,r=()=>{}){this.path=e;this.daemon=n;this.log=r;this.store=new fe(this.path)}type="backlog";capabilities={listAll:!0,create:!1,transition:!1,labels:!0,search:!1};store;getStore(){return this.store}readMd(){let e=et(process.cwd(),this.path);return Qe(e)?Ze(e,"utf-8").split(`
|
|
176
|
+
`):[]}writeMd(e){let n=et(process.cwd(),this.path);fr(n,e.join(`
|
|
177
|
+
`))}async listOpen(e){if(this.store.exists())return this.store.list().filter(s=>s.status!=="done"&&(!s.assignee||s.assignee.toLowerCase()===e.toLowerCase())).map(s=>this.toWorkItem(s));let n=this.readMd(),r=[];for(let i=0;i<n.length;i++){let s=n[i].match(tt);if(!s||s[2].toLowerCase()==="x")continue;let a=s[3],c=a.match(me);if(c&&c[1].toLowerCase()!==e.toLowerCase())continue;let l=a.match(gr),u=a.replace(me,"").replace(gr,"").trim();r.push({id:`backlog:${i}`,title:u||`Task at line ${i+1}`,assignee:c?c[1]:void 0,estimatedSeconds:l?mo(l[1]):void 0,priority:i})}return r}async listAll(e={}){return this.store.exists()?this.store.list().map(n=>this.toWorkItem(n)):[]}async claim(e,n){if(!this.store.exists())return;let r=this.store.findById(n);if(!r)return;let i=this.store.update(n,{assignee:r.assignee||e,status:"doing"});if(i.source&&this.daemon){let s=pr([i.assignee||e],i.source,this.daemon);Ye(i,{assigneeUsernames:s,addLabels:["Doing"],removeLabels:["To Do"]},this.daemon,this.log).catch(o=>this.log(`[backlog-sync] claim failed for ${n}: ${o?.message??o}`))}}async report(e,n){if(this.store.exists()&&this.store.findById(e)){let a={};n.status==="done"?a.status="done":n.status==="blocked"?(a.status="blocked",a.blocker=n.blocker||n.note||void 0):n.status==="in-progress"&&(a.status="doing"),n.timeSeconds&&(a.estimatedSeconds=n.timeSeconds);let c=this.store.update(e,a);if(c.source&&this.daemon){let l={};n.status==="done"?(l.addLabels=["Done"],l.removeLabels=["Doing"]):n.status==="blocked"&&(l.addLabels=["Blocked"]),n.note&&(l.note=n.note),Object.keys(l).length&&Ye(c,l,this.daemon,this.log).catch(u=>this.log(`[backlog-sync] report failed for ${e}: ${u?.message??u}`))}return}if(!e.startsWith("backlog:"))throw new Error(`not a backlog id: ${e}`);let r=parseInt(e.slice(8),10),i=this.readMd(),s=i[r];if(!s)throw new Error(`backlog line ${r} missing`);if(!s.match(tt))throw new Error(`backlog line ${r} not a checkbox`);if(n.status==="done"){i[r]=s.replace(/- \[ \]/,"- [x]");let a=n.timeSeconds?` <!-- done by agent, ${Math.round(n.timeSeconds/60)}m -->`:" <!-- done by agent -->";i.splice(r+1,0,a)}else n.status==="blocked"?i.splice(r+1,0,` <!-- BLOCKED: ${n.blocker||n.note||"?"} -->`):n.note&&i.splice(r+1,0,` <!-- ${n.note.replace(/-->/g,"\u2014 >")} -->`);this.writeMd(i)}toWorkItem(e){let n={todo:"todo",doing:"doing",blocked:"onhold",done:"done"}[e.status];return{id:e.id,title:e.title,description:e.description,assignee:e.assignee,estimatedSeconds:e.estimatedSeconds,priority:e.priority,labels:e.labels,stage:n,milestone:e.milestone,url:e.source?.url,updatedAt:e.updatedAt,state:e.status==="done"?"closed":"opened"}}},rt=class{constructor(e,n){this.root=e;this.glob=n}type="wiki";capabilities={listAll:!1,create:!1,transition:!1,labels:!1,search:!1};walk(e,n){if(Qe(e))for(let r of po(e)){let i=fo(e,r);go(i).isDirectory()?this.walk(i,n):r.endsWith(".md")&&n.push(i)}}async listOpen(e){let n=[];this.walk(et(process.cwd(),this.root),n);let r=[];for(let i of n){let s=Ze(i,"utf-8").split(`
|
|
178
|
+
`);for(let o=0;o<s.length;o++){let a=s[o].match(tt);if(!a||a[2].toLowerCase()==="x")continue;let c=a[3].match(me);!c||c[1].toLowerCase()!==e.toLowerCase()||r.push({id:`wiki:${i}:${o}`,title:a[3].replace(me,"").trim(),assignee:c[1],priority:o})}}return r}async claim(){}async report(e,n){let r=e.split(":"),i=parseInt(r[r.length-1],10),s=r.slice(1,-1).join(":");if(!Qe(s))return;let o=Ze(s,"utf-8").split(`
|
|
179
|
+
`);n.status==="done"&&o[i]&&(o[i]=o[i].replace(/- \[ \]/,"- [x]"),fr(s,o.join(`
|
|
180
|
+
`)))}},it=class{constructor(e,n,r,i,s){this.host=e;this.token=n;this.projects=r;this.agentUsernames=i;this.log=s}type="gitlab";capabilities={listAll:!0,create:!0,transition:!0,labels:!0,search:!0};usernameToAgent(e){for(let[n,r]of Object.entries(this.agentUsernames))if(r.some(i=>i.toLowerCase()===e.toLowerCase()))return n}mapIssue(e,n){let r=e.references?.full?.split("#")[0]||n,i=[],s;Array.isArray(e.labels)&&(e.labels.length&&typeof e.labels[0]=="object"?(s=e.labels.map(l=>({name:l.name,color:l.color,text_color:l.text_color})),i=s.map(l=>l.name)):i=e.labels);let a=(Array.isArray(e.assignees)&&e.assignees.length?e.assignees:e.assignee?[e.assignee]:[]).map(l=>({username:l.username,name:l.name,avatarUrl:l.avatar_url})),c=a[0]?.username;return{id:`gitlab:${r}:issue:${e.iid}`,title:e.title,description:e.description||void 0,assignee:c?this.usernameToAgent(c)||c:void 0,assignees:a,url:e.web_url,priority:e.weight??99,estimatedSeconds:e.time_stats?.time_estimate||void 0,labels:i,labelDetails:s,updatedAt:e.updated_at,state:e.state==="closed"?"closed":"opened",milestone:e.milestone?.title||void 0}}async api(e,n={}){let r=await fetch(`${this.host}/api/v4${e}`,{...n,headers:{"PRIVATE-TOKEN":this.token,"Content-Type":"application/json",...n.headers||{}}});if(!r.ok)throw new Error(`GitLab ${r.status}: ${await r.text().catch(()=>"")}`);return r.json()}async listOpen(e){let n=this.agentUsernames[e]||[];if(!n.length)return[];let r=[],i=this.projects.length?this.projects:["__ALL__"];for(let s of n)for(let o of i)try{let a=`assignee_username=${encodeURIComponent(s)}&state=opened&scope=all&per_page=20`,c=o==="__ALL__"?`/issues?${a}`:`/projects/${encodeURIComponent(o)}/issues?${a}`,l=await this.api(c);for(let u of l){let d=this.mapIssue(u,o);d.assignee=e,r.push(d)}}catch(a){this.log(`[business] GitLab listOpen failed for ${s} on ${o}: ${a.message}`)}return r}async claim(e,n){let{project:r,iid:i,type:s}=this.parseId(n),o=`/projects/${encodeURIComponent(r)}/${s==="issue"?"issues":"merge_requests"}/${i}?add_labels=Doing&remove_labels=To Do`;try{await this.api(o,{method:"PUT"})}catch(a){this.log(`[business] claim label update failed: ${a.message}`)}}async report(e,n){let{project:r,iid:i,type:s}=this.parseId(e),o=s==="issue"?"issues":"merge_requests";if(n.timeSeconds&&n.timeSeconds>0){let a=Math.max(1,Math.round(n.timeSeconds/60)),c=a>=60?`${Math.floor(a/60)}h${a%60?` ${a%60}m`:""}`:`${a}m`;try{await this.api(`/projects/${encodeURIComponent(r)}/${o}/${i}/add_spent_time?duration=${encodeURIComponent(c)}`,{method:"POST"})}catch(l){this.log(`[business] add_spent_time failed: ${l.message}`)}}if(n.status==="done")try{await this.api(`/projects/${encodeURIComponent(r)}/${o}/${i}?add_labels=Done&remove_labels=Doing`,{method:"PUT"})}catch(a){this.log(`[business] done label update failed: ${a.message}`)}else if(n.status==="blocked")try{await this.api(`/projects/${encodeURIComponent(r)}/${o}/${i}?add_labels=Blocked`,{method:"PUT"})}catch(a){this.log(`[business] blocked label update failed: ${a.message}`)}if(n.note)try{await this.api(`/projects/${encodeURIComponent(r)}/${o}/${i}/notes`,{method:"POST",body:JSON.stringify({body:n.note})})}catch(a){this.log(`[business] report note failed: ${a.message}`)}}async listAll(e={}){let n=e.sinceDays??30,r=new Date(Date.now()-n*24*36e5).toISOString(),i=new URLSearchParams,s=e.state??"opened";i.set("state",s),i.set("updated_after",r),i.set("scope","all"),i.set("per_page","100"),i.set("with_labels_details","true"),i.set("order_by","updated_at"),i.set("sort","desc"),e.labels?.length&&i.set("labels",e.labels.join(",")),e.milestone&&i.set("milestone",e.milestone),e.search&&i.set("search",e.search);let o=this.projects.length?this.projects:["__ALL__"],a=[];for(let l of o)try{let u=l==="__ALL__"?`/issues?${i.toString()}`:`/projects/${encodeURIComponent(l)}/issues?${i.toString()}`,d=await this.api(u);for(let p of d)a.push(this.mapIssue(p,l))}catch(u){this.log(`[business] GitLab listAll failed on ${l}: ${u.message}`)}let c=a;if(e.notLabels?.length){let l=new Set(e.notLabels);c=c.filter(u=>!(u.labels||[]).some(d=>l.has(d)))}if(e.withoutScopedPrefix){let l=e.withoutScopedPrefix+"::";c=c.filter(u=>!(u.labels||[]).some(d=>d.startsWith(l)))}return c}async setState(e,n){let{project:r,iid:i,type:s}=this.parseId(e),o=s==="issue"?"issues":"merge_requests";await this.api(`/projects/${encodeURIComponent(r)}/${o}/${i}?state_event=${n}`,{method:"PUT"})}async getItem(e){let{project:n,iid:r}=this.parseId(e);try{let i=await this.api(`/projects/${encodeURIComponent(n)}/issues/${r}?with_labels_details=true`);return{...this.mapIssue(i,n),descriptionHtml:i.description_html||void 0,createdAt:i.created_at,author:i.author?{username:i.author.username,name:i.author.name,avatarUrl:i.author.avatar_url}:void 0,allLabels:Array.isArray(i.labels)?typeof i.labels[0]=="object"?i.labels.map(o=>o.name):i.labels:[]}}catch(i){return this.log(`[board] GitLab getItem failed for ${e}: ${i.message}`),null}}async updateItem(e,n){let{project:r,iid:i}=this.parseId(e),s;n.assigneeUsernames&&(s=(await Promise.all(n.assigneeUsernames.map(async u=>{try{let d=await this.api(`/users?username=${encodeURIComponent(u)}`);return Array.isArray(d)&&d[0]?.id?d[0].id:null}catch{return null}}))).filter(u=>typeof u=="number"));let o;if(n.milestoneTitle!==void 0)if(n.milestoneTitle===null||n.milestoneTitle==="")o=0;else try{let l=await this.api(`/projects/${encodeURIComponent(r)}/milestones?title=${encodeURIComponent(n.milestoneTitle)}`);Array.isArray(l)&&l[0]?.id&&(o=l[0].id)}catch(l){this.log(`[board] milestone lookup failed for "${n.milestoneTitle}": ${l.message}`)}let a={};n.title!==void 0&&(a.title=n.title),n.description!==void 0&&(a.description=n.description),n.labels!==void 0&&(a.labels=n.labels.join(",")),n.addLabels?.length&&(a.add_labels=n.addLabels.join(",")),n.removeLabels?.length&&(a.remove_labels=n.removeLabels.join(",")),s!==void 0&&(a.assignee_ids=s),o!==void 0&&(a.milestone_id=o);let c=await this.api(`/projects/${encodeURIComponent(r)}/issues/${i}`,{method:"PUT",body:JSON.stringify(a)});return this.mapIssue(c,r)}async listMembers(){let e=new Map;for(let n of this.projects)try{let r=await this.api(`/projects/${encodeURIComponent(n)}/members/all?per_page=100`);for(let i of r)i.username&&!e.has(i.username)&&e.set(i.username,{username:i.username,name:i.name,avatarUrl:i.avatar_url})}catch(r){this.log(`[board] listMembers failed for ${n}: ${r.message}`)}return[...e.values()].sort((n,r)=>n.username.localeCompare(r.username))}async listMilestones(){let e=new Map;for(let r of this.projects)try{let i=await this.api(`/projects/${encodeURIComponent(r)}/milestones?per_page=100`);for(let s of i)s.title&&!e.has(s.title)&&e.set(s.title,{id:s.id,title:s.title,state:s.state,dueDate:s.due_date})}catch(i){this.log(`[board] listMilestones failed for ${r}: ${i.message}`)}let n=[...e.values()];return n.sort((r,i)=>r.state!==i.state?r.state==="active"?-1:1:r.title.localeCompare(i.title)),n}async create(e){let n=e.projectHint||this.projects[0];if(!n)throw new Error("GitLabWorkSource.create requires projectHint or configured projects");let r,i=e.assigneeUsernames?.length?e.assigneeUsernames:e.assignee?(this.agentUsernames[e.assignee]||[]).slice(0,1):[];i.length&&(r=(await Promise.all(i.map(async l=>{try{let u=await this.api(`/users?username=${encodeURIComponent(l)}`);return Array.isArray(u)&&u[0]?.id?u[0].id:null}catch(u){return this.log(`[board] user lookup failed for ${l}: ${u.message}`),null}}))).filter(l=>typeof l=="number"));let s;if(e.milestoneTitle)try{let c=await this.api(`/projects/${encodeURIComponent(n)}/milestones?title=${encodeURIComponent(e.milestoneTitle)}`);Array.isArray(c)&&c[0]?.id&&(s=c[0].id)}catch(c){this.log(`[board] milestone lookup failed for "${e.milestoneTitle}": ${c.message}`)}let o={title:e.title,description:e.description,labels:(e.labels||[]).join(",")};r?.length&&(o.assignee_ids=r),s!==void 0&&(o.milestone_id=s);let a=await this.api(`/projects/${encodeURIComponent(n)}/issues`,{method:"POST",body:JSON.stringify(o)});return this.mapIssue(a,n)}async transition(e,n,r){await this.transitionMany(e,n?[n]:[],r?[r]:[])}async transitionMany(e,n,r){let{project:i,iid:s,type:o}=this.parseId(e),a=o==="issue"?"issues":"merge_requests",c=new URLSearchParams;n.length&&c.set("add_labels",n.join(",")),r.length&&c.set("remove_labels",r.join(",")),c.toString()&&await this.api(`/projects/${encodeURIComponent(i)}/${a}/${s}?${c.toString()}`,{method:"PUT"})}parseId(e){let r=e.replace(/^gitlab:/,"").split(":"),i=r.pop(),s=r.pop()==="mr"?"mr":"issue";return{project:r.join(":"),type:s,iid:i}}};function Qc(t,e,n){let r=t.workSource;switch(r.type){case"backlog":return new nt(r.path,e,n);case"wiki":return new rt(r.path,r.glob);case"gitlab":{let i=e.channels.gitlab;if(!i?.token)throw new Error("business.workSource.type=gitlab requires channels.gitlab.token");let s={};for(let a of i.agentMappings)a.gitlabUsernames.length&&(s[a.agentId]=a.gitlabUsernames);let o=r.projects.length?r.projects:i.routes.map(a=>a.project);return new it(i.host,i.token,o,s,n)}}}var st="unmapped";function ot(t,e=[]){if(!t)return st;let n=e.find(i=>i.id===t);if(n?.client)return n.client;let r=t.indexOf("/");return r>0?t.slice(0,r):t}function ho(t){let e=new Map,n=t.projects??[],r=t.orgChart??{};for(let s of n)s.pm&&(e.has(s.pm)||e.set(s.pm,ot(s.id,n)));let i=(s,o=new Set)=>{if(e.has(s))return e.get(s);if(o.has(s))return;o.add(s);let a=r[s]?.reportsTo;if(!a)return;let c=i(a,o);return c&&e.set(s,c),c};for(let s of Object.keys(r))i(s);return e}function bo(t,e,n=[]){if(!n.length)return;let r=n.filter(i=>!i.channel||i.channel===t);return r.find(i=>i.chatId&&e.chatId&&i.chatId===e.chatId)??r.find(i=>i.username&&e.username&&i.username===e.username)??r.find(i=>i.senderId&&e.senderId&&i.senderId===e.senderId)??r.find(i=>i.channel&&!i.chatId&&!i.username&&!i.senderId)}function tu(t,e){let n=bo(t.channel,{chatId:t.chatId,username:t.username},e.contactMap);if(n)return n.client;if(t.project)return ot(t.project,e.projects);if(t.agentId){let r=ho(e).get(t.agentId);if(r)return r}return st}function nu(t,e){let n=String(t||"").split(":");if(n.length<3)return{agentId:e};let[r,i,...s]=n,o=s.join(":"),a={agentId:e||r,channel:i};return i==="gitlab"||i==="github"?a.project=o.split(":")[0]:i==="cron"||i==="api"||(a.chatId=o.split("@")[0]),a}var yo=t=>t===st?"internal":"client";function wo(t,e){let n=e.clients?.[t];return{id:t,name:n?.name||t,kind:n?.kind??yo(t),respondWithinMinutes:So(n?.respondWithin),standing:n?.standing??[],declared:!!n}}function ru(t){let e=new Set;for(let n of t.projects??[])e.add(ot(n.id,t.projects));for(let n of t.contactMap??[])e.add(n.client);for(let n of Object.keys(t.clients??{}))e.add(n);return[...e].sort().map(n=>wo(n,t))}function So(t){let e=/^(\d+)\s*([mhd])$/.exec(String(t??"").trim());if(!e)return;let n=Number(e[1]);return e[2]==="m"?n:e[2]==="h"?n*60:n*1440}import{createHash as ko}from"crypto";var ou={enabled:!0,repeatWindowHours:24,channels:["push","ntfy"],addresses:[],digestNags:!0},xo=40,vo=20,au=90,$o=2e3,mr=36e5;function Eo(t,e,n){if(t.key?.trim())return t.key.trim().slice(0,200);if(!t.textKey)return null;let r=`${e}
|
|
181
|
+
${n}`.replace(/\s+/g," ").trim().toLowerCase();return r.length<xo?null:`text:${ko("sha256").update(r).digest("hex").slice(0,24)}`}function lu(t){let e=t.indexOf(`
|
|
182
|
+
`);return e<0?{title:t.trim(),body:""}:{title:t.slice(0,e).trim(),body:t.slice(e+1).trim()}}function at(t){t.exec(`
|
|
183
|
+
CREATE TABLE IF NOT EXISTS owner_told (
|
|
184
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
185
|
+
at INTEGER NOT NULL,
|
|
186
|
+
item_key TEXT NOT NULL,
|
|
187
|
+
request_id TEXT,
|
|
188
|
+
kind TEXT NOT NULL,
|
|
189
|
+
channel TEXT NOT NULL,
|
|
190
|
+
chat_id TEXT NOT NULL,
|
|
191
|
+
sender TEXT,
|
|
192
|
+
title TEXT NOT NULL,
|
|
193
|
+
text TEXT NOT NULL,
|
|
194
|
+
outcome TEXT NOT NULL,
|
|
195
|
+
digested_at INTEGER
|
|
196
|
+
);
|
|
197
|
+
CREATE INDEX IF NOT EXISTS idx_owner_told_key_at ON owner_told(item_key, at);
|
|
198
|
+
CREATE INDEX IF NOT EXISTS idx_owner_told_at ON owner_told(at);
|
|
199
|
+
CREATE INDEX IF NOT EXISTS idx_owner_told_digest ON owner_told(outcome, digested_at);
|
|
200
|
+
`);let e=new Set(t.prepare("PRAGMA table_info(owner_told)").all().map(n=>n.name));for(let[n,r]of _o)if(!e.has(n))try{t.exec(`ALTER TABLE owner_told ADD COLUMN ${n} ${r}`)}catch(i){if(!/duplicate column/i.test(String(i?.message)))throw i}}var _o=[["urgent","INTEGER NOT NULL DEFAULT 0"],["seen_at","INTEGER"],["seen_via","TEXT"],["answered_at","INTEGER"],["answered_via","TEXT"],["escalated_at","INTEGER"],["escalation_of","INTEGER"]];function he(t){return{id:t.id,at:t.at,key:t.item_key,requestId:t.request_id??null,kind:t.kind,channel:t.channel,chatId:t.chat_id,from:t.sender??null,title:t.title,text:t.text,outcome:t.outcome,digestedAt:t.digested_at??null,urgent:t.urgent===1,seenAt:t.seen_at??null,seenVia:t.seen_via??null,answeredAt:t.answered_at??null,answeredVia:t.answered_via??null,escalatedAt:t.escalated_at??null,escalationOf:t.escalation_of??null,state:t.answered_at!=null?"answered":t.seen_at!=null?"seen":"sent"}}var hr=class{constructor(e){this.db=e;at(e)}add(e){return Number(this.db.prepare(`INSERT INTO owner_told (at, item_key, request_id, kind, channel, chat_id, sender, title, text, outcome, urgent, escalation_of)
|
|
201
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(e.at,e.key,e.requestId,e.kind,e.channel,e.chatId,e.from,e.title.slice(0,300),e.text.slice(0,$o),e.outcome,e.urgent?1:0,e.escalationOf??null).lastInsertRowid)}get(e){let n=this.db.prepare("SELECT * FROM owner_told WHERE id = ?").get(e);return n?he(n):null}setOutcome(e,n){this.db.prepare("UPDATE owner_told SET outcome = ? WHERE id = ?").run(n,e)}lastTold(e,n,r=!0){let i=this.db.prepare(`SELECT * FROM owner_told WHERE item_key = ? AND at >= ? AND outcome IN ('sent', 'held'${r?", 'digest'":""}) ORDER BY at DESC LIMIT 1`).get(e,n);return i?he(i):null}waitingForDigest(e=500){return this.db.prepare("SELECT * FROM owner_told WHERE outcome = 'digest' AND digested_at IS NULL ORDER BY at LIMIT ?").all(e).map(he)}waitingCount(){return this.db.prepare("SELECT COUNT(*) AS n FROM owner_told WHERE outcome = 'digest' AND digested_at IS NULL").get().n}markDigested(e,n){let r=this.db.prepare("UPDATE owner_told SET digested_at = ? WHERE id = ?");this.db.transaction(()=>{for(let i of e)r.run(n,i)})()}since(e,n=200){return this.db.prepare("SELECT * FROM owner_told WHERE at >= ? ORDER BY at DESC, id DESC LIMIT ?").all(e,n).map(he)}counts(e){let n={sent:0,held:0,digest:0,repeat:0,failed:0};for(let r of this.db.prepare("SELECT outcome, COUNT(*) AS n FROM owner_told WHERE at >= ? GROUP BY outcome").all(e))n[r.outcome]=r.n;return n}states(e){let n=this.db.prepare(`SELECT SUM(CASE WHEN answered_at IS NULL AND seen_at IS NULL THEN 1 ELSE 0 END) AS sent,
|
|
202
|
+
SUM(CASE WHEN answered_at IS NULL AND seen_at IS NOT NULL THEN 1 ELSE 0 END) AS seen,
|
|
203
|
+
SUM(CASE WHEN answered_at IS NOT NULL THEN 1 ELSE 0 END) AS answered
|
|
204
|
+
FROM owner_told WHERE at >= ? AND outcome IN ('sent', 'held')`).get(e);return{sent:n.sent??0,seen:n.seen??0,answered:n.answered??0}}prune(e){return this.db.prepare("DELETE FROM owner_told WHERE at < ?").run(e).changes}},br=class{constructor(e,n,r=Date.now){this.ledger=e;this.settings=n;this.now=r}isOwnerAddress(e,n){let r=this.settings();return r.enabled?r.channels.includes(e)||r.addresses.includes(`${e}:${n}`):!1}decide(e,n={}){let r=this.settings(),i=this.now(),s=Eo(n,e.title,e.text),o=n.kind??"notice",a=(s?.startsWith("text:")?Math.min(r.repeatWindowHours,vo):r.repeatWindowHours)*mr,c=r.digestNags&&r.digestAvailable!==!1,l="send";!n.urgent&&s&&this.ledger.lastTold(s,i-a,c)?l="repeat":!n.urgent&&o==="nag"&&c&&(l="digest");let u=this.ledger.add({at:i,key:s??`once:${i}`,requestId:n.requestId??null,kind:o,channel:e.channel,chatId:e.chatId,from:n.from??null,title:e.title,text:e.text,outcome:l==="send"?"sent":l,urgent:n.urgent===!0});return{verdict:l,key:s,rowId:u}}outcome(e,n){this.ledger.setOutcome(e,n)}digestLine(){let e=this.ledger.waitingForDigest();return e.length?{line:Ro(e),ids:e.map(n=>n.id)}:null}markDigested(e){this.ledger.markDigested(e,this.now())}report(e,n=50){return{counts:this.ledger.counts(e),landed:this.ledger.states(e),waitingForDigest:this.ledger.waitingCount(),items:this.ledger.since(e,n)}}prune(e){return this.ledger.prune(this.now()-e*24*mr)}};function cu(t,e){let n=t&&typeof t=="object"?t:{},r=(o,a)=>typeof o=="string"&&o.trim()?o.trim().slice(0,a):void 0,i=n.kind==="nag"||n.kind==="notice"?n.kind:void 0,s=r(n.from,100)??r(e,100);return{...r(n.key,190)?{key:`send:${r(n.key,190)}`}:{},...r(n.requestId,100)?{requestId:r(n.requestId,100)}:{},...i?{kind:i}:{},...n.urgent===!0?{urgent:!0}:{},...n.textKey===!0?{textKey:!0}:{},...s?{from:s}:{}}}function Ro(t,e=5,n=3){let r=new Map;for(let a of t){let[c,...l]=a.title.split(": "),u=l.join(": ").trim(),d=r.get(c.trim())??{count:0,names:[]};d.count++,u&&!d.names.includes(u)&&d.names.push(u),r.set(c.trim(),d)}let i=[...r.entries()].sort((a,c)=>c[1].count-a[1].count),s=i.slice(0,e).map(([a,{count:c,names:l}])=>{let u=l.length?` (${l.slice(0,n).join(", ")}${l.length>n?", \u2026":""})`:"";return`${a}${c>1?` \xD7${c}`:""}${u}`}),o=i.length>e?`; ${i.length-e} more kinds`:"";return`${t.length} notice${t.length===1?"":"s"} held for this digest: ${s.join("; ")}${o}.`}var Ao=/(^|\n)[ \t]*```[ \t]*agentx:ui[ \t]*\r?\n/i,To=/(^|\n)[ \t]*```[ \t]*agentx:ui[ \t]*\r?\n([\s\S]*?)\r?\n[ \t]*```[ \t]*(?=\n|$)/gi;function yr(t){if(!t||!t.includes("agentx:ui"))return{cleanText:t};let e=[...t.matchAll(To)];if(e.length===0)return{cleanText:t};let n=e[e.length-1],r=n[2],i=Io(r);if(!i)return{cleanText:t};let s=n.index??0,o=n[1]??"";return{cleanText:(t.slice(0,s)+o+t.slice(s+n[0].length)).trim(),ui:i}}function du(t){if(!t||!t.includes("agentx:ui"))return t;let e=Ao.exec(t);return e?t.slice(0,e.index).trimEnd():t}function Io(t){let e;try{e=JSON.parse(t)}catch{return null}if(!e||typeof e!="object")return null;let n={};if(Array.isArray(e.buttons)){let r=[],i=[],s=[];for(let o of e.buttons)!o||typeof o.label!="string"||(typeof o.url=="string"&&/^https?:\/\//i.test(o.url)?r.push({label:o.label,url:o.url}):typeof o.reply=="string"&&o.reply.trim()?i.push({label:o.label,reply:o.reply.trim()}):o.action!=null&&s.push(o.label));r.length&&(n.buttons=r),i.length&&(n.replies=i),s.length&&(n.skippedActions=s)}if(e.poll&&typeof e.poll=="object"){let r=e.poll.question,i=e.poll.options;typeof r=="string"&&r.trim()&&Array.isArray(i)&&i.filter(s=>typeof s=="string").length>=2&&(n.poll={question:r,options:i.filter(s=>typeof s=="string"),multiple:e.poll.multiple===!0})}if(e.media&&typeof e.media=="object"){let{type:r,url:i,caption:s}=e.media;["image","document","audio","video"].includes(r)&&typeof i=="string"&&i&&(n.media={type:r,url:i,...typeof s=="string"?{caption:s}:{}})}if(Array.isArray(e.quickReplies)){let r=e.quickReplies.filter(i=>typeof i=="string"&&i.trim()).map(i=>i.trim());r.length&&(n.quickReplies=r)}return!n.buttons&&!n.poll&&!n.media&&!n.quickReplies&&!n.replies&&!n.skippedActions?null:n}function gu(t,e){let n=`${e}/call?to=${encodeURIComponent(t.from)}&callId=${encodeURIComponent(t.callId)}`;return t.reason==="camera"?`\u{1F4F7} ${t.from} is sharing a phone camera \u2014 tap to watch: ${n}&watch=1`:`\u{1F4DE} ${t.from} is calling \u2014 tap to join: ${n}`}var Co=5*6e4;function N(t){return t.toLowerCase().replace(/[^a-z0-9]/g,"")}var wr=class{subs=new Set;log;forwarder;localName;allowedCallers;pending=new Map;cleanupTimer;ringHandler;recentRings=new Map;constructor(e,n=[],r=console.error.bind(console,"[webrtc]")){this.localName=e,this.allowedCallers=new Set(n.map(N)),this.log=r,this.cleanupTimer=setInterval(()=>this.cleanupExpired(),1e4)}pendingKey(e,n){return`${e}|${N(n)}`}cleanupExpired(){let e=Date.now();for(let[n,r]of this.pending){let i=r.filter(s=>s.expiresAt>e);i.length===0?this.pending.delete(n):i.length!==r.length&&this.pending.set(n,i)}for(let[n,r]of this.recentRings)r<=e&&this.recentRings.delete(n)}setForwarder(e){this.forwarder=e}setRingHandler(e){this.ringHandler=e}subscribe(e,n,r){return this.addSubscriber(e,n,{kind:"sse",res:r})}subscribeInternal(e,n,r){return this.addSubscriber(e,n,{kind:"callback",deliver:r})}addSubscriber(e,n,r){let i={callId:e,recipient:n,sink:r};this.subs.add(i);let s=r.kind==="sse"?"sse":"internal";this.log(`subscribe[${s}] call=${e} as=${n} (${this.subs.size} total)`);let o=this.pendingKey(e,n),a=this.pending.get(o);if(a&&a.length){let c=Date.now(),l=a.filter(u=>u.expiresAt>c);for(let{signal:u}of l)this.deliverTo(i,u);this.pending.delete(o),l.length&&this.log(`flushed ${l.length} buffered signal(s) to new subscriber call=${e} as=${n}`)}return()=>{this.subs.delete(i),this.log(`unsubscribe[${s}] call=${e} as=${n} (${this.subs.size} remaining)`)}}deliverTo(e,n){try{return e.sink.kind==="sse"?e.sink.res.write(`event: signal
|
|
205
|
+
data: ${JSON.stringify(n)}
|
|
206
|
+
|
|
207
|
+
`):e.sink.deliver(n),!0}catch{return!1}}async handleOutgoing(e){let n=N(e.to);if(n===N(this.localName)||this.hasSubscriberFor(e.callId,n))return this.fanOut(e),{ok:!0};if(!this.forwarder)return{ok:!1,error:"mesh forwarder not wired"};let i=await this.forwarder(e.to,e);return i?{ok:i}:{ok:!1,error:`forward to "${e.to}" failed`}}hasSubscriberFor(e,n){for(let r of this.subs)if(r.callId===e&&N(r.recipient)===n)return!0;return!1}handleIncoming(e){if((e.kind==="offer"||e.kind==="ring")&&this.allowedCallers.size>0&&!this.allowedCallers.has(N(e.from)))return this.log(`reject inbound ${e.kind} from "${e.from}": not in allowedCallers`),{ok:!1,error:"caller not allowed"};if(e.kind==="ring"){let r=Date.now(),i=this.recentRings.get(e.callId);if(i&&i>r)return this.log(`dedup ring for call=${e.callId} (already notified)`),{ok:!0};if(this.recentRings.set(e.callId,r+Co),this.ringHandler)try{this.ringHandler(e)}catch(s){this.log(`ringHandler threw: ${s.message}`)}else this.log(`ring received for call=${e.callId} but no handler wired`);return{ok:!0}}if(this.fanOut(e)===0){let r=this.pendingKey(e.callId,e.to),i=this.pending.get(r)||[];i.push({signal:e,expiresAt:Date.now()+3e4}),this.pending.set(r,i),this.log(`buffered ${e.kind} for call=${e.callId} to=${e.to} (no subscriber yet, ${i.length} pending)`)}return{ok:!0}}fanOut(e){let n=0,r=N(e.to);for(let i of this.subs)i.callId===e.callId&&N(i.recipient)===r&&(this.deliverTo(i,e)?n++:this.subs.delete(i));return n}get subscriberCount(){return this.subs.size}shutdown(){this.cleanupTimer&&clearInterval(this.cleanupTimer);for(let e of this.subs)if(e.sink.kind==="sse")try{e.sink.res.end()}catch{}this.subs.clear(),this.pending.clear()}};import{mkdirSync as No,readFileSync as Sr,renameSync as Do,rmSync as Lo,writeFileSync as Oo}from"fs";import{homedir as Po}from"os";import{dirname as Mo,join as Uo}from"path";var kr=Uo(Po(),".agentx","call.json");function Wo(t=kr,e=Date.now()){try{let n=JSON.parse(Sr(t,"utf-8"));if(n?.active===!0&&!(n.until&&Date.parse(n.until)<=e))return{active:!0,mode:"call",reason:`in a call${n.reason?`: ${n.reason}`:""}`}}catch{}return{active:!1,mode:null,reason:"not in a call"}}function xr(t=jt,e=Wo){let n=t();return n.active?n:e()}function wu(t,e,n=kr,r=Date.now()){let i=null;try{i=JSON.parse(Sr(n,"utf-8"))}catch{}if(i&&i.source&&i.source!=="daemon")return;if(!t){i&&Lo(n,{force:!0});return}No(Mo(n),{recursive:!0});let s=`${n}.${process.pid}.tmp`;Oo(s,JSON.stringify({active:!0,source:"daemon",reason:t,until:new Date(r+e).toISOString()})+`
|
|
208
|
+
`),Do(s,n)}var vr="owner",be=6e4,Ar=60*be,Fo=24*Ar,jo=7*24*Ar,Vo=2*be,xu={enabled:!1,escalateAfterMinutes:10,ladder:[]};function qo(t){t.exec(`
|
|
209
|
+
CREATE TABLE IF NOT EXISTS owner_presence (
|
|
210
|
+
person TEXT NOT NULL,
|
|
211
|
+
surface TEXT NOT NULL,
|
|
212
|
+
at INTEGER NOT NULL,
|
|
213
|
+
PRIMARY KEY (person, surface)
|
|
214
|
+
);
|
|
215
|
+
`)}var $r=class{constructor(e,n=Date.now,r=3e4){this.db=e;this.now=n;this.throttleMs=r;qo(e)}written=new Map;touch(e,n=vr){let r=this.now(),i=`${n}
|
|
216
|
+
${e}`;r-(this.written.get(i)??-1/0)<this.throttleMs||(this.written.set(i,r),this.db.prepare(`INSERT INTO owner_presence (person, surface, at) VALUES (?, ?, ?)
|
|
217
|
+
ON CONFLICT(person, surface) DO UPDATE SET at = MAX(at, excluded.at)`).run(n,e.slice(0,200),r))}list(e=vr){return this.db.prepare("SELECT surface, at FROM owner_presence WHERE person = ? ORDER BY at DESC").all(e)}};function vu(t,e){let n=t.toLowerCase().split("@")[0];return n==="app"?"phone":n==="dashboard"||n==="voice"||n==="webrtc"?"computer":`${n}:${e}`}var Er=class{constructor(e,n=Date.now){this.db=e;this.now=n;at(e)}openRequestRows(){return this.db.prepare(`SELECT id, request_id AS requestId, at FROM owner_told
|
|
218
|
+
WHERE request_id IS NOT NULL AND answered_at IS NULL AND outcome IN ('sent', 'held') AND at >= ?
|
|
219
|
+
ORDER BY at DESC LIMIT 500`).all(this.now()-jo)}seenByRequest(e,n){return this.byRequest(e,r=>this.seen(r,n))}answeredByRequest(e,n,r=this.now()){return this.byRequest(e,i=>this.answered(i,n,r),r)}seenPush(e,n="phone"){let r=[],i=this.db.prepare("SELECT id, title FROM owner_told WHERE channel = 'push' AND outcome IN ('sent', 'held') AND at BETWEEN ? AND ? ORDER BY at DESC");for(let s of e){let o=s.title.endsWith("\u2026")?s.title.slice(0,-1):s.title,c=i.all(s.at-Vo,s.at).find(l=>l.title===s.title||o!==s.title&&l.title.startsWith(o));c&&r.push(c.id)}return this.seen(r,n)}seen(e,n,r=this.now()){return e.length?this.db.prepare(`UPDATE owner_told SET seen_at = ?, seen_via = ?
|
|
220
|
+
WHERE seen_at IS NULL AND (${_r(e)})`).run(r,n,...e,...e,...e).changes:0}answered(e,n,r=this.now()){return e.length?this.db.prepare(`UPDATE owner_told SET answered_at = ?, answered_via = ?, seen_at = COALESCE(seen_at, ?), seen_via = COALESCE(seen_via, ?)
|
|
221
|
+
WHERE answered_at IS NULL AND (${_r(e)})`).run(r,n,r,n,...e,...e,...e).changes:0}byRequest(e,n,r=1/0){let i=new Set(e.filter(Boolean));if(!i.size)return 0;let s=this.openRequestRows().filter(o=>i.has(o.requestId)&&o.at<=r).map(o=>o.id);return n(s)}};function _r(t){let e=t.map(()=>"?").join(", ");return`id IN (${e}) OR escalation_of IN (${e}) OR id IN (SELECT escalation_of FROM owner_told WHERE id IN (${e}) AND escalation_of IS NOT NULL)`}function $u(t,e,n){let r=t.ladder.length?t.ladder:[...e.channels.map(s=>`${s}:default`),...e.addresses],i=[];for(let s of r){let o=s.trim(),a=J(o);a&&n(a)&&!i.includes(o)&&i.push(o)}return i}function J(t){let e=t.indexOf(":");return e>0?t.slice(0,e):""}function Bo(t){let e=t.indexOf(":");return e>0?t.slice(e+1):"default"}function Ho(t){return J(t)==="push"?["phone","desk"]:[t]}function Eu(t,e,n){let r=t.filter(s=>n(J(s)));if(!r.length)return null;let i=null;for(let s of r){let o=Math.max(-1/0,...e.filter(a=>Ho(s).includes(a.surface)).map(a=>a.at));Number.isFinite(o)&&(!i||o>i.at)&&(i={stop:s,at:o})}return i?.stop??r[0]}function zo(t,e,n){let r=t.indexOf(e);return(r<0?t:[...t.slice(r+1),...t.slice(0,r)]).find(s=>s!==e&&n(J(s)))??null}async function _u(t){let{db:e,ledger:n,settings:r,now:i}=t;if(!r.enabled||!(r.escalateAfterMinutes>0))return 0;let s=e.prepare(`SELECT id FROM owner_told
|
|
222
|
+
WHERE urgent = 1 AND outcome = 'sent' AND seen_at IS NULL AND answered_at IS NULL
|
|
223
|
+
AND escalated_at IS NULL AND escalation_of IS NULL AND at <= ? AND at >= ?
|
|
224
|
+
ORDER BY at LIMIT 20`).all(i-r.escalateAfterMinutes*be,i-Fo),o=e.prepare("UPDATE owner_told SET escalated_at = ? WHERE id = ? AND escalated_at IS NULL"),a=0;for(let{id:c}of s){if(o.run(i,c).changes!==1)continue;let l=n.get(c);if(!l)continue;let u=`${l.channel}:${l.chatId}`,d=zo(t.stops,u,t.up);if(!d){t.log(`[reach] urgent "${l.title.slice(0,80)}" unseen on ${u}; no other address to try`);continue}let p=Math.round((i-l.at)/be),g=`${l.text?`${l.text}
|
|
225
|
+
|
|
226
|
+
`:""}(Sent again here: not seen on ${l.channel} for ${p} min.)`,f=n.add({at:i,key:l.key,requestId:l.requestId,kind:l.kind,channel:J(d),chatId:Bo(d),from:l.from,title:l.title,text:g,outcome:"sent",urgent:!0,escalationOf:l.id});try{await t.send(d,l,g),a++,t.log(`[reach] urgent "${l.title.slice(0,80)}" unseen on ${u} for ${p} min; sent once to ${d}`)}catch(m){n.setOutcome(f,"failed"),t.log(`[reach] urgent "${l.title.slice(0,80)}": escalation to ${d} failed: ${m?.message??m}`)}}return a}var Rr=class{constructor(e){this.opts=e}state=new Map;tick(){let e=(this.opts.now??Date.now)(),n=this.opts.confirmChecks??2;for(let[r,i]of Object.entries(this.opts.read())){let s=this.state.get(r)??{downSince:null,checks:0,told:!1};if(i.up){s.downSince!==null&&this.opts.log(`[reach] channel ${r} is up again`),this.state.set(r,{downSince:null,checks:0,told:!1});continue}s.downSince??=e,s.checks++,s.reason=i.reason,!s.told&&s.checks>=n&&(s.told=!0,this.opts.log(`[reach] channel ${r} is down: ${i.reason??"no reason given"}`),this.opts.notice(r,i.reason??"no reason given")),this.state.set(r,s)}}downSince(e){return this.state.get(e)?.downSince??void 0}};var Xo={perClient:5,global:10,windowMs:10*60*1e3,lockoutMs:5*60*1e3},Tr=class{constructor(e=Xo,n=Date.now){this.opts=e;this.now=n}clients=new Map;all={failures:[],lockedUntil:0};retryAfter(e){let n=this.now(),r=Math.max(this.all.lockedUntil,this.clients.get(e)?.lockedUntil??0);return r>n?Math.ceil((r-n)/1e3):0}fail(e){let n=this.clients.get(e);n||this.clients.set(e,n={failures:[],lockedUntil:0}),this.bump(n,this.opts.perClient),this.bump(this.all,this.opts.global)}succeed(e){this.clients.delete(e)}bump(e,n){let r=this.now();e.failures=e.failures.filter(i=>r-i<this.opts.windowMs),e.failures.push(r),e.failures.length>=n&&(e.lockedUntil=r+this.opts.lockoutMs)}},Go="That code didn't work. Check it, or run agentx app pair for a new one.",Ko=400;async function Tu(t,e){let n=Date.now(),r=e.log??(l=>console.log(l)),i=t.socket.remoteAddress||"unknown",s=e.limiter.retryAfter(i);if(s>0)return r(`[app] pair-code refused from ${i}: locked out for ${s}s more`),{status:429,body:{error:"too many attempts",retryAfter:s},retryAfter:s};let o=null;try{o=(await rn(t,1024)).code}catch{}let a=e.codes.redeem(o),c=a?e.verify(a.token):null;return await Jo(n,e.minMs??Ko),!a||!c?(e.limiter.fail(i),r(`[app] pair-code failed from ${i}`),{status:401,body:{error:Go}}):(e.limiter.succeed(i),r(`[app] pair-code paired ${a.tokenId} (${c}) from ${i}`),{status:200,body:{device:c},token:a.token})}async function Jo(t,e){let n=t+e-Date.now();n>0&&await new Promise(r=>setTimeout(r,n))}import{execFile as ga}from"child_process";import{mkdtempSync as oa,rmSync as aa,writeFileSync as la}from"fs";import{tmpdir as ca}from"os";import{join as Lr}from"path";import{randomBytes as Yo}from"crypto";var T={top:8,side:32,bottom:60},lt={height:56,keep:52},Ir=`
|
|
227
|
+
:root {
|
|
228
|
+
--ac-paper: #fafaf9; --ac-soft: #f4f4f2; --ac-edge: #e9e9e6;
|
|
229
|
+
--ac-text: #0b1413; --ac-muted: #4a5856; --ac-subtle: #7c8987;
|
|
230
|
+
--ac-primary: #1e3a8a; --ac-primary-deep: #172554;
|
|
231
|
+
--ac-blue: #1e3a8a; --ac-blue-2: #1d4ed8;
|
|
232
|
+
--ac-accent: #14b8a6; --ac-accent-deep: #0f766e;
|
|
233
|
+
--ac-rim: rgba(11,20,19,.10);
|
|
234
|
+
--ac-shadow: 0 0 0 .5px rgba(11,20,19,.05), 0 24px 48px -16px rgba(11,20,19,.30), 0 2px 6px rgba(11,20,19,.06);
|
|
235
|
+
--ac-lift: 0 12px 40px rgba(30,58,138,.20), 0 2px 6px rgba(11,20,19,.06);
|
|
236
|
+
--ac-font: "Geist", -apple-system, BlinkMacSystemFont, "Noto Sans Arabic", system-ui, sans-serif;
|
|
237
|
+
--ac-mono: "Geist Mono", ui-monospace, "SF Mono", Menlo, monospace;
|
|
238
|
+
color-scheme: light;
|
|
239
|
+
}
|
|
240
|
+
[data-theme="dark"] {
|
|
241
|
+
--ac-paper: #111c1b; --ac-soft: #172322; --ac-edge: #263433;
|
|
242
|
+
--ac-text: #e8eeed; --ac-muted: #a7b3b1; --ac-subtle: #83918f;
|
|
243
|
+
--ac-primary: #2563eb; --ac-primary-deep: #1d4ed8;
|
|
244
|
+
--ac-blue: #93c5fd; --ac-blue-2: #60a5fa;
|
|
245
|
+
--ac-accent-deep: #5eead4;
|
|
246
|
+
--ac-rim: rgba(255,255,255,.09);
|
|
247
|
+
--ac-shadow: 0 0 0 .5px rgba(0,0,0,.6), 0 28px 56px -16px rgba(0,0,0,.65), 0 2px 8px rgba(0,0,0,.3);
|
|
248
|
+
color-scheme: dark;
|
|
249
|
+
}
|
|
250
|
+
* { box-sizing: border-box; }
|
|
251
|
+
html, body { margin: 0; background: transparent; }
|
|
252
|
+
body {
|
|
253
|
+
font: 13.5px/1.5 var(--ac-font); color: var(--ac-text); font-feature-settings: "ss01", "cv11";
|
|
254
|
+
-webkit-font-smoothing: antialiased; -webkit-user-select: none; user-select: none; cursor: default;
|
|
255
|
+
}
|
|
256
|
+
.wrap { padding: ${T.top}px ${T.side}px ${T.bottom}px; }
|
|
257
|
+
.card {
|
|
258
|
+
background: var(--ac-paper); border: 1px solid var(--ac-rim); border-radius: 18px;
|
|
259
|
+
box-shadow: var(--ac-shadow); overflow: hidden; animation: ac-in .26s cubic-bezier(.65,0,.35,1);
|
|
260
|
+
position: relative; display: flex; flex-direction: column; max-height: calc(100vh - ${T.top+T.bottom}px);
|
|
261
|
+
}
|
|
262
|
+
.card::before {
|
|
263
|
+
content: ""; position: absolute; top: 6px; left: 50%; width: 32px; height: 4px; margin-left: -16px;
|
|
264
|
+
border-radius: 2px; background: var(--ac-edge);
|
|
265
|
+
}
|
|
266
|
+
@keyframes ac-in { from { transform: translateY(14px); opacity: 0; } }
|
|
267
|
+
.card.still { animation: none; }
|
|
268
|
+
@media (prefers-reduced-motion: reduce) { .card { animation: none; } }
|
|
269
|
+
.head { flex: none; display: flex; align-items: center; gap: 10px; padding: 18px 20px 0; cursor: grab; }
|
|
270
|
+
.body { padding: 14px 20px 16px; min-height: 0; overflow-y: auto; }
|
|
271
|
+
.fold {
|
|
272
|
+
flex: none; width: 24px; height: 24px; display: grid; place-items: center; padding: 0; border-radius: 6px;
|
|
273
|
+
border: 1px solid var(--ac-edge); background: var(--ac-soft); color: var(--ac-subtle); cursor: pointer;
|
|
274
|
+
}
|
|
275
|
+
.fold:hover { color: var(--ac-text); border-color: var(--ac-subtle); }
|
|
276
|
+
.folded .fold svg { transform: rotate(180deg); }
|
|
277
|
+
.folded .body > :not(h1), .folded .foot { display: none; }
|
|
278
|
+
.avatar {
|
|
279
|
+
flex: none; width: 32px; height: 32px; border-radius: 50%; display: grid; place-items: center;
|
|
280
|
+
font-size: 14px; font-weight: 600; color: #fff;
|
|
281
|
+
background: linear-gradient(135deg, var(--ac-blue-2), var(--ac-accent));
|
|
282
|
+
}
|
|
283
|
+
.who { display: flex; flex-direction: column; gap: 1px; min-width: 0; flex: 1; }
|
|
284
|
+
.who b { font-size: 13.5px; font-weight: 560; letter-spacing: -.008em; line-height: 1.25; }
|
|
285
|
+
.who span { font-size: 12px; line-height: 1.3; color: var(--ac-subtle); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
286
|
+
.label, .kind, .edited { font-family: var(--ac-mono); font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; }
|
|
287
|
+
.kind {
|
|
288
|
+
padding: 3px 8px; border-radius: 999px; white-space: nowrap;
|
|
289
|
+
color: var(--ac-muted); background: var(--ac-soft); border: 1px solid var(--ac-edge);
|
|
290
|
+
}
|
|
291
|
+
.kind.decision {
|
|
292
|
+
color: var(--ac-blue); background: color-mix(in oklab, var(--ac-blue-2) 10%, var(--ac-paper));
|
|
293
|
+
border-color: color-mix(in oklab, var(--ac-blue-2) 22%, var(--ac-edge));
|
|
294
|
+
}
|
|
295
|
+
h1 { margin: 0; font-size: 18.5px; line-height: 1.3; font-weight: 540; letter-spacing: -.018em; text-wrap: balance; }
|
|
296
|
+
h1:dir(rtl) { letter-spacing: 0; }
|
|
297
|
+
.context { margin: 6px 0 0; color: var(--ac-muted); white-space: pre-wrap; unicode-bidi: plaintext; text-wrap: pretty; }
|
|
298
|
+
.ask { margin: 10px 0 0; font-weight: 500; unicode-bidi: plaintext; }
|
|
299
|
+
.label-row { margin-top: 16px; display: flex; align-items: center; gap: 8px; min-height: 18px; }
|
|
300
|
+
.label { color: var(--ac-subtle); margin-inline-end: auto; }
|
|
301
|
+
.dot { flex: none; width: 6px; height: 6px; border-radius: 50%; background: var(--ac-accent); }
|
|
302
|
+
.rec { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: var(--ac-subtle); }
|
|
303
|
+
.why { margin: 8px 0 0; font-size: 12px; line-height: 1.4; color: var(--ac-subtle); unicode-bidi: plaintext; }
|
|
304
|
+
.options { margin-top: 8px; display: flex; flex-wrap: wrap; gap: 6px; }
|
|
305
|
+
.opt {
|
|
306
|
+
display: inline-flex; align-items: center; gap: 8px; padding: 5px 12px 5px 5px; padding-inline: 5px 12px;
|
|
307
|
+
border-radius: 999px; border: 1px solid var(--ac-edge); background: var(--ac-paper); color: var(--ac-text);
|
|
308
|
+
font: inherit; font-size: 13px; line-height: 1.35; text-align: start; cursor: pointer;
|
|
309
|
+
transition: background .15s ease, border-color .15s ease, transform .15s ease;
|
|
310
|
+
}
|
|
311
|
+
.opt:hover { transform: translateY(-1px); }
|
|
312
|
+
.opt .n {
|
|
313
|
+
flex: none; width: 20px; height: 20px; display: grid; place-items: center; border-radius: 6px;
|
|
314
|
+
border: 1px solid var(--ac-edge); border-bottom-width: 2px; background: var(--ac-soft); color: var(--ac-subtle);
|
|
315
|
+
font-family: var(--ac-mono); font-size: 10.5px;
|
|
316
|
+
}
|
|
317
|
+
.opt .t { unicode-bidi: plaintext; }
|
|
318
|
+
.opt[aria-checked="true"] { background: var(--ac-primary); border-color: var(--ac-primary); color: #fff; }
|
|
319
|
+
.opt[aria-checked="true"] .n { background: rgba(255,255,255,.16); border-color: rgba(255,255,255,.28); color: #fff; }
|
|
320
|
+
.opt[aria-checked="true"] .dot { background: #5eead4; }
|
|
321
|
+
.edited { display: none; align-items: center; gap: 5px; color: var(--ac-accent-deep); }
|
|
322
|
+
.edited .dot { width: 5px; height: 5px; }
|
|
323
|
+
.reset { display: none; border: 0; background: none; padding: 0; font: inherit; font-size: 12px; color: var(--ac-muted); cursor: pointer; }
|
|
324
|
+
.reset:hover { color: var(--ac-text); }
|
|
325
|
+
.touched .edited { display: inline-flex; }
|
|
326
|
+
.touched .reset { display: inline; }
|
|
327
|
+
textarea {
|
|
328
|
+
margin-top: 6px; display: block; width: 100%; min-height: 60px; max-height: 240px; resize: none; overflow-y: auto;
|
|
329
|
+
padding: 10px 12px; border: 1px solid var(--ac-edge); border-radius: 10px; background: var(--ac-soft);
|
|
330
|
+
color: var(--ac-text); font: inherit; line-height: 1.45; outline: none; unicode-bidi: plaintext;
|
|
331
|
+
-webkit-user-select: text; user-select: text; cursor: text; transition: border-color .15s ease, box-shadow .15s ease;
|
|
332
|
+
}
|
|
333
|
+
textarea::placeholder { color: var(--ac-subtle); }
|
|
334
|
+
textarea:focus {
|
|
335
|
+
border-color: var(--ac-accent); background: var(--ac-paper);
|
|
336
|
+
box-shadow: 0 0 0 4px color-mix(in oklab, var(--ac-accent) 18%, transparent);
|
|
337
|
+
}
|
|
338
|
+
.notes { margin-top: 14px; display: flex; flex-direction: column; gap: 5px; font-size: 12px; line-height: 1.4; color: var(--ac-subtle); }
|
|
339
|
+
.notes div { display: flex; gap: 7px; align-items: flex-start; }
|
|
340
|
+
.notes svg { flex: none; margin-top: 1.5px; }
|
|
341
|
+
.foot { flex: none; display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--ac-edge); background: var(--ac-soft); }
|
|
342
|
+
.foot .later { margin-inline-end: auto; }
|
|
343
|
+
button.btn {
|
|
344
|
+
display: inline-flex; align-items: center; gap: 8px; padding: 9px 14px; border-radius: 999px;
|
|
345
|
+
font: inherit; font-weight: 500; letter-spacing: -.005em; white-space: nowrap; cursor: pointer;
|
|
346
|
+
background: transparent; color: var(--ac-text); border: 1px solid var(--ac-edge);
|
|
347
|
+
transition: transform .15s ease, background .15s ease, box-shadow .15s ease, border-color .15s ease;
|
|
348
|
+
}
|
|
349
|
+
button.btn:hover { background: var(--ac-soft); border-color: var(--ac-subtle); }
|
|
350
|
+
button.primary, button.primary:hover { background: var(--ac-primary); border-color: transparent; color: #fff; }
|
|
351
|
+
button.primary:hover:not(:disabled) { transform: translateY(-1px); background: var(--ac-primary-deep); box-shadow: var(--ac-lift); }
|
|
352
|
+
button.primary:disabled { opacity: .55; cursor: not-allowed; }
|
|
353
|
+
kbd { font: 11px var(--ac-mono); color: var(--ac-subtle); }
|
|
354
|
+
button.primary kbd { color: inherit; opacity: .7; }
|
|
355
|
+
`;var Cr=t=>t.toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit"}),Nr=t=>`${t.getDate()} ${t.toLocaleDateString("en-US",{month:"short"})}`;function Zo(t,e){let n=new Date(t);return Number.isNaN(n.getTime())?"":`since ${n.toDateString()===new Date(e).toDateString()?Cr(n):Nr(n)}`}function Qo(t,e){let n=new Date(t),r=n.getTime()-e<6*864e5;return[n.toLocaleDateString("en-US",{weekday:"short"}),...r?[]:[Nr(n)],Cr(n)].join(" ")}function ea(t){let e=t.trim().split(/[\s_-]+/).filter(Boolean);return(e.length>1?e[0][0]+e[1][0]:t.trim().slice(0,1)).toUpperCase()}function ta(t){return t.origin?.kind==="request"?"Hand it back":t.draft?"Send":t.choices?.length?"Choose":"Yes"}var ct=t=>`<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${t}</svg>`,na=ct('<circle cx="12" cy="12" r="9"/><path d="M12 7v5l3 2"/>'),ra=ct('<path d="m6 15 6-6 6 6"/>'),ia=ct('<rect x="5" y="11" width="14" height="10" rx="2"/><path d="M8 11V7a4 4 0 0 1 8 0v4"/>');function Dr(t,e={}){let n=e.now??Date.now(),r=e.from||t.raised_by,i=Yo(12).toString("base64"),s=t.choices??[],o=t.origin?.kind==="reminder",a=t.origin?.kind==="request",c=Yt(t),l=s.map((p,g)=>`<button class="opt" role="radio" aria-checked="false" data-i="${g}"><span class="n">${g+1}</span><span class="t">${w(p)}</span>${g===c.index?'<i class="dot"></i>':""}</button>`).join(""),u=t.recommend?`<span class="rec"><i class="dot"></i>${w(r)} recommends</span>`:"",d={choices:s,draft:t.draft??"",placeholder:Kt,chime:e.sound==="chime",volume:Math.min(1,Math.max(0,e.volume??.4)),theme:e.theme??"system",pick:e.pick&&e.pick<=s.length?e.pick-1:c.index};return`<!doctype html>
|
|
356
|
+
<html lang="en"><head><meta charset="utf-8">
|
|
357
|
+
<meta http-equiv="Content-Security-Policy" content="default-src 'none'; style-src 'unsafe-inline'; script-src 'nonce-${i}'">
|
|
358
|
+
<title>AgentX</title>
|
|
359
|
+
<style>${Ir}</style></head>
|
|
360
|
+
<body><div class="wrap"><main class="card${e.still?" still":""}">
|
|
361
|
+
<div class="head" title="Drag to move"><span class="avatar">${w(ea(r))}</span>
|
|
362
|
+
<div class="who"><b>${w(r)}</b><span>${w(Zo(t.created_at,n))}</span></div>
|
|
363
|
+
<span class="kind${o?"":" decision"}">${o?"Reminder":a?"Request":"Decision"}</span>
|
|
364
|
+
<button class="fold" id="fold" title="Shrink" aria-label="Shrink the card" aria-expanded="true">${ra}</button></div>
|
|
365
|
+
<div class="body"><h1 dir="auto">${w(t.title)}</h1>
|
|
366
|
+
${t.context?`<p class="context" dir="auto">${w(t.context)}</p>`:""}
|
|
367
|
+
<p class="ask" dir="auto">${w(t.ask)}</p>
|
|
368
|
+
${l||u?`<div class="label-row">${l?'<span class="label">Pick one</span>':""}${u}</div>`:""}
|
|
369
|
+
${l?`<div class="options" role="radiogroup">${l}</div>`:""}
|
|
370
|
+
${c.why?`<p class="why" dir="auto">${w(c.why)}</p>`:""}
|
|
371
|
+
${t.draft?`<div class="label-row" id="msg"><span class="label">Message</span><span class="edited"><i class="dot"></i>Edited</span><button class="reset" id="reset">Reset</button></div>
|
|
372
|
+
<textarea id="text" dir="auto" rows="2" spellcheck="true"></textarea>`:""}
|
|
373
|
+
<div class="notes"><div>${na}<span>${a?"It stays in Open requests on the Approvals page until you close it.":`If you don't answer by ${w(Qo(t.expires,n))}: ${w(t.if_silent)}.`}</span></div>
|
|
374
|
+
<div>${ia}<span>Nothing goes out until you click.</span></div></div>
|
|
375
|
+
</div><div class="foot"><button class="btn later" id="later">Not now<kbd>esc</kbd></button>
|
|
376
|
+
<button class="btn" id="no">${a?"Drop":"No"}</button>
|
|
377
|
+
<button class="btn primary" id="yes">${ta(t)}<kbd>\u2318\u21A9</kbd></button></div>
|
|
378
|
+
</main></div>
|
|
379
|
+
<script type="application/json" id="data">${JSON.stringify(d).replace(/</g,"\\u003c")}</script>
|
|
380
|
+
<script nonce="${i}">${sa}</script>
|
|
381
|
+
</body></html>`}var sa=`
|
|
382
|
+
(function () {
|
|
383
|
+
var d = JSON.parse(document.getElementById("data").textContent);
|
|
384
|
+
var root = document.documentElement;
|
|
385
|
+
var dark = d.theme === "dark" || (d.theme === "system" && matchMedia("(prefers-color-scheme: dark)").matches);
|
|
386
|
+
if (dark) root.setAttribute("data-theme", "dark");
|
|
387
|
+
var opts = [].slice.call(document.querySelectorAll(".opt"));
|
|
388
|
+
var box = document.getElementById("text");
|
|
389
|
+
var msg = document.getElementById("msg");
|
|
390
|
+
var yes = document.getElementById("yes");
|
|
391
|
+
var card = document.querySelector(".card");
|
|
392
|
+
var body = document.querySelector(".body");
|
|
393
|
+
var fold = document.getElementById("fold");
|
|
394
|
+
var pick = -1, touched = false, done = false;
|
|
395
|
+
|
|
396
|
+
function fill(i) { return d.draft.split(d.placeholder).join(i >= 0 ? d.choices[i] : "").trim(); }
|
|
397
|
+
function send(a) { if (done) return; done = true; window.agentxAnswer = JSON.stringify(a); document.title = "agentx:answer"; }
|
|
398
|
+
// What the card needs, not what it has: in a window too short for it, its middle scrolls.
|
|
399
|
+
function fit() {
|
|
400
|
+
// The title carries the answer signal once sent: a late resize must not write over it.
|
|
401
|
+
if (done) return;
|
|
402
|
+
var wrap = document.querySelector(".wrap"), head = document.querySelector(".head"), foot = document.querySelector(".foot");
|
|
403
|
+
document.title = "agentx:size:" + Math.ceil(wrap.offsetHeight - card.clientHeight + head.offsetHeight + body.scrollHeight + foot.offsetHeight);
|
|
404
|
+
}
|
|
405
|
+
function mark() { touched = box.value.trim() !== fill(pick); msg.classList.toggle("touched", touched); }
|
|
406
|
+
function update() {
|
|
407
|
+
opts.forEach(function (o, i) { o.setAttribute("aria-checked", String(i === pick)); });
|
|
408
|
+
yes.disabled = (d.choices.length > 0 && pick < 0) || (box && !box.value.trim());
|
|
409
|
+
}
|
|
410
|
+
function grow() { if (!box) return; box.style.height = "auto"; box.style.height = box.scrollHeight + 4 + "px"; }
|
|
411
|
+
// quiet: the pick made on open. The box is not focused, so the number keys still pick.
|
|
412
|
+
function choose(i, quiet) {
|
|
413
|
+
// An edited message keeps the edits, with the old pick swapped for the new one.
|
|
414
|
+
if (box && touched && pick >= 0) box.value = box.value.split(d.choices[pick]).join(d.choices[i]);
|
|
415
|
+
else if (box) box.value = fill(i);
|
|
416
|
+
pick = i;
|
|
417
|
+
if (box) { grow(); mark(); }
|
|
418
|
+
update();
|
|
419
|
+
if (box && !quiet) box.focus();
|
|
420
|
+
}
|
|
421
|
+
opts.forEach(function (o, i) { o.addEventListener("click", function () { choose(i); }); });
|
|
422
|
+
if (box) {
|
|
423
|
+
box.value = d.choices.length ? "" : fill(-1);
|
|
424
|
+
box.placeholder = d.choices.length ? "Pick an option above" : "";
|
|
425
|
+
grow();
|
|
426
|
+
box.addEventListener("input", function () { grow(); mark(); update(); });
|
|
427
|
+
document.getElementById("reset").addEventListener("click", function () { box.value = fill(pick); grow(); mark(); update(); box.focus(); });
|
|
428
|
+
}
|
|
429
|
+
yes.addEventListener("click", function () {
|
|
430
|
+
if (yes.disabled) return;
|
|
431
|
+
var a = { action: "yes" };
|
|
432
|
+
if (pick >= 0) a.choice = d.choices[pick];
|
|
433
|
+
if (box) a.text = box.value.trim();
|
|
434
|
+
send(a);
|
|
435
|
+
});
|
|
436
|
+
document.getElementById("no").addEventListener("click", function () { send({ action: "no" }); });
|
|
437
|
+
document.getElementById("later").addEventListener("click", function () { send({ action: "dismiss", why: "not now" }); });
|
|
438
|
+
fold.addEventListener("click", function () {
|
|
439
|
+
var shrunk = card.classList.toggle("folded");
|
|
440
|
+
fold.setAttribute("aria-expanded", String(!shrunk));
|
|
441
|
+
fold.title = shrunk ? "Show all" : "Shrink";
|
|
442
|
+
fit();
|
|
443
|
+
});
|
|
444
|
+
document.addEventListener("keydown", function (e) {
|
|
445
|
+
if (e.key === "Escape") return send({ action: "dismiss", why: "not now" });
|
|
446
|
+
// A shrunk card hides its buttons: no answer by key while they can't be seen.
|
|
447
|
+
if (card.classList.contains("folded")) return;
|
|
448
|
+
if (e.key === "Enter" && (e.metaKey || e.ctrlKey)) { e.preventDefault(); return yes.click(); }
|
|
449
|
+
// preventDefault: choose() focuses the box, and the digit must not be typed into it.
|
|
450
|
+
if (document.activeElement !== box && /^[1-9]$/.test(e.key) && opts[+e.key - 1]) { e.preventDefault(); choose(+e.key - 1); }
|
|
451
|
+
});
|
|
452
|
+
if (d.pick >= 0) choose(d.pick, true); else update();
|
|
453
|
+
requestAnimationFrame(fit);
|
|
454
|
+
if (box) new ResizeObserver(fit).observe(box);
|
|
455
|
+
|
|
456
|
+
if (d.chime) try {
|
|
457
|
+
var ac = new AudioContext(), t = ac.currentTime + 0.05;
|
|
458
|
+
[[783.99, 0], [1046.5, 0.14]].forEach(function (n) {
|
|
459
|
+
[1, 2].forEach(function (h) {
|
|
460
|
+
var o = ac.createOscillator(), g = ac.createGain();
|
|
461
|
+
o.type = "sine"; o.frequency.value = n[0] * h;
|
|
462
|
+
g.gain.setValueAtTime(0, t + n[1]);
|
|
463
|
+
g.gain.linearRampToValueAtTime(d.volume * (h === 1 ? 0.5 : 0.12), t + n[1] + 0.012);
|
|
464
|
+
g.gain.exponentialRampToValueAtTime(0.0001, t + n[1] + 1.4);
|
|
465
|
+
o.connect(g).connect(ac.destination); o.start(t + n[1]); o.stop(t + n[1] + 1.5);
|
|
466
|
+
});
|
|
467
|
+
});
|
|
468
|
+
} catch (e) {}
|
|
469
|
+
})();
|
|
470
|
+
`;var ua=440,da=`
|
|
471
|
+
ObjC.import("Cocoa"); ObjC.import("WebKit")
|
|
472
|
+
function run(argv) {
|
|
473
|
+
var path = argv[0], side = ${T.side}, gripTop = ${T.top}, gripEnd = ${T.top+lt.height}, keep = ${lt.keep}, w = Number(argv[1]) + 2 * side, h = 420, secs = Number(argv[2]), capture = argv[3] || ""
|
|
474
|
+
var app = $.NSApplication.sharedApplication
|
|
475
|
+
app.setActivationPolicy($.NSApplicationActivationPolicyAccessory)
|
|
476
|
+
ObjC.registerSubclass({ name: "AgentXCardView", superclass: "WKWebView", methods: {
|
|
477
|
+
"acceptsFirstMouse:": { types: ["bool", ["id"]], implementation: function () { return true } },
|
|
478
|
+
} })
|
|
479
|
+
var vis = $.NSScreen.mainScreen.visibleFrame
|
|
480
|
+
var top = vis.origin.y + vis.size.height - 4, right = vis.origin.x + vis.size.width - 14 + side
|
|
481
|
+
var mask = $.NSWindowStyleMaskTitled | $.NSWindowStyleMaskClosable | $.NSWindowStyleMaskFullSizeContentView | $.NSWindowStyleMaskNonactivatingPanel
|
|
482
|
+
var win = $.NSPanel.alloc.initWithContentRectStyleMaskBackingDefer($.NSMakeRect(right - w, top - h, w, h), mask, $.NSBackingStoreBuffered, false)
|
|
483
|
+
win.titlebarAppearsTransparent = true
|
|
484
|
+
win.titleVisibility = $.NSWindowTitleHidden
|
|
485
|
+
win.movableByWindowBackground = true
|
|
486
|
+
win.level = $.NSFloatingWindowLevel
|
|
487
|
+
win.releasedWhenClosed = false
|
|
488
|
+
win.hidesOnDeactivate = false
|
|
489
|
+
win.collectionBehavior = $.NSWindowCollectionBehaviorCanJoinAllSpaces
|
|
490
|
+
win.opaque = false
|
|
491
|
+
win.backgroundColor = $.NSColor.clearColor
|
|
492
|
+
win.hasShadow = false
|
|
493
|
+
;[$.NSWindowCloseButton, $.NSWindowMiniaturizeButton, $.NSWindowZoomButton].forEach(function (b) { win.standardWindowButton(b).hidden = true })
|
|
494
|
+
var cfg = $.WKWebViewConfiguration.alloc.init
|
|
495
|
+
cfg.mediaTypesRequiringUserActionForPlayback = 0
|
|
496
|
+
var web = $.AgentXCardView.alloc.initWithFrameConfiguration($.NSMakeRect(0, 0, w, h), cfg)
|
|
497
|
+
web.autoresizingMask = $.NSViewWidthSizable | $.NSViewHeightSizable
|
|
498
|
+
web.setValueForKey(false, "drawsBackground")
|
|
499
|
+
win.contentView = web
|
|
500
|
+
var url = $.NSURL.fileURLWithPath(path)
|
|
501
|
+
web.loadFileURLAllowingReadAccessToURL(url, url.URLByDeletingLastPathComponent)
|
|
502
|
+
// No focus taken (#394): shown over the app in use, which keeps the keyboard.
|
|
503
|
+
// A click on the card still lands (acceptsFirstMouse); a click in its text
|
|
504
|
+
// box makes the panel key without bringing anything else forward.
|
|
505
|
+
win.orderFrontRegardless
|
|
506
|
+
var end = Date.now() + secs * 1000, sized = 0, shot = false, seen = "", asked = false, answer = null
|
|
507
|
+
// Every kind of event, as a number. JXA hands the AppKit constant for it over as text, and a
|
|
508
|
+
// loop asking with that gets no event at all: no click, no drag, no key.
|
|
509
|
+
var anyEvent = Number.MAX_SAFE_INTEGER
|
|
510
|
+
while (Date.now() < end) {
|
|
511
|
+
var ev = app.nextEventMatchingMaskUntilDateInModeDequeue(anyEvent, $.NSDate.dateWithTimeIntervalSinceNow(0.05), $.NSDefaultRunLoopMode, true)
|
|
512
|
+
if (ev && !ev.isNil()) {
|
|
513
|
+
if (onGrip(ev)) win.performWindowDragWithEvent(ev); else app.sendEvent(ev)
|
|
514
|
+
}
|
|
515
|
+
if (answer !== null) { win.close; return answer }
|
|
516
|
+
if (!win.isVisible) return gone("closed")
|
|
517
|
+
var t = web.title.js || ""
|
|
518
|
+
if (t === "agentx:answer" && !asked) {
|
|
519
|
+
asked = true
|
|
520
|
+
web.evaluateJavaScriptCompletionHandler("window.agentxAnswer", function (r) { answer = String(ObjC.unwrap(r) || "") })
|
|
521
|
+
}
|
|
522
|
+
if (t !== seen && t.indexOf("agentx:size:") === 0) {
|
|
523
|
+
seen = t
|
|
524
|
+
var want = Math.min(Number(t.slice(12)), vis.size.height - 8)
|
|
525
|
+
var f = win.frame
|
|
526
|
+
if (want > 80) { win.setFrameDisplayAnimate($.NSMakeRect(f.origin.x, f.origin.y + f.size.height - want, w, want), true, true); sized = Date.now() }
|
|
527
|
+
}
|
|
528
|
+
if (capture && !shot && sized && Date.now() - sized > 900) {
|
|
529
|
+
shot = true
|
|
530
|
+
var sa = Application.currentApplication(); sa.includeStandardAdditions = true
|
|
531
|
+
sa.doShellScript("/usr/sbin/screencapture -x -o -l " + win.windowNumber + " " + quoted(capture))
|
|
532
|
+
end = Math.min(end, Date.now() + 300)
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
win.close
|
|
536
|
+
return gone("timed out")
|
|
537
|
+
function onGrip(ev) {
|
|
538
|
+
if (ev.type != $.NSEventTypeLeftMouseDown || ev.windowNumber != win.windowNumber) return false
|
|
539
|
+
var p = ev.locationInWindow, down = win.frame.size.height - p.y
|
|
540
|
+
return down >= gripTop && down <= gripEnd && p.x >= side && p.x <= w - side - keep
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
function gone(why) { return JSON.stringify({ action: "dismiss", why: why }) }
|
|
544
|
+
function quoted(s) { return "'" + String(s).replace(/'/g, "'\\\\''") + "'" }
|
|
545
|
+
`;function pa(t,e){let n=t.trim().split(`
|
|
546
|
+
`).pop()??"",r;try{r=JSON.parse(n)}catch{return{action:"dismiss"}}if(r?.action==="no")return{action:"no"};if(r?.action==="dismiss"&&Pr.includes(r.why))return{action:"dismiss",why:r.why};if(r?.action!=="yes")return{action:"dismiss"};let i=e.choices??[],s;if(i.length){if(typeof r.choice!="string"||!i.includes(r.choice))return{action:"dismiss"};s=r.choice}let o;return e.draft&&(o=typeof r.text=="string"?r.text.replace(/\r\n?/g,`
|
|
547
|
+
`).trim():"",!o||o.length>Gt.text)?{action:"dismiss"}:{action:"yes",...s?{choice:s}:{},...o?{text:o}:{}}}async function Or(t,e,n){let r=Math.max(10,Math.round(e.timeoutSeconds)),i=oa(Lr(ca(),"agentx-card-"));try{let s=Lr(i,"card.html");la(s,Dr(t,{...e,still:!!e.capture}),{mode:384});let o=["-l","JavaScript","-e",da,s,String(ua),String(r),...e.capture?[e.capture]:[]],a=await n("/usr/bin/osascript",o,(r+10)*1e3);return a.ok?pa(a.stdout,t):null}finally{aa(i,{recursive:!0,force:!0})}}var Pr=["not now","timed out","closed"],fa=(t,e,n)=>new Promise(r=>{try{ga(t,e,{timeout:n},(i,s)=>r({ok:!i,stdout:String(s??"")}))}catch{r({ok:!1,stdout:""})}}),Mr=160,ut=15e3;function Ur(t){let e=(t.say||t.title).replace(/\s+/g," ").trim();return e.length>Mr?e.slice(0,Mr):e}async function ma(t,e,n,r,i){let s=e.sound==="chime"?r?"":"Glass":e.sound,o=s?nn(s):null;if(o&&await n("/usr/bin/afplay",["-v",String(Math.min(1,Math.max(0,e.volume))),o],ut),!!e.speak)if(i){let a;await Promise.race([i(Ur(t)),new Promise(c=>{a=setTimeout(c,ut)})]),clearTimeout(a)}else{let a=e.voice&&/^[\w .()-]+$/.test(e.voice)?["-v",e.voice]:[];await n("/usr/bin/say",[...a,"--",Ur(t)],ut)}}function jr(t,e){return[...t.flatMap(n=>["-e",n]),...e]}function ha(t,e,n){return jr(["on run argv","set opts to items 3 thru -1 of argv",'set pick to choose from list opts with title (item 1 of argv) with prompt (item 2 of argv) OK button name "Next" cancel button name "Not now"','if pick is false then return ""',"return item 1 of pick","end run"],[t,e,...n])}function Wr(t,e,n,r,i){return jr(["on run argv","set AppleScript's text item delimiters to linefeed","set btns to text items of (item 3 of argv)","set secs to (item 5 of argv) as integer","if (count of argv) > 5 then","set r to display dialog (item 2 of argv) with title (item 1 of argv) default answer (item 6 of argv) buttons btns default button (item 4 of argv) giving up after secs","else","set r to display dialog (item 2 of argv) with title (item 1 of argv) buttons btns default button (item 4 of argv) giving up after secs","end if",'if gave up of r then return ""',"if (count of argv) > 5 then return (button returned of r) & linefeed & (text returned of r)","return button returned of r","end run"],[t,e,n.join(`
|
|
548
|
+
`),n[n.length-1],String(r),...i!==void 0?[i]:[]])}function Fr(t){let e=t.replace(/\n$/,"");if(!e)return null;let n=e.indexOf(`
|
|
549
|
+
`);return n<0?{button:e,text:""}:{button:e.slice(0,n),text:e.slice(n+1)}}function dt(t){return[t.ask,t.recommend?`Recommended: ${t.recommend}`:"",`From ${t.raised_by}`].filter(Boolean).join(`
|
|
550
|
+
|
|
551
|
+
`)}async function Vr(t,e,n={}){let r=n.run??fa,i=(e.style??"card")==="card",s=ma(t,e,r,i,n.speak).catch(()=>{});if(i){let o=await Or(t,{timeoutSeconds:e.timeoutSeconds,sound:e.sound,volume:e.volume,theme:e.theme,from:n.from,capture:n.capture,pick:n.pick},r).catch(()=>null);if(o)return o}return await s,ba(t,e,r)}async function ba(t,e,n){let r=Math.max(10,Math.round(e.timeoutSeconds)),i=(r+5)*1e3,s=`${t.raised_by}: ${t.title}`,o;if(t.choices?.length){let l=await n("/usr/bin/osascript",ha(s,dt(t),t.choices),i);if(o=l.stdout.replace(/\n$/,""),!l.ok||!t.choices.includes(o))return{action:"dismiss"};if(!t.draft)return{action:"yes",choice:o}}if(t.draft){let l=o?`${o}
|
|
552
|
+
|
|
553
|
+
Message (edit it if you like):`:`${dt(t)}
|
|
554
|
+
|
|
555
|
+
Message (edit it if you like):`,d=await n("/usr/bin/osascript",Wr(s,l,o?["Cancel","Send"]:["Not now","No","Send"],r,Jt(t.draft,o)),i),p=d.ok?Fr(d.stdout):null;if(p?.button==="Send"){let g=p.text.trim();return g?{action:"yes",...o?{choice:o}:{},text:g}:{action:"dismiss"}}return p?.button==="No"?{action:"no"}:{action:"dismiss"}}let a=await n("/usr/bin/osascript",Wr(s,dt(t),["Not now","No","Yes"],r),i),c=a.ok?Fr(a.stdout):null;return c?.button==="Yes"?{action:"yes"}:c?.button==="No"?{action:"no"}:{action:"dismiss"}}var ft=24*36e5,pt=!1,ya=10*6e4,qr=-1/0;function Br(t){let e=t.now??Date.now(),{popped:n={},snoozed:r,passAt:i,wanted:s=[]}=x(t.root),o=i?Date.parse(i):NaN,a=M(t.root,"pending").filter(l=>Date.parse(l.expires)>e),c=a.find(l=>s.includes(`card:${l.id}`));if(c)return c;for(let l of a){let u=`card:${l.id}`;if(n[u]||ln(t.root,l.id,e))continue;let d=r[u];if(d&&Date.parse(d)>e)continue;let p=Date.parse(l.created_at);if(!(e-p>ft&&!(p<=o)))return l}return null}function gt(t){try{return X(t)?.listByState("needs_attention")??[]}catch{return[]}}var Hr=new Map;function wa(t){let e=t.now??Date.now(),{popped:n={},snoozed:r}=x(t.root);return gt(t).find(i=>{let s=`request:${i.id}`,o=r[s];return Hr.get(i.id)===q(i,Ae.title).headline?!1:!n[s]&&!(o&&Date.parse(o)>e)&&e-i.updatedAt<=ft})??null}function Sa(t,e){return{id:t.id,title:q(t,Ae.title).headline,context:t.attentionReason??"It is not finished and nothing is retrying it.",ask:`Hand it back to ${t.agentId}, or drop it?`,say:"A request you made is not finished.",recommend:"",if_silent:"keep",expires:new Date(e+ft).toISOString(),raised_by:t.agentId,created_at:new Date(t.createdAt).toISOString(),status:"pending",origin:{kind:"request",id:t.id}}}async function sd(t){let{ctx:e,settings:n,log:r}=t;if(!n.enabled||(t.platform??process.platform)!=="darwin")return"off";if(pt)return"busy";let i=Br(e)?null:wa(e),s=i?Sa(i,e.now??Date.now()):Br(e);if(!s)return"none";let o=(t.focus??xr)();if(o.active)return"held";let a=`${i?"request":"card"}:${s.id}`,c=(x(e.root).wanted??[]).includes(a)?null:sn(s);if(c){if(i)Hr.set(i.id,s.title);else{let l=[...M(e.root,"pending").map(u=>`card:${u.id}`),...gt(e).map(u=>`request:${u.id}`)];Ne(e.root,a,l,e.now)}return r(`[approvals] popup: ${a} not shown, back to ${s.raised_by}: ${c}`),t.returnToAuthor?.(s,on(s,c)),"returned"}pt=!0;try{let l=[...M(e.root,"pending").map(b=>`card:${b.id}`),...gt(e).map(b=>`request:${b.id}`)];Ne(e.root,a,l,e.now),r(`[approvals] popup: showing ${a} from ${s.raised_by}`);let u=e.now??Date.now(),d=!!i&&u-qr<ya;i&&n.speak&&(qr=u);let p=t.agentName?.(s.raised_by)??(s.node?`${s.raised_by} on ${s.node}`:void 0),g=await(t.show??Vr)(s,d?{...n,speak:!1}:n,{from:p,speak:t.speak});if(g.action==="dismiss")return r(`[approvals] popup: ${a} left waiting: ${g.why??"no answer"} (${Vt(o)} when shown)`),"shown";if(!(i?X(e)?.get(i.id)?.state==="needs_attention":B(e.root,s.id)?.status==="pending"))return r(`[approvals] popup: ${a} was answered elsewhere; popup answer ignored`),"shown";let m=await On(e,a,g.action,{by:"operator (popup)",...g.action==="yes"?{choice:g.choice,text:g.text}:{}});return r(m.ok?`[approvals] popup: ${m.message}`:`[approvals] popup: ${a} not recorded: ${m.error}`),"shown"}catch(l){return r(`[approvals] popup failed for ${a}: ${l?.message??l}`),"shown"}finally{pt=!1}}function od(t,e,n){if(!n)return{ok:!1,error:"the Mac popup is off; turn it on with `agentx approvals settings --popup on`"};let r=e.startsWith("card:")?B(t.root,e.slice(5)):null;return r?r.status!=="pending"?{ok:!1,error:`${e} is already ${r.status}`}:(yn(t.root,e),{ok:!0,message:`${e} will show on the Mac within a minute`}):{ok:!1,error:`no card "${e}"; the popup shows decision cards only`}}import{closeSync as Y,createReadStream as ka,fstatSync as xa,openSync as va,realpathSync as zr}from"fs";import{isAbsolute as $a,resolve as Xr,sep as Gr}from"path";var pd="/app-files",Ea=/^[A-Za-z0-9_.:-]{1,64}$/;function _a(t,e,n=process.cwd()){if(!e||e.length>re.pathChars||e.includes("\0"))return{ok:!1,status:400,error:"bad file path"};if(e.split(/[\\/]/).includes(".."))return{ok:!1,status:403,error:`the path leaves the workspace: copy the file into ${Re}/ first`};if(!_e(e))return{ok:!1,status:415,error:"this type of file is not served"};let r;try{r=zr(Xr(n,t))}catch{return{ok:!1,status:404,error:"the agent's workspace is missing"}}let i;try{i=zr($a(e)?e:Xr(r,e))}catch{return{ok:!1,status:404,error:"file not found"}}if(!i.startsWith(r+Gr))return{ok:!1,status:403,error:`the file is outside the agent's workspace: copy it into ${Re}/ first`};let s=_e(i);return s?{ok:!0,path:i,...s}:{ok:!1,status:415,error:"this type of file is not served"}}function Ra(t,e){let n=t.replace(/[^\x20-\x7e]/g,"_").replace(/["\\;]/g,"_").slice(0,120)||"file";return`${e==="file"?"attachment":"inline"}; filename="${n}"; filename*=UTF-8''${encodeURIComponent(t.slice(0,120))}`}function Aa(t,e,n){return{"Content-Type":t,"Content-Disposition":Ra(e,n),"X-Content-Type-Options":"nosniff","Content-Security-Policy":"default-src 'none'; img-src 'self'; media-src 'self'; sandbox","Cache-Control":"private, max-age=3600","Accept-Ranges":"bytes"}}function Ta(t,e){let n=/^bytes=(\d*)-(\d*)$/.exec(String(t||"").trim());if(!n||!n[1]&&!n[2])return null;let r,i;return n[1]?(r=Number(n[1]),i=n[2]?Math.min(Number(n[2]),e-1):e-1):(r=Math.max(0,e-Number(n[2])),i=e-1),r>i||r>=e?"unsatisfiable":{start:r,end:i}}function gd(t,e,n,r){let i=(C,P)=>{e.writeHead(C,{"Content-Type":"application/json","Cache-Control":"no-store","X-Content-Type-Options":"nosniff"}),e.end(JSON.stringify(P))};if(t.method!=="GET"&&t.method!=="HEAD")return i(405,{error:"GET only"});let s=n.searchParams.get("agent")||"",o=n.searchParams.get("file")||"";if(!Ea.test(s))return i(400,{error:"bad agent id"});let a=r.workspaceOf(s);if(!a)return i(404,{error:`no agent "${s}" on this computer`});let c=_a(a,o,r.cwd);if(!c.ok)return r.log?.(`[app-files] refused ${JSON.stringify(o.slice(0,200))} from ${s}: ${c.error}`),i(c.status,{error:c.error});let l;try{l=va(c.path,"r")}catch{return i(404,{error:"file not found"})}let u;try{let C=xa(l);if(!C.isFile())return Y(l),i(404,{error:"not a file"});u=C.size}catch{return Y(l),i(404,{error:"file not found"})}if(u>re.bytes)return Y(l),i(413,{error:`the file is larger than ${re.bytes/1024/1024} MB`});let d=c.path.split(Gr).pop()||"file",p=Aa(c.mime,d,c.kind),g=Ta(t.headers.range,u);if(g==="unsatisfiable"){Y(l),e.writeHead(416,{...p,"Content-Range":`bytes */${u}`}),e.end();return}let{start:f,end:m}=g??{start:0,end:u-1};if(e.writeHead(g?206:200,{...p,"Content-Length":String(u?m-f+1:0),...g?{"Content-Range":`bytes ${f}-${m}/${u}`}:{}}),t.method==="HEAD"||u===0){Y(l),e.end();return}let b=ka("",{fd:l,start:f,end:m,autoClose:!0});b.on("error",()=>e.destroy()),e.on("close",()=>b.destroy()),b.pipe(e)}import{spawn as Z}from"child_process";import{readFileSync as Ia,writeFileSync as Ca}from"fs";import{tmpdir as Na}from"os";import{join as Da}from"path";At();var Kr=t=>t&&t!==1?Math.round(175*t):null,ye=t=>t&&t!==1?Math.min(1.2,Math.max(.7,t)):null;function La(){if(process.env.ELEVENLABS_API_KEY)return process.env.ELEVENLABS_API_KEY;for(let t of[`${process.env.HOME}/.elevenlabs/key`,`${process.env.HOME}/.agentx/elevenlabs-key.txt`])try{let e=Ia(t,"utf8").trim();if(e)return e}catch{}return null}function xd(t=La()){let e=0;return async(n,r)=>{if(n.voice.provider==="system")return null;if(!t){if(n.voice.fallback)return null;throw new Error("ElevenLabs is the voice provider but no key is set")}try{let i=await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${n.voice.elevenlabs}/stream?output_format=mp3_44100_128`,{method:"POST",headers:{"xi-api-key":t,"Content-Type":"application/json"},body:JSON.stringify({text:n.text,model_id:"eleven_flash_v2_5",...ye(n.voice.rate)?{voice_settings:{speed:ye(n.voice.rate)}}:{}}),signal:r});if(!i.ok)throw new Error(`ElevenLabs ${i.status}: ${(await i.text()).slice(0,160)}`);let s=Da(Na(),`agentx-say-${process.pid}-${++e}.mp3`);return Ca(s,Buffer.from(await i.arrayBuffer())),s}catch(i){if(r.aborted||!n.voice.fallback)throw i;return L(`elevenlabs:${String(i?.message??i).slice(0,40)}`,`[voice] ElevenLabs failed (${String(i?.message??i).slice(0,160)}); speaking with the system voice`),null}}}function Jr(t,e){let n=t.languages&&ne(e);return n&&t.languages&&n in t.languages?t.languages[n]:t.system}var Oa=(t,e="")=>{let n=Jr(t,e),r=Kr(t.rate);return[...n&&!Vn(n)?["-v",n]:[],...r?["-r",String(r)]:[]]};function Pa(t,e,n){if(!n)return["say",Oa(t,e)];let r=Jr(t,e);return["/bin/sh",[n,...r?[r]:[]]]}function vd(t){let e=t.split(/\s+/).filter(Boolean).length;return Math.min(300,5+Math.floor(e*6/10))*1e3}function $d(t=mt()){if(t)try{Z("/bin/sh",[t,"--stop"],{stdio:"ignore"}).on("error",()=>{})}catch{}}function Ed(t=mt()){if(t)try{Z("/bin/sh",[t,"--restore"],{stdio:"ignore"}).on("error",()=>{})}catch{}}function mt(t={},e){if(!We(t))return null;try{return Hn(e)}catch(n){return L("siri-say",`[voice] could not write the Siri voice script (${String(n?.message??n).split(`
|
|
556
|
+
`)[0]}); Siri voices will not switch`),null}}var _d=(t,e)=>{if(!t){let[n,r]=Pa(e.voice,e.text,mt()),i=Kr(e.voice.rate),s=i?{...process.env,AGENTX_SAY_RATE:String(i)}:process.env,o=Z(n,r,{stdio:["pipe","ignore","ignore"],env:s});return o.stdin?.on("error",()=>{}),o.stdin?.end(e.text),o}return process.platform==="darwin"?Z("afplay",[t],{stdio:"ignore"}):Z("mpg123",["-q",t],{stdio:"ignore"})};import{spawn as Yr}from"child_process";import{accessSync as Ma,constants as Ua,readFileSync as Zr}from"fs";import{homedir as Wa}from"os";import{basename as j,delimiter as Qr,dirname as Fa,extname as ja,join as ht}from"path";var D={ms:2*60*1e3,bitsPerSecond:64e3,bytes:2*1024*1024};function ei(t){let e=Array.isArray(t)?t[0]:t;return!e||!/^\d{1,9}$/.test(e.trim())?{status:400,error:"Say how long the recording is (X-Audio-Duration-Ms)."}:Number(e)>D.ms+2e3?{status:413,error:yt()}:null}var yt=()=>`The recording is longer than ${D.ms/6e4} minutes.`,Va={"audio/webm":"webm","video/webm":"webm","audio/ogg":"ogg","audio/mp4":"m4a","video/mp4":"m4a","audio/x-m4a":"m4a","audio/aac":"aac","audio/mpeg":"mp3","audio/wav":"wav","audio/x-wav":"wav","audio/wave":"wav"};function ti(t){let e=String(t||"").split(";")[0].trim().toLowerCase();return Va[e]??null}function ni(t,e){let n=[];return t!=="local"&&e.key&&n.push("elevenlabs"),e.ffmpeg&&(e.mlx?n.push("mlx-whisper"):e.whisper&&n.push("whisper")),n}function wt(t,e){return t==="local"&&e.key&&!e.ffmpeg?"Speech to text is set to local, and this computer has no ffmpeg.":(e.mlx||e.whisper)&&!e.ffmpeg?"This computer has Whisper but no ffmpeg to read the recording.":"Add an ElevenLabs key on this computer, or install mlx-whisper and ffmpeg."}function bt(t,e,n=process.env.PATH??""){let r=Wa(),i=[ht(r,".local","bin"),"/opt/homebrew/bin","/usr/local/bin",...n.split(Qr).filter(Boolean)];for(let s of[e,...i.map(o=>ht(o,t))])if(s)try{return Ma(s,Ua.X_OK),s}catch{}return null}function Nd(t){return{key:t,mlx:bt("mlx_whisper",process.env.AGENTX_MLX_WHISPER),whisper:bt("whisper",process.env.AGENTX_WHISPER),ffmpeg:qa()}}var qa=()=>bt("ffmpeg",process.env.AGENTX_FFMPEG);async function ri(t,e){return e?Ba(t,e):null}async function Ba(t,e){let n=String(D.ms/1e3+5);try{let i=[...(await Ha(e,["-nostdin","-hide_banner","-t",n,"-i",t,"-f","null","-"],3e4)).matchAll(/time=(\d+):(\d+):(\d+(?:\.\d+)?)/g)],s=i[i.length-1];return s?Number(s[1])*3600+Number(s[2])*60+Number(s[3]):null}catch{return null}}async function ii(t,e,n,r){let i=new FormData;i.set("model_id",process.env.AGENTX_STT_MODEL||"scribe_v1"),i.set("file",new Blob([Zr(t)],{type:e}),j(t));let s=await fetch("https://api.elevenlabs.io/v1/speech-to-text",{method:"POST",headers:{"xi-api-key":n},body:i,signal:r??AbortSignal.timeout(6e4)});if(!s.ok)throw new Error(`ElevenLabs ${s.status}: ${(await s.text().catch(()=>"")).slice(0,160)}`);let o=await s.json();if(typeof o.text!="string")throw new Error("ElevenLabs returned no text");return o.text.trim()}async function si(t,e,n,r,i){let s=t==="mlx-whisper"?["--model",process.env.AGENTX_MLX_MODEL||"mlx-community/whisper-large-v3-turbo","--output-format","txt","--output-dir",i,n]:[n,"--model",process.env.AGENTX_WHISPER_MODEL||"base","--output_format","txt","--output_dir",i,"--fp16","False"],o={...process.env,PATH:`${Fa(r)}${Qr}${process.env.PATH??""}`};await za(e,s,12e4,o);try{return Zr(ht(i,j(n,ja(n))+".txt"),"utf8").trim()}catch{throw new Error(`${t} wrote no transcript`)}}function Ha(t,e,n){return new Promise((r,i)=>{let s=Yr(t,e,{stdio:["ignore","ignore","pipe"]}),o="",a=setTimeout(()=>{s.kill("SIGKILL"),i(new Error(`${j(t)} took too long`))},n);s.stderr.on("data",c=>{o=(o+c).slice(-8192)}),s.on("error",c=>{clearTimeout(a),i(c)}),s.on("exit",c=>{clearTimeout(a),c===0?r(o):i(new Error(`${j(t)} exited ${c}`))})})}function za(t,e,n,r=process.env){return new Promise((i,s)=>{let o=Yr(t,e,{stdio:["ignore","pipe","pipe"],env:r}),a="",c="",l=setTimeout(()=>{o.kill("SIGKILL"),s(new Error(`${j(t)} took longer than ${n/1e3}s`))},n);o.stdout.on("data",u=>{a.length<4096&&(a+=u)}),o.stderr.on("data",u=>{c.length<4096&&(c+=u)}),o.on("error",u=>{clearTimeout(l),s(u)}),o.on("exit",u=>{clearTimeout(l),u===0?i(a):s(new Error(`${j(t)} exited ${u}: ${c.trim().split(`
|
|
557
|
+
`).pop()?.slice(0,160)??""}`))})})}import{mkdtemp as Xa,rm as Ga,writeFile as Ka}from"fs/promises";import{tmpdir as Ja}from"os";import{join as oi}from"path";mi();var Ya=64*1024,Za=2,St=0;function qd(t){return t==="/voice/transcribe"||t==="/voice/speak"}async function Bd(t,e,n,r){return t.method!=="POST"?y(e,405,{error:"POST"}):n==="/voice/transcribe"?Qa(t,e,r):nl(t,e,r)}async function Qa(t,e,n){let r=String(t.headers["content-type"]||""),i=ti(r);if(!i)return y(e,415,{error:"Send the recording as audio (webm, mp4, ogg, mp3 or wav)."});let s=ei(t.headers["x-audio-duration-ms"]);if(s)return t.resume(),y(e,s.status,{error:s.error});let o=await ai(t,D.bytes);if(!o)return y(e,413,{error:sl()});if(!o.length)return y(e,400,{error:"The recording is empty."});if(St>=Za)return y(e,429,{error:"This computer is already writing down other recordings. Try again in a moment."});let a=n.stt(),c=n.host(),l=ni(a,c);if(!c.ffmpeg&&!n.allowUnmeasured?.()){let d=n.nodeName?`on ${n.nodeName}`:"on this computer",p=l.length?"":" "+wt(a,c);return y(e,503,{error:"Voice input isn't set up on this computer.",hint:`Install ffmpeg ${d} for voice input from the phone.${p}`,setup:!1})}if(!l.length)return y(e,503,{error:"Voice input isn't set up on this computer.",hint:wt(a,c),setup:!1});St++;let u;try{let d=await Xa(oi(Ja(),"agentx-phone-voice-"));try{u=await el(d,`speech.${i}`,o,r,c,l,n)}finally{await Ga(d,{recursive:!0,force:!0}).catch(()=>{})}}finally{St--}y(e,u[0],u[1])}async function el(t,e,n,r,i,s,o){let a=oi(t,e);if(await Ka(a,n,{mode:384}),i.ffmpeg){let l=await(o.measure??ri)(a,i.ffmpeg);if(l==null)return[422,{error:"This computer couldn't read the recording. Try again."}];if(l*1e3>D.ms+2e3)return[413,{error:yt()}]}let c=[];for(let l of s)try{let u=await tl(l,a,r,i,t,o);return o.log(`[voice] phone recording transcribed with ${l} (${Math.round(n.length/1024)} KB)`),u?[200,{text:u,engine:l}]:[422,{error:"No words were heard. Hold the orb and speak, then let go.",engine:l}]}catch(u){let d=String(u?.message??u).split(`
|
|
558
|
+
`)[0].slice(0,200);c.push(`${l}: ${d}`),o.log(`[voice] phone transcription with ${l} failed: ${d}`)}return[502,{error:"Speech to text failed on this computer.",detail:c.join("; ")}]}function tl(t,e,n,r,i,s){let o=s.engines?.[t];if(o)return o(e,n,r,i);if(t==="elevenlabs")return ii(e,n,r.key);let a=t==="mlx-whisper"?r.mlx:r.whisper;return si(t,a,e,r.ffmpeg,i)}async function nl(t,e,n){let r=await ai(t,Ya);if(!r)return y(e,413,{error:"request too large"});let i;try{i=JSON.parse(r.toString("utf8")||"{}")}catch{return y(e,400,{error:"expected JSON"})}let s=typeof i?.agent=="string"?i.agent:"",o=typeof i?.peer=="string"&&i.peer?i.peer:void 0;if(!s)return y(e,400,{error:"agent is required"});let a=rl(typeof i?.text=="string"?i.text:"",n.spokenMaxChars?.());if(!a)return y(e,422,{error:"Nothing in this answer can be said aloud."});let c=n.pronounce?n.pronounce(a):a,l=n.voiceOf(s,o);if(!l)return y(e,404,{error:`Unknown agent: ${s}`});let u=(p,g=200)=>y(e,g,{fallback:"browser",reason:p,text:c});if(l.provider!=="elevenlabs")return u("This agent speaks with a system voice, which only plays on the computer.");let d=n.elevenLabsKey();if(!d)return u("No ElevenLabs key is set on this computer.");try{let p=await(n.synth??il)(d,l,c);e.writeHead(200,{"Content-Type":"audio/mpeg","Content-Length":p.length,"Cache-Control":"no-store","X-Spoken-Chars":String(c.length)}),e.end(p)}catch(p){return n.log(`[voice] phone answer synthesis failed: ${String(p?.message??p).split(`
|
|
559
|
+
`)[0].slice(0,160)}`),u("ElevenLabs could not speak this answer.",502)}}function rl(t,e){let n=yr(Ft(t).text).cleanText.replace(/!\[[^\]\n]*\]\([^)\n]*\)/g,"");return Tt(n,e)}async function il(t,e,n){let r=ye(e.rate),i=await fetch(`https://api.elevenlabs.io/v1/text-to-speech/${encodeURIComponent(e.elevenlabs)}?output_format=mp3_44100_128`,{method:"POST",headers:{"xi-api-key":t,"Content-Type":"application/json"},body:JSON.stringify({text:n,model_id:"eleven_flash_v2_5",...r?{voice_settings:{speed:r}}:{}}),signal:AbortSignal.timeout(6e4)});if(!i.ok)throw new Error(`ElevenLabs ${i.status}: ${(await i.text().catch(()=>"")).slice(0,160)}`);return Buffer.from(await i.arrayBuffer())}var sl=()=>`The recording is larger than ${D.bytes/1024/1024} MB, more than ${D.ms/6e4} minutes of speech.`;function Hd(t,e,n,r){if(e)return r.speakerOn(e,t,!1)?.voice??null;let i=n.agents??{};return i[t]?Ke(ze(t,i,n.voice)):null}function ai(t,e){return Number(t.headers["content-length"])>e?(t.resume(),Promise.resolve(null)):new Promise((n,r)=>{let i=[],s=0;t.on("data",o=>{if(!(s>e)){if(s+=o.length,s>e){i.length=0,n(null);return}i.push(o)}}),t.on("end",()=>{s<=e&&n(Buffer.concat(i))}),t.on("error",r)})}function y(t,e,n){t.writeHead(e,{"Content-Type":"application/json","Cache-Control":"no-store"}),t.end(JSON.stringify(n))}import{existsSync as $,readFileSync as $t,writeFileSync as Et,mkdirSync as we,readdirSync as vt,rmSync as ol,statSync as R}from"fs";import{resolve as li,relative as al,dirname as _t,join as O,extname as Q,basename as Rt}from"path";var k=200*1024,ci=new Set([".md",".markdown",".txt"]),V=new Set([".md",".markdown",".txt",".json",".jsonc",".yaml",".yml",".toml",".sh",".bash",".zsh",".py",".js",".mjs",".ts",".tsx",".jsx",".css",".html",".lock",".cfg",".ini",".env",".gitignore"]),ll=6,cl=new Set(["CLAUDE.md","SOUL.md","IDENTITY.md","PERSONA.md","AGENT.md","BOOTSTRAP.md"]);function I(t,e){if(!t)throw new Error("Agent workspace is not configured.");let n=li(t);if($(n)||we(n,{recursive:!0}),!e||e.startsWith("/")||e.match(/^[a-zA-Z]:/))throw new Error(`Path must be relative to the workspace: ${e}`);let r=li(n,e);if(al(n,r).startsWith(".."))throw new Error(`Path escapes the workspace: ${e}`);return r}function ui(t){let e=Q(t).toLowerCase();if(!ci.has(e))throw new Error(`Only text files (.md, .markdown, .txt) can be edited from the admin panel (got ${e}).`)}function kt(t,e){let n=t.split(/\r?\n/).find(r=>r.trim().startsWith("#"));return n&&n.replace(/^#+\s*/,"").trim().slice(0,120)||e}function Kd(t){let e=I(t,"."),n=[],r=new Set,i=[];try{i=vt(e)}catch{}for(let a of cl){let c=O(e,a);if($(c)&&R(c).isFile()){let l=R(c).size,u=l>0&&l<k?xt(c):"";n.push({path:a,title:kt(u,a),size:l,exists:!0}),r.add(a)}else n.push({path:a,title:a,size:0,exists:!1}),r.add(a)}for(let a of i){if(r.has(a))continue;let c=O(e,a);try{let l=R(c);if(!l.isFile()||!ci.has(Q(a).toLowerCase()))continue;let u=l.size>0&&l.size<k?xt(c):"";n.push({path:a,title:kt(u,a),size:l.size,exists:!0})}catch{}}let s=[],o=O(e,".claude","skills");if($(o))try{for(let a of vt(o)){let c=O(o,a),l;try{l=R(c)}catch{continue}if(!l.isDirectory())continue;let u=O(c,"SKILL.md");if(!$(u))continue;let d=R(u),p=d.size<k?xt(u):"";s.push({slug:a,title:kt(p,a),size:d.size,path:`.claude/skills/${a}/SKILL.md`})}}catch{}return s.sort((a,c)=>a.slug.localeCompare(c.slug)),{identity:n,skills:s,workspace:e}}function xt(t){try{return $t(t,"utf-8")}catch{return""}}function Jd(t,e){let n=I(t,e);if(ui(n),!$(n))return{path:e,content:""};if(R(n).size>k)throw new Error(`File is too large to edit in the admin panel (> ${k/1024} KB).`);return{path:e,content:$t(n,"utf-8")}}function Yd(t,e,n){let r=I(t,e);ui(r);let i=Buffer.from(n,"utf-8");if(i.length>k)throw new Error(`File too large (${i.length} > ${k} bytes).`);return we(_t(r),{recursive:!0}),Et(r,i),{path:e,bytes:i.length}}function Zd(t,e,n={}){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error("Skill slug must be lowercase (letters, digits, -, _) and start with a letter or digit.");let r=`.claude/skills/${e}/SKILL.md`,i=I(t,r);if($(i))throw new Error(`Skill "${e}" already exists.`);we(_t(i),{recursive:!0});let s=n.content?.trim()?n.content:ul(e,n.title);return Et(i,s),{slug:e,path:r}}function Qd(t,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let n=I(t,`.claude/skills/${e}`);if(!$(n))throw new Error(`Skill "${e}" not found.`);if(!R(n).isDirectory())throw new Error(`Skill path is not a directory: ${e}`);return ol(n,{recursive:!0,force:!0}),{slug:e}}function ep(t,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let n=I(t,`.claude/skills/${e}`);if(!$(n))throw new Error(`Skill "${e}" not found.`);let r=[];return i(n,"",0,r),r.sort((s,o)=>s.kind!==o.kind?s.kind==="dir"?-1:1:s.path.localeCompare(o.path)),r;function i(s,o,a,c){if(a>ll)return;let l=[];try{l=vt(s)}catch{return}for(let u of l){if(u==="node_modules"||u===".git")continue;let d=O(s,u),p=o?`${o}/${u}`:u,g;try{g=R(d)}catch{continue}if(g.isDirectory())c.push({path:`${p}/`,kind:"dir",size:0,editable:!1}),i(d,p,a+1,c);else if(g.isFile()){let f=Q(u).toLowerCase(),m=V.has(f)||V.has(u.toLowerCase());c.push({path:p,kind:"file",size:g.size,editable:m&&g.size<k})}}}}function tp(t,e,n){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);if(!n||n.includes(".."))throw new Error(`Invalid file path: ${n}`);let r=I(t,`.claude/skills/${e}/${n}`);if(!$(r)||!R(r).isFile())throw new Error(`File not found: ${n}`);let i=Q(r).toLowerCase(),s=Rt(r).toLowerCase();if(!V.has(i)&&!V.has(s))throw new Error(`File type not editable: ${n}`);if(R(r).size>k)throw new Error(`File too large to edit (> ${k/1024} KB): ${n}`);return{path:n,content:$t(r,"utf-8")}}function np(t,e,n,r){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);if(!n||n.includes(".."))throw new Error(`Invalid file path: ${n}`);let i=I(t,`.claude/skills/${e}/${n}`),s=Q(i).toLowerCase(),o=Rt(i).toLowerCase();if(!V.has(s)&&!V.has(o))throw new Error(`File type not editable: ${n}`);let a=Buffer.from(r,"utf-8");if(a.length>k)throw new Error(`File too large (${a.length} > ${k} bytes).`);return we(_t(i),{recursive:!0}),Et(i,a),{path:n,bytes:a.length}}function rp(t,e){if(!/^[a-z0-9][a-z0-9_-]*$/.test(e))throw new Error(`Invalid skill slug: ${e}`);let n=I(t,`.claude/skills/${e}`);if(!$(n))throw new Error(`Skill "${e}" not found.`);let r=i=>$(O(n,i));return r("pnpm-lock.yaml")?{manager:"pnpm",file:"pnpm-lock.yaml",command:"pnpm install"}:r("yarn.lock")?{manager:"yarn",file:"yarn.lock",command:"yarn install"}:r("package.json")?{manager:"npm",file:"package.json",command:"npm install"}:r("requirements.txt")?{manager:"pip",file:"requirements.txt",command:"pip install -r requirements.txt"}:r("Gemfile")?{manager:"bundle",file:"Gemfile",command:"bundle install"}:{manager:null,file:null,command:null}}function ul(t,e){return`# ${e?.trim()||t}
|
|
560
|
+
|
|
561
|
+
Describe what this skill does and when the agent should use it. Keep it under ~5 lines \u2014 the agent reads this to decide whether to invoke the skill.
|
|
562
|
+
|
|
563
|
+
## When to use
|
|
564
|
+
- Trigger condition 1
|
|
565
|
+
- Trigger condition 2
|
|
566
|
+
|
|
567
|
+
## How to use
|
|
568
|
+
Document the arguments, commands, or files the skill expects. Example:
|
|
569
|
+
|
|
570
|
+
\`\`\`bash
|
|
571
|
+
# Example invocation
|
|
572
|
+
claude-code ${Rt(t)} --arg value
|
|
573
|
+
\`\`\`
|
|
574
|
+
`}Pt();var di={maxPixels:12e5,timeoutMs:5e3,intervalMs:100,changeThreshold:.015,stableMs:400,regions:{},buffer:{enabled:!1,seconds:10,fps:2,region:"screen",maxPixels:3e5}};function pi(t){return{...di,...t??{},regions:{...t?.regions??{}},buffer:{...di.buffer,...t?.buffer??{}}}}function op(t){try{return pi(Ot(t).screen)}catch{return pi(void 0)}}var dl=/^[a-z][\w-]*$/,gi=["window","screen","menubar","notifications"];function ee(t,e,n,r){let i=Number(t);if(t===""||t===null||!Number.isFinite(i)||!n(i))throw new Error(`${e} must be ${r}`);return i}var Se=(t,e)=>ee(t,e,n=>Number.isInteger(n)&&n>0,"a whole number above 0");function fi(t){let e=typeof t=="string"?t.split(",").map(Number):[t?.x,t?.y,t?.width,t?.height].map(Number);if(e.length!==4||!e.every(Number.isFinite)||e[2]<=0||e[3]<=0)throw new Error("a region is x,y,width,height in screen points, with width and height above 0");return{x:e[0],y:e[1],width:e[2],height:e[3]}}function ap(t,e){let n={...t??{}};if("maxPixels"in e&&(n.maxPixels=Se(e.maxPixels,"maxPixels")),"timeoutMs"in e&&(n.timeoutMs=Se(e.timeoutMs,"timeoutMs")),"intervalMs"in e&&(n.intervalMs=Se(e.intervalMs,"intervalMs")),"stableMs"in e&&(n.stableMs=ee(e.stableMs,"stableMs",i=>Number.isInteger(i)&&i>=0,"a whole number, 0 or more")),"changeThreshold"in e&&(n.changeThreshold=ee(e.changeThreshold,"changeThreshold",i=>i>=0&&i<=1,"between 0 and 1")),e.regions&&typeof e.regions=="object"){let i={...n.regions??{}};for(let[s,o]of Object.entries(e.regions)){if(!dl.test(s))throw new Error(`region name "${s}" must be lowercase letters, digits, - or _`);o===null?delete i[s]:i[s]=fi(o)}n.regions=i}if(e.buffer&&typeof e.buffer=="object"){let i=e.buffer,s={...n.buffer??{}};"enabled"in i&&(s.enabled=!!i.enabled),"seconds"in i&&(s.seconds=ee(i.seconds,"buffer.seconds",o=>o>0&&o<=120,"above 0 and at most 120")),"fps"in i&&(s.fps=ee(i.fps,"buffer.fps",o=>o>0&&o<=10,"above 0 and at most 10")),"maxPixels"in i&&(s.maxPixels=Se(i.maxPixels,"buffer.maxPixels")),"region"in i&&(s.region=String(i.region).trim()),n.buffer=s}let r=n.buffer?.region;if(r!==void 0&&!gi.includes(r)&&!n.regions?.[r])try{fi(r)}catch{throw new Error(`buffer.region "${r}" is not a region \u2014 use ${[...gi,...Object.keys(n.regions??{})].join(", ")}, or x,y,w,h`)}return n}export{cn as a,fl as b,ot as c,ho as d,bo as e,tu as f,nu as g,ru as h,ou as i,au as j,lu as k,hr as l,br as m,cu as n,yr as o,du as p,gu as q,N as r,wr as s,S as t,yl as u,xr as v,wu as w,xu as x,$r as y,vu as z,Er as A,$u as B,J as C,Bo as D,Eu as E,_u as F,Rr as G,Rl as H,Hi as I,Dl as J,_n as K,x as L,vl as M,$l as N,Pl as O,ae as P,Ml as Q,Tn as R,ql as S,X as T,sc as U,oc as V,On as W,Tr as X,Tu as Y,pr as Z,Ye as _,lo as $,Vr as aa,sd as ba,od as ca,pd as da,Aa as ea,gd as fa,lc as ga,cc as ha,uc as ia,dc as ja,pc as ka,gc as la,mc as ma,hc as na,Ec as oa,Ve as pa,_c as qa,Rc as ra,G as sa,K as ta,La as ua,xd as va,vd as wa,$d as xa,Ed as ya,mt as za,_d as Aa,He as Ba,ze as Ca,ge as Da,Ls as Ea,Ps as Fa,Nc as Ga,Ge as Ha,Ke as Ia,Ms as Ja,Us as Ka,tr as La,sr as Ma,Dc as Na,js as Oa,Lc as Pa,Ks as Qa,Zs as Ra,Je as Sa,eo as Ta,or as Ua,D as Va,ei as Wa,ti as Xa,ni as Ya,Nd as Za,qa as _a,ii as $a,si as ab,qd as bb,Bd as cb,sl as db,Hd as eb,ai as fb,fe as gb,zc as hb,it as ib,Qc as jb,Kd as kb,Jd as lb,Yd as mb,Zd as nb,Qd as ob,ep as pb,tp as qb,np as rb,rp as sb,di as tb,pi as ub,op as vb,gi as wb,ap as xb};
|
|
575
|
+
//# sourceMappingURL=chunk-H7DLFJBV.js.map
|