agentix-cli 0.154.1 → 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-OKVQYPPF.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-HFN24YLA.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 +2 -1
- package/dist/board-dashboard-642BO7GV.js.map +0 -1
- package/dist/chunk-HSCZSNOX.js +0 -533
- package/dist/chunk-HSCZSNOX.js.map +0 -1
- package/dist/chunk-OKVQYPPF.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-HFN24YLA.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
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{y as l,z as T}from"./chunk-
|
|
2
|
-
//# sourceMappingURL=chunk-
|
|
1
|
+
import{y as l,z as T}from"./chunk-4RHM4XTI.js";import{a,f as w}from"./chunk-72SZI2UW.js";T();w();var d="task-tier";function u(e){return{task:b(e.message,2e3),agent:e.agent,channel:e.channel??null,continuesExistingSession:e.isFollowUp??!1,...e.toolsAvailable?{toolsAvailable:e.toolsAvailable}:{}}}var c={needsFlagship:a("This task needs the strongest available model to be done correctly.",{true:"Doing it well takes real reasoning: writing or reviewing code, diagnosing a failure, planning work across several steps, weighing a judgement, handling money, touching production or anything irreversible, following a long or ambiguous instruction, or work where being subtly wrong would not be noticed. Anything where a weaker model would produce something that LOOKS right belongs here.",false:"It is mechanical and its result is obvious on sight: acknowledging or confirming a message, restating or formatting something already written, a greeting, a status echo, reading one file and reporting what it says, a lookup with one right answer, a scheduled heartbeat with nothing to decide."})};function g(e){return e.needsFlagship.noul}function b(e,n){return e.length>n?`${e.slice(0,n)}\u2026`:e}var h=.2;function p(e){return e==="voice"||e==="desktop"||e==="opencode"}var f=60*60*1e3;function M(e,n){if(e==="claude-code"){let t=n?.cheapModels?.[e]??n?.cheapModel;return t&&/^(claude-|haiku$|sonnet$|opus$)/.test(t)?t:void 0}if(e==="codex-cli"){let t=n?.cheapModels?.[e];return t&&/^(gpt-|o\d)/.test(t)?t:void 0}}async function v(e){let n=(s,r=null)=>({downgraded:!1,needsFlagship:r,reason:s});if(p(e.channel))return n(`${e.channel} requests retain the configured model`);if(!e.cheapModel)return n("no cheaper model configured");if(e.isFollowUp){let s=e.sessionIdleMs??0,r=e.cacheTtlMs??f;if(s<r)return n(`follow-up on a warm cache (idle ${Math.round(s/1e3)}s) \u2014 swapping would cost more than it saves`)}let t;try{t=await l(d,u(e),c,{incumbent:{needsFlagship:1},features:{agent:e.agent,channel:e.channel??"unknown"},timeoutMs:1500})}catch{return n("routing seat threw \u2014 keeping the flagship")}if(!t)return n("routing seat unavailable \u2014 keeping the flagship");let o=g(t.answers);if(t.mode!=="active")return n(`shadow \u2014 would ${o<(e.threshold??h)?"downgrade":"keep"} (${o.toFixed(2)})`,o);let i=e.threshold??h;return o>=i?n(`needs the flagship (${o.toFixed(2)})`,o):{model:e.cheapModel,downgraded:!0,needsFlagship:o,reason:`mechanical task (${o.toFixed(2)} below ${i}) \u2192 ${e.cheapModel}`}}var A=["labeled","unlabeled","closed"];function y(e){let n=r=>({downgraded:!1,needsFlagship:null,reason:r});if(p(e.channel))return n(`${e.channel} requests retain the configured model`);let t=e.eventActions??[];if(!t.length)return n("not started by a platform event");let o=e.triage?.actions??A,i=t.find(r=>!o.includes(r));if(i)return n(`"${i}" is not a triage action`);let s=M(e.tier,{cheapModels:e.triage?.models});if(!s)return n(`no triage model set for ${e.tier}`);if(e.isFollowUp){let r=e.sessionIdleMs??0,m=e.cacheTtlMs??f;if(r<m)return n(`follow-up on a warm cache (idle ${Math.round(r/1e3)}s) \u2014 keeping the session's model`)}return{model:s,downgraded:!0,needsFlagship:null,reason:`triage event (${t.join(", ")}) \u2192 ${s}`}}export{h as a,p as b,M as c,v as d,A as e,y as f};
|
|
2
|
+
//# sourceMappingURL=chunk-KYRF2FPT.js.map
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import{d as h}from"./chunk-
|
|
1
|
+
import{d as h}from"./chunk-YXBQSLFV.js";var d=["main contact","primary contact","point of contact","billing contact","technical contact","decision maker"],f=/[\w.+-]+@[\w-]+(?:\.[\w-]+)+/g,w=/\b(role|title|position|job)\s*\**\s*[::]\s*\**\s*([^\n;|]+)/gi;function L(t,c,e=[]){let l=[c,...e].join(`
|
|
2
2
|
`),a=l.toLowerCase(),i=h(t,l);for(let s of p(t.match(f)??[]))a.includes(s.toLowerCase())||i.push(`email ${s}`);let m=t.toLowerCase();for(let s of d)m.includes(s)&&!a.includes(s)&&i.push(`marker "${s}"`);for(let s of t.matchAll(w)){let n=s[2].replace(/\*+/g,"").trim();n&&!a.includes(n.toLowerCase())&&i.push(`${s[1].toLowerCase()} "${n}"`)}return p(i)}function p(t){return[...new Set(t)]}var $=.9,g=/^\s*(?:wait\b|here(?:'s| is) the (?:updated|modified|patched|revised)|i (?:must|need to|will|should) (?:output|return|write)|(?:okay|ok|sure)[,.!]\s|let me\b|as requested\b)/im;function k(t,c){let e=[],l=t.split(`
|
|
3
3
|
`).filter(o=>o.trim()).length,a=c.split(`
|
|
4
4
|
`).filter(o=>o.trim()).length;l>=5&&a<Math.floor(l*$)&&e.push(`the article shrinks from ${l} to ${a} lines`);let i=c.match(g);i&&!t.match(g)&&e.push(`it contains the model's own commentary ("${i[0].trim().slice(0,40)}")`);let m=o=>{let r=new Map;for(let u of o.split(`
|
|
5
5
|
`))/^#{1,6} /.test(u)&&r.set(u.trim(),(r.get(u.trim())??0)+1);return r},s=m(t);for(let[o,r]of m(c))r>1&&r>(s.get(o)??0)&&e.push(`the heading "${o}" appears ${r} times`);let n=L(t,c);return n.length&&e.push(`it drops ${n.length} fact(s): ${n.slice(0,6).join(", ")}${n.length>6?", \u2026":""}`),e}export{L as a,k as b};
|
|
6
|
-
//# sourceMappingURL=chunk-
|
|
6
|
+
//# sourceMappingURL=chunk-NJEJU5XN.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{Pa as l,Ya as m}from"./chunk-SQTL2CLY.js";m();import{existsSync as g,readFileSync as u,writeFileSync as a,copyFileSync as y}from"fs";import{resolve as p}from"path";function P(e,n={}){let t=n.configPath||p(process.cwd(),"agentx.json");if(!g(t))throw new Error(`agentx.json not found at ${t}`);let i=JSON.parse(u(t,"utf-8")),o=e(i),s=typeof o=="string"?{summary:o}:o,c=JSON.stringify(i,null,2)+`
|
|
2
2
|
`,f=t+".tmp";a(f,c,"utf-8");try{JSON.parse(u(f,"utf-8"))}catch(d){throw new Error(`config did not round-trip cleanly: ${d.message}`)}let r;return n.skipBackup||(r=`${t}.bak.${Date.now()}`,y(t,r)),a(t,c,"utf-8"),n.skipReload||h().catch(()=>{}),{summary:s.summary,payload:s.payload,backupPath:r}}function S(e,n={}){let t=n.configPath||p(process.cwd(),"agentx.json");if(g(t)&&!n.overwrite)throw new Error(`agentx.json already exists at ${t}. Pass overwrite=true to replace it.`);return a(t,JSON.stringify(e,null,2)+`
|
|
3
3
|
`,"utf-8"),{file:t}}async function h(){try{let n=l().dashboard.daemonUrl?.replace(/\/+$/,"")||"http://127.0.0.1:18800";await fetch(`${n}/reload`,{method:"POST"}).catch(()=>null)}catch{}}export{P as a,S as b};
|
|
4
|
-
//# sourceMappingURL=chunk-
|
|
4
|
+
//# sourceMappingURL=chunk-PI6CKUUH.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{a as Le,b as k,c as z,d as Be,e as pe,f as ue,g as N,h as ge,i as O,j as U,n as je,o as He,p as qe,q as Fe}from"./chunk-XVTD6I34.js";import{e as Oe,i as Ue}from"./chunk-QAWKN2N4.js";import{b as ce}from"./chunk-MTX4UPNB.js";import{a as X,b as Ne}from"./chunk-
|
|
1
|
+
import{a as Le,b as k,c as z,d as Be,e as pe,f as ue,g as N,h as ge,i as O,j as U,n as je,o as He,p as qe,q as Fe}from"./chunk-XVTD6I34.js";import{e as Oe,i as Ue}from"./chunk-QAWKN2N4.js";import{b as ce}from"./chunk-MTX4UPNB.js";import{a as X,b as Ne}from"./chunk-PI6CKUUH.js";import{c as Me,d as le,e as P,f as de,g as j}from"./chunk-B2QYMUTD.js";import{a as We}from"./chunk-7LN4VKDU.js";import{a as Te}from"./chunk-IMLAPB7G.js";import{c as Re,e as De}from"./chunk-LQE73AZR.js";import{b as ze}from"./chunk-MRIVENBY.js";import{n as se,p as _e}from"./chunk-LQS27HSV.js";import{b as Y}from"./chunk-QHRQ3PUX.js";import{G as J}from"./chunk-GIFUJ3LL.js";import{a as G,b as K}from"./chunk-IW4AZVJR.js";import{a as Pe}from"./chunk-6CXJAZZD.js";import{c as Ce,d as Ie}from"./chunk-OWNQ2FD4.js";import{a as Ee}from"./chunk-ROLSSYXE.js";import{Ma as oe,O as ae,Oa as F,P as $e,Pa as Se,W as Rt,Ya as ie,ma as Ae,ta as Dt}from"./chunk-SQTL2CLY.js";import{existsSync as H,readFileSync as Je,writeFileSync as Ye,appendFileSync as Wt,mkdirSync as Mt}from"fs";import{dirname as Nt,resolve as W}from"path";import{fileURLToPath as Ot}from"url";import{hostname as Ut}from"os";import{spawn as Lt}from"child_process";function Ge(e,t){let n=e.configExists?e.agentCount===0?"Add your first agent":"Add another agent":"Set up your team",r=`<header class="ax-topbar">
|
|
2
2
|
<div class="ax-topbar__left">
|
|
3
3
|
<div class="ax-brand">
|
|
4
4
|
<span class="ax-brand__mark">AX</span>
|
|
@@ -859,4 +859,4 @@ tr:hover td { background: var(--bg-hover); }
|
|
|
859
859
|
WHERE person = ? AND state IN (${n.map(()=>"?").join(",")})
|
|
860
860
|
${r}`).all(t,...n,...a).map(i=>({id:i.id,state:i.state,channel:i.channel,agentId:i.agent_id,text:i.text,createdAt:i.created_at})):[]}function Ta(e,t){return wt(e,t,ae,"ORDER BY created_at")}function Ra(e,t,n=50){return wt(e,t,[...ae,...$e],"ORDER BY created_at DESC, rowid DESC LIMIT ?",Math.max(1,Math.min(n,500)))}Dt();var we="member:",Mn=90,Nn="members",On="tailscale-user-login",Un=400,Ln=60*60*1e3;function Bn(e){let t=e.scopes.find(n=>n.startsWith(we));return t?t.slice(we.length):null}function At(e,t){if(!e)return null;let n=t.verify(e),r=n?Bn(n):null;return n&&r?{rec:n,personId:r}:null}function Oa(e,t){let n=e.people().find(s=>s.id===t);if(!n)return{ok:!1,error:`No person "${t}". Add them first: agentx people add ${t} --name "\u2026"`};let{token:r,record:a}=e.tokens.create({name:`${n.name} (machine not paired yet)`,scopes:[`${we}${n.id}`],expiresInDays:Mn}),{code:o,expiresAt:i}=e.codes.create({token:r,tokenId:a.id,name:n.id});return e.members.log({person:n.id,device:a.id,event:"invited"}),{ok:!0,code:o,expiresAt:i,tokenId:a.id,person:n}}var jn=Math.round(Re/6e4);function Hn(e){return e==="client"?"Your project":"My work"}function Ua(e){let t=Hn(e.person.role),n=e.person.role==="client"?`I use AgentX to keep track of the work I do for you. It has a page for you, "${t}": what you asked us for, and where each request stands.`:`I use AgentX to give our AI agents their jobs. It has a page for you, "${t}": what you asked the agents for, and where each request stands.`;return[`Hi ${e.person.name},`,"",n,"","To open it:","1. Accept the Tailscale share I sent you. Tailscale is a small program that connects your computer to mine, privately.",`2. Open ${e.origin.replace(/\/+$/,"")}/member in Edge or Chrome, give your computer a name and type this code: ${De(e.code)}`,` The code works once, for ${jn} minutes. If it has stopped working, tell me and I will send you a new one.`,"","I then approve your machine, and the page opens by itself."].join(`
|
|
861
861
|
`)}var qn="The private network says someone else is connecting from this machine. Ask the person who invited you for a new code.",Fn="Too many machines are waiting for the owner's answer. Try again later.",$t="That code didn't work. Check it, or ask the person who invited you for a new one.",St=(e,t)=>{let n=e.headers[t];return(Array.isArray(n)?n[0]:n||"").trim()};function Ct(e){let t=e.socket?.remoteAddress||"";return t==="127.0.0.1"||t==="::1"||t==="::ffff:127.0.0.1"}function Gn(e){if(!Ct(e))return null;let t=St(e,On).toLowerCase();return t?t.slice(0,80):null}function Kn(e){let t=e.socket?.remoteAddress||"unknown";if(!Ct(e))return t;let n=St(e,"x-forwarded-for").split(",").pop().trim();return/^[0-9a-fA-F:.]{2,45}$/.test(n)?n:t}function Jn(e){return typeof e=="string"?e.replace(/[^\p{L}\p{N} ._'()-]/gu," ").replace(/\s+/g," ").trim().slice(0,60):""}function Yn(e){return e.identities.map(Ae).filter(t=>!!t&&t.channel==="tailscale").map(t=>t.value)}async function La(e,t){let n=Date.now(),r=t.now??Date.now,a=t.log??(v=>console.log(v)),o=Kn(e),i=t.limiter.retryAfter(o);if(i>0)return a(`[member] pair-code refused from ${o}: locked out for ${i}s more`),{status:429,body:{error:"too many attempts",retryAfter:i},retryAfter:i};let s=null,l="";try{let v=await Y(e,2048);s=v.code,l=Jn(v.machine)}catch{}let d=t.codes.redeem(s),c=d?At(d.token,t.tokens):null;if(await Vn(n,t.minMs??Un),!d||!c)return t.limiter.fail(o),a(`[member] pair-code failed from ${o}`),{status:401,body:{error:$t}};t.limiter.succeed(o);let{rec:m,personId:p}=c,w=t.people().find(v=>v.id===p);if(!w)return t.tokens.revoke(m.id),t.members.log({person:p,device:m.id,event:"refused",address:o,detail:"person no longer listed"}),a(`[member] pair-code for ${p} refused from ${o}: person no longer listed`),{status:403,body:{error:$t}};let g=Gn(e),f=Yn(w);if(f.length&&(!g||!f.includes(g)))return t.tokens.revoke(m.id),t.members.log({person:p,device:m.id,event:"refused",address:o,detail:`network login ${g??"missing"}; expected ${f.join(" or ")}`}),a(`[member] ${p}: pairing from ${o} refused: the network says "${g??"nobody"}", expected ${f.join(" or ")}`),{status:403,body:{error:qn}};let C=l||"Unnamed machine",x=w.name.slice(0,40),b=_e(t.root,{title:`New machine for ${x}`,ask:`From ${o}, ${g?`network login ${g}`:"no network login reported"}. Machine name, as typed there: "${C}". Let it see ${x}'s own work?`,recommend:`Yes if ${x} told you they just paired this machine. No if you did not expect it.`,if_silent:"discard",raised_by:Nn},{now:r()});return b.ok?(t.members.add({tokenId:m.id,personId:p,name:C,state:"pending",cardId:b.card.id,address:o,...g?{network:g}:{},createdAt:new Date(r()).toISOString()}),t.members.log({person:p,device:m.id,event:"paired",address:o,detail:C}),a(`[member] ${p} paired "${C}" from ${o}; waiting for the owner (card ${b.card.id})`),{status:200,body:{person:p,name:C,state:"pending"},token:d.token}):(t.tokens.revoke(m.id),t.members.log({person:p,device:m.id,event:"refused",address:o,detail:b.error}),a(`[member] ${p}: pairing from ${o} refused: ${b.error}`),{status:503,body:{error:Fn}})}var re="this machine is not paired",Xn="waiting for the owner to approve this machine";function It(e){return e?e.status==="decided"?e.verdict??null:e.status==="expired"?"no":null:null}function Ba(e,t){return t.state==="pending"&&t.cardId?It(se(e,t.cardId)):null}function ja(e,t,n){let r=t.now??Date.now,a=t.log??(c=>console.log(c)),o=At(e,t.tokens);if(!o)return{ok:!1,status:401,error:re};let{rec:i,personId:s}=o,l=t.members.byToken(i.id);if(!l||l.state==="removed"||l.personId!==s)return{ok:!1,status:401,error:re};if(t.people&&!t.people().some(c=>c.id===s))return Et(t,i.id,"person no longer listed"),a(`[member] ${s}: "${l.name}" ended: person no longer listed`),{ok:!1,status:401,error:re};if(l.state==="pending"){let c=l.cardId?se(t.root,l.cardId):null,m=It(c);if(m==="yes")l=t.members.update(i.id,{state:"active",approvedAt:new Date(r()).toISOString()})??l,t.members.log({person:s,device:i.id,event:"approved",detail:c?.decided_by}),a(`[member] ${s}: "${l.name}" approved`);else if(m==="no"){let p=c?.status==="expired"?"the owner did not answer in time":"the owner said no";return t.tokens.revoke(i.id),t.members.update(i.id,{state:"removed",removedAt:new Date(r()).toISOString(),removedReason:p}),t.members.log({person:s,device:i.id,event:"refused",detail:p}),a(`[member] ${s}: "${l.name}" refused: ${p}`),{ok:!1,status:401,error:re}}else return{ok:!1,status:403,error:Xn,waiting:!0}}let d=l.lastSeenAt?Date.parse(l.lastSeenAt):0;return n&&(r()-d>=Ln||l.lastAddress!==n)&&(l=t.members.update(i.id,{lastSeenAt:new Date(r()).toISOString(),lastAddress:n})??l,t.members.log({person:s,device:i.id,event:"signed-in",address:n})),{ok:!0,device:l,personId:s,rec:i}}function Et(e,t,n="removed by the owner"){let r=e.members.byToken(t);if(!r)return null;e.tokens.revoke(t);let a=e.members.update(t,{state:"removed",removedAt:new Date((e.now??Date.now)()).toISOString(),removedReason:n});return e.members.log({person:r.personId,device:t,event:"removed",detail:n}),a}function Ha(e,t){return e.members.devices(t).filter(n=>n.state!=="removed").map(n=>Et(e,n.tokenId,"person removed by the owner")).filter(n=>!!n)}async function Vn(e,t){let n=e+t-Date.now();n>0&&await new Promise(r=>setTimeout(r,n))}var Pt=["gitlab","github"];function Qn(e,t,n){let r=String(t||"").trim().toLowerCase();if(!Pt.includes(r))throw new Error(`Request status works on ${Pt.join(" and ")}, not on "${t}".`);e.requestStatus=e.requestStatus&&typeof e.requestStatus=="object"?e.requestStatus:{};let a=Array.isArray(e.requestStatus.channels)?e.requestStatus.channels.map(o=>String(o).toLowerCase()):[];return e.requestStatus.channels=n?[...new Set([...a,r])]:a.filter(o=>o!==r),`request status on ${r} is ${n?"on":"off"}`}function Ga(e){return Array.isArray(e?.requestStatus?.channels)?e.requestStatus.channels.map(t=>String(t).toLowerCase()):[]}function Ka(e){let{summary:t}=X(n=>Qn(n,e?.channel,!!e?.enabled));return{summary:t}}function Ya(e){return new Set(Object.entries(e).filter(([,t])=>t?.wiki?.absorb?.enabled===!1).map(([t])=>t))}function Xa(e,t){return t.only?{agents:[t.only],skipped:[]}:{agents:e.filter(n=>!t.off.has(n)),skipped:e.filter(n=>t.off.has(n))}}function Va(e,t){return`absorb is off for this agent (agents.${e}.wiki.absorb.enabled); skipped.${e===t?" It reads the wiki notes inbox, so the inbox was not read.":""} Use --agent ${e} to run it anyway.`}function Qa(e,t){if(t){if(!e.wiki?.absorb)return;delete e.wiki.absorb.enabled,Object.keys(e.wiki.absorb).length===0&&delete e.wiki.absorb,Object.keys(e.wiki).length===0&&delete e.wiki;return}e.wiki??={},e.wiki.absorb??={},e.wiki.absorb.enabled=!1}var Zn="https://ntfy.sh";function eo(e){return{enabled:!!e?.enabled,server:e?.server||Zn,topicSet:!!e?.topic,tokenSet:!!e?.token}}function to(e,t){let n={...e??{}};if("enabled"in t&&(n.enabled=!!t.enabled),"server"in t){let r=String(t.server).trim().replace(/\/+$/,"");if(!/^https?:\/\/\S+$/.test(r))throw new Error("ntfy server must be an http(s) URL");n.server=r}if("topic"in t){let r=String(t.topic).trim();if(!r)throw new Error("ntfy topic cannot be empty");n.topic=r}if("token"in t){let r=String(t.token??"").trim();r?n.token=r:delete n.token}if(n.enabled&&!n.topic)throw new Error("set a topic before enabling ntfy");return n}export{Xe as a,cr as b,pr as c,ur as d,xr as e,yr as f,tn as g,an as h,me as i,kr as j,wr as k,kt as l,Sa as m,Ca as n,Ir as o,Er as p,Pr as q,Pa as r,Ta as s,Ra as t,Mn as u,Oa as v,jn as w,Hn as x,Ua as y,Kn as z,La as A,Ba as B,ja as C,Et as D,Ha as E,Pt as F,Qn as G,Ga as H,Ka as I,Ya as J,Xa as K,Va as L,Qa as M,eo as N,to as O,Cn as P,Vr as Q,ra as R,aa as S};
|
|
862
|
-
//# sourceMappingURL=chunk-
|
|
862
|
+
//# sourceMappingURL=chunk-Q7LUTCNO.js.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import{a as g}from"./chunk-JWDJMFU6.js";import{mkdirSync as D,readFileSync as F,renameSync as H,writeFileSync as K}from"fs";import{dirname as M,resolve as J}from"path";function k(t,e=160){if(!t)return t;let r=t.replace(/\s+/g," ").trim();return r.length>e?r.slice(0,e-1)+"\u2026":r}function j(t){let e=new Map;for(let n of t.cronRuns??[]){let s=e.get(n.jobId);(!s||s.startedAt<n.startedAt)&&e.set(n.jobId,n)}let r={success:0,failed:0};for(let n of t.cronRuns??[])n.status==="success"?r.success++:r.failed++;return{id:t.id,name:t.name,url:t.url,reachable:t.reachable,error:k(t.error),uptimeSec:t.uptimeSec,inflight:t.inflight,restart:t.restart?.state&&t.restart.state!=="none"?t.restart:void 0,agents:t.agents.map(n=>({id:n.id,name:n.name,tier:n.tier,active:n.active,errors:n.errors,lastActive:n.lastActive,last:n.lastSummary?{text:k(n.lastSummary.text),at:n.lastSummary.at,ok:n.lastSummary.ok}:void 0})),crons:{today:r,items:(t.crons??[]).map(n=>{let s=e.get(n.id);return{id:n.id,enabled:n.enabled,schedule:n.schedule,agent:n.agent,nextRun:n.nextRun,consecutiveErrors:n.consecutiveErrors,last:s?{status:s.status,at:s.startedAt,text:k(s.status==="success"?s.responseSummary:s.errorSummary)}:void 0}})}}}function E(t){let e=[];for(let r of t)for(let n of r.agents)for(let s of n.runningTasks??[])e.push({node:r.url,nodeName:r.name,agentId:n.id,agentName:n.name,taskId:s.id,preview:k(s.messagePreview),channel:s.channel,startedAt:s.startedAt});return e.sort((r,n)=>r.startedAt<n.startedAt?1:-1)}var O=/^\/api\/app\/tasks\/(cancel|followup)$/;async function W(t,e,r,n,s,o){let a=new URL(t.url||r,"http://localhost");if(n==="GET"&&(r==="/api/app/fleet"||r==="/api/app/activity")){let p=$(a.searchParams.get("timezone")),c=await o.snapshot({date:U(p),timezone:p});return r==="/api/app/activity"?b(e,200,{ts:c.ts,tasks:E(c.nodes)}):b(e,200,{ts:c.ts,nodes:c.nodes.map(j)})}if(n==="GET"&&r==="/api/app/approvals")return b(e,200,{nodes:await o.approvals()});if(n!=="POST")return!1;let d=r.match(O);if(!(d||["/api/app/nodes/restart","/api/app/nodes/reload","/api/app/crons/toggle","/api/app/approvals/decide"].includes(r)))return!1;let i;try{i=await R(t)}catch(p){return b(e,400,{error:p.message})}let h=w(i.node);if(!h)return b(e,400,{error:"node is required"});let A=`operator (phone: ${s})`,u;if(d){let p=w(i.taskId);if(!p)return b(e,400,{error:"taskId is required"});let c=`/api/tasks/${encodeURIComponent(p)}/${d[1]}`;if(d[1]==="cancel")u=await o.nodePost(h,c,{reason:`cancelled by ${A}`});else{let y=w(i.message);if(!y)return b(e,400,{error:"message is required"});u=await o.nodePost(h,c,{message:y,sender:A})}}else if(r==="/api/app/nodes/restart")u=await o.nodePost(h,i.cancel===!0?"/daemon/restart/cancel":"/daemon/restart",{});else if(r==="/api/app/nodes/reload")u=await o.nodePost(h,"/reload",{});else if(r==="/api/app/crons/toggle"){let p=w(i.cronId);if(!p||typeof i.enabled!="boolean")return b(e,400,{error:"cronId and enabled (true or false) are required"});u=await o.nodePost(h,`/crons/${encodeURIComponent(p)}/enabled`,{enabled:i.enabled})}else{let p=w(i.key),c=i.action;if(!p||c!=="yes"&&c!=="no"&&c!=="later")return b(e,400,{error:"key and action (yes, no or later) are required"});let y=typeof i.choice=="number"?i.choice:w(i.choice),l=typeof i.text=="string"&&i.text.trim()?i.text:"",C={...y!==""?{choice:y}:{},...l?{text:l}:{}};u=await o.decide(h,p,c,A,C)}return b(e,u.status,u.body)}function w(t){return typeof t=="string"?t.trim():""}function $(t){if(!t)return"UTC";try{return new Intl.DateTimeFormat("en-US",{timeZone:t}),t}catch{return"UTC"}}function U(t,e=new Date){return new Intl.DateTimeFormat("en-CA",{timeZone:t,year:"numeric",month:"2-digit",day:"2-digit"}).format(e)}async function R(t,e=16*1024){let r=0,n=[];for await(let a of t){if(r+=a.length,r>e)throw new Error("body too large");n.push(a)}let s=Buffer.concat(n).toString("utf-8").trim();if(!s)return{};let o=JSON.parse(s);if(!o||typeof o!="object"||Array.isArray(o))throw new Error("expected a JSON object");return o}function b(t,e,r){return t.writeHead(e,{"Content-Type":"application/json","Cache-Control":"no-store"}),t.end(JSON.stringify(r)),!0}function I(t){return t==="/api/app/push"||t.startsWith("/api/app/push/")||t==="/api/app/alerts"||t==="/api/app/alerts/read"}var G=200,_=2048,N=/^[A-Za-z0-9_-]{8,256}$/;async function ee(t,e,r,n,s,o){if(!I(r))return!1;let a={};if(n==="POST")try{a=await R(t)}catch(i){return P(e,400,{error:i.message})}let d={path:r,method:n,body:a,device:{id:s.id,name:s.name}};if(!o.forward){let i=B(d,o);return P(e,i.status,i.body)}let m;try{m=await o.forward(d),S.note(d,m)}catch(i){m=z(d,`${o.reason??"Can't reach the computer that sends notifications"}: ${i?.message??i}`)}return P(e,m.status,m.body)}var v=class{phones=new Map;file=null;persistTo(e){if(this.file!==e){this.file=e;try{let r=JSON.parse(F(e,"utf-8"));for(let[n,s]of Object.entries(r??{}))s&&typeof s=="object"&&typeof s.subscribed=="boolean"&&this.phones.set(n,{subscribed:s.subscribed,prefs:q(s.prefs)})}catch{}}}note(e,r){if(r.status!==200||!r.body||typeof r.body!="object")return;let n=r.body,s=this.phones.get(e.device.id)??{subscribed:!1,prefs:{}};if(e.method==="GET"&&e.path==="/api/app/push")s.subscribed=n.available===!0&&Number(n.subscriptions)>0,s.prefs=x(n);else if(e.path==="/api/app/push/prefs")s.prefs={...s.prefs,...x(n)};else if(e.path==="/api/app/push/subscribe")s.subscribed=!0;else if(e.path==="/api/app/push/unsubscribe")n.removed&&(s.subscribed=!1);else return;this.phones.set(e.device.id,s),this.save()}save(){if(this.file)try{D(M(this.file),{recursive:!0});let e=`${this.file}.${process.pid}.tmp`;K(e,JSON.stringify(Object.fromEntries(this.phones))+`
|
|
2
|
+
`,{encoding:"utf-8",mode:384}),H(e,this.file)}catch{}}on(e,r){return this.phones.get(e)?.prefs[g[r].field]??g[r].default}finishOn(e){return!!this.phones.get(e)?.subscribed&&this.on(e,"finish")}},S=new v;function te(){return S.persistTo(J(process.cwd(),".agentx/push-relayed.json")),S}function x(t){let e={};for(let r of Object.values(g))typeof t[r.field]=="boolean"&&(e[r.field]=t[r.field]);return e}function q(t){return t&&typeof t=="object"&&!Array.isArray(t)?x(t):{}}function z(t,e){return t.method==="GET"&&t.path==="/api/app/push"?{status:200,body:{available:!1,reason:e,publicKey:null,subscriptions:0,...T()}}:t.method==="GET"&&t.path==="/api/app/alerts"?{status:200,body:{items:[],error:e}}:{status:502,body:{error:e}}}function B(t,e){let{path:r,method:n,body:s,device:o}=t;if(!I(r))return{status:404,body:{error:"not found"}};let a=e.store(),d=a?e.publicKey():null,m=a?d?void 0:"This computer has no push keys yet. On it, run: agentx app push-keys":e.reason??"Notifications are not set up on this computer.";if(n==="GET"&&r==="/api/app/push")return f(200,{available:!m,reason:m??null,publicKey:d,subscriptions:a&&d?a.list(o.id).filter(u=>u.publicKey===d).length:0,...a?a.prefs.all(o.id):T()});if(n==="GET"&&r==="/api/app/alerts")return f(200,{items:a?a.recent(e.keepRecent,o.id):[]});if(n==="POST"&&r==="/api/app/alerts/read"){let u=Array.isArray(s.ids)?s.ids.filter(c=>Number.isInteger(c)).slice(0,G):null;if(!u)return f(400,{error:"send { ids: [push ids] }"});let p=a?a.recent(e.keepRecent,o.id).filter(c=>u.includes(c.id)):[];return f(200,{ok:!0,seen:p.length&&e.alertsRead?e.alertsRead(p):0})}if(n!=="POST"||r!=="/api/app/push/subscribe"&&r!=="/api/app/push/unsubscribe"&&r!=="/api/app/push/prefs")return f(404,{error:"not found"});if(!a||m)return f(503,{error:m});if(r==="/api/app/push/prefs"){let u=Object.keys(g).filter(l=>s[g[l].field]!==void 0),p=u.map(l=>g[l].field).join(", "),c=Object.values(g).map(l=>l.field).join(" or ");if(u.length===0)return f(400,{error:`send ${c}, true or false`});if(u.some(l=>typeof s[g[l].field]!="boolean"))return f(400,{error:`${p} must be true or false`});let y={};for(let l of u)a.prefs.set(o.id,l,s[g[l].field]),y[g[l].field]=s[g[l].field];return f(200,{ok:!0,...y})}let i=L(s.endpoint,e.allowedHosts);if(!i){let u="";try{u=new URL(String(s.endpoint)).hostname}catch{}return f(400,{error:`not a known push service${u?`: ${u}`:""} (see channels.push.allowedHosts)`})}if(r==="/api/app/push/unsubscribe")return f(200,{ok:!0,removed:a.unsubscribe(i,o.id)});let h=s.keys?.p256dh,A=s.keys?.auth;return typeof h!="string"||!N.test(h)||typeof A!="string"||!N.test(A)?f(400,{error:"keys.p256dh and keys.auth are required"}):s.publicKey!==void 0&&s.publicKey!==d?f(409,{error:"this computer's push keys changed; turn notifications on again"}):(a.subscribe({endpoint:i,p256dh:h,auth:A,deviceId:o.id,deviceName:o.name,publicKey:d}),f(200,{ok:!0}))}function f(t,e){return{status:t,body:e}}function T(){let t={};for(let e of Object.values(g))t[e.field]=e.default;return t}function L(t,e){if(typeof t!="string"||t.length>_)return null;try{let r=new URL(t);if(r.protocol!=="https:"||r.port||r.username||r.password)return null;let n=r.hostname.toLowerCase();return e.some(o=>{let a=o.toLowerCase().replace(/^\.+|\.+$/g,"");return!!a&&(n===a||n.endsWith("."+a))})?t:null}catch{return null}}function P(t,e,r){return t.writeHead(e,{"Content-Type":"application/json","Cache-Control":"no-store"}),t.end(JSON.stringify(r)),!0}export{W as a,R as b,I as c,ee as d,S as e,te as f,B as g};
|
|
3
|
+
//# sourceMappingURL=chunk-QHRQ3PUX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon/app-push.ts","../src/daemon/app-fleet.ts"],"sourcesContent":["import type { IncomingMessage, ServerResponse } from \"http\"\nimport { mkdirSync, readFileSync, renameSync, writeFileSync } from \"fs\"\nimport { dirname, resolve } from \"path\"\nimport type { TokenRecord } from \"./token-store\"\nimport type { PushStore } from \"@/channels/push-store\"\nimport { PUSH_PREFS, type PushPrefName } from \"@/channels/push-prefs\"\nimport { readJson } from \"./app-fleet\"\n\n// --- Phone app: notifications (/api/app/push*, /api/app/alerts) ---\n//\n// Phase 4 of the mobile epic. Runs behind the device-token check in\n// app-routes.ts. A phone subscribes here; the daemon's PushAdapter reads the\n// same table to deliver. Each subscription is tied to the phone's device\n// token, so `agentx app revoke` also stops that phone's notifications.\n//\n// On a node that relays pushes (channels.push.relayTo), `forward` is set and\n// every call goes to the push host instead, through this node's daemon and\n// the mesh (push-bridge.ts), so a phone paired with any node can turn\n// notifications on (#711).\n\nexport interface AppPushDeps {\n /** Null when this node doesn't send pushes itself; `reason` says why. */\n store: () => PushStore | null\n /** VAPID public key the browser subscribes with, or null without keys. */\n publicKey: () => string | null\n keepRecent: number\n /** channels.push.allowedHosts: push services a phone may subscribe with. */\n allowedHosts: string[]\n reason?: string\n /** Set on a relaying node: sends each call to the push host. */\n forward?: (call: AppPushCall) => Promise<AppPushResult>\n /** The phone marked these pushes read: the told rows they carried are\n * seen (#1138 step B). Returns how many rows changed. */\n alertsRead?: (items: Array<{ at: number; title: string }>) => number\n}\n\n/** One phone-app push request, as handled here or by the push host. */\nexport interface AppPushCall {\n path: string\n method: string\n body: Record<string, any>\n device: { id: string; name: string }\n}\n\nexport interface AppPushResult {\n status: number\n body: unknown\n}\n\n/** The routes this module answers. */\nexport function isAppPushPath(path: string): boolean {\n return path === \"/api/app/push\" || path.startsWith(\"/api/app/push/\") || path === \"/api/app/alerts\" || path === \"/api/app/alerts/read\"\n}\n\n/** Most push ids one read mark may name (\"Mark all read\" on a full list). */\nconst READ_MAX = 200\n\nconst MAX_ENDPOINT = 2048\nconst B64URL = /^[A-Za-z0-9_-]{8,256}$/\n\nexport async function handleAppPush(\n req: IncomingMessage,\n res: ServerResponse,\n path: string,\n method: string,\n device: TokenRecord,\n deps: AppPushDeps,\n): Promise<boolean> {\n if (!isAppPushPath(path)) return false\n let body: Record<string, any> = {}\n if (method === \"POST\") {\n try { body = await readJson(req) } catch (e: any) { return json(res, 400, { error: e.message }) }\n }\n const call: AppPushCall = { path, method, body, device: { id: device.id, name: device.name } }\n if (!deps.forward) {\n const out = runAppPush(call, deps)\n return json(res, out.status, out.body)\n }\n let out: AppPushResult\n try {\n out = await deps.forward(call)\n relayedPush.note(call, out)\n } catch (e: any) {\n out = unreachable(call, `${deps.reason ?? \"Can't reach the computer that sends notifications\"}: ${e?.message ?? e}`)\n }\n return json(res, out.status, out.body)\n}\n\n/** On a relaying node: what the push host last said about each phone, for\n * the checks that must answer at once (a finish notification, the\n * announcement switch). Filled from the phone's own calls on their way\n * through, so it is as fresh as the phone's last look at Alerts. */\nexport class RelayedPushState {\n private phones = new Map<string, { subscribed: boolean; prefs: Record<string, boolean> }>()\n private file: string | null = null\n\n /** Keeps the state in `file` too, so a dashboard restart doesn't stop\n * finish notifications until the phone next opens Alerts. Loads it the\n * first time; later calls with the same file do nothing. */\n persistTo(file: string): void {\n if (this.file === file) return\n this.file = file\n try {\n const saved = JSON.parse(readFileSync(file, \"utf-8\")) as Record<string, any>\n for (const [id, p] of Object.entries(saved ?? {})) {\n if (p && typeof p === \"object\" && typeof p.subscribed === \"boolean\") {\n this.phones.set(id, { subscribed: p.subscribed, prefs: pickPrefsFrom(p.prefs) })\n }\n }\n } catch { /* none yet, or unreadable: start empty */ }\n }\n\n note(call: AppPushCall, out: AppPushResult): void {\n if (out.status !== 200 || !out.body || typeof out.body !== \"object\") return\n const body = out.body as Record<string, any>\n const phone = this.phones.get(call.device.id) ?? { subscribed: false, prefs: {} }\n if (call.method === \"GET\" && call.path === \"/api/app/push\") {\n phone.subscribed = body.available === true && Number(body.subscriptions) > 0\n phone.prefs = pickPrefs(body)\n } else if (call.path === \"/api/app/push/prefs\") {\n phone.prefs = { ...phone.prefs, ...pickPrefs(body) }\n } else if (call.path === \"/api/app/push/subscribe\") {\n phone.subscribed = true\n } else if (call.path === \"/api/app/push/unsubscribe\") {\n if (body.removed) phone.subscribed = false\n } else {\n return\n }\n this.phones.set(call.device.id, phone)\n this.save()\n }\n\n private save(): void {\n if (!this.file) return\n try {\n mkdirSync(dirname(this.file), { recursive: true })\n const tmp = `${this.file}.${process.pid}.tmp`\n writeFileSync(tmp, JSON.stringify(Object.fromEntries(this.phones)) + \"\\n\", { encoding: \"utf-8\", mode: 0o600 })\n renameSync(tmp, this.file)\n } catch { /* kept in memory; written again at the next change */ }\n }\n\n /** A switch as last seen, or its default. */\n on(deviceId: string, name: PushPrefName): boolean {\n return this.phones.get(deviceId)?.prefs[PUSH_PREFS[name].field] ?? PUSH_PREFS[name].default\n }\n\n /** Subscribed and wanting finish notifications, as far as last seen. */\n finishOn(deviceId: string): boolean {\n return !!this.phones.get(deviceId)?.subscribed && this.on(deviceId, \"finish\")\n }\n}\n\n/** One per dashboard process. */\nexport const relayedPush = new RelayedPushState()\n\n/** relayedPush, kept in .agentx/ of the folder the dashboard runs in. */\nexport function relayedPushHere(): RelayedPushState {\n relayedPush.persistTo(resolve(process.cwd(), \".agentx/push-relayed.json\"))\n return relayedPush\n}\n\nfunction pickPrefs(body: Record<string, any>): Record<string, boolean> {\n const out: Record<string, boolean> = {}\n for (const p of Object.values(PUSH_PREFS)) if (typeof body[p.field] === \"boolean\") out[p.field] = body[p.field]\n return out\n}\n\nfunction pickPrefsFrom(v: unknown): Record<string, boolean> {\n return v && typeof v === \"object\" && !Array.isArray(v) ? pickPrefs(v as Record<string, any>) : {}\n}\n\n/** What a relaying node answers when the push host can't be reached: the\n * Alerts card says why instead of failing to load. */\nfunction unreachable(call: AppPushCall, reason: string): AppPushResult {\n if (call.method === \"GET\" && call.path === \"/api/app/push\") {\n return { status: 200, body: { available: false, reason, publicKey: null, subscriptions: 0, ...prefDefaults() } }\n }\n if (call.method === \"GET\" && call.path === \"/api/app/alerts\") return { status: 200, body: { items: [], error: reason } }\n return { status: 502, body: { error: reason } }\n}\n\n/** Answers one call against this node's own subscriptions table. Used for\n * a phone paired here, and by the push host for a phone paired with a\n * relaying node (push-bridge.ts), whose device id is then scoped to it. */\nexport function runAppPush(call: AppPushCall, deps: AppPushDeps): AppPushResult {\n const { path, method, body, device } = call\n if (!isAppPushPath(path)) return { status: 404, body: { error: \"not found\" } }\n const store = deps.store()\n const publicKey = store ? deps.publicKey() : null\n const unavailable = !store\n ? deps.reason ?? \"Notifications are not set up on this computer.\"\n : !publicKey ? \"This computer has no push keys yet. On it, run: agentx app push-keys\" : undefined\n\n if (method === \"GET\" && path === \"/api/app/push\") {\n return ok(200, {\n available: !unavailable,\n reason: unavailable ?? null,\n publicKey,\n // Only rows made with the current key count, so a phone subscribed\n // before `push-keys --force` sees 0 and subscribes again.\n subscriptions: store && publicKey ? store.list(device.id).filter((s) => s.publicKey === publicKey).length : 0,\n // Per-phone switches (push-prefs.ts): chatFinish, announce.\n ...(store ? store.prefs.all(device.id) : prefDefaults()),\n })\n }\n if (method === \"GET\" && path === \"/api/app/alerts\") {\n return ok(200, { items: store ? store.recent(deps.keepRecent, device.id) : [] })\n }\n if (method === \"POST\" && path === \"/api/app/alerts/read\") {\n // { ids }: the push log rows the phone marked read (#1138 step B).\n const ids = Array.isArray(body.ids) ? body.ids.filter((x: unknown) => Number.isInteger(x)).slice(0, READ_MAX) as number[] : null\n if (!ids) return ok(400, { error: \"send { ids: [push ids] }\" })\n const items = store ? store.recent(deps.keepRecent, device.id).filter((r) => ids.includes(r.id)) : []\n return ok(200, { ok: true, seen: items.length && deps.alertsRead ? deps.alertsRead(items) : 0 })\n }\n if (method !== \"POST\" || (path !== \"/api/app/push/subscribe\" && path !== \"/api/app/push/unsubscribe\" && path !== \"/api/app/push/prefs\")) {\n return ok(404, { error: \"not found\" })\n }\n if (!store || unavailable) return ok(503, { error: unavailable })\n\n if (path === \"/api/app/push/prefs\") {\n // Any of the switches, each true or false; answers with those it set.\n const changes = (Object.keys(PUSH_PREFS) as PushPrefName[]).filter((n) => body[PUSH_PREFS[n].field] !== undefined)\n const fields = changes.map((n) => PUSH_PREFS[n].field).join(\", \")\n const all = Object.values(PUSH_PREFS).map((p) => p.field).join(\" or \")\n if (changes.length === 0) return ok(400, { error: `send ${all}, true or false` })\n if (changes.some((n) => typeof body[PUSH_PREFS[n].field] !== \"boolean\")) return ok(400, { error: `${fields} must be true or false` })\n const out: Record<string, boolean> = {}\n for (const n of changes) {\n store.prefs.set(device.id, n, body[PUSH_PREFS[n].field])\n out[PUSH_PREFS[n].field] = body[PUSH_PREFS[n].field]\n }\n return ok(200, { ok: true, ...out })\n }\n const endpoint = validEndpoint(body.endpoint, deps.allowedHosts)\n if (!endpoint) {\n let host = \"\"\n try { host = new URL(String(body.endpoint)).hostname } catch { /* not a URL */ }\n return ok(400, { error: `not a known push service${host ? `: ${host}` : \"\"} (see channels.push.allowedHosts)` })\n }\n\n if (path === \"/api/app/push/unsubscribe\") {\n return ok(200, { ok: true, removed: store.unsubscribe(endpoint, device.id) })\n }\n const p256dh = body.keys?.p256dh\n const auth = body.keys?.auth\n if (typeof p256dh !== \"string\" || !B64URL.test(p256dh) || typeof auth !== \"string\" || !B64URL.test(auth)) {\n return ok(400, { error: \"keys.p256dh and keys.auth are required\" })\n }\n // An endpoint belongs to one browser. If another device had it (a phone\n // re-paired under a new name), the latest pairing takes it over.\n // The browser subscribed with the key GET /api/app/push just handed it;\n // `publicKey` in the body must match it, so a stale page can't store a\n // subscription made with replaced keys.\n if (body.publicKey !== undefined && body.publicKey !== publicKey) {\n return ok(409, { error: \"this computer's push keys changed; turn notifications on again\" })\n }\n store.subscribe({ endpoint, p256dh, auth, deviceId: device.id, deviceName: device.name, publicKey: publicKey! })\n return ok(200, { ok: true })\n}\n\nfunction ok(status: number, body: unknown): AppPushResult {\n return { status, body }\n}\n\nfunction prefDefaults(): Record<string, boolean> {\n const out: Record<string, boolean> = {}\n for (const p of Object.values(PUSH_PREFS)) out[p.field] = p.default\n return out\n}\n\n/** The endpoint if it is https on an allowed push-service host (or a\n * subdomain of one). The daemon POSTs to whatever is stored, so a name\n * that merely looks public (and may resolve inside the network) is refused. */\nexport function validEndpoint(v: unknown, allowedHosts: string[]): string | null {\n if (typeof v !== \"string\" || v.length > MAX_ENDPOINT) return null\n try {\n const u = new URL(v)\n if (u.protocol !== \"https:\" || u.port || u.username || u.password) return null\n const host = u.hostname.toLowerCase()\n const ok = allowedHosts.some((h) => {\n const allowed = h.toLowerCase().replace(/^\\.+|\\.+$/g, \"\")\n return !!allowed && (host === allowed || host.endsWith(\".\" + allowed))\n })\n return ok ? v : null\n } catch {\n return null\n }\n}\n\nfunction json(res: ServerResponse, status: number, body: unknown): true {\n res.writeHead(status, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" })\n res.end(JSON.stringify(body))\n return true\n}\n","import type { IncomingMessage, ServerResponse } from \"http\"\n\n// --- Phone app: fleet, activity and control (/api/app/fleet, …) ---\n//\n// Phase 3 of the mobile epic. Runs behind the device-token check in\n// app-routes.ts, so every handler here may assume a paired phone.\n//\n// Reads reuse the dashboard's live snapshot (every node the dashboard can\n// see, including peers found through /mesh) and trim it to what a phone\n// shows: short previews, never full prompts, replies or errors. Writes go\n// to a node daemon through the dashboard's allowlisted, token-carrying POST\n// (AppFleetDeps.nodePost), the same path the desktop task and restart\n// buttons use, so the phone gets no power the dashboard doesn't already have.\n\nexport const PREVIEW_CHARS = 160\n\nexport interface SnapshotNode {\n id: string\n name: string\n url: string\n reachable: boolean\n error?: string\n uptimeSec?: number\n inflight?: number\n restart?: { state: string; requestedAt?: string; deadline?: string }\n agents: Array<{\n id: string\n name: string\n tier?: string\n color?: string\n active: number\n errors: number\n lastActive?: string\n lastSummary?: { text: string; at: string; ok: boolean }\n runningTasks?: Array<{ id: string; traceId?: string; messagePreview: string; channel: string; chatId?: string; sender?: string; startedAt: string }>\n }>\n crons?: Array<{ id: string; enabled: boolean; schedule: string; agent: string; kind?: string; nextRun?: string; consecutiveErrors: number }>\n cronRuns?: Array<{ jobId: string; startedAt: string; status: string; responseSummary?: string; errorSummary?: string; isRetry?: boolean }>\n}\n\nexport interface ApprovalItem {\n key: string\n title: string\n ask?: string\n recommend?: string\n /** What yes and no do, in words. */\n yes?: string\n no?: string\n raisedBy?: string\n expires?: string\n /** Cards: the ready-made answers; yes must pick one (#743). */\n choices?: string[]\n /** Cards: the suggested message, `{choice}` not filled in yet. */\n draft?: string\n /** A short excerpt of what is being decided, for the Desk (#959). */\n detail?: string\n createdAt?: string\n}\n\n/** A card's pick (1-based number or label) and the message as edited. */\nexport interface ChoiceAnswer { choice?: string | number; text?: string }\n\nexport interface NodeApprovals {\n node: string\n nodeName: string\n items: ApprovalItem[]\n error?: string\n}\n\nexport interface NodeReply { status: number; body: any }\n\nexport interface AppFleetDeps {\n snapshot(day?: { date: string; timezone: string }): Promise<{ ts: string; nodes: SnapshotNode[] }>\n /** POST to a node daemon on the dashboard's allowlist, with its token. */\n nodePost(nodeUrl: string, path: string, body: unknown): Promise<NodeReply>\n approvals(): Promise<NodeApprovals[]>\n /** `by` names the phone, e.g. \"operator (phone: My phone)\". */\n decide(nodeUrl: string, key: string, action: \"yes\" | \"no\" | \"later\", by: string, answer?: ChoiceAnswer): Promise<NodeReply>\n}\n\nexport function clip(text: string | undefined, max = PREVIEW_CHARS): string | undefined {\n if (!text) return text\n const flat = text.replace(/\\s+/g, \" \").trim()\n return flat.length > max ? flat.slice(0, max - 1) + \"…\" : flat\n}\n\n/** One node, trimmed for the Fleet tab. Cron outcomes come from the node's\n * persisted run files (cronRuns), never the scheduler's counters. */\nexport function summarizeNode(n: SnapshotNode) {\n const lastRun = new Map<string, NonNullable<SnapshotNode[\"cronRuns\"]>[number]>()\n for (const r of n.cronRuns ?? []) {\n const prev = lastRun.get(r.jobId)\n if (!prev || prev.startedAt < r.startedAt) lastRun.set(r.jobId, r)\n }\n const today = { success: 0, failed: 0 }\n for (const r of n.cronRuns ?? []) {\n if (r.status === \"success\") today.success++\n else today.failed++\n }\n return {\n id: n.id,\n name: n.name,\n url: n.url,\n reachable: n.reachable,\n error: clip(n.error),\n uptimeSec: n.uptimeSec,\n inflight: n.inflight,\n restart: n.restart?.state && n.restart.state !== \"none\" ? n.restart : undefined,\n agents: n.agents.map((a) => ({\n id: a.id,\n name: a.name,\n tier: a.tier,\n active: a.active,\n errors: a.errors,\n lastActive: a.lastActive,\n last: a.lastSummary ? { text: clip(a.lastSummary.text), at: a.lastSummary.at, ok: a.lastSummary.ok } : undefined,\n })),\n crons: {\n today,\n items: (n.crons ?? []).map((c) => {\n const r = lastRun.get(c.id)\n return {\n id: c.id,\n enabled: c.enabled,\n schedule: c.schedule,\n agent: c.agent,\n nextRun: c.nextRun,\n consecutiveErrors: c.consecutiveErrors,\n last: r ? { status: r.status, at: r.startedAt, text: clip(r.status === \"success\" ? r.responseSummary : r.errorSummary) } : undefined,\n }\n }),\n },\n }\n}\n\n/** Every running task across the fleet, newest first. */\nexport function summarizeActivity(nodes: SnapshotNode[]) {\n const tasks = []\n for (const n of nodes) {\n for (const a of n.agents) {\n for (const t of a.runningTasks ?? []) {\n tasks.push({\n node: n.url,\n nodeName: n.name,\n agentId: a.id,\n agentName: a.name,\n taskId: t.id,\n preview: clip(t.messagePreview),\n channel: t.channel,\n startedAt: t.startedAt,\n })\n }\n }\n }\n return tasks.sort((x, y) => (x.startedAt < y.startedAt ? 1 : -1))\n}\n\nconst TASK_ACTION = /^\\/api\\/app\\/tasks\\/(cancel|followup)$/\n\n/** Handles /api/app/{fleet,activity,tasks,nodes,crons,approvals}. Returns\n * false for any other path so the caller can fall through. */\nexport async function handleAppFleet(\n req: IncomingMessage,\n res: ServerResponse,\n path: string,\n method: string,\n device: string,\n deps: AppFleetDeps,\n): Promise<boolean> {\n const url = new URL(req.url || path, \"http://localhost\")\n\n if (method === \"GET\" && (path === \"/api/app/fleet\" || path === \"/api/app/activity\")) {\n const timezone = validTimezone(url.searchParams.get(\"timezone\"))\n const snap = await deps.snapshot({ date: dateIn(timezone), timezone })\n if (path === \"/api/app/activity\") return json(res, 200, { ts: snap.ts, tasks: summarizeActivity(snap.nodes) })\n return json(res, 200, { ts: snap.ts, nodes: snap.nodes.map(summarizeNode) })\n }\n\n if (method === \"GET\" && path === \"/api/app/approvals\") {\n return json(res, 200, { nodes: await deps.approvals() })\n }\n\n if (method !== \"POST\") return false\n const taskAction = path.match(TASK_ACTION)\n const known = taskAction || [\n \"/api/app/nodes/restart\", \"/api/app/nodes/reload\", \"/api/app/crons/toggle\", \"/api/app/approvals/decide\",\n ].includes(path)\n if (!known) return false\n\n let body: Record<string, unknown>\n try { body = await readJson(req) } catch (e: any) { return json(res, 400, { error: e.message }) }\n const node = str(body.node)\n if (!node) return json(res, 400, { error: \"node is required\" })\n const by = `operator (phone: ${device})`\n\n let reply: NodeReply\n if (taskAction) {\n const taskId = str(body.taskId)\n if (!taskId) return json(res, 400, { error: \"taskId is required\" })\n const upstream = `/api/tasks/${encodeURIComponent(taskId)}/${taskAction[1]}`\n if (taskAction[1] === \"cancel\") {\n reply = await deps.nodePost(node, upstream, { reason: `cancelled by ${by}` })\n } else {\n const message = str(body.message)\n if (!message) return json(res, 400, { error: \"message is required\" })\n reply = await deps.nodePost(node, upstream, { message, sender: by })\n }\n } else if (path === \"/api/app/nodes/restart\") {\n reply = await deps.nodePost(node, body.cancel === true ? \"/daemon/restart/cancel\" : \"/daemon/restart\", {})\n } else if (path === \"/api/app/nodes/reload\") {\n reply = await deps.nodePost(node, \"/reload\", {})\n } else if (path === \"/api/app/crons/toggle\") {\n const cronId = str(body.cronId)\n if (!cronId || typeof body.enabled !== \"boolean\") return json(res, 400, { error: \"cronId and enabled (true or false) are required\" })\n reply = await deps.nodePost(node, `/crons/${encodeURIComponent(cronId)}/enabled`, { enabled: body.enabled })\n } else {\n const key = str(body.key)\n const action = body.action\n if (!key || (action !== \"yes\" && action !== \"no\" && action !== \"later\")) {\n return json(res, 400, { error: \"key and action (yes, no or later) are required\" })\n }\n const choice = typeof body.choice === \"number\" ? body.choice : str(body.choice)\n const text = typeof body.text === \"string\" && body.text.trim() ? body.text : \"\"\n const answer: ChoiceAnswer = { ...(choice !== \"\" ? { choice } : {}), ...(text ? { text } : {}) }\n reply = await deps.decide(node, key, action, by, answer)\n }\n return json(res, reply.status, reply.body)\n}\n\nfunction str(v: unknown): string {\n return typeof v === \"string\" ? v.trim() : \"\"\n}\n\nfunction validTimezone(tz: string | null): string {\n if (!tz) return \"UTC\"\n try { new Intl.DateTimeFormat(\"en-US\", { timeZone: tz }); return tz } catch { return \"UTC\" }\n}\n\n/** YYYY-MM-DD for \"today\" in the phone's time zone. */\nexport function dateIn(timezone: string, now = new Date()): string {\n return new Intl.DateTimeFormat(\"en-CA\", { timeZone: timezone, year: \"numeric\", month: \"2-digit\", day: \"2-digit\" }).format(now)\n}\n\nexport async function readJson(req: IncomingMessage, limit = 16 * 1024): Promise<Record<string, unknown>> {\n let size = 0\n const chunks: Buffer[] = []\n for await (const chunk of req) {\n size += (chunk as Buffer).length\n if (size > limit) throw new Error(\"body too large\")\n chunks.push(chunk as Buffer)\n }\n const raw = Buffer.concat(chunks).toString(\"utf-8\").trim()\n if (!raw) return {}\n const parsed = JSON.parse(raw)\n if (!parsed || typeof parsed !== \"object\" || Array.isArray(parsed)) throw new Error(\"expected a JSON object\")\n return parsed\n}\n\nfunction json(res: ServerResponse, status: number, body: unknown): true {\n res.writeHead(status, { \"Content-Type\": \"application/json\", \"Cache-Control\": \"no-store\" })\n res.end(JSON.stringify(body))\n return true\n}\n"],"mappings":"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","names":["mkdirSync","readFileSync","renameSync","writeFileSync","dirname","resolve","clip","text","max","flat","summarizeNode","n","lastRun","r","prev","today","a","c","summarizeActivity","nodes","tasks","t","x","y","TASK_ACTION","handleAppFleet","req","res","path","method","device","deps","url","timezone","validTimezone","snap","dateIn","json","taskAction","body","readJson","e","node","str","by","reply","taskId","upstream","message","cronId","key","action","choice","answer","v","tz","now","limit","size","chunks","chunk","raw","parsed","status","isAppPushPath","path","READ_MAX","MAX_ENDPOINT","B64URL","handleAppPush","req","res","method","device","deps","body","readJson","e","json","call","out","runAppPush","relayedPush","unreachable","RelayedPushState","file","saved","readFileSync","id","p","pickPrefsFrom","phone","pickPrefs","mkdirSync","dirname","tmp","writeFileSync","renameSync","deviceId","name","PUSH_PREFS","relayedPushHere","resolve","v","reason","prefDefaults","store","publicKey","unavailable","ok","s","ids","x","items","r","changes","n","fields","all","endpoint","validEndpoint","host","p256dh","auth","status","allowedHosts","u","h","allowed"]}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import{b as le,d as ue,h as Zt}from"./chunk-BRK6M6LA.js";import{l as de,m as $,n as Jt}from"./chunk-UIBUZ2N3.js";import{a as se,e as Yt}from"./chunk-2QNT5JDS.js";import{a as g,b as Vt}from"./chunk-QIJLFPWE.js";import{z as l}from"zod";var Qt,en,tn,nn,rn,an,on,sn,ln,ce,me=g(()=>{"use strict";Qt=l.enum(["mon","tue","wed","thu","fri","sat","sun"]),en=l.object({days:l.array(Qt).default(["mon","tue","wed","thu","fri"]),start:l.string().regex(/^\d{2}:\d{2}$/),end:l.string().regex(/^\d{2}:\d{2}$/),lunch:l.object({start:l.string().regex(/^\d{2}:\d{2}$/),end:l.string().regex(/^\d{2}:\d{2}$/)}).optional()}),tn=l.object({title:l.string(),responsibilities:l.array(l.string()).default([]),sopPath:l.string().optional(),kpis:l.array(l.string()).default([])}),nn=l.object({role:l.string(),reportsTo:l.string().optional(),schedule:en,utilizationTarget:l.number().min(0).max(1).default(.8)}),rn=l.object({channel:l.string(),chatId:l.string(),accountId:l.string().optional()}),an=l.discriminatedUnion("type",[l.object({type:l.literal("backlog"),path:l.string().default(".agentx/backlog.md")}),l.object({type:l.literal("gitlab"),projects:l.array(l.string()).default([])}),l.object({type:l.literal("wiki"),path:l.string(),glob:l.string().default("**/*.md")})]),on=l.object({id:l.string(),pm:l.string().optional(),client:l.string().optional()}),sn=l.object({channel:l.string().optional(),chatId:l.string().optional(),username:l.string().optional(),senderId:l.string().optional(),client:l.string(),project:l.string().optional(),displayName:l.string().optional()}),ln=l.object({name:l.string().optional(),kind:l.enum(["client","internal","own"]).optional(),respondWithin:l.string().optional(),standing:l.array(l.string()).default([])}),ce=l.object({enabled:l.boolean().default(!1),timezone:l.string().default("UTC"),mainChannel:rn,workSource:an,roles:l.record(l.string(),tn).default({}),orgChart:l.record(l.string(),nn).default({}),projects:l.array(on).default([]),contactMap:l.array(sn).default([]),clients:l.record(l.string(),ln).default({}),workTickMinutes:l.number().int().min(1).max(60).default(15),idleQueueThreshold:l.number().int().min(0).default(0),standup:l.object({enabled:l.boolean().default(!0),plansDir:l.string().default(".agentx/plans"),maxAgentsPerDay:l.number().int().min(1).max(100).default(20),dryRun:l.boolean().default(!1)}).default({})})});import{z as u}from"zod";function Vr(r,t){let n=new Set(r.labels||[]);if(r.state==="closed"){let o=t.find(i=>i.kind==="closed");if(o)return o.id}for(let o of t)if(o.kind==="scoped-label"&&o.scopedLabel&&n.has(o.scopedLabel))return o.id;for(let o of t)if(o.kind==="label"&&o.mapsToLabel&&n.has(o.mapsToLabel))return o.id;let a=t.find(o=>o.kind==="open-backlog");if(a){let o=a.scopedPrefix+"::";return[...n].some(s=>s.startsWith(o))?null:a.id}return t[0]?.id??null}function Yr(r,t){let n=[],a=[],o=!1,i=!1;return t.kind==="scoped-label"&&t.scopedLabel?n.push(t.scopedLabel):t.kind==="label"&&t.mapsToLabel?n.push(t.mapsToLabel):t.kind==="closed"&&(o=!0),r&&(r.kind==="scoped-label"&&r.scopedLabel?a.push(r.scopedLabel):r.kind==="label"&&r.mapsToLabel?a.push(r.mapsToLabel):r.kind==="closed"&&t.kind!=="closed"&&(i=!0)),{addLabels:n.length?n:void 0,removeLabels:a.length?a:void 0,closeIssue:o||void 0,reopen:i||void 0}}var un,dn,cn,mn,fn,gn,fe,pn,ge,pe=g(()=>{"use strict";un=u.object({name:u.string(),color:u.string().regex(/^#[0-9a-f]{6}$/i).default("#6366f1"),description:u.string().optional()}),dn=u.object({id:u.string().regex(/^[a-z0-9_-]+$/,"column id must be lowercase slug"),title:u.string(),kind:u.enum(["open-backlog","scoped-label","closed","label"]).default("label"),mapsToLabel:u.string().optional(),scopedLabel:u.string().optional(),scopedPrefix:u.string().default("Status"),accent:u.string().optional()}),cn=u.object({enabled:u.boolean().default(!0),staleDoingMinutes:u.number().int().min(1).default(45),respectLunchBreak:u.boolean().default(!0),respectSchedule:u.boolean().default(!0),action:u.enum(["badge","notify"]).default("badge")}),mn=u.discriminatedUnion("type",[u.object({type:u.literal("gitlab"),projects:u.array(u.string()).min(1)})]),fn=[{id:"open",title:"Open",kind:"open-backlog",scopedPrefix:"Status",accent:"#6b7280"},{id:"todo",title:"To Do",kind:"scoped-label",scopedLabel:"Status::To Do",scopedPrefix:"Status",accent:"#fb7a35"},{id:"doing",title:"Doing",kind:"scoped-label",scopedLabel:"Status::Doing",scopedPrefix:"Status",accent:"#22c55e"},{id:"onhold",title:"On Hold",kind:"scoped-label",scopedLabel:"Status::On Hold",scopedPrefix:"Status",accent:"#f59e0b"},{id:"review",title:"Review",kind:"scoped-label",scopedLabel:"Status::Review",scopedPrefix:"Status",accent:"#3b82f6"},{id:"closed",title:"Closed",kind:"closed",scopedPrefix:"Status",accent:"#6b7280"}],gn=u.object({id:u.string().regex(/^[a-z0-9_-]+$/,"board id must be lowercase slug"),name:u.string(),source:mn,primaryToolLabel:u.string().optional(),labels:u.array(un).default([]),columns:u.array(dn).min(1).default(fn),timeRangeDays:u.number().int().min(1).max(365).default(30),closedWindowDays:u.number().int().min(1).max(365).default(30),reconciliation:cn.default({})}),fe=u.array(gn).default([]),pn=u.object({name:u.string(),url:u.string(),dashboardUrl:u.string().optional(),token:u.string().optional(),dashboardToken:u.string().optional()}),ge=u.object({enabled:u.boolean().default(!1),port:u.number().int().default(4202),bind:u.string().default("127.0.0.1"),token:u.string().optional(),daemonUrl:u.string().default("http://localhost:18800"),daemons:u.array(pn).default([]),draftAgent:u.string().optional()}).default({})});function _n(r){let t=String(r??"").trim().toLowerCase();if(!t)return{ok:!1,error:"is empty"};let n=t.split("+").map(i=>i.trim());if(n.some(i=>!i))return{ok:!1,error:`"${r}" has an empty part; write it like opt+space`};let a=yn[n[n.length-1]]??n[n.length-1],o=new Set;for(let i of n.slice(0,-1)){let s=hn[i];if(!s)return{ok:!1,error:`"${i}" is not a modifier; use ctrl, opt, shift or cmd`};if(o.has(s))return{ok:!1,error:`"${r}" names ${s} twice`};o.add(s)}return kn(a)?!he(a)&&!["ctrl","opt","cmd"].some(i=>o.has(i))?{ok:!1,error:`"${r}" needs ctrl, opt or cmd, or it would take that key away from your typing`}:{ok:!0,value:[...bn.filter(i=>o.has(i)),a].join("+")}:{ok:!1,error:`"${a}" is not a key AgentX Voice can use; use a letter, a digit, f1 to f20, or space, period, comma, slash, return, tab`}}function be(r){let t=_n(r);return t.ok?void 0:t.error}function Jr(r){let t={ctrl:"\u2303",opt:"\u2325",shift:"\u21E7",cmd:"\u2318"},n={space:"Space",period:".",comma:",",slash:"/",semicolon:";",quote:"'",minus:"-",equal:"=",leftbracket:"[",rightbracket:"]",backslash:"\\",grave:"`",return:"Return",tab:"Tab"},a=r.split("+"),o=a.pop()??"";return a.map(i=>t[i]??i).join("")+(n[o]??o.toUpperCase())}var hn,bn,xn,yn,he,kn,O,xe=g(()=>{"use strict";hn={ctrl:"ctrl",control:"ctrl",opt:"opt",option:"opt",alt:"opt",shift:"shift",cmd:"cmd",command:"cmd"},bn=["ctrl","opt","shift","cmd"],xn=["space","period","comma","slash","semicolon","quote","minus","equal","leftbracket","rightbracket","backslash","grave","return","tab"],yn={".":"period",",":"comma","/":"slash",";":"semicolon","'":"quote","-":"minus","=":"equal","`":"grave",enter:"return"},he=r=>/^f([1-9]|1[0-9]|20)$/.test(r),kn=r=>/^[a-z0-9]$/.test(r)||xn.includes(r)||he(r);O={talk:"opt+space",stop:"cmd+opt+period",paste:"cmd+opt+v"}});function _e(r){let t=/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(r);if(!t)return null;let[n,a,o]=t.slice(1).map(f=>parseInt(f,16)/255),i=Math.max(n,a,o),s=Math.min(n,a,o),d=i-s;return d<.04?null:((i===n?(a-o)/d%6:i===a?(o-n)/d+2:(n-a)/d+4)*60+360)%360}function Sn(r){let t=_e(r);if(t===null)return T.find(o=>o.id==="lagoon");let n=T[0],a=360;for(let o of T){let i=Math.abs(o.hue-t),s=Math.min(i,360-i);s<a&&(n=o,a=s)}return n}function ea(r,t=D){let n=s=>T.find(d=>d.id===s),a=n(r?.palette);if(a)return{...a,set:!0};let o=r?.color;return{...o&&_e(o)!==null?Sn(o):n(t)??n(D),set:!1}}var T,ye,ke,Z,D,Se=g(()=>{"use strict";T=[{id:"sunrise",label:"Sunrise",hue:18,colors:["#E8505B","#F2726F","#F79A5A","#FBBF4A","#FFE3A3"]},{id:"desert",label:"Desert",hue:36,colors:["#A4492C","#C8683E","#D9905E","#E7B98A","#F6E3C4"]},{id:"forest",label:"Forest",hue:100,colors:["#2F5D34","#4C7D3A","#6FA046","#9CC25E","#D5EBA4"]},{id:"lagoon",label:"Lagoon",hue:172,colors:["#0B6E73","#0E9594","#1FBFB2","#56DCCB","#B8F4EA"]},{id:"ocean",label:"Ocean",hue:205,colors:["#15457E","#1C6BA8","#2B93CF","#5DBBE8","#BFE6FA"]},{id:"dusk",label:"Dusk",hue:268,colors:["#2E2A7A","#5B3E9E","#8C4FB0","#C8649E","#F4A7B9"]},{id:"blossom",label:"Blossom",hue:330,colors:["#A3245B","#CF3F78","#E8699A","#F49BB8","#FFD6E0"]}],ye=["orb","character"],ke=["off","rarely","sometimes","often"],Z=T.map(r=>r.id);D="lagoon"});function Ee(r){let t=(r.match(/[-ۿ]/g)??[]).length,n=(r.match(/[a-zA-ZÀ-ÿ]/g)??[]).length;if(t&&t>=n)return"ar";let a=0,o=0;for(let i of r.toLowerCase().replace(/[’]/g,"'").match(/[a-zà-ÿœ']+/g)??[])En.has(i)&&a++,wn.has(i)||i.startsWith("l'")||i.startsWith("d'")||i.startsWith("qu'")?o++:Tn.test(i)&&(o+=.5);return a===o?null:a>o?"en":"fr"}var En,wn,Tn,we=g(()=>{"use strict";En=new Set("the and is are was were i you he she we they it this that to of in for with not but yes what have has will would can your my".split(" ")),wn=new Set("le la les des du de un une et est sont je tu il elle nous vous ils elles ce cette c'est pas pour que qui avec dans sur au aux mais tr\xE8s oui non merci bonjour votre mon".split(" ")),Tn=/[éèêëàâçùûîïôœ]/});function aa(r,t=J){let n=r;if(n=n.replace(/```[\s\S]*?```/g,". "),n=n.replace(/`[^`]*`/g,""),n=n.replace(/\*\*(.*?)\*\*/g,"$1"),n=n.replace(/__(.*?)__/g,"$1"),n=n.replace(/\*([^*]+)\*/g,"$1"),n=n.replace(/_([^_]+)_/g,"$1"),n=n.replace(/~~(.*?)~~/g,"$1"),n=n.replace(/\[([^\]]+)\]\([^)]+\)/g,"$1"),n=n.replace(/!\[([^\]]*)\]\([^)]+\)/g,"image$1 "),n=n.replace(/^#{1,6}\s+/gm,""),n=n.replace(/^[\s]*[-*+]\s+/gm,". "),n=n.replace(/^[\s]*\d+\.\s+/gm,". "),n=n.replace(/^[-=_]{3,}$/gm,""),n=n.replace(/^>\s*/gm,""),n=n.replace(/https?:\/\/\S+/g,"link"),n=n.replace(/\/\w+(\/\w+)+/g,a=>a.split("/").filter(Boolean).join(" slash ")),n=n.replace(/&/g," and "),n=n.replace(/@/g," at "),n=n.replace(/=>/g," returns "),n=n.replace(/->/g," to "),n=n.replace(/\|/g," or "),n=n.replace(/\be\.g\./gi,"for example"),n=n.replace(/\bi\.e\./gi,"that is"),n=n.replace(/\betc\./gi,"etcetera"),n=n.replace(/\bvs\.?\b/gi,"versus"),n=n.replace(/\n{2,}/g,". "),n=n.replace(/\n/g," "),n=n.replace(/\s+/g," "),n=n.replace(/\.\s*\./g,"."),n=n.replace(/\s+([.,!?])/g,"$1"),n=n.trim().replace(/^[.,:;]+\s*/,""),n.length>t){let a=n.slice(0,t),o=([...n.slice(0,t+1).matchAll(/[.!?؟](?=\s)/g)].at(-1)?.index??-1)+1;n=(o>=t/2?a.slice(0,o):a.replace(/\s+\S*$/,"")+"...")+" "+vn[Ee(a)??"en"]}return n}var J,vn,Te=g(()=>{"use strict";we();J=500,vn={en:"The rest is on screen.",fr:"La suite est \xE0 l'\xE9cran.",ar:"\u0627\u0644\u0628\u0627\u0642\u064A \u0639\u0644\u0649 \u0627\u0644\u0634\u0627\u0634\u0629."}});function sa(r,t=Q){let n=new Set(t.map(ve)),a=r.replace(/\[([^\][]*)\]|\(([^()]*)\)/g,(o,i,s)=>n.has(ve(i??s))?"":o);return!/[\p{L}\p{N}]/u.test(a)}var Q,ia,ve,Re=g(()=>{"use strict";Q=["background noise","noise","mumbling","babbling","unintelligible","inaudible","muffled speech","pause","silence","music","outro jingle","blank audio"],ia="I didn't catch that.",ve=r=>r.toLowerCase().replace(/[_\s]+/g," ").trim()});import{randomBytes as Rn}from"crypto";function Ln(r){try{return process.kill(r,0),!0}catch(t){return t?.code==="EPERM"}}function da(r,t=Date.now()){if(!r.held)return"The floor is free.";let n=Math.max(0,Math.round(((r.expiresAt??t)-t)/1e3)),a=[r.sound?"plays sound":"",r.hide?"character and bubbles hidden":""].filter(Boolean).join(", ");return`The floor is held by ${r.holder}${a?` (${a})`:""} for up to ${n} more seconds: other lines wait until it is free.`}var j,An,In,Cn,Nn,Ae,Ie=g(()=>{"use strict";j={enabled:!0,defaultSeconds:120,maxSeconds:1800},An=80,In=/^[a-f0-9]{24}$/,Cn=25e3,Nn=2e3;Ae=class{constructor(t={}){this.deps=t;this.now=t.now??Date.now,this.alive=t.alive??Ln}hold=null;seq=0;ended=null;expiry=null;watch=null;waiters=new Set;now;alive;get settings(){return{...j,...this.deps.settings?.()}}get current(){return this.hold&&this.now()>=this.hold.expiresAt&&this.end("expired"),this.hold}view(){let t=this.current;return{seq:this.seq,held:!!t,holder:t?.holder??null,agentId:t?.agentId??null,sound:t?.sound??!1,hide:t?.hide??!1,since:t?.since??null,expiresAt:t?.expiresAt??null,ended:this.ended}}holds(t){let n=this.current;return!!n&&typeof t=="string"&&t===n.id}take(t){let n=this.settings,a=(R,A)=>({ok:!1,reason:R,error:A,view:this.view()});if(!n.enabled)return a("off","The floor is off on this computer (voice.floor.enabled)");let o=String(t.holder??"").replace(/\s+/g," ").trim().slice(0,An),i=t.seconds??Math.min(n.defaultSeconds,n.maxSeconds),s=t.pid??null,d=t.id??Rn(12).toString("hex");if(!In.test(d))return a("invalid","id is 24 hex characters, as a take returned it");if(!o||!Number.isFinite(i)||i<=0||i>n.maxSeconds||s!==null&&!(Number.isInteger(s)&&s>1))return a("invalid",`Required: holder (who takes the floor); seconds is up to ${n.maxSeconds}; pid is a process on this computer`);let c=this.current;if(c)return a("held",`The floor is held by ${c.holder}`);let f=this.deps.busy?.();if(f)return a("busy",`The floor is taken by ${f}`);if(s!==null&&!this.alive(s))return a("invalid",`No process ${s} runs on this computer`);let y=this.now(),k={id:d,holder:o,agentId:t.agentId||null,sound:t.sound===!0,hide:t.hide===!0,pid:s,since:y,expiresAt:y+i*1e3};return this.hold=k,this.ended=null,this.arm(),this.changed(),{ok:!0,id:k.id,view:this.view()}}renew(t,n){if(!this.holds(t))return null;let a=this.settings,o=n??Math.min(a.defaultSeconds,a.maxSeconds);return!Number.isFinite(o)||o<=0||o>a.maxSeconds?null:(this.hold.expiresAt=this.now()+o*1e3,this.arm(),this.changed(),this.view())}release(t){return this.holds(t)?(this.end("released"),!0):!1}revoke(){return this.current?(this.end("owner"),!0):!1}next(t,n=Cn,a){return t!==this.view().seq?Promise.resolve(this.view()):new Promise(o=>{let i=()=>{clearTimeout(s),this.waiters.delete(i),o(this.view())},s=setTimeout(i,n);s.unref?.(),this.waiters.add(i),a?.addEventListener("abort",i,{once:!0})})}close(){this.disarm();for(let t of[...this.waiters])t()}end(t){this.hold&&(this.hold=null,this.ended=t,this.disarm(),this.changed())}arm(){this.disarm();let t=this.hold;if(!t||(this.expiry=setTimeout(()=>{this.hold===t&&this.view()},Math.max(0,t.expiresAt-this.now())+5),this.expiry.unref?.(),t.pid===null))return;let n=t.pid;this.watch=setInterval(()=>{this.hold===t&&!this.alive(n)&&this.end("holder gone")},Nn),this.watch.unref?.()}disarm(){this.expiry&&clearTimeout(this.expiry),this.watch&&clearInterval(this.watch),this.expiry=this.watch=null}changed(){this.seq++;try{this.deps.onChange?.(this.view(),this.hold?.id??null)}catch{}for(let t of[...this.waiters])t()}}});import{z as m}from"zod";var Ce,$n,Ne,Le=g(()=>{"use strict";Ce=m.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/,"use HH:MM, 24-hour"),$n=m.object({id:m.string().regex(/^[a-z0-9][a-z0-9_-]{0,63}$/,"lowercase letters, digits, - and _"),enabled:m.boolean().default(!0),chats:m.array(m.string().min(1)).default([]),senders:m.array(m.string().min(1)).default([]),agent:m.string().min(1),prompt:m.string().max(4e3).optional(),quietHours:m.object({start:Ce,end:Ce}).optional(),autoAck:m.boolean().default(!1)}).refine(r=>r.chats.length>0||r.senders.length>0,{message:"a rule needs at least one chat or sender"}),Ne=m.object({enabled:m.boolean().default(!1),secretEnv:m.string().regex(/^[A-Z_][A-Z0-9_]*$/).default("WACLI_WEBHOOK_SECRET"),batchSeconds:m.number().int().min(0).max(600).default(20),timezone:m.string().optional(),allowAutoAck:m.boolean().default(!1),describeMedia:m.boolean().default(!0),wacli:m.object({bin:m.string().default("wacli"),account:m.string().optional(),store:m.string().optional()}).default({}),rules:m.array($n).default([])}).default({})});function Oe(r){let t=$e(r.url);if(!/^https?:\/\/\S+$/i.test(t))return"url must be an http(s) address";if(t.length>1e3)return"url is longer than 1000 characters";let n=$e(r.contains);return n.length>200?"contains is longer than 200 characters":{kind:"url",url:t,...n?{contains:n}:{}}}async function De(r,t){let n=t.fetch??fetch,a;try{a=await n(r.url,{signal:AbortSignal.timeout(1e4),redirect:"follow"})}catch(o){return{verdict:"unverifiable",detail:`${r.url} could not be reached: ${String(o?.message??o).slice(0,200)}`}}return a.ok?r.contains&&!(await a.text()).slice(0,1e6).includes(r.contains)?{verdict:"not_met",detail:`${r.url} answered without "${r.contains}"`}:{verdict:"met",detail:`${r.url} answered${r.contains?` with "${r.contains}"`:""}`}:{verdict:"not_met",detail:`${r.url} answered HTTP ${a.status}`}}var $e,je=g(()=>{"use strict";$e=r=>typeof r=="string"?r.trim():""});function qe(r,t){let n=On(r,t);return n&&n.project.split("/").every(a=>a!=="."&&a!=="..")?n:null}function On(r,t){let n=t.match(/^([\w.-]+(?:\/[\w.-]+)+)([#!])(\d+)$/);if(n)return r==="github"&&(n[2]==="!"||n[1].split("/").length!==2)?null:{project:n[1],number:Number(n[3]),mr:n[2]==="!"};let a;try{a=new URL(t)}catch{return null}let o=a.pathname.replace(/\/+$/,"");if(r==="github"){let s=o.match(/^\/([\w.-]+\/[\w.-]+)\/(issues|pull)\/(\d+)$/);return s?{project:s[1],number:Number(s[3]),mr:s[2]==="pull"}:null}let i=o.match(/^\/(.+?)\/-\/(issues|merge_requests)\/(\d+)$/);return i?{project:i[1],number:Number(i[3]),mr:i[2]==="merge_requests"}:null}function Ue(r){let t=P(r.provider).toLowerCase();if(t!=="github"&&t!=="gitlab")return"provider must be github or gitlab";let n=P(r.ref);if(!n||n.length>500||!qe(t,n))return t==="github"?'ref must be "owner/repo#12" or the issue or pull request address':'ref must be "group/project#12" (issue), "group/project!12" (merge request) or its address';let a=P(r.state).toLowerCase();if(a&&!Pe.includes(a))return`state must be one of ${Pe.join(", ")}`;let o=P(r.notePattern);return o.length>200?"notePattern is longer than 200 characters":{kind:"issue",provider:t,ref:n,...a?{state:a}:{},...o?{notePattern:o}:{}}}async function Fe(r,t){if(!t.forge)return{verdict:"unverifiable",detail:`no ${r.provider} connector on this node`};let n=qe(r.provider,r.ref);if(!n)return{verdict:"unverifiable",detail:`${r.ref} is not an issue reference`};let a=await t.forge(r.provider,n,{notes:!!r.notePattern});if("error"in a)return{verdict:"unverifiable",detail:`${r.ref}: ${a.error}`};let o=r.state??(r.notePattern?null:"closed");if(o&&Dn(o,a.state))return{verdict:"met",detail:`${a.url} is ${a.state}`};if(r.notePattern){let i=r.notePattern.toLowerCase();if((a.notes??[]).some(s=>s.slice(0,1e4).toLowerCase().includes(i)))return{verdict:"met",detail:`${a.url} has a note with "${r.notePattern}"`}}return{verdict:"not_met",detail:`${a.url} is ${a.state}${r.notePattern?`, no note has "${r.notePattern}"`:""}`}}function Me(r){return Array.isArray(r)?r.map(t=>typeof t=="string"?t:String(t?.name??"")).filter(Boolean):[]}function pa(r,t=fetch){let n=async(a,o)=>{let i;try{i=await t(a,{headers:o,signal:AbortSignal.timeout(1e4)})}catch(s){return{ok:!1,error:`could not be reached: ${String(s?.message??s).slice(0,200)}`}}return i.ok?{ok:!0,body:await i.json().catch(()=>null)}:{ok:!1,error:i.status===404?"not found (or not visible to the connector)":`HTTP ${i.status}`}};return async(a,o,i)=>{if(a==="github"){let S=r.github;if(!S)return{error:"no github connector on this node (channels.github or requests.checkers.github)"};let Gt=S.apiUrl.replace(/\/$/,""),V={Accept:"application/vnd.github+json",...S.token?{Authorization:`Bearer ${S.token}`}:{}},Y=`${Gt}/repos/${o.project}`,E=await n(`${Y}/issues/${o.number}`,V);if(!E.ok)return{error:E.error};let C=E.body?.state==="closed"?"closed":"open";if(E.body?.pull_request&&C==="closed"){let L=await n(`${Y}/pulls/${o.number}`,V);L.ok&&L.body?.merged&&(C="merged")}let oe=String(E.body?.html_url??`${o.project}#${o.number}`),ie=Me(E.body?.labels);if(!i.notes)return{state:C,url:oe,labels:ie};let N=await n(`${Y}/issues/${o.number}/comments?per_page=100`,V);return N.ok?{state:C,url:oe,labels:ie,notes:(Array.isArray(N.body)?N.body:[]).map(L=>String(L?.body??""))}:{error:`its notes: ${N.error}`}}let s=r.gitlab;if(!s)return{error:"no gitlab connector on this node (channels.gitlab or requests.checkers.gitlab)"};let d=s.token?{"PRIVATE-TOKEN":s.token}:{},c=`${s.url.replace(/\/$/,"")}/api/v4/projects/${encodeURIComponent(o.project)}/${o.mr?"merge_requests":"issues"}/${o.number}`,f=await n(c,d);if(!f.ok)return{error:f.error};let y=String(f.body?.state??""),k=y==="merged"?"merged":y==="closed"?"closed":"open",R=String(f.body?.web_url??`${o.project}${o.mr?"!":"#"}${o.number}`),A=Me(f.body?.labels);if(!i.notes)return{state:k,url:R,labels:A};let I=await n(`${c}/notes?per_page=100&sort=desc&order_by=created_at`,d);return I.ok?{state:k,url:R,labels:A,notes:(Array.isArray(I.body)?I.body:[]).map(S=>String(S?.body??"")).reverse()}:{error:`its notes: ${I.error}`}}}var Pe,P,Dn,He=g(()=>{"use strict";Pe=["open","closed","merged"],P=r=>typeof r=="string"?r.trim():"";Dn=(r,t)=>r===t||r==="closed"&&t==="merged"});import{execFile as jn}from"child_process";function We(r,t){let n=ee(r.repo),a=Object.keys(t.repos);if(!n||!Object.prototype.hasOwnProperty.call(t.repos,n))return a.length?`repo must be one of ${a.join(", ")} (requests.checkers.repos)`:"no repos are set up for this check on this node (requests.checkers.repos)";let o=ee(r.commit);if(!Mn.test(o))return"commit must be a commit hash (7 to 64 hex characters)";let i=ee(r.ref);return i&&!qn.test(i)?"ref must be a branch or tag name, for example main or origin/main":{kind:"git-ancestor",repo:n,commit:o,...i?{ref:i}:{}}}async function Be(r,t){let n=t.settings.repos[r.repo];if(!n)return{verdict:"unverifiable",detail:`no repo "${r.repo}" in requests.checkers.repos on this node`};let a=r.ref??"HEAD",{code:o,stderr:i}=await(t.git??Un)(n,["merge-base","--is-ancestor",r.commit,a]);if(o===0)return{verdict:"met",detail:`commit ${r.commit} is in ${a} of ${r.repo}`};if(o===1)return{verdict:"not_met",detail:`commit ${r.commit} is not in ${a} of ${r.repo} yet`};let s=i.replace(/\s+/g," ").trim().slice(0,200)||`git exited with ${o}`;return{verdict:"unverifiable",detail:`could not check ${r.commit} in ${r.repo}: ${s}`}}var Pn,Mn,qn,ee,Un,ze=g(()=>{"use strict";Pn=1e4,Mn=/^[0-9a-f]{7,64}$/i,qn=/^(?!-)(?!.*\.\.)[A-Za-z0-9._\/-]{1,200}$/,ee=r=>typeof r=="string"?r.trim():"";Un=(r,t)=>new Promise(n=>{jn("git",["-C",r,...t],{timeout:Pn,windowsHide:!0},(a,o,i)=>{n({code:a?typeof a.code=="number"?a.code:-1:0,stderr:String(i??"")})})})});function Ke(r,t){let n=M(r.channel).toLowerCase(),a=M(r.chatId),o=M(r.from);if(!n||!a)return"a reply check needs channel and chatId: the chat the answer arrives in";if(!o)return"a reply check needs from: the id of the person who answers (people)";if([n,a,o].some(s=>s.length>200))return"channel, chatId and from are at most 200 characters";let i=t;if(r.after!==void 0){let s=typeof r.after=="number"?r.after:Date.parse(M(r.after));if(!Number.isFinite(s)||s<0)return"after must be epoch milliseconds or an ISO date";i=s}return{kind:"reply",channel:n,chatId:a,from:o,after:i}}async function Xe(r,t){if(!t.inbound)return{verdict:"unverifiable",detail:"this node keeps no record of inbound messages"};let n=r.after??0,a=t.inbound(r.channel,r.chatId,n,r.from);if(!a)return{verdict:"unverifiable",detail:"this node keeps no record of inbound messages"};let o=r.from.toLowerCase(),i=a.find(s=>s.at>n&&(s.person??"").toLowerCase()===o);return i?{verdict:"met",detail:`${r.from} wrote on ${r.channel} chat ${r.chatId} at ${new Date(i.at).toISOString().slice(0,16).replace("T"," ")} UTC`}:{verdict:"not_met",detail:`no message from ${r.from} on ${r.channel} chat ${r.chatId} yet`}}function ya(r){return(t,n,a,o)=>{try{return r.prepare(`SELECT person, started_at AS at FROM task_traces
|
|
2
|
+
WHERE (channel = ? OR channel LIKE ? ESCAPE '\\') AND chat_id = ? AND started_at > ? AND lower(person) = lower(?)
|
|
3
|
+
ORDER BY started_at LIMIT ?`).all(t,`${t.replace(/[\\%_]/g,"\\$&")}@%`,n,a,o,Fn)}catch{return null}}}var M,Fn,Ge=g(()=>{"use strict";M=r=>typeof r=="string"?r.trim():"";Fn=5});function Ta(r,t=Hn,n=Date.now()){if(!r||typeof r!="object"||Array.isArray(r))return"closeWhen must be an object with a kind: url, issue, git-ancestor or reply";let a=r,o=typeof a.kind=="string"?a.kind.trim().toLowerCase():"";if(!q.includes(o))return`closeWhen.kind must be one of ${q.join(", ")}`;if(!t.enabled.includes(o))return`the ${o} check is turned off on this node (requests.checkers.enabled)`;let i=o==="url"?Oe(a):o==="issue"?Ue(a):o==="git-ancestor"?We(a,t):Ke(a,n);return typeof i=="string"?`closeWhen: ${i}`:JSON.stringify(i).length>Ve?`closeWhen is longer than ${Ve} characters`:i}async function va(r,t){if(!t.settings.enabled.includes(r.kind))return{verdict:"unverifiable",detail:`the ${r.kind} check is turned off on this node (requests.checkers.enabled)`};try{switch(r.kind){case"url":return await De(r,t);case"issue":return await Fe(r,t);case"git-ancestor":return await Be(r,t);case"reply":return await Xe(r,t)}}catch(n){return{verdict:"unverifiable",detail:`the ${r.kind} check failed: ${String(n?.message??n).slice(0,200)}`}}}function Ra(r){switch(r.kind){case"url":return`${r.url} answers${r.contains?` with "${r.contains}"`:""}`;case"issue":{let t=[r.state||!r.notePattern?`is ${r.state??"closed"}`:null,r.notePattern?`has a note with "${r.notePattern}"`:null];return`${r.provider} ${r.ref} ${t.filter(Boolean).join(" or ")}`}case"git-ancestor":return`commit ${r.commit} is in ${r.ref??"HEAD"} of ${r.repo}`;case"reply":return`${r.from} replies on ${r.channel} chat ${r.chatId}`}}function Ye(r){if(!r)return null;try{let t=JSON.parse(r);return t&&typeof t=="object"&&q.includes(t.kind)?t:null}catch{return null}}var q,Hn,Ve,Ze=g(()=>{"use strict";je();He();ze();Ge();q=["url","issue","git-ancestor","reply"],Hn={enabled:[...q],checkEveryMinutes:10,repos:{}},Ve=2e3});function Qe(r){let t=/https?:\/\/[^\s<>"'`]+/i.exec(r??"");if(!t)return null;let n=t[0].replace(/[.,;:!?\]*_]+$/,"");for(;n.endsWith(")")&&(n.match(/\(/g)?.length??0)<(n.match(/\)/g)?.length??0);)n=n.slice(0,-1).replace(/[.,;:!?\]*_]+$/,"");return n}function Bn(r){r.exec(`
|
|
4
|
+
CREATE TABLE IF NOT EXISTS requests (
|
|
5
|
+
id TEXT PRIMARY KEY,
|
|
6
|
+
state TEXT NOT NULL,
|
|
7
|
+
channel TEXT NOT NULL,
|
|
8
|
+
chat_id TEXT NOT NULL,
|
|
9
|
+
sender TEXT,
|
|
10
|
+
agent_id TEXT NOT NULL,
|
|
11
|
+
text TEXT NOT NULL,
|
|
12
|
+
created_at INTEGER NOT NULL,
|
|
13
|
+
updated_at INTEGER NOT NULL,
|
|
14
|
+
attention_reason TEXT,
|
|
15
|
+
notified_at INTEGER,
|
|
16
|
+
question TEXT,
|
|
17
|
+
closed_at INTEGER,
|
|
18
|
+
evidence TEXT,
|
|
19
|
+
close_reason TEXT
|
|
20
|
+
);
|
|
21
|
+
CREATE INDEX IF NOT EXISTS idx_requests_state_created ON requests(state, created_at);
|
|
22
|
+
CREATE INDEX IF NOT EXISTS idx_requests_chat ON requests(agent_id, channel, chat_id, created_at);
|
|
23
|
+
CREATE TABLE IF NOT EXISTS request_links (
|
|
24
|
+
request_id TEXT NOT NULL,
|
|
25
|
+
kind TEXT NOT NULL,
|
|
26
|
+
ref TEXT NOT NULL,
|
|
27
|
+
at INTEGER NOT NULL,
|
|
28
|
+
PRIMARY KEY (kind, ref),
|
|
29
|
+
FOREIGN KEY (request_id) REFERENCES requests(id) ON DELETE CASCADE
|
|
30
|
+
);
|
|
31
|
+
CREATE INDEX IF NOT EXISTS idx_request_links_request ON request_links(request_id);
|
|
32
|
+
`),$(r,"requests",de),$(r,"requests",[["state_changed_at","INTEGER"]]),$(r,"request_links",[["ended","TEXT"],["ended_at","INTEGER"],["proof","TEXT"]]);let t=r.prepare("PRAGMA table_info(requests)").all();for(let[n,a]of[["pickup_at","INTEGER"],["owner_note","TEXT"],["note_said","INTEGER"],["asked_agent","TEXT"],["home_kind","TEXT"],["home_link","TEXT"],["owed_to","TEXT"],["promise","INTEGER"],["due_at","INTEGER"],["close_when","TEXT"],["checked_at","INTEGER"],["verified_at","INTEGER"],["verify_state","TEXT"],["verify_detail","TEXT"],["work","TEXT"],["estimate_minutes","REAL"],["first_work","TEXT"],["first_estimate_minutes","REAL"],["headline","TEXT"],["need","TEXT"],["headline_by","TEXT"],["headline_tried_at","INTEGER"]])if(!t.some(o=>o.name===n))try{r.exec(`ALTER TABLE requests ADD COLUMN ${n} ${a}`)}catch(o){if(!/duplicate column name/i.test(String(o?.message)))throw o}}function $a(r){return!!r.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'requests'").get()}function b(r){return{id:r.id,state:r.state,channel:r.channel,chatId:r.chat_id,sender:r.sender??null,person:r.person??null,agentId:r.agent_id,text:r.text,createdAt:r.created_at,updatedAt:r.updated_at,stateChangedAt:r.state_changed_at??null,attentionReason:r.attention_reason??null,notifiedAt:r.notified_at??null,question:r.question??null,closedAt:r.closed_at??null,evidence:r.evidence??null,closeReason:r.close_reason??null,ownerNote:r.owner_note??null,noteSaid:!!r.note_said,askedAgent:r.asked_agent??null,home:r.home_kind&&r.home_link?{kind:r.home_kind,link:r.home_link}:null,owedTo:r.owed_to??null,promise:!!r.promise,dueAt:r.due_at??null,closeWhen:Ye(r.close_when),checkedAt:r.checked_at??null,verifiedAt:r.verified_at??null,verifyState:r.verify_state??null,verifyDetail:r.verify_detail??null,work:r.work??null,estimateMinutes:r.estimate_minutes??null,firstWork:r.first_work??null,firstEstimateMinutes:r.first_estimate_minutes??null,headline:r.headline??null,need:r.need??null,headlineBy:r.headline_by==="agent"||r.headline_by==="model"?r.headline_by:null,headlineTriedAt:r.headline_tried_at??null}}var x,p,Na,Wn,U,La,F,_,h,te,Je,et=g(()=>{"use strict";Jt();Ze();x=["in_progress","waiting_owner","waiting_other","needs_attention"],p=["done","declined","dropped"],Na=["issue","idea","approval","erp","card"],Wn=["issue","idea","approval","erp"],U=500,La="owner";F=4e3,_=500;h=r=>Array(r).fill("?").join(","),te="state_changed_at = CASE WHEN state IS ? THEN state_changed_at ELSE ? END",Je=class{constructor(t){this.db=t;Bn(t),t.pragma("foreign_keys = ON")}addCandidate(t){let n=t.person??t.sender??null;this.db.prepare(`INSERT OR IGNORE INTO requests (id, state, channel, chat_id, sender, person, agent_id, text, created_at, updated_at, state_changed_at, owed_to)
|
|
33
|
+
VALUES (?, 'candidate', ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(t.id,t.channel,t.chatId,t.sender??null,t.person??null,t.agentId,t.text.slice(0,F),t.now,t.now,t.now,n),this.link(t.id,"run",t.runId,t.now)}addPromise(t){this.db.prepare(`INSERT INTO requests (id, state, channel, chat_id, agent_id, text, created_at, updated_at, state_changed_at, owed_to, promise, due_at, close_when, home_kind, home_link)
|
|
34
|
+
VALUES (?, 'in_progress', ?, ?, ?, ?, ?, ?, ?, ?, 1, ?, ?, ?, ?)`).run(t.id,t.channel,t.chatId,t.agentId,t.text.slice(0,F),t.now,t.now,t.now,t.owedTo,t.dueAt??null,t.closeWhen?JSON.stringify(t.closeWhen):null,t.home?.kind??null,t.home?t.home.link.slice(0,U):null),t.home&&this.linkHome(t.id,t.home,t.now)}openPromises(t){return this.db.prepare(`SELECT COUNT(*) AS n FROM requests WHERE agent_id = ? AND promise = 1 AND state IN (${h(x.length)})`).get(t,...x).n}setLoop(t,n,a){n.dueAt!==void 0&&this.db.prepare("UPDATE requests SET due_at = ?, updated_at = ? WHERE id = ?").run(n.dueAt,a,t),n.closeWhen!==void 0&&this.db.prepare("UPDATE requests SET close_when = ?, verify_state = NULL, verify_detail = NULL, updated_at = ? WHERE id = ?").run(n.closeWhen?JSON.stringify(n.closeWhen):null,a,t)}setWork(t,n){n.work!==void 0&&this.db.prepare("UPDATE requests SET work = ?, first_work = COALESCE(first_work, ?) WHERE id = ?").run(n.work,n.work,t),n.estimateMinutes!==void 0&&this.db.prepare("UPDATE requests SET estimate_minutes = ?, first_estimate_minutes = COALESCE(first_estimate_minutes, ?) WHERE id = ?").run(n.estimateMinutes,n.estimateMinutes,t)}setHeadline(t,n,a){if(n.headline===void 0&&n.need===void 0)return!1;let o=a==="model"?" AND headline IS NULL":"";return this.db.prepare(`UPDATE requests SET headline = COALESCE(?, headline), need = COALESCE(?, need),
|
|
35
|
+
headline_by = CASE WHEN ? IS NULL THEN headline_by ELSE ? END WHERE id = ?${o}`).run(n.headline??null,n.need??null,n.headline??null,a,t).changes>0}withoutHeadline(t=20){return this.db.prepare(`SELECT * FROM requests WHERE state IN (${h(x.length)}) AND headline IS NULL AND headline_tried_at IS NULL
|
|
36
|
+
ORDER BY created_at DESC, rowid DESC LIMIT ?`).all(...x,t).map(b)}markHeadlineTried(t,n){return this.db.prepare("UPDATE requests SET headline_tried_at = ? WHERE id = ? AND headline_tried_at IS NULL").run(n,t).changes>0}markChecked(t,n,a,o){this.db.prepare("UPDATE requests SET checked_at = ?, verify_state = ?, verify_detail = ? WHERE id = ?").run(o,n,a.slice(0,_)||null,t)}closeVerified(t,n,a){let o=n.trim().slice(0,_)||"the check was met";return this.db.prepare(`UPDATE requests SET state = 'done', closed_at = ?, updated_at = ?, state_changed_at = ?, evidence = ?, close_reason = NULL,
|
|
37
|
+
checked_at = ?, verified_at = ?, verify_state = 'verified', verify_detail = ?
|
|
38
|
+
WHERE id = ? AND state NOT IN (${h(p.length)})`).run(a,a,a,o,a,a,o,t,...p).changes>0}dueForCheck(t,n=50){return this.db.prepare(`SELECT * FROM requests WHERE state IN (${h(x.length)}) AND close_when IS NOT NULL
|
|
39
|
+
AND (checked_at IS NULL OR checked_at < ?) ORDER BY COALESCE(checked_at, 0), created_at LIMIT ?`).all(...x,t,n).map(b)}overdue(t,n){return this.db.prepare(`SELECT * FROM requests WHERE state IN ('in_progress', 'waiting_other') AND due_at IS NOT NULL AND due_at < ?
|
|
40
|
+
AND (updated_at <= due_at OR updated_at < ?)
|
|
41
|
+
ORDER BY due_at, rowid`).all(t,n).map(b)}get(t){let n=this.db.prepare("SELECT * FROM requests WHERE id = ?").get(t);return n?b(n):null}byLink(t,n){let a=this.db.prepare("SELECT r.* FROM requests r JOIN request_links l ON l.request_id = r.id WHERE l.kind = ? AND l.ref = ?").get(t,n);return a?b(a):null}link(t,n,a,o){this.db.prepare("INSERT OR IGNORE INTO request_links (request_id, kind, ref, at) VALUES (?, ?, ?, ?)").run(t,n,a,o)}endLink(t,n,a,o,i){this.db.prepare("UPDATE request_links SET ended = ?, ended_at = ?, proof = ? WHERE kind = ? AND ref = ?").run(a,i,o?o.slice(0,U):null,t,n)}supersedeHandBacks(t){this.db.prepare("UPDATE request_links SET ended = 'superseded' WHERE request_id = ? AND kind = 'delegation' AND ended = 'done'").run(t)}handedBack(t){return this.db.prepare(`SELECT r.* FROM requests r
|
|
42
|
+
WHERE r.state = 'in_progress' AND r.updated_at < ? AND r.due_at IS NULL AND r.close_when IS NULL
|
|
43
|
+
AND COALESCE(r.promise, 0) = 0 AND r.pickup_at IS NULL
|
|
44
|
+
AND EXISTS (SELECT 1 FROM request_links l WHERE l.request_id = r.id AND l.kind = 'delegation' AND l.ended = 'done')
|
|
45
|
+
AND NOT EXISTS (SELECT 1 FROM request_links l WHERE l.request_id = r.id AND l.kind = 'delegation'
|
|
46
|
+
AND (l.ended IS NULL OR l.ended NOT IN ('done', 'superseded')))
|
|
47
|
+
AND NOT EXISTS (SELECT 1 FROM request_links l WHERE l.request_id = r.id AND l.kind = 'workflow')
|
|
48
|
+
ORDER BY r.created_at, r.rowid`).all(t).map(b)}handBackProof(t){return this.db.prepare("SELECT proof, ref FROM request_links WHERE request_id = ? AND kind = 'delegation' AND ended = 'done' AND proof IS NOT NULL ORDER BY ended_at DESC, rowid DESC LIMIT 1").get(t)??null}settle(t,n,a,o){let i=(n??`unproven: ${a}`).trim().slice(0,_);return this.db.prepare(`UPDATE requests SET state = 'done', closed_at = ?, updated_at = ?, state_changed_at = ?, evidence = ?, close_reason = NULL,
|
|
49
|
+
verify_state = ?, verify_detail = ?
|
|
50
|
+
WHERE id = ? AND state = 'in_progress'`).run(o,o,o,i,"unverified",a.slice(0,_),t).changes>0}links(t){return this.db.prepare("SELECT kind, ref, at FROM request_links WHERE request_id = ? ORDER BY at, rowid").all(t)}lastAnswer(t){try{return this.db.prepare(`SELECT t.agent_id AS agentId, t.final_response AS text, COALESCE(t.finished_at, t.started_at) AS at
|
|
51
|
+
FROM request_links l JOIN task_traces t ON t.task_id = l.ref
|
|
52
|
+
WHERE l.request_id = ? AND l.kind = 'run' AND t.final_response IS NOT NULL AND t.final_response != ''
|
|
53
|
+
ORDER BY t.started_at DESC LIMIT 1`).get(t)??null}catch{return null}}touch(t,n){this.db.prepare("UPDATE requests SET updated_at = ? WHERE id = ?").run(n,t)}progress(t,n,a="in_progress"){this.db.prepare(`UPDATE requests SET ${te}, state = ?, updated_at = ?, attention_reason = NULL, notified_at = NULL, question = NULL,
|
|
54
|
+
need = CASE WHEN state IN ('waiting_owner', 'needs_attention') THEN NULL ELSE need END
|
|
55
|
+
WHERE id = ? AND state NOT IN (${h(p.length)})`).run(a,n,a,n,t,...p)}waitOnOwner(t,n,a,o=null){this.db.prepare(`UPDATE requests SET ${te}, state = 'waiting_owner', updated_at = ?, question = ?, need = ?, attention_reason = NULL, notified_at = NULL
|
|
56
|
+
WHERE id = ? AND state NOT IN (${h(p.length)})`).run("waiting_owner",a,a,n.slice(0,F),o,t,...p)}needsAttention(t,n,a){return this.db.prepare(`UPDATE requests SET state = 'needs_attention', updated_at = ?, state_changed_at = ?, attention_reason = ?, notified_at = NULL
|
|
57
|
+
WHERE id = ? AND state NOT IN ('needs_attention', ${h(p.length)})`).run(a,a,n.replace(/\s+/g," ").trim().slice(0,_),t,...p).changes>0}close(t,n,a,o){let i=a.trim();if(!i)throw new Error(n==="done"?"a finished request needs a link to the evidence":`a ${n} request needs a reason`);return this.db.prepare(`UPDATE requests SET state = ?, closed_at = ?, updated_at = ?, state_changed_at = ?, evidence = ?, close_reason = ?
|
|
58
|
+
WHERE id = ? AND state NOT IN (${h(p.length)})`).run(n,o,o,o,n==="done"?i.slice(0,_):null,n==="done"?null:i.slice(0,_),t,...p).changes>0}discardCandidate(t){return this.db.prepare("DELETE FROM requests WHERE id = ? AND state = 'candidate'").run(t).changes>0}listOpen(){return this.db.prepare(`SELECT * FROM requests WHERE state IN (${h(x.length)}) ORDER BY created_at, rowid`).all(...x).map(b)}quietSince(t,n){return this.db.prepare(`SELECT * FROM requests WHERE state IN (${h(t.length)}) AND updated_at < ? AND due_at IS NULL ORDER BY created_at, rowid`).all(...t,n).map(b)}awaitingNotice(){return this.db.prepare("SELECT * FROM requests WHERE state = 'needs_attention' AND notified_at IS NULL ORDER BY created_at, rowid").all().map(b)}markNotified(t,n){this.db.prepare("UPDATE requests SET notified_at = ? WHERE id = ?").run(n,t)}listByState(t){return this.db.prepare("SELECT * FROM requests WHERE state = ? ORDER BY created_at, rowid").all(t).map(b)}listClosedSince(t,n=50){return this.db.prepare(`SELECT * FROM requests WHERE state IN (${h(p.length)}) AND closed_at >= ? ORDER BY closed_at DESC, rowid DESC LIMIT ?`).all(...p,t,n).map(b)}setHome(t,n,a,o={}){let i=n.link.slice(0,U);o.touch===!1?this.db.prepare("UPDATE requests SET home_kind = ?, home_link = ? WHERE id = ?").run(n.kind,i,t):this.db.prepare("UPDATE requests SET home_kind = ?, home_link = ?, updated_at = ? WHERE id = ?").run(n.kind,i,a,t),this.linkHome(t,n,a)}linkHome(t,n,a){Wn.includes(n.kind)&&this.link(t,n.kind,n.link.slice(0,U),a)}query(t){let n=[`state IN (${h(t.states.length)})`],a=[...t.states];t.since!==void 0&&(n.push("created_at >= ?"),a.push(t.since)),t.closedSince!==void 0&&(n.push("closed_at >= ?"),a.push(t.closedSince));let o=n.join(" AND "),i=this.db.prepare(`SELECT COUNT(*) AS n FROM requests WHERE ${o}`).get(...a).n,s=t.newest?"DESC":"ASC",d=this.db.prepare(`SELECT * FROM requests WHERE ${o} ORDER BY created_at ${s}, rowid ${s} LIMIT ? OFFSET ?`).all(...a,t.limit,t.offset??0).map(b);return{items:t.newest?d.reverse():d,total:i}}requestPickup(t,n){return this.db.transaction(()=>{let a=this.db.prepare(`UPDATE requests SET state = 'in_progress', updated_at = ?, state_changed_at = ?, attention_reason = NULL, notified_at = NULL, pickup_at = ?, need = NULL
|
|
59
|
+
WHERE id = ? AND state = 'needs_attention'`).run(n,n,n,t).changes>0;return a&&this.supersedeHandBacks(t),a})()}handOff(t,n,a,o){return this.db.transaction(()=>{let i=this.db.prepare(`UPDATE requests SET ${te}, agent_id = ?, state = 'in_progress', updated_at = ?, attention_reason = NULL, notified_at = NULL,
|
|
60
|
+
question = NULL, need = NULL, pickup_at = ?, owner_note = ?, note_said = NULL, asked_agent = COALESCE(asked_agent, agent_id)
|
|
61
|
+
WHERE id = ? AND state IN (${h(x.length)})`).run("in_progress",o,n,o,o,a.trim().slice(0,F)||null,t,...x).changes>0;return i&&this.supersedeHandBacks(t),i})()}setNoteSaid(t,n,a){this.db.prepare(`UPDATE requests SET note_said = ? WHERE id = ? AND owner_note = ?${a?" AND pickup_at IS NULL":""}`).run(a?1:null,t,n)}takePickups(){return this.db.prepare("UPDATE requests SET pickup_at = NULL WHERE pickup_at IS NOT NULL RETURNING *").all().map(b).filter(t=>t.state==="in_progress").sort((t,n)=>t.createdAt-n.createdAt)}pruneClosed(t){return this.db.prepare(`DELETE FROM requests WHERE state IN (${h(p.length)}) AND closed_at < ?`).run(...p,t).changes}}});function ja(r){return Object.assign({channel:rt,chatId:r,sender:"operator"},{[at]:!0})}function Pa(r){return!!r&&r[at]===!0}function Xn(r,t,n,a=!1){let o=H(t);if(r.channels.length&&!r.channels.map(H).includes(o))return!1;if(W.has(o))return a&&!zn.has((n?.name??"").toLowerCase());let i=Kn.has(o)?n?.username:n?.id,s=i?tt(o,i):"";return s?r.from.some(d=>{let c=d.trim().toLowerCase(),f=c.indexOf(":");return f>0&&c.slice(0,f)===o&&tt(o,c.slice(f+1))===s}):!1}function Ma(r){if(!r)return;let t=a=>typeof a=="string"&&a?a.slice(0,120):void 0,n={name:t(r.sender),id:t(r.senderId),username:t(r.senderUsername)};return n.name||n.id||n.username?n:void 0}function Gn(r){return/\btimed? ?out\b|timeout/i.test(r)?"timed out":"failed"}var rt,at,W,zn,H,Kn,tt,w,v,nt,ot=g(()=>{"use strict";et();rt="requests",at=Symbol("requests.pickup");W=new Set(["voice","app","dashboard","webrtc"]),zn=new Set(["camera"]),H=r=>r.toLowerCase().split("@")[0],Kn=new Set(["github","gitlab"]),tt=(r,t)=>r==="whatsapp"?t.replace(/@.*$/,"").replace(/\D/g,""):t.trim().toLowerCase().replace(/^@/,"");w=(r,t=200)=>{let n=r.replace(/\s+/g," ").trim();return n.length>t?`${n.slice(0,t-1)}\u2026`:n},v=(r,t,n)=>`${r}\0${t}\0${n}`,nt=class{constructor(t,n,a,o=Date.now){this.store=t;this.settings=n;this.log=a;this.now=o}live=new Map;guard(t,n){try{n()}catch(a){this.log(`[requests] ${t} failed: ${a?.message??a}`)}}isListedOwner(t){let n=H(t.channel),a=this.settings().channels;return a.length&&!a.map(H).includes(n)?!1:t.person?.role==="owner"&&!W.has(n)}liveRequestId(t,n,a){return this.live.get(v(t,n,a))?.requestId??null}taskStarted(t){t.taskId&&this.guard("capture",()=>{if(!this.settings().enabled)return;let n=this.now(),a=t.resumedFrom?this.store.byLink("run",t.resumedFrom):null,o=v(t.agentId,t.channel,t.chatId);if(a){this.store.link(a.id,"run",t.taskId,n),a.state==="candidate"?this.store.touch(a.id,n):this.store.progress(a.id,n),this.live.set(o,{requestId:a.id,runId:t.taskId});return}let i=t.pickup&&t.channel===rt?this.store.get(t.chatId):null;if(i&&i.agentId===t.agentId){this.store.link(i.id,"run",t.taskId,n),this.store.touch(i.id,n),this.live.set(o,{requestId:i.id,runId:t.taskId});return}!t.humanRoot||!(Xn(this.settings(),t.channel,t.sender,t.operator===!0)||this.isListedOwner(t))||(this.live.set(o,{requestId:`req-${t.taskId}`,runId:t.taskId}),this.store.addCandidate({id:`req-${t.taskId}`,runId:t.taskId,channel:t.channel,chatId:t.chatId,sender:t.sender?.name??t.sender?.username??t.sender?.id??null,person:t.person?.id??null,agentId:t.agentId,text:t.requestText??t.fullMessage??t.messagePreview,now:n}))})}taskCompleted(t){t.taskId&&this.guard("run end",()=>{let n=v(t.agentId,t.channel,t.chatId);this.live.get(n)?.runId===t.taskId&&this.live.delete(n);let a=this.store.byLink("run",t.taskId);if(!a)return;let o=this.now();if(!t.interrupted){if(t.error&&t.errorKind!=="cancelled"&&!t.stopped&&t.errorKind!=="stopped"){let i=Gn(t.error);this.store.needsAttention(a.id,`${t.agentId} ${i}: ${w(t.error)}`,o)&&this.log(`[requests] ${a.id} needs attention: run ${t.taskId} ${i}`);return}a.state==="candidate"?this.store.discardCandidate(a.id):this.store.touch(a.id,o)}})}delegationStarted(t){this.guard("delegation start",()=>{if(!this.settings().enabled)return;let n=this.live.get(v(t.caller,t.origin.channel,t.origin.chatId));if(!n)return;let a=this.now();this.store.link(n.requestId,"delegation",t.id,a),this.store.progress(n.requestId,a,"waiting_other")})}delegationDone(t,n,a){this.guard("delegation end",()=>{let o=this.store.byLink("delegation",t.id);if(!o)return;let i=this.now(),s=t.peer?`${t.callee} on ${t.peer}`:t.callee;if(this.store.endLink("delegation",t.id,n,n==="done"?Qe(a):null,i),n==="done"){this.store.progress(o.id,i);return}if(n==="waiting"){this.store.progress(o.id,i,"waiting_other");return}let d={error:"failed",timeout:"did not answer in time",lost:"was lost in a restart"}[n];this.store.needsAttention(o.id,`The work handed to ${s} ${d}: ${w(a)}`,i)&&this.log(`[requests] ${o.id} needs attention: delegation ${t.id} ${n}`)})}relayFailed(t,n,a){this.guard("delegation relay",()=>{let o=this.store.byLink("delegation",t.id);if(!o)return;let i=t.peer?`${t.callee} on ${t.peer}`:t.callee;this.store.needsAttention(o.id,`The answer from ${i} was not passed on after a restart (${w(a)}). It said: ${w(n)}`,this.now())&&this.log(`[requests] ${o.id} needs attention: delegation ${t.id} answer not relayed`)})}cardRaised(t,n){n&&this.guard("card",()=>{if(!this.settings().enabled)return;let a=this.live.get(v(t.raised_by,n.channel,n.chatId))?.requestId;if(!a)return;let o=this.now();this.store.link(a,"card",t.id,o),this.store.waitOnOwner(a,t.ask,o)})}cardResolved(t){this.guard("card result",()=>{let n=this.store.byLink("card",t.id);if(!n)return;let a=this.now(),o=t.outcome??t.if_silent;t.status==="decided"||t.status==="resolved"?n.state==="waiting_owner"&&this.store.progress(n.id,a):this.store.needsAttention(n.id,`Your answer did not come before the card expired (${w(t.ask)}); "${o}" was applied`,a)&&this.log(`[requests] ${n.id} needs attention: card ${t.id} expired unanswered`)})}closingNote(t){try{let n=this.store.byLink("delegation",t);return!n||!x.includes(n.state)?void 0:`This is part of the owner's open request ${n.id}. It stays on their list until it is closed. If it is finished now, close it with agentx_request: {action:"done", id:"${n.id}", evidence:"<link to the proof>"}. If you will not do it, use decline with the reason. If work goes on, give it a due time with {action:"accept", id:"${n.id}", due:"<when>"}: without one, it is closed as done once it goes quiet, with this answer's link as its proof.`}catch{return}}resumeOutcome(t){this.guard("restart",()=>{let n=this.store.byLink("run",t.taskId);if(!n)return;let a=this.now();t.decision==="resumed"?n.state==="candidate"?this.store.touch(n.id,a):this.store.progress(n.id,a):(t.decision==="reported"||t.decision==="resume-failed"||t.decision==="skipped")&&this.store.needsAttention(n.id,`Cut off by a restart and not picked up again (${w(t.reason)})`,a)&&this.log(`[requests] ${n.id} needs attention: run ${t.taskId} cut off by a restart`)})}}});import{randomBytes as Vn,timingSafeEqual as st}from"crypto";import{chmodSync as Yn,existsSync as Zn,mkdirSync as Jn,readFileSync as Qn,writeFileSync as er}from"fs";import{networkInterfaces as tr}from"os";import{dirname as nr,resolve as rr}from"path";function Ba(r){return Object.assign(r,{[lt]:!0})}function ut(r){return!!r&&r[lt]===!0}function or(r=process.cwd()){return rr(r,".agentx","operator.key")}function za(r=process.cwd(),t={}){let n=or(r);try{if(Zn(n)){let o=Qn(n,"utf-8").trim();if(o)return o}if(!t.create)return null;let a=Vn(32).toString("hex");return Jn(nr(n),{recursive:!0}),er(n,a+`
|
|
62
|
+
`,{mode:384}),Yn(n,384),a}catch{return null}}function ir(r,t){let n=Array.isArray(t)?t[0]:t;if(!r||!n)return!1;let a=Buffer.from(r),o=Buffer.from(n.trim());return a.length===o.length&&st(a,o)}function lr(r,t,n,a){if(!r||typeof r!="object")return!1;let o=String(r.channel??"").toLowerCase().split("@")[0];return a.has(o)?ir(n,t[ar]):!1}function ur(r,t){let n=r[sr];if((Array.isArray(n)?n[0]:n)!=="1")return!1;let a=r.authorization,o=(Array.isArray(a)?a[0]:a)||"",i=o.toLowerCase().startsWith("bearer ")?o.slice(7).trim():"";if(!i)return!1;let s=Buffer.from(i);for(let d of t){if(!d)continue;let c=Buffer.from(d);if(c.length===s.length&&st(c,s))return!0}return!1}function dr(r,t=cr()){let n=it(r??"");if(!n||n==="::1"||n.startsWith("127."))return!0;for(let a of t)if(it(a)===n)return!0;return!1}function cr(){return Object.values(tr()).flatMap(r=>(r??[]).map(t=>t.address))}function Ka(r,t,n){if(lr(r,t.headers,n.key,n.operatorChannels))return!0;if(!r||typeof r!="object")return!1;let a=String(r.channel??"").toLowerCase().split("@")[0];return!n.operatorChannels.has(a)||dr(t.remoteAddress,n.ownAddresses)?!1:ur(t.headers,n.peerTokens)}var ar,lt,sr,it,dt=g(()=>{"use strict";ar="x-agentx-operator",lt=Symbol("requests.operator");sr="x-agentx-operator-vouch";it=r=>r.toLowerCase().replace(/^::ffff:/,"").replace(/%.*$/,"")});function Ja(r,t){return r.find(n=>n.id===t)?.name??(t===B?"Owner":t)}function ct(r,t){let n=t.trim().toLowerCase();return r==="whatsapp"?n.replace(/@.*$/,"").replace(/\D/g,""):n.replace(/^@/,"")}function mt(r){let t=r.indexOf(":");if(t<=0||t===r.length-1)return null;let n=ne(r.slice(0,t).trim()),a=ct(n,r.slice(t+1));return a?{channel:n,value:a}:null}function ft(r){let t=new Set,n=new Map;for(let a of r){if(t.has(a.id))return`person "${a.id}" is listed twice`;if(t.add(a.id),a.id===B&&a.role!=="owner")return`the id "${B}" is kept for a person with the owner role`;for(let o of a.identities){let i=mt(o);if(!i)return`identity "${o}" of "${a.id}" must be written channel:id`;let s=`${i.channel}:${i.value}`,d=n.get(s);if(d&&d!==a.id)return`identity "${o}" belongs to both "${d}" and "${a.id}"`;n.set(s,a.id)}}return null}function mr(r,t,n){let a=ne(t),i=(a==="whatsapp"?[n?.id]:[n?.id,n?.username]).filter(s=>typeof s=="string"&&!!s).map(s=>ct(a,s)).filter(Boolean);return i.length?r.find(s=>s.identities.some(d=>{let c=mt(d);return!!c&&c.channel===a&&i.includes(c.value)}))??null:null}function fr(r){let t=r.filter(n=>n.role==="owner");return t.length===0?{id:B,role:"owner"}:t.length===1?{id:t[0].id,role:"owner"}:null}function gt(r,t){let n=le(t);if(n)return n.person?{id:n.person}:null;if(!t||ue(t)!=="human")return null;let a=String(t.channel??"");if(W.has(ne(a)))return ut(t)?fr(r):null;let o=s=>typeof s=="string"?s:void 0,i=mr(r,a,{id:o(t.senderId),username:o(t.senderUsername)});return i?{id:i.id,role:i.role}:null}function gr(r,t){let n=r.agents??[];return r.role==="owner"||n.length===0||n.includes(t)}function Qa(r,t,n){let a=gt(r,n);if(!a)return null;let o=r.find(s=>s.id===a.id);if(!o||gr(o,t))return null;let i=(o.agents??[]).join(", ");return{person:o,note:`${o.name}, you can reach ${i} here, not ${t}. Ask the owner if you need ${t}.`}}function eo(r,t){let n=r.trim().split("*").map(a=>a.replace(/[.+?^${}()|[\]\\]/g,"\\$&")).join(".*");return new RegExp(`^${n}$`,"i").test(t.trim())}function to(r,t){let n=gt(r,t);if(!n)return null;let a=r.find(s=>s.id===n.id);if(!a||a.role==="owner")return null;let o=a.deny?.tools??[],i=a.deny?.skills??[];return!o.length&&!i.length?null:{personId:a.id,name:a.name,tools:[...o],skills:[...i]}}var Za,B,ne,pt=g(()=>{"use strict";Zt();ot();dt();Za=["owner","member","client","guest"],B="owner";ne=r=>r.toLowerCase().split("@")[0]});function ao(r){return typeof r=="string"&&hr.includes(r)}function ht(r){let t=new Set(pr);return r.map(n=>n.trim()).filter(n=>n&&!t.has(n))}var pr,hr,ro,bt=g(()=>{"use strict";pr=["agentx_generate","agentx_inspect","agentx_skill_match","agentx_detect_output_type","agentx_channel_reply","agentx_channel_label","agentx_send","agentx_send_agent","agentx_recent","agentx_events","agentx_signal","agentx_waiting","agentx_send_contact","agentx_task","agentx_agents","agentx_voice_queue","agentx_screen","agentx_call_owner","agentx_camera_ask","agentx_camera_look","agentx_health","agentx_attach_next","agentx_attach_answer","agentx_crons","agentx_schedule","agentx_approval","agentx_note","agentx_request","agentx_workflow","agentx_debug","agentx_wiki_query","agentx_wiki_patch","agentx_wiki_interview","agentx_graph_review"],hr=["read","full"],ro=["agentx_inspect","agentx_skill_match","agentx_detect_output_type","agentx_recent","agentx_events","agentx_agents","agentx_voice_queue","agentx_screen","agentx_health","agentx_crons","agentx_wiki_query"]});var Br={};Vt(Br,{INTEGRATION_KINDS:()=>wr,WIKI_CONTRIBUTE_JOB:()=>Wt,WIKI_CONTRIBUTE_MERGE_JOB:()=>Bt,WIKI_SUMMARIZE_JOB:()=>re,approvalsConfigSchema:()=>Ut,callsSchema:()=>Ct,cameraBotSchema:()=>At,crossFieldProblems:()=>Ht,daemonConfigSchema:()=>Ft,eventSubscriptionSchema:()=>Rt,expandEnvVars:()=>G,isCronExpression:()=>It,loadDaemonConfig:()=>Hr,membersConfigSchema:()=>Pt,mergePolicyConfigSchema:()=>Lt,peopleConfigSchema:()=>jt,personSchema:()=>Dt,qualityReportConfigSchema:()=>qt,requestStatusConfigSchema:()=>Mt,requestsConfigSchema:()=>Ot,reviewGateConfigSchema:()=>Nt,validateWorkspaces:()=>Wr,watchersConfigSchema:()=>$t,withContributionJobs:()=>zt,withSummariesJob:()=>Kt});import{z as e}from"zod";import{readFileSync as Et,existsSync as K}from"fs";import{resolve as X}from"path";function br(r){let t=X(r,".env");if(!K(t))return;let n=Et(t,"utf-8");for(let a of n.split(`
|
|
63
|
+
`)){let o=a.trim();if(!o||o.startsWith("#"))continue;let i=o.indexOf("=");if(i===-1)continue;let s=o.slice(0,i).trim(),d=o.slice(i+1).trim();process.env[s]||(process.env[s]=d)}}function It(r){let t=[[0,59],[0,23],[1,31],[1,12],[0,6]],n=r.trim().split(/\s+/);return n.length!==5?!1:n.every((a,o)=>a.split(",").every(i=>{let s=/^(\*|(\d+)(?:-(\d+))?)(?:\/(\d+))?$/.exec(i);if(!s)return!1;let[d,c]=t[o],f=s[2]===void 0?d:Number(s[2]),y=s[3]===void 0?f:Number(s[3]),k=s[4]===void 0?1:Number(s[4]);return f>=d&&y<=c&&f<=y&&k>=1}))}function Ar(r){try{return new Intl.DateTimeFormat("en-US",{timeZone:r}),!0}catch{return!1}}function G(r){if(typeof r=="string")return r.replace(/\$\{(\w+)\}/g,(t,n)=>process.env[n]||"");if(Array.isArray(r))return r.map(G);if(r!==null&&typeof r=="object"){let t={};for(let[n,a]of Object.entries(r))t[n]=G(a);return t}return r}function Fr(r,t){let n=[],a=new Set;for(let o of r){let i=o.path.join("."),s=/^channels\.telegram\.accounts\.([^.]+)\.token$/.exec(i);if(s&&o.message.toLowerCase().includes("required")){let d=s[1],c=`tg:${d}`;if(a.has(c))continue;a.add(c),n.push(`Hint: Telegram account "${d}" is enabled but has no bot token.
|
|
64
|
+
Either:
|
|
65
|
+
A) Open ${t} and set channels.telegram.accounts.${d}.token to your bot token (or "\${TG_${d.toUpperCase()}_BOT_TOKEN}" + the matching .env entry).
|
|
66
|
+
B) If you don't need Telegram on this instance, remove the entire "telegram" block under "channels" (or set channels.telegram.enabled to false AND drop accounts.${d}).`)}}return n}function Hr(r){let t=r?[r]:[X(process.cwd(),"agentx.json"),X(process.cwd(),".agentx/config.json")];br(process.cwd());let n,a;for(let c of t)if(K(c)){n=Et(c,"utf-8"),a=c;break}if(!n||!a)throw new Error(`No config found. Create agentx.json or .agentx/config.json
|
|
67
|
+
Searched: ${t.join(", ")}`);let o;try{o=JSON.parse(n)}catch(c){throw new Error(`Invalid JSON in ${a}: ${c.message}`)}let i=G(o),s=Ft.safeParse(i);if(!s.success){let c=s.error.issues.map(k=>` ${k.path.join(".")}: ${k.message}`).join(`
|
|
68
|
+
`),f=Fr(s.error.issues,a),y=f.length?`
|
|
69
|
+
|
|
70
|
+
${f.join(`
|
|
71
|
+
|
|
72
|
+
`)}`:"";throw new Error(`Config validation failed (${a}):
|
|
73
|
+
${c}${y}`)}let d=Ht(s.data);if(d.length)throw new Error(`Config validation failed (${a}):
|
|
74
|
+
${d.map(c=>` ${c}`).join(`
|
|
75
|
+
`)}`);return Kt(zt(s.data))}function Ht(r){let t=[],n=r.native?.mirror;return n?.enabled&&(r.reminders?.lists?.includes(n.list)&&t.push(`native.mirror.list: "${n.list}" is also in reminders.lists; a mirrored task would be handed back to an agent. Use another list.`),r.approvals?.checkin?.lists?.includes(n.list)&&t.push(`native.mirror.list: "${n.list}" is also in approvals.checkin.lists; a mirrored task would come back as a card. Use another list.`)),t}function Wr(r){let t=[];for(let[n,a]of Object.entries(r.agents)){if(!K(a.workspace)){t.push(`Agent "${n}": workspace not found at ${a.workspace}`);continue}if(a.tier==="claude-code"){let s=X(a.workspace,".claude");K(s)||t.push(`Agent "${n}": no .claude/ directory in workspace ${a.workspace}. Claude Code native features (hooks, MCP, skills) won't be available.`)}let o=a.provider||"claude",i=r.providers[o];!["claude-code","codex-cli","opencode"].includes(a.tier)&&(!i||!i.apiKey)&&t.push(`Agent "${n}": provider "${o}" has no API key configured. Set providers.${o}.apiKey in config or use a CLI-backed tier ("claude-code", "codex-cli", or "opencode").`)}for(let[n,a]of Object.entries(r.crons))r.agents[a.agent]||t.push(`Cron "${n}": references unknown agent "${a.agent}"`);if(r.channels.telegram.enabled)for(let[n,a]of Object.entries(r.channels.telegram.accounts))r.agents[a.agentBinding]||t.push(`Telegram account "${n}": references unknown agent "${a.agentBinding}"`);return t}function zt(r,t=Xt()){let n=Object.entries(r.agents).filter(([,s])=>s.wiki?.contribute?.enabled).map(([s])=>s).sort();if(n.length===0)return r;let a=r.wiki.contributions,o=(s,d)=>ae.parse({schedule:s,timezone:a.timezone,agent:n[0],command:d,timeout:3600,onError:"log"}),i={[Wt]:o(a.schedule,`${t} wiki contribute --all`),[Bt]:o(a.mergeSchedule,`${t} wiki contributions merge`)};return{...r,crons:{...i,...r.crons}}}function Kt(r,t=Xt()){let n=r.wiki.summaries,a=n.agent??r.node.defaultAgent??Object.keys(r.agents).sort()[0];if(!n.schedule?.trim()||!a)return r;let o=ae.safeParse({schedule:n.schedule,timezone:n.timezone,agent:a,command:`${t} wiki summarize --all`,timeout:3600,onError:"log"});if(!o.success)throw new Error(`wiki.summaries: the ${re} job can't be built: ${o.error.issues.map(i=>i.message).join("; ")}`);return{...r,crons:{[re]:o.data,...r.crons}}}function Xt(){let r=process.argv[1]??"";return/[\\/]cli\.(?:c|m)?js$/.test(r)?`"${process.execPath}" "${r}"`:"agentx"}var xt,xr,yr,kr,_r,Sr,Er,wr,wt,Tt,z,vt,Rt,Tr,vr,At,Rr,ae,yt,kt,_t,Ir,Cr,Nr,Lr,$r,Or,Dr,jr,Pr,Ct,Mr,qr,Ur,Nt,Lt,$t,Ot,Dt,jt,Pt,Mt,St,qt,Ut,Ft,Wt,Bt,re,zr=g(()=>{me();pe();Yt();xe();Se();Te();Re();Ie();Le();pt();bt();xt=e.array(e.string().min(1)).superRefine((r,t)=>{let n=ht(r);n.length&&t.addIssue({code:e.ZodIssueCode.custom,message:`not an agentx tool: ${n.join(", ")}. Use the names the agentx server lists, without "mcp__agentx__".`})}),xr=e.object({apiKey:e.string().optional(),defaultModel:e.string().optional(),baseUrl:e.string().optional(),thinking:e.boolean().optional()}),yr=e.object({type:e.literal("stdio").optional(),command:e.string(),args:e.array(e.string()).default([]),env:e.record(e.string(),e.string()).optional()}),kr=e.object({type:e.literal("http"),url:e.string().url(),headers:e.record(e.string(),e.string()).optional()}),_r=e.union([kr,yr]),Sr=e.object({tokenEnv:e.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),privateAppTokenEnv:e.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),apiKeyEnv:e.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),refreshTokenEnv:e.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),clientIdEnv:e.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),clientSecretEnv:e.string().regex(/^[A-Z][A-Z0-9_]*$/).optional(),auth:e.literal("keyring").optional(),sessionDir:e.string().optional()}).strict(),Er=e.object({kind:e.string().min(1),label:e.string().min(1),credentials:Sr.default({}),metadata:e.record(e.string(),e.union([e.string(),e.number(),e.boolean()])).default({}),enabled:e.boolean().default(!0)}),wr=["telegram-bot","whatsapp","gitlab-user","github-user","gmail","hotmail","smtp","hubspot","salesforce","pipedrive","odoo","trello","linear","asana","jira","notion","stripe","sentry","vercel","twilio","custom"],wt=e.enum(["system","elevenlabs"]),Tt=e.union([e.string(),e.record(e.string(),e.string())]),z=e.string().superRefine((r,t)=>{let n=be(r);n&&t.addIssue({code:e.ZodIssueCode.custom,message:`shortcut ${n}`})}),vt=e.object({provider:wt.optional(),system:Tt.optional(),fallbacks:e.array(e.string().min(1)).optional(),elevenlabsVoiceId:e.string().optional(),gender:e.enum(["female","male","neutral"]).optional(),style:e.string().optional(),intro:e.string().optional(),narrate:e.enum(["off","on","all"]).optional(),rate:e.number().min(.75).max(1.5).optional(),priority:e.enum(["high","normal","low"]).optional(),hotkey:z.optional()}),Rt=e.object({kinds:e.array(e.string().min(1)).min(1),agents:e.array(e.string().min(1)).optional(),nodes:e.array(e.string().min(1)).optional(),match:e.string().min(1).optional(),delivery:e.enum(["pull","digest","wake"]).default("pull"),maxPerHour:e.number().int().min(1).max(60).default(4)}),Tr=e.object({name:e.string(),workspace:e.string(),tier:e.enum(["claude-code","codex-cli","opencode","sdk","orchestrator"]).default("claude-code"),provider:e.string().optional(),model:e.string().optional(),systemPrompt:e.string().optional(),mentions:e.array(e.string()).default([]),intents:e.array(e.string()).default([]),maxDelegationDepth:e.number().int().min(0).max(50).default(5),mcp:e.record(e.string(),_r).optional(),codegraph:e.boolean().default(!1),contextStrategy:e.enum(["layered","planner"]).optional(),contextReferences:e.boolean().default(!1),richMessages:e.boolean().default(!0),maxConcurrent:e.number().default(1),maxExecutionMinutes:e.number().int().min(1).max(240).default(20),preSpawnTimeoutSec:e.number().int().min(10).max(3600).default(300),drainTimeoutSeconds:e.number().int().min(0).max(86400).optional(),wiki:e.object({contribute:e.object({enabled:e.boolean().default(!1),maxPatches:e.number().int().min(1).max(200).default(30),maxCostUsd:e.number().min(0).max(20).optional(),model:e.string().optional()}).default({}),absorb:e.object({enabled:e.boolean().default(!0)}).default({}),notes:e.object({dir:e.string().min(1).optional()}).default({})}).optional(),permissionMode:e.string().default("default"),billing:e.enum(["subscription","api"]).default("subscription"),cloudSessions:e.object({enabled:e.boolean().default(!1),maxPerDay:e.number().int().min(0).default(0),openHours:e.number().min(1).max(168).default(24),launchTimeoutSeconds:e.number().int().min(10).max(600).default(120)}).default({}),toolUseRequired:e.array(e.string()).default([]),gitlabAutoReply:e.boolean().optional(),persistentProcess:e.boolean().default(!1),queueMode:e.enum(["collect","followup","drop"]).default("collect"),heartbeat:e.object({enabled:e.boolean().default(!1),intervalMinutes:e.number().default(30),prompt:e.string().default("Check inbox, pending tasks, and system health. Report anything that needs attention."),channel:e.string().default("heartbeat")}).default({}),access:e.enum(["private","public"]).default("private"),admin:e.boolean().optional(),subscriptions:e.array(Rt).default([]),integrations:e.array(Er).default([]),voice:vt.optional(),presence:e.object({color:e.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),palette:e.enum(Z).optional(),initial:e.string().max(2).optional(),label:e.string().optional(),allowActions:e.boolean().optional()}).optional()}),vr=e.object({token:e.string(),agentBinding:e.string(),allowFrom:e.array(e.string()).optional(),pollInbound:e.boolean().default(!0)}),At=e.object({frameIntervalSeconds:e.number().int().min(0).max(3600).default(0),maxSessionMinutes:e.number().int().min(1).max(240).default(10),maxFrameEdge:e.number().int().min(160).max(3840).default(1024),keepFrames:e.boolean().default(!1),streamFrameSeconds:e.number().int().min(2).max(60).default(5),streamMaxSeconds:e.number().int().min(10).max(600).default(60)}).default({}),Rr=e.object({telegram:e.object({enabled:e.boolean().default(!1),accounts:e.record(e.string(),vr).default({}),policy:e.object({dm:e.enum(["pair","block"]).default("pair"),group:e.enum(["mention-required","all"]).default("mention-required"),allowFrom:e.array(e.string()).optional()}).default({})}).default({}),whatsapp:e.object({enabled:e.boolean().default(!1),sessionDir:e.string().default(".agentx/whatsapp-sessions"),defaultAgent:e.string().optional(),allowFrom:e.array(e.string()).optional(),routes:e.array(e.object({contact:e.string().optional(),group:e.string().optional(),agent:e.string()})).default([]),ingest:e.object({enabled:e.boolean().default(!1),mode:e.enum(["metadata-only","messages"]).default("metadata-only"),allowContacts:e.array(e.string()).default([]),allowGroups:e.array(e.string()).default([]),denyContacts:e.array(e.string()).default([]),denyGroups:e.array(e.string()).default([]),messageCap:e.number().int().min(1).max(500).default(50),historyDays:e.number().int().min(1).max(365).default(30),contactRefreshDays:e.number().int().min(1).max(90).default(7),throttle:e.object({minMsBetweenCalls:e.number().int().min(100).default(1500),maxCallsPerMinute:e.number().int().min(1).default(20),maxChatsPerSweep:e.number().int().min(1).default(25)}).default({}),retentionDays:e.number().int().min(0).default(0)}).default({})}).default({}),gitlab:e.object({enabled:e.boolean().default(!1),webhookPort:e.number().default(18810),webhookSecret:e.string().optional(),host:e.string().default("https://gitlab.com"),token:e.string().optional(),autoReplyLegacy:e.boolean().default(!0),routes:e.array(e.object({project:e.string(),agent:e.string()})).default([]),agentMappings:e.array(e.object({agentId:e.string(),gitlabUsernames:e.array(e.string()).default([]),keywords:e.array(e.string()).default([]),token:e.string().optional(),node:e.string().optional()})).default([]),agentUsernamePrefixes:e.array(e.string().min(1)).default([])}).default({}),github:e.object({enabled:e.boolean().default(!1),autoReplyLegacy:e.boolean().default(!0),cloudSessions:e.boolean().default(!1),token:e.string().optional(),tokenFile:e.string().optional(),appId:e.number().optional(),clientId:e.string().optional(),privateKeyFile:e.string().optional(),webhookSecret:e.string().optional(),routes:e.array(e.object({repo:e.string(),agent:e.string()})).default([]),agentMappings:e.array(e.object({agentId:e.string(),githubUsernames:e.array(e.string()).default([]),token:e.string().optional(),tokenFile:e.string().optional(),node:e.string().optional()})).default([]),issueActions:e.array(e.string().min(1)).default(["opened","reopened","assigned"]),pullRequestActions:e.array(e.string().min(1)).default(["opened","reopened","ready_for_review"]),ignoreOwnChanges:e.boolean().default(!0),debounceSeconds:e.number().min(0).max(3600).default(30)}).default({}),ntfy:e.object({enabled:e.boolean().default(!1),server:e.string().default("https://ntfy.sh"),topic:e.string().optional(),token:e.string().optional(),defaultPriority:e.number().int().min(1).max(5).default(3),defaultTitle:e.string().optional()}).default({}),push:e.object({enabled:e.boolean().default(!1),keysFile:e.string().default(".agentx/push-keys.json"),subject:e.string().regex(/^(mailto:|https:\/\/)/,"must start with mailto: or https://").optional(),relayTo:e.string().optional(),ttlSeconds:e.number().int().min(0).max(2419200).default(86400),keepRecent:e.number().int().min(0).max(1e3).default(50),allowedHosts:e.array(e.string().min(1)).default(["fcm.googleapis.com","push.services.mozilla.com","push.apple.com","notify.windows.com"])}).default({}),webrtc:e.object({enabled:e.boolean().default(!1),stunServers:e.array(e.string()).default(["stun:stun.l.google.com:19302"]),turnServers:e.array(e.object({urls:e.string(),username:e.string().optional(),credential:e.string().optional()})).default([]),allowedCallers:e.array(e.string()).default([]),ringNotify:e.array(e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()})).default([]),callUrlBase:e.string().optional(),bot:e.object({enabled:e.boolean().default(!1),defaultAgentId:e.string().optional(),whisperBackend:e.enum(["auto","mlx","openai"]).default("auto"),whisperModel:e.string().optional(),whisperLanguage:e.string().default("auto"),mlxBinary:e.string().optional(),transcriptChannel:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional(),maxCallMinutes:e.number().int().min(1).max(240).default(30)}).default({}),camera:e.object({width:e.number().int().min(160).max(3840).default(1280),height:e.number().int().min(120).max(2160).default(720),frameRate:e.number().int().min(1).max(60).default(15),maxSeconds:e.number().int().min(10).max(7200).default(600),voiceInput:e.boolean().default(!0),speakAnswers:e.boolean().default(!0),bot:At}).default({})}).default({})}),ae=e.object({enabled:e.boolean().default(!0),schedule:e.string(),timezone:e.string().default("UTC"),agent:e.string(),prompt:e.string().default(""),command:e.string().optional(),timeout:e.number().default(600),model:e.string().optional(),maxOutputTokens:e.number().int().min(50).max(8e3).optional(),taskType:e.string().trim().min(1).max(64).optional(),autonomy:se.optional(),onError:e.union([e.enum(["log","notify","disable"]),e.array(e.enum(["log","notify","disable"]))]).default("log").transform(r=>Array.isArray(r)?r:[r]),notify:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional(),deliverResult:e.boolean().optional(),fireToken:e.string().optional(),createdBy:e.string().optional(),approval:e.object({action:e.enum(["create","delete"]),requestedBy:e.string(),requestedAt:e.string()}).optional()}).refine(r=>!!(r.command?.trim()||r.prompt?.trim()),{message:"a cron needs either a prompt (dispatch an agent) or a command (run it directly)"}).refine(r=>!(r.command?.trim()&&r.autonomy&&r.autonomy!=="act"),{message:"autonomy applies to agent routines; a command cron runs no agent (remove autonomy or the command)"});yt=e.string().regex(/^(?!.*(?:^|\/)\.+(?:\/|$))[\w.-]+(\/[\w.-]+)+$/,"expected owner/name"),kt=e.union([yt,e.object({repo:yt,about:e.string().max(200).optional()})]),_t={tokenEnv:e.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).optional(),tokenFile:e.string().optional()},Ir=e.discriminatedUnion("type",[e.object({type:e.literal("github"),apiUrl:e.string().url().default("https://api.github.com"),..._t,repos:e.array(kt).min(1)}),e.object({type:e.literal("gitlab"),url:e.string().url().optional(),..._t,repos:e.array(kt).min(1)}),e.object({type:e.literal("agentx"),url:e.string().url().optional(),peers:e.boolean().default(!0)})]),Cr=e.object({enabled:e.boolean().default(!1),inbox:e.string().min(1).optional(),crons:e.array(e.string().min(1)).default([]),absorbAgent:e.string().min(1).optional(),maxNotesPerRun:e.number().int().min(1).max(100).default(20),maxDeferrals:e.number().int().min(1).max(20).default(3)}).refine(r=>!r.enabled||!!r.inbox,{message:"wikiNotes.enabled needs wikiNotes.inbox: the agent that runs the wiki observe/sweep schedule"}),Nr=e.object({name:e.string(),triggers:e.array(e.object({pattern:e.string(),channel:e.string().optional()})),allowedContacts:e.array(e.string()).optional(),agent:e.string(),prompt:e.string(),schedule:e.string().optional(),timezone:e.string().default("UTC"),notify:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional()}),Lr=e.object({url:e.string(),name:e.string(),token:e.string().optional()}),$r=e.object({name:e.string(),agent:e.string(),enabled:e.boolean().default(!0)}),Or=e.object({enabled:e.boolean().default(!1),peers:e.array(Lr).default([]),inboxes:e.array($r).default([]),discovery:e.enum(["static","mdns"]).default("static"),healthCheck:e.object({interval:e.number().default(60),timeout:e.number().default(10)}).default({}),feed:e.object({enabled:e.boolean().default(!0),skipTypes:e.array(e.string().min(1)).default(["task:step"])}).default({}),delegation:e.object({asyncWhenHuman:e.boolean().default(!0),timeoutMinutes:e.number().int().min(1).max(240).default(30),requeueRelayOnRestart:e.boolean().default(!0),callbackSends:e.boolean().default(!0)}).default({})}),Dr=e.object({enabled:e.boolean().default(!1),baseDir:e.string().default(".agentx/graph"),draftAgent:e.string().optional(),reviewAgent:e.string().optional(),autoApproveStructure:e.enum(["strict","extend-leaves","any"]).default("extend-leaves"),autoApproveConfidence:e.number().min(0).max(1).default(1),classifierModel:e.string().default("claude-haiku-4-5-20251001"),retrievalWeights:e.object({graph:e.number().min(0).default(.6),bm25:e.number().min(0).default(.4)}).default({})}).default({}),jr=e.object({mode:e.enum(["off","shadow","active"]).default("off"),backend:e.string().optional(),model:e.string().optional(),timeoutMs:e.number().int().min(100).optional(),temperature:e.number().min(.01).default(1),explore:e.number().min(0).max(1).default(.15),holdout:e.number().min(0).max(1).optional()}).default({}),Pr=e.object({enabled:e.boolean().default(!1),dbPath:e.string().default(".agentx/decisions/decisions.sqlite"),redactState:e.boolean().default(!1),keepStateRows:e.number().int().min(0).default(2e3),defaultBackend:e.string().default("local"),backends:e.object({local:e.object({provider:e.string().default("claude-code"),model:e.string().default("claude-haiku-4-5-20251001"),structureMode:e.enum(["auto","tool","text"]).default("auto"),normalizeProbabilities:e.boolean().default(!0),nRetryMalformedStructure:e.number().int().min(0).max(3).default(1),maxStateChars:e.number().int().min(500).default(24e3)}).default({}),simpleJev:e.object({baseUrl:e.string().default("http://127.0.0.1:8000/v1"),model:e.string().optional(),apiKeyEnv:e.string().optional(),timeoutMs:e.number().int().min(100).default(3e4),maxStateChars:e.number().int().min(500).default(6e3),maxChoiceOptions:e.number().int().min(2).max(255).default(50)}).default({}),jev:e.object({baseUrl:e.string().default("https://openrouter.ai/api/alpha"),path:e.string().default("/decisions"),model:e.string().default("jev-latest"),apiKeyEnv:e.string().default("OPENROUTER_API_KEY"),timeoutMs:e.number().int().min(100).default(3e4),maxStateChars:e.number().int().min(500).default(9e4),maxChoiceOptions:e.number().int().min(2).max(255).default(255)}).default({}),typesafe:e.object({baseUrl:e.string().default("https://api.typesafe.ai/v1"),path:e.string().default("/systemone"),model:e.string().default("jev-latest"),apiKeyEnv:e.string().default("TYPESAFE_API_KEY"),timeoutMs:e.number().int().min(100).default(3e4),maxStateChars:e.number().int().min(500).default(9e4),maxChoiceOptions:e.number().int().min(2).max(255).default(255)}).default({})}).default({}),seats:e.record(e.string(),jr).default({}),routing:e.object({cheapModel:e.string().optional(),cheapModels:e.object({"claude-code":e.string().optional(),"codex-cli":e.string().optional()}).optional()}).default({})}).default({}),Ct=e.object({allow:e.array(e.string()).default([]),maxPerHour:e.number().int().min(1).max(60).default(3),ringSeconds:e.number().int().min(10).max(300).default(45),maxCallMinutes:e.number().int().min(1).max(240).default(30),ringSound:e.string().regex(/^[\w -]+$/).default("Submarine"),summary:e.boolean().default(!0),busy:e.object({retryMinutes:e.number().int().min(1).max(60).default(5),maxWaitMinutes:e.number().int().min(1).max(240).default(10),title:e.string().min(1).max(120).default("Call waiting: {agent}"),notice:e.string().min(1).max(200).default("AgentX Voice is busy. The call rings when it is free.")}).default({})}).default({}),Mr=e.object({longTaskThreshold:e.number().default(30),destination:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional(),channel:e.string().optional(),on:e.object({taskComplete:e.boolean().default(!0),taskError:e.boolean().default(!0),taskQueued:e.boolean().default(!1)}).default({}),local:e.object({banner:e.boolean().default(!0),sound:e.boolean().default(!0),soundName:e.string().regex(/^[\w -]+$/).default("Glass"),volume:e.number().min(0).max(1).default(.4),icon:e.string().optional(),quietWaitSeconds:e.number().int().min(0).max(120).default(15)}).default({}),told:e.object({enabled:e.boolean().default(!0),repeatWindowHours:e.number().positive().max(24*30).default(24),channels:e.array(e.string().min(1)).default(["push","ntfy"]),addresses:e.array(e.string().regex(/^[a-z0-9_-]+:\S+$/i,"use channel:chatId, for example telegram:123456789")).default([]),digestNags:e.boolean().default(!0)}).default({}),reach:e.object({enabled:e.boolean().default(!1),escalateAfterMinutes:e.number().int().min(1).max(24*60).default(10),ladder:e.array(e.string().regex(/^[a-z0-9_-]+:\S+$/i,"use channel:chatId, for example telegram:123456789")).default([])}).default({})}).default({}),qr=e.object({x:e.number(),y:e.number(),width:e.number().positive(),height:e.number().positive()}),Ur=e.object({maxPixels:e.number().int().positive().default(12e5),timeoutMs:e.number().int().positive().default(5e3),intervalMs:e.number().int().positive().default(100),changeThreshold:e.number().min(0).max(1).default(.015),stableMs:e.number().int().nonnegative().default(400),regions:e.record(e.string().regex(/^[a-z][\w-]*$/),qr).default({}),buffer:e.object({enabled:e.boolean().default(!1),seconds:e.number().positive().max(120).default(10),fps:e.number().positive().max(10).default(2),region:e.string().default("screen"),maxPixels:e.number().int().positive().default(3e5)}).default({})}).default({}),Nt=e.object({enabled:e.boolean().default(!1),mode:e.enum(["advisory","required"]).default("advisory"),reviewer:e.object({agent:e.string().min(1).optional(),model:e.string().min(1).optional()}).default({}),paths:e.array(e.string().min(1)).default([]),maxCostPerReview:e.number().min(0).default(2),maxMinutes:e.number().positive().max(120).default(10),maxReviewsPerPrPerDay:e.number().int().min(0).max(100).default(6),ciWaitMinutes:e.number().min(0).max(60).default(5),maxDiffKb:e.number().int().positive().max(2048).default(256)}).default({}),Lt=e.object({enabled:e.boolean().default(!1),mode:e.enum(["advisory","required"]).default("advisory"),corePaths:e.array(e.string().min(1)).default([]),edgePaths:e.array(e.string().min(1)).default([]),label:e.string().min(1).max(50).default("core-change"),approvers:e.array(e.string().min(1)).default([]),ownerCard:e.boolean().default(!0)}).default({}),$t=e.object({enabled:e.boolean().default(!1),skipChannels:e.array(e.string().min(1)).default([]),defaultDueMinutes:e.number().positive().max(30*24*60).default(120),noBelow:e.number().min(0).max(1).default(.2),yesAt:e.number().min(0).max(1).default(.7),escalateAfterMinutes:e.number().positive().max(7*24*60).default(30),owner:e.object({channel:e.string().min(1).optional(),chatId:e.string().min(1).optional()}).default({}),retentionDays:e.number().positive().max(3650).default(30),doneSignal:e.enum(["result","explicit"]).default("result"),evidence:e.record(e.string().min(1),e.object({accept:e.array(e.enum(["test","output","link"])).min(1),minOutputChars:e.number().int().min(1).max(2e3).default(40),linkHosts:e.array(e.string().min(1)).default([])})).default({})}).default({}).refine(r=>r.noBelow<=r.yesAt,{message:"watchers.noBelow must not be above watchers.yesAt"}),Ot=e.object({enabled:e.boolean().default(!1),channels:e.array(e.string().min(1)).default([]),from:e.array(e.string().regex(/^[a-z0-9_-]+:\S+$/i,"use channel:id, for example telegram:123456789")).default([]),staleAfterHours:e.number().positive().max(24*365).default(24),settleHandedBack:e.boolean().default(!0),retentionDays:e.number().positive().max(3650).default(90),plans:e.object({enabled:e.boolean().default(!0),stallMinutes:e.number().positive().max(7*24*60).default(30),maxNudges:e.number().int().min(0).max(20).default(3),approveKinds:e.array(e.string().regex(/^[a-z][a-z0-9_-]{0,30}$/,"a step kind: a short lower-case word")).default(["message"]),disabledAgents:e.array(e.string().min(1)).default([]),requireForWork:e.array(e.string().regex(/^[a-z][a-z0-9_-]{0,30}$/,"a kind of work: a short lower-case word")).default(["code"]),requireOverMinutes:e.number().min(0).max(7*24*60).default(30)}).default({}),checkers:e.object({enabled:e.array(e.enum(["url","issue","git-ancestor","reply"])).default(["url","issue","git-ancestor","reply"]),checkEveryMinutes:e.number().positive().max(24*60).default(10),repos:e.record(e.string(),e.string().min(1)).default({}),github:e.object({apiUrl:e.string().url().default("https://api.github.com"),token:e.string().optional()}).optional(),gitlab:e.object({url:e.string().url().optional(),token:e.string().optional()}).optional()}).default({}),statusDrift:e.object({enabled:e.boolean().default(!0),checkEveryMinutes:e.number().int().min(5).max(24*60).default(30),lookbackDays:e.number().int().min(1).max(60).default(7),projects:e.array(e.object({provider:e.enum(["gitlab","github"]),project:e.string().regex(/^[\w.-]+(\/[\w.-]+)+$/,"group/project or owner/repo").refine(r=>r.split("/").every(t=>t!=="."&&t!==".."),"a project path has no . or .. part"),live:e.array(e.union([e.literal("self"),e.string().url()])).default([]),repo:e.string().min(1).optional()})).default([]),liveField:e.string().min(1).default("commit"),labels:e.object({todo:e.array(e.string().min(1)).default(["To Do","Status::To Do"]),doing:e.array(e.string().min(1)).default(["Doing","Status::Doing"]),review:e.array(e.string().min(1)).default(["Review","Status::Review"]),done:e.array(e.string().min(1)).default(["Done","Status::Done"])}).default({}),doneNotLiveAfterHours:e.number().min(0).max(24*30).default(24),write:e.boolean().default(!1),writeProjects:e.array(e.string().min(1)).default([])}).default({}),headlines:e.object({enabled:e.boolean().default(!0),provider:e.string().min(1).default("claude-code"),model:e.string().min(1).default("claude-haiku-4-5-20251001"),timeoutSeconds:e.number().int().min(5).max(120).default(30)}).default({})}).default({}),Dt=e.object({id:e.string().regex(/^[a-z0-9][a-z0-9_-]{0,39}$/,"lower-case letters, digits, - and _"),name:e.string().min(1).max(80),say:e.string().trim().min(1).max(120).optional(),role:e.enum(["owner","member","client","guest"]).default("member"),identities:e.array(e.string().regex(/^[A-Za-z][\w-]*:\S.*$/,"write it as channel:id")).default([]),agents:e.array(e.string().min(1)).default([]),deny:e.object({tools:e.array(e.string().min(1)).default([]),skills:e.array(e.string().min(1)).default([])}).strict().default({})}),jt=e.array(Dt).default([]).superRefine((r,t)=>{let n=ft(r);n&&t.addIssue({code:e.ZodIssueCode.custom,message:n})}),Pt=e.object({logRetentionDays:e.number().int().min(1).max(3650).default(90)}).default({}),Mt=e.object({channels:e.array(e.string().min(1)).default([])}).default({}),St=e.string().regex(/^\d{4}-\d{2}-\d{2}$/,"use YYYY-MM-DD"),qt=e.object({enabled:e.boolean().default(!1),repo:e.string().regex(/^[\w.-]+\/[\w.-]+$/,"use owner/name").optional(),issue:e.number().int().positive().optional(),time:e.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/,"use HH:MM, 24-hour").default("08:00"),timezone:e.string().refine(r=>{try{return new Intl.DateTimeFormat("en-US",{timeZone:r}),!0}catch{return!1}},"unknown timezone").optional(),command:e.array(e.string().min(1)).min(1).default(["agentx","quality","report","--public"]),baseline:e.string().regex(/^[\w.-]{1,64}$/,"use letters, digits, dot, dash or underscore").default("baseline"),timeoutSeconds:e.number().int().min(10).max(3600).default(600),gates:e.array(e.object({id:e.string().min(1),label:e.string().min(1).optional(),since:St,until:St.optional()})).default([])}).default({}).superRefine((r,t)=>{r.enabled&&(!r.repo||!r.issue)&&t.addIssue({code:e.ZodIssueCode.custom,message:"qualityReport.enabled needs repo and issue"})}),Ut=e.object({defaultExpiryDays:e.number().positive().max(365).default(3),maxExpiryDays:e.number().positive().max(365).default(30),laterHours:e.number().positive().max(24*30).default(24),notifyAgent:e.boolean().default(!0),forwardTo:e.string().min(1).optional(),digest:e.object({enabled:e.boolean().default(!0),time:e.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/,"use HH:MM, 24-hour").default("09:00"),timezone:e.string().refine(r=>{try{return new Intl.DateTimeFormat("en-US",{timeZone:r}),!0}catch{return!1}},"unknown timezone").optional(),destination:e.object({channel:e.string(),chatId:e.string(),accountId:e.string().optional()}).optional()}).default({}),popup:e.object({enabled:e.boolean().default(!1),style:e.enum(["card","dialog"]).default("card"),theme:e.enum(["system","light","dark"]).default("system"),speak:e.boolean().default(!0),voice:e.string().regex(/^[\w .()-]+$/,"a voice name, as `say -v '?'` lists them").optional(),sound:e.string().regex(/^[\w -]*$/,"a sound name, like chime or Glass").default("chime"),volume:e.number().min(0).max(1).default(.4),timeoutSeconds:e.number().int().min(10).max(3600).default(600)}).default({}),checkin:e.object({enabled:e.boolean().default(!1),times:e.array(e.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/,"use HH:MM, 24-hour")).default(["11:00","14:00","17:00"]),dailyAt:e.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/,"use HH:MM, 24-hour").default("09:00"),timezone:e.string().refine(r=>{try{return new Intl.DateTimeFormat("en-US",{timeZone:r}),!0}catch{return!1}},"unknown timezone").optional(),lists:e.array(e.string().min(1)).default(["Reminders"]),agent:e.string().min(1).optional(),dueWithinHours:e.number().positive().max(24*30).default(24),maxAsksPerPass:e.number().int().min(1).max(20).default(5),composeTimeoutSeconds:e.number().int().min(30).max(3600).default(300)}).default({})}).default({}),Ft=e.object({node:e.object({id:e.string(),name:e.string(),bind:e.string().default("127.0.0.1:18800"),defaultAgent:e.string().optional(),connectAttemptTimeoutMs:e.number().int().min(250).max(6e4).default(5e3)}),providers:e.record(e.string(),xr).default({}),agents:e.record(e.string(),Tr).default({}),wiki:e.object({contributions:e.object({schedule:e.string().default("40 22 * * *"),mergeSchedule:e.string().default("20 23 * * *"),timezone:e.string().default("UTC"),maxCostUsd:e.number().min(0).max(20).default(.5),model:e.string().default("sonnet")}).default({}),query:e.object({shared:e.boolean().default(!0),method:e.enum(["auto","summaries","catalog"]).default("auto"),candidates:e.number().int().min(1).max(50).default(12),sharedCandidates:e.number().int().min(0).max(50).default(4),notes:e.object({enabled:e.boolean().default(!1),types:e.array(e.string().min(1)).default(["project","reference"]),candidates:e.number().int().min(1).max(50).default(4)}).default({}),maxPages:e.number().int().min(1).max(10).default(3),pageChars:e.number().int().min(200).max(4e4).default(4e3),linkedPages:e.number().int().min(0).max(10).default(0),linkedChars:e.number().int().min(200).max(4e4).default(6e3),navigatorModel:e.string().min(1).default("haiku"),answerModel:e.string().min(1).default("sonnet"),live:e.object({enabled:e.boolean().default(!0),maxReads:e.number().int().min(0).max(20).default(6),timeoutMs:e.number().int().min(1e3).max(12e4).default(15e3),plannerModel:e.string().min(1).default("haiku"),sources:e.array(Ir).default([])}).default({})}).default({}),summaries:e.object({model:e.string().min(1).default("haiku"),batchSize:e.number().int().min(1).max(50).default(20),maxWords:e.number().int().min(5).max(120).default(35),schedule:e.string().refine(r=>r.trim()===""||It(r),'expected a cron of 5 fields, such as "30 23 * * *"').optional(),timezone:e.string().refine(Ar,'expected a time zone such as "UTC" or "Europe/Paris"').default("UTC"),agent:e.string().min(1).optional()}).default({}),curator:e.object({enabled:e.boolean().default(!0),agent:e.string().min(1).optional()}).default({})}).default({}),channels:Rr.default({}),crons:e.record(e.string(),ae).default({}),wikiNotes:Cr.default({}),services:e.record(e.string(),Nr).default({}),notifications:Mr,calls:Ct,whatsappTriage:Ne,approvals:Ut,quality:e.object({specTests:e.object({enabled:e.boolean().default(!1),paths:e.array(e.string().min(1)).default(["src/daemon"]),agent:e.string().min(1).default("spec-tests"),model:e.string().min(1).optional(),maxCostPerIssue:e.number().positive().max(100).default(2),maxMinutes:e.number().int().min(1).max(120).default(20)}).default({}),canary:e.object({enabled:e.boolean().default(!1),soakMinutes:e.number().int().min(1).max(10080).default(120),thresholds:e.object({maxRestarts:e.number().int().min(0).default(1),maxCrashes:e.number().int().min(0).default(0),maxFailedTasks:e.number().int().min(0).default(3),maxErrorRateIncrease:e.number().min(0).max(1).default(.1),minTasksForRate:e.number().int().min(1).default(10)}).default({})}).default({}),reviewGate:Nt,mergePolicy:Lt}).default({}),shutdown:e.object({drainTimeoutSeconds:e.number().int().min(0).max(86400).optional(),restart:e.object({allowBy:e.string().optional(),window:e.string().regex(/^\d{1,2}:\d{2}$/,"window must be HH:MM").optional(),windowWaitMinutes:e.number().int().min(1).max(1440).default(180),forbidOnTimeoutRestart:e.boolean().default(!1)}).default({})}).default({}),resume:e.object({enabled:e.boolean().default(!0),maxAgeMinutes:e.number().int().positive().default(30),maxAttempts:e.number().int().min(0).default(1),reportOnlyChannels:e.array(e.string()).default([]),directChannels:e.array(e.string()).default([]),crashLoop:e.object({restarts:e.number().int().positive().default(3),windowMinutes:e.number().positive().default(10)}).default({})}).default({}),signals:e.object({enabled:e.boolean().default(!0),windDownSeconds:e.number().int().min(10).max(1800).default(120),allowAgents:e.array(e.string().min(1)).default([]),allowPeers:e.array(e.string().min(1)).default([]),maxPerRoot:e.number().int().min(1).max(100).default(6)}).default({}),requests:Ot,watchers:$t,people:jt,members:Pt,requestStatus:Mt,qualityReport:qt,reminders:e.object({enabled:e.boolean().default(!1),lists:e.array(e.string().min(1)).default(["AgentX"]),pollSeconds:e.number().int().min(15).default(60),lookbackHours:e.number().positive().default(24),command:e.string().min(1).default("remindctl")}).default({}),native:e.object({adapter:e.enum(["auto","none"]).default("auto"),mirror:e.object({enabled:e.boolean().default(!1),list:e.string().min(1).refine(r=>!r.startsWith("-"),'must not start with "-"').default("AgentX Desk"),dueAfterMinutes:e.number().int().min(0).max(1440).default(5),include:e.array(e.enum(["card","request"])).min(1).default(["card","request"]),answerUrl:e.string().url().optional()}).default({}),forwardTo:e.string().min(1).optional(),notes:e.object({enabled:e.boolean().default(!1),folder:e.string().min(1).default("AgentX"),perAgentPerDay:e.number().int().min(1).max(50).default(10)}).default({})}).default({}),app:e.object({android:e.object({packageName:e.string().regex(/^[a-zA-Z][\w]*(\.[a-zA-Z][\w]*)+$/,"must look like com.example.app").default("dev.agentx.phone"),certFingerprints:e.array(e.string().regex(/^([0-9A-Fa-f]{2}:){31}[0-9A-Fa-f]{2}$/,"must be 32 hex pairs separated by colons")).default([])}).default({}),places:e.object({enabled:e.boolean().default(!0),file:e.string().min(1).default(".agentx/places.json"),defaultRadiusMeters:e.number().int().min(50).max(5e4).default(150),minRadiusMeters:e.number().int().min(50).max(5e4).default(100),maxRadiusMeters:e.number().int().min(50).max(5e4).default(5e3),maxPlaces:e.number().int().min(1).max(100).default(50),maxRulesPerPlace:e.number().int().min(1).max(50).default(10),cooldownMinutes:e.number().int().min(0).max(1440).default(10),maxEventAgeMinutes:e.number().int().min(1).max(1440).default(30),syncMinutes:e.number().int().min(15).max(1440).default(60)}).default({}),pins:e.object({file:e.string().min(1).default(".agentx/app-pins.json"),max:e.number().int().min(1).max(50).default(12),pages:e.array(e.object({label:e.string().trim().min(1).max(40),url:e.string().max(2e3).refine(r=>{try{let t=new URL(r);return t.protocol==="https:"&&!!t.hostname&&!t.username&&!t.password}catch{return!1}},"must be an https:// address with no user name or password")}).strict()).max(50).default([])}).refine(r=>r.pages.length<=r.max,{path:["pages"],message:"has more pages than app.pins.max allows; raise app.pins.max or remove some pages"}).default({})}).default({}),demo:e.object({startupTimeoutSeconds:e.number().int().positive().max(3600).optional()}).default({}),events:e.object({ringSize:e.number().int().min(1).max(1e5).default(1e3)}).default({}),screen:Ur,mesh:Or.default({}),meshVoices:e.record(e.string(),vt.omit({rate:!0,priority:!0,hotkey:!0}).extend({name:e.string().optional()})).default({}),voice:e.object({provider:wt.default("system"),fallback:e.enum(["system","none"]).default("system"),system:Tt.optional(),locale:e.string().default("en"),listener:e.string().optional(),pronunciations:e.array(e.object({written:e.string().trim().min(1).max(80),spoken:e.string().trim().min(1).max(120),languages:e.array(e.enum(["en","fr","ar"])).optional()}).strict()).max(200).default([]),pointer:e.boolean().default(!0),stt:e.enum(["auto","elevenlabs","local"]).default("auto"),allowUnmeasured:e.boolean().default(!1),spokenMaxChars:e.number().int().min(300).max(1500).default(J),noiseFilter:e.object({enabled:e.boolean().default(!0),markers:e.array(e.string()).default(Q)}).default({}),localStt:e.enum(["mlx-whisper","parakeet"]).default("mlx-whisper"),endOfTurn:e.enum(["vad","volume"]).default("vad"),hotkeys:e.object({talk:z.default(O.talk),stop:z.default(O.stop),paste:z.default(O.paste)}).default({}),look:e.enum(ye).default("orb"),startReduced:e.boolean().default(!1),stroll:e.boolean().default(!1),animations:e.enum(ke).default("sometimes"),palette:e.enum(Z).default(D),floor:e.object({enabled:e.boolean().default(!0),defaultSeconds:e.number().int().min(5).max(3600).default(j.defaultSeconds),maxSeconds:e.number().int().min(5).max(14400).default(j.maxSeconds)}).default({}),card:e.object({timeout:e.number().min(0).max(600).default(30),maxHeight:e.number().min(120).max(800).default(320)}).default({})}).default({}),business:ce.optional(),boards:fe,dashboard:ge,graph:Dr,decisions:Pr,workflows:e.object({enabled:e.boolean().default(!1),dir:e.string().default(".agentx/workflows"),n8n:e.object({baseUrl:e.string().default(""),apiKey:e.string().default("")}).default({baseUrl:"",apiKey:""}),matching:e.object({enabled:e.boolean().default(!1),mode:e.enum(["suggest","auto"]).default("suggest"),autoRunThreshold:e.number().min(0).max(1).default(.85),suggestThreshold:e.number().min(0).max(1).default(.65)}).default({}),followUp:e.object({enabled:e.boolean().default(!0),agents:e.record(e.boolean()).default({}),stallMinutes:e.number().positive().max(7*24*60).default(30),maxNudges:e.number().int().min(0).max(20).default(2),approval:e.enum(["start","step"]).default("step")}).default({}),required:e.object({enabled:e.boolean().default(!1),agents:e.record(e.boolean()).default({}),exemptQuestions:e.boolean().default(!0),retentionDays:e.number().int().min(0).max(3650).default(30)}).default({}),editor:e.enum(["disabled","readonly","edit"]).default("edit")}).default({}),procedures:e.object({enabled:e.boolean().default(!0),dir:e.string().default(".agentx/procedures"),extraction:e.object({enabled:e.boolean().default(!1),onTaskCompletion:e.boolean().default(!1),minOccurrences:e.number().int().min(2).default(3),sinceDays:e.number().int().min(1).default(7),maxClusters:e.number().int().min(1).default(5),via:e.string().optional()}).default({}),injection:e.object({enabled:e.boolean().default(!0),maxProcedures:e.number().int().min(1).default(2),minScore:e.number().min(0).max(1).default(.5)}).default({})}).default({}),webhooks:e.array(e.object({id:e.string().regex(/^[a-z0-9][a-z0-9_-]*$/,"webhook id must be lowercase slug"),source:e.string().min(1),agentId:e.string(),secretEnv:e.string().optional(),description:e.string().optional(),enabled:e.boolean().default(!0),node:e.string().optional(),triggers:e.record(e.string(),e.string()).optional(),defaultWorkflow:e.string().optional()})).default([]),session:e.object({staleMinutes:e.number().int().min(1).max(1440).default(720),maxTurnsPerSession:e.number().int().min(2).max(200).default(40),tierTwoThresholdTokens:e.number().int().min(5e4).max(2e5).default(18e4),tierTwoThresholdTokensByChannel:e.record(e.number().int().min(5e4).max(2e5)).default({}),contextStrategy:e.enum(["layered","planner"]).default("layered"),maxClaudeCodeDispatchesPerHour:e.number().int().min(0).max(1e4).optional(),maxClaudeCodeDispatchesPer5h:e.number().int().min(0).max(5e4).optional(),continuityStateTurns:e.number().int().min(0).max(5).default(0),profileByChannel:e.record(e.enum(["full","lean"])).default({}),lean:e.object({mcpServers:e.array(e.string().min(1)).default(["agentx"]),settingSources:e.array(e.enum(["user","project","local"])).default(["project","local"]),contextOnDemand:e.boolean().default(!0),tools:e.array(e.string().min(1)).default([]),toolsByChannel:e.record(e.array(e.string().min(1))).default({}),agentxTools:xt.default([]),agentxToolsByChannel:e.record(xt).default({})}).default({}),triage:e.object({models:e.object({"claude-code":e.string().min(1).optional(),"codex-cli":e.string().min(1).optional()}).default({}),actions:e.array(e.string().min(1)).default(["labeled","unlabeled","closed"])}).default({}),memoryIndexMaxChars:e.number().int().min(0).max(2e5).default(0),resumeGate:e.object({mode:e.enum(["off","shadow","active"]).default("off"),freshTokens:e.number().int().min(1e3).max(2e5).default(3e4),cacheTtlMinutes:e.number().int().min(1).max(1440).default(60),cacheWriteFactor:e.number().min(1).max(4).default(2),margin:e.number().min(1).max(10).default(1.5)}).default({}),observationPack:e.object({enabled:e.boolean().default(!1),limitBytes:e.number().int().min(1024).max(1048576).default(10240),headBytes:e.number().int().min(0).max(65536).default(1024),tailBytes:e.number().int().min(0).max(65536).default(1024),tools:e.array(e.string().min(1)).default(["Bash","Grep","Read","WebFetch","mcp__.*"]),retentionDays:e.number().int().min(0).max(3650).default(0)}).default({})}).default({}),plugins:e.array(e.string()).default([]),processPool:e.object({maxIdleSeconds:e.number().int().min(5).max(86400).default(30),maxAgeSeconds:e.number().int().min(60).max(86400).default(2700),sweepIntervalSeconds:e.number().int().min(1).max(300).default(5)}).default({})});Wt="wiki-contribute",Bt="wiki-contribute-merge";re="wiki-summarize"});export{Vr as a,Yr as b,pe as c,_n as d,Jr as e,O as f,xe as g,T as h,ye as i,ke as j,Z as k,ea as l,Se as m,Ee as n,we as o,aa as p,Te as q,ia as r,sa as s,Re as t,Ae as u,da as v,Ie as w,Ne as x,Le as y,De as z,je as A,qe as B,pa as C,He as D,Be as E,ze as F,ya as G,Ge as H,q as I,Hn as J,Ta as K,va as L,Ra as M,Ze as N,x as O,p as P,Na as Q,U as R,La as S,F as T,$a as U,Je as V,et as W,ja as X,Pa as Y,W as Z,Xn as _,Ma as $,nt as aa,ot as ba,ar as ca,Ba as da,ut as ea,za as fa,lr as ga,Ka as ha,dt as ia,Za as ja,B as ka,Ja as la,mt as ma,mr as na,fr as oa,gt as pa,Qa as qa,eo as ra,to as sa,pt as ta,hr as ua,ro as va,ao as wa,bt as xa,wr as ya,Rt as za,At as Aa,It as Ba,Ct as Ca,Nt as Da,Lt as Ea,$t as Fa,Ot as Ga,Dt as Ha,jt as Ia,Pt as Ja,Mt as Ka,qt as La,Ut as Ma,Ft as Na,G as Oa,Hr as Pa,Ht as Qa,Wr as Ra,Wt as Sa,Bt as Ta,zt as Ua,re as Va,Kt as Wa,Br as Xa,zr as Ya};
|
|
76
|
+
//# sourceMappingURL=chunk-SQTL2CLY.js.map
|