agentix-cli 0.142.0 → 0.144.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-VHAMAS4I.js → App-N2U77ED7.js} +2 -2
- package/dist/{ChatApp-Y7HOL45K.js → ChatApp-GYVTECF6.js} +2 -2
- package/dist/{absorb-eval-VHACXZ3H.js → absorb-eval-WSAVWCZV.js} +2 -2
- package/dist/{agent-M7ID5U3U.js → agent-SLRGXBMK.js} +2 -2
- package/dist/{board-dashboard-VHUZMYOV.js → board-dashboard-B3T7VCRQ.js} +320 -268
- package/dist/board-dashboard-B3T7VCRQ.js.map +1 -0
- package/dist/{chunk-S3LWGPNZ.js → chunk-2GJFLPS7.js} +3 -3
- package/dist/{chunk-TFNBUHRW.js → chunk-3LAQ74H7.js} +2 -2
- package/dist/{chunk-3NKCOZQO.js → chunk-45TKVOJX.js} +2 -2
- package/dist/{chunk-B5744ATM.js → chunk-4JHMV3D3.js} +2 -2
- package/dist/{chunk-QSKG754X.js → chunk-5TTHN7DP.js} +12 -8
- package/dist/chunk-5TTHN7DP.js.map +1 -0
- package/dist/{chunk-S6QUFAHD.js → chunk-BAO77R4L.js} +2 -2
- package/dist/{chunk-S6QUFAHD.js.map → chunk-BAO77R4L.js.map} +1 -1
- package/dist/{chunk-IFUN332M.js → chunk-CZWEXRIE.js} +2 -2
- package/dist/{chunk-2TLOZN7P.js → chunk-DFWPTNZ5.js} +2 -2
- package/dist/chunk-DG2DC43E.js +11 -0
- package/dist/chunk-DG2DC43E.js.map +1 -0
- package/dist/chunk-DPIQCHXB.js +62 -0
- package/dist/chunk-DPIQCHXB.js.map +1 -0
- package/dist/{chunk-5SEUBOUH.js → chunk-EX4AREN5.js} +2 -2
- package/dist/{chunk-QTS7MHIA.js → chunk-GOAMSJ2D.js} +2 -2
- package/dist/{chunk-6PJKKLKV.js → chunk-GWBBWYGM.js} +58 -55
- package/dist/chunk-GWBBWYGM.js.map +1 -0
- package/dist/{chunk-6YPJVGYF.js → chunk-JGX5IWNX.js} +2 -2
- package/dist/chunk-KV4ZA7FS.js +4 -0
- package/dist/chunk-KV4ZA7FS.js.map +1 -0
- package/dist/{chunk-YXFTUOZR.js → chunk-LLJZMS23.js} +2 -2
- package/dist/{chunk-ZWL44UJS.js → chunk-LVQHNTXV.js} +2 -2
- package/dist/{chunk-UNIXYV3U.js → chunk-M7527M7D.js} +2 -2
- package/dist/{chunk-NFI4BCCO.js → chunk-MXWAREAQ.js} +2 -2
- package/dist/{chunk-UF4OTZ7D.js → chunk-PJGLIAEP.js} +282 -274
- package/dist/chunk-PJGLIAEP.js.map +1 -0
- package/dist/{chunk-7H7LUBPC.js → chunk-Q73QQPDB.js} +2 -2
- package/dist/{chunk-25XO6P7W.js → chunk-QI4FYJGJ.js} +2 -2
- package/dist/{chunk-KEBTFUOZ.js → chunk-R5VGRKUW.js} +2 -2
- package/dist/{chunk-SLJFY2AQ.js → chunk-RGBQULXX.js} +2 -2
- package/dist/{chunk-GPCBOXZM.js → chunk-RV4WJZW5.js} +2 -2
- package/dist/{chunk-7Y3FEIX7.js → chunk-SFUNL7RV.js} +2 -2
- package/dist/{chunk-EMA67BJ5.js → chunk-SVNYI6GR.js} +2 -2
- package/dist/{chunk-WEHEI3JO.js → chunk-TTBCXVPJ.js} +6 -6
- package/dist/chunk-TTBCXVPJ.js.map +1 -0
- package/dist/{chunk-MLLMBE2W.js → chunk-UII54DAP.js} +2 -2
- package/dist/{chunk-JH6C2Y5D.js → chunk-UNZRZOED.js} +2 -2
- package/dist/{chunk-Y4CYQV7G.js → chunk-VY7TBMJU.js} +2 -2
- package/dist/chunk-VY7TBMJU.js.map +1 -0
- package/dist/{chunk-3E4AEZ2J.js → chunk-VYLMC5DO.js} +2 -2
- package/dist/{chunk-SHEUCITK.js → chunk-WBG6NA7R.js} +2 -2
- package/dist/{chunk-SHEUCITK.js.map → chunk-WBG6NA7R.js.map} +1 -1
- package/dist/cli.js +1 -1
- package/dist/{client-ISQF24N5.js → client-B7FXDTYK.js} +2 -2
- package/dist/config-MLP33NWR.js +2 -0
- package/dist/{config-mutate-YZ3IH6CJ.js → config-mutate-XQRUAVEH.js} +2 -2
- package/dist/config-mutator-5JFTMQ43.js +2 -0
- package/dist/{contributions-FQGIRD44.js → contributions-ESUSYUQG.js} +2 -2
- package/dist/{doctor-FMW2SSCN.js → doctor-DIYZGSIS.js} +2 -2
- package/dist/{failure-candidates-XZSZP4HT.js → failure-candidates-NQX2JMMS.js} +2 -2
- package/dist/{follow-up-tool-I7PPM2R5.js → follow-up-tool-QXEYEWX7.js} +2 -2
- package/dist/{grants-CEUGZBQ4.js → grants-MM7U6GCK.js} +2 -2
- package/dist/{hub-Z3X4LWMI.js → hub-OC7PH2AR.js} +2 -2
- package/dist/index.d.ts +382 -131
- package/dist/index.js +1 -1
- package/dist/{program-I7WCAMBP.js → program-LZPM6I2Q.js} +289 -252
- package/dist/program-LZPM6I2Q.js.map +1 -0
- package/dist/{promote-7YUBVKRC.js → promote-SIC6RWCX.js} +2 -2
- package/dist/{query-TMALX5BE.js → query-OP2MSR2G.js} +2 -2
- package/dist/{query-settings-3PEV5GIH.js → query-settings-67332F5V.js} +2 -2
- package/dist/{registry-6ZIBMOZQ.js → registry-G2ZG5TIN.js} +2 -2
- package/dist/{request-planner-GLNHHQXA.js → request-planner-THTPE3FP.js} +2 -2
- package/dist/{review-candidates-DJXNTOG6.js → review-candidates-7PS7CHXF.js} +2 -2
- package/dist/{routing-YWP2AF56.js → routing-PE7DB7WA.js} +2 -2
- package/dist/schedule-tool-3FXRKJGN.js +2 -0
- package/dist/{scheduler-HK362U53.js → scheduler-YTXCIQTZ.js} +2 -2
- package/dist/{seat-4PPUZXFN.js → seat-6DMK4274.js} +2 -2
- package/dist/{select-ULCUVM7N.js → select-CLDYPAOS.js} +2 -2
- package/dist/{set-enabled-G4PLW5WK.js → set-enabled-ZH2DH4GU.js} +2 -2
- package/dist/tool-4AO3ZMAY.js +6 -0
- package/dist/tool-4AO3ZMAY.js.map +1 -0
- package/dist/{tool-KGOC2NUZ.js → tool-MKDTKBJ4.js} +3 -3
- package/dist/{tool-KGOC2NUZ.js.map → tool-MKDTKBJ4.js.map} +1 -1
- package/dist/traces-GVEQZX24.js +2 -0
- package/dist/{wiki-32G6TGCI.js → wiki-CH2HYJVI.js} +2 -2
- package/dist/wiki-enrich-VEAYFVR2.js +2 -0
- package/dist/wiki-enrich-VEAYFVR2.js.map +1 -0
- package/dist/workflow-pilot-HCPSMGIC.js +2 -0
- package/dist/workflow-pilot-HCPSMGIC.js.map +1 -0
- package/package.json +1 -1
- package/dist/board-dashboard-VHUZMYOV.js.map +0 -1
- package/dist/chunk-3W2BHUGM.js +0 -11
- package/dist/chunk-3W2BHUGM.js.map +0 -1
- package/dist/chunk-6PJKKLKV.js.map +0 -1
- package/dist/chunk-AKWBDCMU.js +0 -4
- package/dist/chunk-AKWBDCMU.js.map +0 -1
- package/dist/chunk-JYPXNH4C.js +0 -54
- package/dist/chunk-JYPXNH4C.js.map +0 -1
- package/dist/chunk-QSKG754X.js.map +0 -1
- package/dist/chunk-UF4OTZ7D.js.map +0 -1
- package/dist/chunk-WEHEI3JO.js.map +0 -1
- package/dist/chunk-Y4CYQV7G.js.map +0 -1
- package/dist/config-Z64VWERV.js +0 -2
- package/dist/config-mutator-3HTNXOPU.js +0 -2
- package/dist/program-I7WCAMBP.js.map +0 -1
- package/dist/schedule-tool-4O4XWGVA.js +0 -2
- package/dist/tool-CBDT7TAG.js +0 -6
- package/dist/tool-CBDT7TAG.js.map +0 -1
- package/dist/wiki-enrich-XBRY4ZRD.js +0 -2
- /package/dist/{App-VHAMAS4I.js.map → App-N2U77ED7.js.map} +0 -0
- /package/dist/{ChatApp-Y7HOL45K.js.map → ChatApp-GYVTECF6.js.map} +0 -0
- /package/dist/{absorb-eval-VHACXZ3H.js.map → absorb-eval-WSAVWCZV.js.map} +0 -0
- /package/dist/{agent-M7ID5U3U.js.map → agent-SLRGXBMK.js.map} +0 -0
- /package/dist/{chunk-S3LWGPNZ.js.map → chunk-2GJFLPS7.js.map} +0 -0
- /package/dist/{chunk-TFNBUHRW.js.map → chunk-3LAQ74H7.js.map} +0 -0
- /package/dist/{chunk-3NKCOZQO.js.map → chunk-45TKVOJX.js.map} +0 -0
- /package/dist/{chunk-B5744ATM.js.map → chunk-4JHMV3D3.js.map} +0 -0
- /package/dist/{chunk-IFUN332M.js.map → chunk-CZWEXRIE.js.map} +0 -0
- /package/dist/{chunk-2TLOZN7P.js.map → chunk-DFWPTNZ5.js.map} +0 -0
- /package/dist/{chunk-5SEUBOUH.js.map → chunk-EX4AREN5.js.map} +0 -0
- /package/dist/{chunk-QTS7MHIA.js.map → chunk-GOAMSJ2D.js.map} +0 -0
- /package/dist/{chunk-6YPJVGYF.js.map → chunk-JGX5IWNX.js.map} +0 -0
- /package/dist/{chunk-YXFTUOZR.js.map → chunk-LLJZMS23.js.map} +0 -0
- /package/dist/{chunk-ZWL44UJS.js.map → chunk-LVQHNTXV.js.map} +0 -0
- /package/dist/{chunk-UNIXYV3U.js.map → chunk-M7527M7D.js.map} +0 -0
- /package/dist/{chunk-NFI4BCCO.js.map → chunk-MXWAREAQ.js.map} +0 -0
- /package/dist/{chunk-7H7LUBPC.js.map → chunk-Q73QQPDB.js.map} +0 -0
- /package/dist/{chunk-25XO6P7W.js.map → chunk-QI4FYJGJ.js.map} +0 -0
- /package/dist/{chunk-KEBTFUOZ.js.map → chunk-R5VGRKUW.js.map} +0 -0
- /package/dist/{chunk-SLJFY2AQ.js.map → chunk-RGBQULXX.js.map} +0 -0
- /package/dist/{chunk-GPCBOXZM.js.map → chunk-RV4WJZW5.js.map} +0 -0
- /package/dist/{chunk-7Y3FEIX7.js.map → chunk-SFUNL7RV.js.map} +0 -0
- /package/dist/{chunk-EMA67BJ5.js.map → chunk-SVNYI6GR.js.map} +0 -0
- /package/dist/{chunk-MLLMBE2W.js.map → chunk-UII54DAP.js.map} +0 -0
- /package/dist/{chunk-JH6C2Y5D.js.map → chunk-UNZRZOED.js.map} +0 -0
- /package/dist/{chunk-3E4AEZ2J.js.map → chunk-VYLMC5DO.js.map} +0 -0
- /package/dist/{client-ISQF24N5.js.map → client-B7FXDTYK.js.map} +0 -0
- /package/dist/{config-Z64VWERV.js.map → config-MLP33NWR.js.map} +0 -0
- /package/dist/{config-mutate-YZ3IH6CJ.js.map → config-mutate-XQRUAVEH.js.map} +0 -0
- /package/dist/{config-mutator-3HTNXOPU.js.map → config-mutator-5JFTMQ43.js.map} +0 -0
- /package/dist/{contributions-FQGIRD44.js.map → contributions-ESUSYUQG.js.map} +0 -0
- /package/dist/{doctor-FMW2SSCN.js.map → doctor-DIYZGSIS.js.map} +0 -0
- /package/dist/{failure-candidates-XZSZP4HT.js.map → failure-candidates-NQX2JMMS.js.map} +0 -0
- /package/dist/{follow-up-tool-I7PPM2R5.js.map → follow-up-tool-QXEYEWX7.js.map} +0 -0
- /package/dist/{grants-CEUGZBQ4.js.map → grants-MM7U6GCK.js.map} +0 -0
- /package/dist/{hub-Z3X4LWMI.js.map → hub-OC7PH2AR.js.map} +0 -0
- /package/dist/{promote-7YUBVKRC.js.map → promote-SIC6RWCX.js.map} +0 -0
- /package/dist/{query-TMALX5BE.js.map → query-OP2MSR2G.js.map} +0 -0
- /package/dist/{query-settings-3PEV5GIH.js.map → query-settings-67332F5V.js.map} +0 -0
- /package/dist/{registry-6ZIBMOZQ.js.map → registry-G2ZG5TIN.js.map} +0 -0
- /package/dist/{request-planner-GLNHHQXA.js.map → request-planner-THTPE3FP.js.map} +0 -0
- /package/dist/{review-candidates-DJXNTOG6.js.map → review-candidates-7PS7CHXF.js.map} +0 -0
- /package/dist/{routing-YWP2AF56.js.map → routing-PE7DB7WA.js.map} +0 -0
- /package/dist/{schedule-tool-4O4XWGVA.js.map → schedule-tool-3FXRKJGN.js.map} +0 -0
- /package/dist/{scheduler-HK362U53.js.map → scheduler-YTXCIQTZ.js.map} +0 -0
- /package/dist/{seat-4PPUZXFN.js.map → seat-6DMK4274.js.map} +0 -0
- /package/dist/{select-ULCUVM7N.js.map → select-CLDYPAOS.js.map} +0 -0
- /package/dist/{set-enabled-G4PLW5WK.js.map → set-enabled-ZH2DH4GU.js.map} +0 -0
- /package/dist/{wiki-32G6TGCI.js.map → traces-GVEQZX24.js.map} +0 -0
- /package/dist/{wiki-enrich-XBRY4ZRD.js.map → wiki-CH2HYJVI.js.map} +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{Ka as l,Sa as m}from"./chunk-DPIQCHXB.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-CZWEXRIE.js.map
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{e as g}from"./chunk-
|
|
1
|
+
import{e as g}from"./chunk-2GJFLPS7.js";async function $(e,i,r,n,o={}){let a=o.retrieve??g,c=o.perEntry??2,s=o.maxArticles??8,f=o.queryChars??1500,h=new Map;for(let t of e){let l=[];try{l=await a(t.content.slice(0,f),i,r,{pool:n,maxCandidates:c,maxHops:1,maxArticles:c*2,stage:"absorb-covering"})}catch{}for(let u of l){let p=h.get(u.path);p?(p.hop=Math.min(p.hop,u.hop),p.entries.includes(t.id)||p.entries.push(t.id)):h.set(u.path,{...u,entries:[t.id]})}}return[...h.values()].sort((t,l)=>t.hop-l.hop||l.entries.length-t.entries.length||t.path.localeCompare(l.path)).slice(0,s)}function x(e){if(e.length===0)return"";let i=e.map(r=>{let n=`### ${r.meta.title} \u2014 ${r.meta.type||"untyped"} (${r.path})`,o=`Found for entries: ${r.entries.join(", ")}${r.hop>0?" (linked from a match)":""}`,a=r.meta.related?.length?`Related: ${r.meta.related.join(", ")}`:"";return[n,o,a,"",r.content].filter(c=>c!=="").join(`
|
|
2
2
|
`)}).join(`
|
|
3
3
|
|
|
4
4
|
---
|
|
@@ -14,4 +14,4 @@ These ${e.length} articles were found by searching the wiki for each entry below
|
|
|
14
14
|
|
|
15
15
|
${i}
|
|
16
16
|
`}var d=/\bhttps?:\/\/[^\s<>"'`)\]]+/gi,y=/\[\[([^\]|]+)(?:\|[^\]]*)?\]\]/g,w=/(?<![\w-])(?=[0-9a-f]*[0-9])(?=[0-9a-f]*[a-f])[0-9a-f]{7,40}(?![\w-])/gi,k=/(?<![\w.,:/-])\d+(?:[.,:\-/]\d+)*(?![\w])/g,C=/^\s*\d+[.)]\s/gm;function A(e){let i=[...e.matchAll(d)].map(s=>s[0].replace(/[.,;:!?'"]+$/,"")),r=[...e.matchAll(y)].map(s=>s[1].trim()),n=e.replace(d," ").replace(y," ").replace(C," "),o=[...n.matchAll(w)].map(s=>s[0].toLowerCase()),c=[...n.replace(w," ").matchAll(k)].map(s=>v(s[0]));return{commits:m(o),urls:m(i),wikilinks:m(r),numbers:m(c)}}function R(e,i){let r=A(e),n=A(i),o=i.toLowerCase(),a=[];for(let t of r.commits)!o.includes(t)&&!n.commits.some(l=>l.startsWith(t)||t.startsWith(l))&&a.push(`commit ${t}`);let c=t=>t.toLowerCase().replace(/\/+$/,""),s=new Set(n.urls.map(c));for(let t of r.urls)s.has(c(t))||a.push(`link ${t}`);let f=new Set(n.wikilinks.map(t=>t.toLowerCase()));for(let t of r.wikilinks)f.has(t.toLowerCase())||a.push(`link [[${t}]]`);let h=new Set(n.numbers);for(let t of r.numbers)h.has(t)||a.push(`number ${t}`);return a}function v(e){return/^\d{1,3}(,\d{3})+$/.test(e)?e.replace(/,/g,""):e}function m(e){return[...new Set(e)]}function L(e,i){if(i.some(n=>n.path===e.path))return e.path;let r=b(e.title);return r?i.find(n=>b(n.title)===r)?.path??e.path:e.path}function b(e){return(e||"").toLowerCase().replace(/[^\p{L}\p{N}]+/gu," ").trim()}export{$ as a,x as b,A as c,R as d,L as e};
|
|
17
|
-
//# sourceMappingURL=chunk-
|
|
17
|
+
//# sourceMappingURL=chunk-DFWPTNZ5.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import{a as w,b as f}from"./chunk-WBG6NA7R.js";import{randomUUID as _}from"crypto";function p(r){return r.status&&r.status!=="ringing"?", waiting_since=NULL, told_at=NULL":""}var E=["ringing","answered","later"],A=`id, agent_id AS agentId, kind, reason, urgency, status, created_at AS createdAt,
|
|
2
|
+
ringing_since AS ringingSince, answered_at AS answeredAt, ended_at AS endedAt,
|
|
3
|
+
ring_again_at AS ringAgainAt, note, summary, channel, chat_id AS chatId`,y=class{constructor(t){this.db=t;t.exec(`CREATE TABLE IF NOT EXISTS calls (
|
|
4
|
+
id TEXT PRIMARY KEY, agent_id TEXT NOT NULL, reason TEXT NOT NULL,
|
|
5
|
+
urgency TEXT NOT NULL, status TEXT NOT NULL, created_at INTEGER NOT NULL,
|
|
6
|
+
ringing_since INTEGER, answered_at INTEGER, ended_at INTEGER,
|
|
7
|
+
ring_again_at INTEGER, note TEXT, summary TEXT);
|
|
8
|
+
CREATE INDEX IF NOT EXISTS calls_recent ON calls(created_at DESC);`);let e=new Set(t.prepare("PRAGMA table_info(calls)").all().map(n=>n.name));e.has("kind")||t.exec("ALTER TABLE calls ADD COLUMN kind TEXT NOT NULL DEFAULT 'voice'"),e.has("channel")||t.exec("ALTER TABLE calls ADD COLUMN channel TEXT"),e.has("chat_id")||t.exec("ALTER TABLE calls ADD COLUMN chat_id TEXT"),e.has("waiting_since")||t.exec("ALTER TABLE calls ADD COLUMN waiting_since INTEGER"),e.has("told_at")||t.exec("ALTER TABLE calls ADD COLUMN told_at INTEGER")}waiting(){let t=this.db.prepare("SELECT id, waiting_since AS since, told_at AS toldAt FROM calls WHERE waiting_since IS NOT NULL AND status='ringing'").all();return new Map(t.map(({id:e,since:n,toldAt:i})=>[e,{since:n,toldAt:i??n}]))}setWaiting(t,e){this.db.prepare("UPDATE calls SET waiting_since=?, told_at=? WHERE id=?").run(e?.since??null,e?.toldAt??null,t)}insert(t){this.db.prepare(`INSERT INTO calls (id, agent_id, kind, reason, urgency, status, created_at, ringing_since,
|
|
9
|
+
answered_at, ended_at, ring_again_at, note, summary, channel, chat_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(t.id,t.agentId,t.kind,t.reason,t.urgency,t.status,t.createdAt,t.ringingSince,t.answeredAt,t.endedAt,t.ringAgainAt,t.note,t.summary,t.channel,t.chatId)}get(t){return this.db.prepare(`SELECT ${A} FROM calls WHERE id=?`).get(t)}update(t,e){let n={reason:"reason",urgency:"urgency",status:"status",ringingSince:"ringing_since",answeredAt:"answered_at",endedAt:"ended_at",ringAgainAt:"ring_again_at",note:"note",summary:"summary"},i=Object.keys(e).filter(a=>a in n);if(i.length){let a=i.map(s=>`${n[s]}=?`).join(", ")+p(e);this.db.prepare(`UPDATE calls SET ${a} WHERE id=?`).run(...i.map(s=>e[s]??null),t)}return this.get(t)}transition(t,e,n){let i=Array.isArray(e)?e:[e],a={status:"status",ringingSince:"ringing_since",answeredAt:"answered_at",endedAt:"ended_at",ringAgainAt:"ring_again_at",note:"note",summary:"summary"},s=Object.keys(n).filter(l=>l in a),o=s.map(l=>`${a[l]}=?`).join(", ")+p(n),u=i.map(()=>"?").join(",");return this.db.prepare(`UPDATE calls SET ${o} WHERE id=? AND status IN (${u})`).run(...s.map(l=>n[l]??null),t,...i).changes?this.get(t):void 0}list(t={}){let e=Math.min(Math.max(t.limit??50,1),200),n=[],i=[];t.status?.length&&(n.push(`status IN (${t.status.map(()=>"?").join(",")})`),i.push(...t.status)),t.kind&&(n.push("kind=?"),i.push(t.kind));let a=n.length?`WHERE ${n.join(" AND ")}`:"";return this.db.prepare(`SELECT ${A} FROM calls ${a} ORDER BY created_at DESC, rowid DESC LIMIT ?`).all(...i,e)}countSince(t,e){return this.db.prepare("SELECT COUNT(*) AS n FROM calls WHERE agent_id=? AND created_at>?").get(t,e).n}};var C=200,k=1e4,I=2*36e5,$=10,T=240;function D(r=process.env){return r.AGENTX_TASK_ID?{"X-AgentX-Task":r.AGENTX_TASK_ID}:r.AGENTX_CHANNEL&&r.AGENTX_CHAT_ID?{"X-AgentX-Channel":r.AGENTX_CHANNEL,"X-AgentX-Chat":r.AGENTX_CHAT_ID}:{}}function N(r){return`[Call answered] The owner just picked up the voice call you asked for. You called because: "${r.reason}". Speak first: greet them in a few words and say why you called, then let them answer.`}var M="[Call ended] The owner hung up. In two or three plain sentences, summarise what was said and decided on this call, for your records. Reply with the summary only.",S=class{constructor(t){this.deps=t;this.waiting=t.store.waiting()}widgetSeenAt=0;widget={};waiting;setWaiting(t,e){if(e)this.waiting.set(t,e);else if(!this.waiting.delete(t))return;this.deps.store.setWaiting(t,e)}now(){return this.deps.now?.()??Date.now()}name(t){return this.deps.agentName(t)??t}async request(t,e={}){let n=String(t.agentId??"").trim(),i=String(t.reason??"").replace(/\s+/g," ").trim(),a=t.urgency==="urgent"?"urgent":"normal",s=t.kind==="camera"?"camera":"voice";if(t.kind!==void 0&&t.kind!=="voice"&&t.kind!=="camera")return{ok:!1,status:400,error:"kind must be voice or camera"};if(!n||!i)return{ok:!1,status:400,error:"Required: agentId and reason"};if(i.length>C)return{ok:!1,status:413,error:`reason is over ${C} characters; keep it to one line`};if(this.deps.agentName(n)===null)return{ok:!1,status:404,error:`Unknown agent: ${n}`};if(!this.deps.isRunningTurn(n,e))return{ok:!1,status:403,error:`No running turn of ${n} placed this call. An agent calls from inside its own run.`};let o=this.deps.config();if(!o.allow.includes("*")&&!o.allow.includes(n))return{ok:!1,status:403,error:`${n} ${s==="camera"?"may not ask to see through the owner's camera":"may not call the owner"}. Add it to calls.allow in agentx.json (or: agentx call allow ${n}).`};if(await this.sweep(),this.deps.store.list({status:E,limit:200}).some(m=>m.agentId===n))return{ok:!1,status:409,error:`${n} already has a call in progress`};let u=this.now();if(this.deps.store.countSince(n,u-36e5)>=o.maxPerHour)return{ok:!1,status:429,error:`${n} has placed ${o.maxPerHour} calls in the last hour (calls.maxPerHour)`};let g=s==="camera"?this.deps.turnSession?.(n,e)??e:{},l={id:`call-${_().slice(0,8)}`,agentId:n,kind:s,reason:i,urgency:a,status:"ringing",createdAt:u,ringingSince:u,answeredAt:null,endedAt:null,ringAgainAt:null,note:null,summary:null,channel:g.channel&&g.chatId?g.channel:null,chatId:g.channel&&g.chatId?g.chatId:null};this.deps.store.insert(l),this.deps.log?.(`[calls] ${n} asks ${s==="camera"?"to see":"for a call"} (${a}): ${i}`);let d=await this.ring(l),h=this.deps.store.get(l.id);return d!=="waiting"?{ok:!0,call:h,rang:d}:this.waiting.has(l.id)?{ok:!0,call:h,rang:d,busy:this.busyInfo(l)}:{ok:!0,call:h,rang:"widget"}}async cancel(t,e={}){let n=String(t.agentId??"").trim();if(!n)return{ok:!1,status:400,error:"Required: agentId"};if(!this.deps.isRunningTurn(n,e))return{ok:!1,status:403,error:`No running turn of ${n} asked for this. An agent withdraws its own call from inside its run.`};let i=this.deps.store.list({status:["ringing","later"],kind:"voice",limit:200}).find(s=>s.agentId===n);if(!i)return{ok:!1,status:404,error:`${n} has no call waiting or ringing`};let a=this.deps.store.transition(i.id,["ringing","later"],{status:"ended",endedAt:this.now(),ringingSince:null,ringAgainAt:null,note:b});return a?(this.setWaiting(i.id,null),this.deps.log?.(`[calls] ${i.id} withdrawn by ${n}`),{ok:!0,call:a}):{ok:!1,status:409,error:`call is ${this.deps.store.get(i.id)?.status}`}}async ring(t){let e=(this.deps.focus??w)();return e.active&&t.urgency!=="urgent"?(this.deps.store.transition(t.id,"ringing",{status:"missed",ringingSince:null,note:f(e)})&&await this.notice(t,this.missedTitle(t)),!1):t.kind==="camera"?(await this.notice(t,`${this.name(t.agentId)} wants to see through your camera`,`Open the phone app and tap Show within ${this.deps.config().ringSeconds} s.`),"notify"):this.widgetAlive()?!this.widget.busy&&!this.otherCalls(t).length?"widget":(this.startWaiting(t),await this.callWaiting(t),"waiting"):(await this.notice(t,`${this.name(t.agentId)} is calling`,`Open AgentX Voice to answer within ${this.deps.config().ringSeconds} s.`),"notify")}async notice(t,e,n){try{await this.deps.alert({title:e,message:n?`${t.reason}
|
|
10
|
+
${n}`:t.reason,urgent:t.urgency==="urgent",from:t.agentId})}catch(i){this.deps.log?.(`[calls] notice for ${t.id} failed: ${i?.message??i}`)}}startWaiting(t){let e=this.now();this.setWaiting(t.id,{since:e,toldAt:e}),this.deps.log?.(`[calls] ${t.id} from ${t.agentId} waits: the widget is busy`)}otherCalls(t){return this.deps.store.list({status:["ringing","answered"],kind:"voice",limit:200}).filter(e=>e.id!==t.id).reverse()}async callWaiting(t){let e=this.deps.config().busy;await this.notice(t,e.title.replaceAll("{agent}",this.name(t.agentId)),e.notice)}busyInfo(t){let e=this.deps.config().busy,{since:n,toldAt:i}=this.waiting.get(t.id)??{since:this.now(),toldAt:this.now()},a=this.otherCalls(t),s=a.find(d=>d.status==="answered"),o=a.find(d=>d.id===this.widget.showing)??a.find(d=>!this.waiting.has(d.id)),u=n+e.maxWaitMinutes*6e4+this.deps.config().ringSeconds*1e3,g=i+e.retryMinutes*6e4,l=n+e.maxWaitMinutes*6e4;return{why:s?"call":o?"ringing":"voice",with:s?this.name(s.agentId):o?this.name(o.agentId):null,remindAt:g<l?g:null,waitsUntil:u}}missedTitle(t){return t.kind==="camera"?`${this.name(t.agentId)} asked to see through your camera`:`Missed call from ${this.name(t.agentId)}`}inCall(){if(this.widgetAlive()&&this.widget.busy)return"the widget is busy";let t=this.now()-I,e=this.deps.store.list({status:["answered"],limit:5}).find(n=>(n.answeredAt??0)>=t);return e?`on a call with ${this.name(e.agentId)}`:null}widgetAlive(){return this.now()-this.widgetSeenAt<k}async ringing(t={}){return this.widgetSeenAt=this.now(),this.widget=t,await this.hold(t),await this.sweep(),this.deps.store.list({status:["ringing"],kind:"voice",limit:20}).reverse()}async hold(t){let e=this.now(),n=this.deps.config().busy,i=[],a=this.deps.store.list({status:["ringing"],kind:"voice",limit:20});for(let s of[...this.waiting.keys()])a.some(o=>o.id===s)||this.setWaiting(s,null);for(let s of a){if(s.id===t.showing){this.waiting.has(s.id)&&(this.setWaiting(s.id,null),this.deps.store.transition(s.id,"ringing",{ringingSince:e}));continue}if(!t.busy)continue;let o=this.waiting.get(s.id);if(!(o&&e-o.since>=n.maxWaitMinutes*6e4)&&this.deps.store.transition(s.id,"ringing",{ringingSince:e})){if(!o)this.startWaiting(s);else if(e-o.toldAt>=n.retryMinutes*6e4)this.setWaiting(s.id,{since:o.since,toldAt:e});else continue;i.push(s)}}for(let s of i)await this.callWaiting(s)}async asking(){return await this.sweep(),this.deps.store.list({status:["ringing"],kind:"camera",limit:20}).reverse()}async sweep(){let t=this.now(),e=this.deps.config().ringSeconds*1e3,n=this.deps.config().maxCallMinutes*6e4;for(let i of this.deps.store.list({status:["ringing","later","answered"],limit:200}))if(i.status==="ringing"&&i.ringingSince!==null&&t-i.ringingSince>=e){if(!this.deps.store.transition(i.id,"ringing",{status:"missed",ringingSince:null,note:this.waiting.has(i.id)?"widget busy":"not answered"}))continue;this.deps.log?.(`[calls] ${i.id} from ${i.agentId} missed`),await this.notice(i,this.missedTitle(i))}else if(i.status==="answered"&&i.answeredAt!==null&&t-i.answeredAt>=n){if(!this.deps.store.transition(i.id,"answered",{status:"ended",endedAt:t,note:"no hang-up"}))continue;this.deps.log?.(`[calls] ${i.id} from ${i.agentId} ended: no hang-up in ${this.deps.config().maxCallMinutes} min`)}else if(i.status==="later"&&i.ringAgainAt!==null&&t>=i.ringAgainAt){let a=this.deps.store.transition(i.id,"later",{status:"ringing",ringingSince:t,ringAgainAt:null});if(!a)continue;this.setWaiting(i.id,null),await this.ring(a)}}answer(t){let e=this.deps.store.get(t);if(!e)return c(t);let n=this.deps.store.transition(t,"ringing",{status:"answered",answeredAt:this.now(),ringingSince:null});return n?(this.deps.log?.(`[calls] ${t} answered (${e.agentId})`),n.kind==="camera"?{ok:!0,call:n}:{ok:!0,call:n,opener:N(n)}):{ok:!1,status:409,error:`call is ${this.deps.store.get(t)?.status}, not ringing`}}decline(t){if(!this.deps.store.get(t))return c(t);let n=this.deps.store.transition(t,["ringing","later"],{status:"declined",endedAt:this.now(),ringingSince:null,ringAgainAt:null});return n?{ok:!0,call:n}:{ok:!1,status:409,error:`call is ${this.deps.store.get(t)?.status}`}}later(t,e){if(!this.deps.store.get(t))return c(t);let i=e===void 0?$:Number(e);if(!Number.isInteger(i)||i<1||i>T)return{ok:!1,status:400,error:`minutes must be a whole number from 1 to ${T}`};let a=this.deps.store.transition(t,"ringing",{status:"later",ringingSince:null,ringAgainAt:this.now()+i*6e4});return a?{ok:!0,call:a}:{ok:!1,status:409,error:`call is ${this.deps.store.get(t)?.status}, not ringing`}}hangup(t){let e=this.deps.store.get(t);if(!e)return c(t);let n=this.deps.store.transition(t,"answered",{status:"ended",endedAt:this.now()});if(!n)return{ok:!1,status:409,error:`call is ${e.status}, not answered`};this.deps.log?.(`[calls] ${t} ended (${e.agentId})`);let i=this.deps.summarize;if(n.kind==="camera"||!this.deps.config().summary||!i)return{ok:!0,call:n};let a=(async()=>{try{let s=(await i(n))?.trim();if(!s)return;let o=this.deps.store.update(t,{summary:s});this.deps.file?.(o,s)}catch(s){this.deps.log?.(`[calls] summary for ${t} failed: ${s?.message??s}`)}})();return{ok:!0,call:n,summarized:a}}endCamera(t,e){let n=this.deps.store.get(t);if(!n||n.kind!=="camera")return!1;let i=this.deps.store.transition(t,"answered",{status:"ended",endedAt:this.now(),note:e??null});return i&&this.deps.log?.(`[calls] ${t} ended (${n.agentId}): ${e??"share over"}`),!!i}get(t){return this.deps.store.get(t)}list(t={}){return this.deps.store.list(t)}},b="withdrawn by the caller";function W(r,t=Date.now(),e){let n=a=>`${new Date(a).toLocaleTimeString("en-GB",{hour:"2-digit",minute:"2-digit",hour12:!1,timeZoneName:"short",timeZone:e})} (in ${Math.max(1,Math.round((a-t)/6e4))} min)`;return`${r.why==="call"?`The owner is busy in another call${r.with?` (with ${r.with})`:""}.`:r.why==="ringing"?`The owner is busy: another call${r.with?` (from ${r.with})`:""} is ringing.`:"The owner is busy on the voice widget."} They were told you are calling, and why. Your call waits and rings by itself as soon as they are free: do not call again. `+(r.remindAt!==null?`Still busy at ${n(r.remindAt)}, they are told again that you are waiting. `:"")+`It waits until ${n(r.waitsUntil)} at most, then counts as a missed call.`}var c=r=>({ok:!1,status:404,error:`No such call: ${r}`});export{y as a,D as b,M as c,S as d,W as e};
|
|
11
|
+
//# sourceMappingURL=chunk-DG2DC43E.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/calls/service.ts","../src/calls/store.ts"],"sourcesContent":["// --- An agent rings the owner (#321) ---\n//\n// An agent that needs the owner directly asks for a call. It rings on the\n// desktop widget (AgentX Voice), which polls `ringing()`. Answering starts\n// the widget's hands-free conversation with that agent, which opens with\n// its reason. When the widget is not running, `notify` stands in. While\n// the widget is busy (a turn, the microphone, another call) the call waits\n// for it: its ring time does not run, and the owner gets a call-waiting\n// notice (#408), again every calls.busy.retryMinutes. The caller is told\n// the owner is busy, with whom, and until when its call waits (#969). It\n// rings by itself once the widget is free, oldest first; the caller may\n// withdraw it meanwhile (`cancel`).\n//\n// Checked before anything rings, in this order:\n// 0. the caller is that agent: a turn of it is running now, named by its\n// task id or by its channel and chat (see CallerProof),\n// 1. the owner allows this agent (calls.allow; empty allows nobody),\n// 2. the agent has no other call in progress,\n// 3. it is under calls.maxPerHour,\n// 4. Focus: a non-urgent call does not ring. It becomes a missed call,\n// and its notice goes through notify, which holds it until Focus\n// ends. Urgent calls ring through, as `notify --urgent` does.\n//\n// A camera ask (#325 phase 3, kind \"camera\") goes through the same checks\n// and the same table. It never rings the widget: the notice goes to the\n// phone through notify, and the phone app polls `asking()` to show a\n// Show / Decline bar. Answering it is the owner tapping Show; the camera\n// opens only then, and the share ends the ask (see camera-api.ts).\n\nimport { randomUUID } from \"crypto\"\nimport { focusLabel, readFocus, type FocusState } from \"@/notify/focus\"\nimport type { CallsConfig } from \"@/daemon/config\"\nimport { LIVE, type Call, type CallKind, type CallStore, type CallUrgency, type Waiting } from \"./store\"\n\nexport const REASON_MAX = 200\n/** The widget polls every 2 s; missing this long means it is not running. */\nexport const WIDGET_FRESH_MS = 10_000\n/** An answered call older than this holds nothing: one never marked ended\n * must not hold the owner's notices forever. */\nexport const CALL_HOLD_MAX_MS = 2 * 3_600_000\nexport const LATER_DEFAULT_MINUTES = 10\nexport const LATER_MAX_MINUTES = 240\n\n/** What a caller is told when the owner cannot take its call now (#969). */\nexport interface BusyInfo {\n /** \"call\": the owner is in a call; \"ringing\": another call is ringing;\n * \"voice\": AgentX Voice is in a turn of the owner's, or recording. */\n why: \"call\" | \"ringing\" | \"voice\"\n /** The agent the owner is in a call with, or whose call is ringing. */\n with: string | null\n /** When the owner is told again that this call waits, if still busy\n * then; null when the wait ends first. Ms since the epoch. */\n remindAt: number | null\n /** The call stops waiting then: still busy, it counts as missed. */\n waitsUntil: number\n}\n\nexport type CallResult =\n | { ok: true; call: Call }\n | { ok: false; status: number; error: string }\n\n/** What the request says about the turn placing it: the run's\n * AGENTX_TASK_ID, or, for a warm process that has none, its\n * AGENTX_CHANNEL and AGENTX_CHAT_ID. Loopback is not an identity. */\nexport interface CallerProof {\n taskId?: string\n channel?: string\n chatId?: string\n}\n\n/** The proof headers for a call placed from inside an agent's run. */\nexport function callerHeaders(env: NodeJS.ProcessEnv = process.env): Record<string, string> {\n if (env.AGENTX_TASK_ID) return { \"X-AgentX-Task\": env.AGENTX_TASK_ID }\n if (env.AGENTX_CHANNEL && env.AGENTX_CHAT_ID) {\n return { \"X-AgentX-Channel\": env.AGENTX_CHANNEL, \"X-AgentX-Chat\": env.AGENTX_CHAT_ID }\n }\n return {}\n}\n\n/** What the widget says with its poll. A widget from before #408 says nothing. */\nexport interface WidgetState {\n /** It cannot ring a new call now. */\n busy?: boolean\n /** The call ringing on it. */\n showing?: string\n}\n\nexport interface CallDeps {\n store: CallStore\n config: () => CallsConfig\n /** The agent's display name, or null when there is no such agent here. */\n agentName: (agentId: string) => string | null\n /** True when `proof` names a running turn of `agentId`. */\n isRunningTurn: (agentId: string, proof: CallerProof) => boolean\n /** The channel and chat of that running turn, when it has one, so a\n * camera ask is answered where it was asked. Unset: only a proof that\n * names them itself is kept. */\n turnSession?: (agentId: string, proof: CallerProof) => { channel?: string; chatId?: string } | null\n /** Tell the owner through notify. `urgent` passes Focus, like notify --urgent. */\n alert: (notice: { title: string; message: string; urgent: boolean; from: string }) => Promise<void>\n /** Ask the agent, in its voice session, to summarise the call. */\n summarize?: (call: Call) => Promise<string | null>\n /** File a finished summary where the dashboard shows it. */\n file?: (call: Call, summary: string) => void\n focus?: () => FocusState\n now?: () => number\n log?: (m: string) => void\n}\n\n/** What the widget sends through /ask when the owner answers, so the\n * agent speaks first and says why it called. */\nexport function openerPrompt(call: Pick<Call, \"reason\">): string {\n return \"[Call answered] The owner just picked up the voice call you asked for. \" +\n `You called because: \"${call.reason}\". Speak first: greet them in a few words ` +\n \"and say why you called, then let them answer.\"\n}\n\nexport const SUMMARY_PROMPT =\n \"[Call ended] The owner hung up. In two or three plain sentences, summarise what \" +\n \"was said and decided on this call, for your records. Reply with the summary only.\"\n\nexport class CallService {\n private widgetSeenAt = 0\n /** What the widget said with its last poll. */\n private widget: WidgetState = {}\n /** Calls waiting for a busy widget: since when, and when the owner was\n * last told. Written through to the store, so a restart keeps them. */\n private waiting: Map<string, Waiting>\n\n constructor(private deps: CallDeps) {\n this.waiting = deps.store.waiting()\n }\n\n private setWaiting(id: string, w: Waiting | null): void {\n if (w) this.waiting.set(id, w)\n else if (!this.waiting.delete(id)) return\n this.deps.store.setWaiting(id, w)\n }\n\n private now(): number { return this.deps.now?.() ?? Date.now() }\n private name(agentId: string): string { return this.deps.agentName(agentId) ?? agentId }\n\n /** An agent asks for a call, or to see (kind \"camera\"). 201 ringing, or\n * waiting for a busy owner (`rang: \"waiting\"`, with `busy`); 202 held\n * as missed (Focus). */\n async request(input: { agentId?: unknown; reason?: unknown; urgency?: unknown; kind?: unknown }, proof: CallerProof = {}): Promise<CallResult & { rang?: Rang; busy?: BusyInfo }> {\n const agentId = String(input.agentId ?? \"\").trim()\n const reason = String(input.reason ?? \"\").replace(/\\s+/g, \" \").trim()\n const urgency: CallUrgency = input.urgency === \"urgent\" ? \"urgent\" : \"normal\"\n const kind: CallKind = input.kind === \"camera\" ? \"camera\" : \"voice\"\n if (input.kind !== undefined && input.kind !== \"voice\" && input.kind !== \"camera\") return { ok: false, status: 400, error: \"kind must be voice or camera\" }\n if (!agentId || !reason) return { ok: false, status: 400, error: \"Required: agentId and reason\" }\n if (reason.length > REASON_MAX) return { ok: false, status: 413, error: `reason is over ${REASON_MAX} characters; keep it to one line` }\n if (this.deps.agentName(agentId) === null) return { ok: false, status: 404, error: `Unknown agent: ${agentId}` }\n if (!this.deps.isRunningTurn(agentId, proof)) {\n return { ok: false, status: 403, error: `No running turn of ${agentId} placed this call. An agent calls from inside its own run.` }\n }\n\n const cfg = this.deps.config()\n if (!cfg.allow.includes(\"*\") && !cfg.allow.includes(agentId)) {\n const verb = kind === \"camera\" ? \"may not ask to see through the owner's camera\" : \"may not call the owner\"\n return { ok: false, status: 403, error: `${agentId} ${verb}. Add it to calls.allow in agentx.json (or: agentx call allow ${agentId}).` }\n }\n await this.sweep()\n if (this.deps.store.list({ status: LIVE, limit: 200 }).some((c) => c.agentId === agentId)) {\n return { ok: false, status: 409, error: `${agentId} already has a call in progress` }\n }\n const now = this.now()\n if (this.deps.store.countSince(agentId, now - 3_600_000) >= cfg.maxPerHour) {\n return { ok: false, status: 429, error: `${agentId} has placed ${cfg.maxPerHour} calls in the last hour (calls.maxPerHour)` }\n }\n\n // Where the ask came from, for kind \"camera\": the turn's own chat, or\n // the one the proof names. A task run has neither.\n const session: { channel?: string; chatId?: string } = kind === \"camera\" ? (this.deps.turnSession?.(agentId, proof) ?? proof) : {}\n const call: Call = {\n id: `call-${randomUUID().slice(0, 8)}`, agentId, kind, reason, urgency, status: \"ringing\", createdAt: now,\n ringingSince: now, answeredAt: null, endedAt: null, ringAgainAt: null, note: null, summary: null,\n channel: session.channel && session.chatId ? session.channel : null,\n chatId: session.channel && session.chatId ? session.chatId : null,\n }\n this.deps.store.insert(call)\n this.deps.log?.(`[calls] ${agentId} asks ${kind === \"camera\" ? \"to see\" : \"for a call\"} (${urgency}): ${reason}`)\n const rang = await this.ring(call)\n const saved = this.deps.store.get(call.id)!\n if (rang !== \"waiting\") return { ok: true, call: saved, rang }\n // The widget may have come free while the owner was being told.\n return this.waiting.has(call.id) ? { ok: true, call: saved, rang, busy: this.busyInfo(call) } : { ok: true, call: saved, rang: \"widget\" }\n }\n\n /** The caller withdraws its own voice call while it waits or rings, for\n * example to send a message instead (#969). */\n async cancel(input: { agentId?: unknown }, proof: CallerProof = {}): Promise<CallResult> {\n const agentId = String(input.agentId ?? \"\").trim()\n if (!agentId) return { ok: false, status: 400, error: \"Required: agentId\" }\n if (!this.deps.isRunningTurn(agentId, proof)) {\n return { ok: false, status: 403, error: `No running turn of ${agentId} asked for this. An agent withdraws its own call from inside its run.` }\n }\n // No sweep first: a call back that is due must not ring just to be withdrawn.\n const mine = this.deps.store.list({ status: [\"ringing\", \"later\"], kind: \"voice\", limit: 200 }).find((c) => c.agentId === agentId)\n if (!mine) return { ok: false, status: 404, error: `${agentId} has no call waiting or ringing` }\n const ended = this.deps.store.transition(mine.id, [\"ringing\", \"later\"], { status: \"ended\", endedAt: this.now(), ringingSince: null, ringAgainAt: null, note: CANCELLED_NOTE })\n if (!ended) return { ok: false, status: 409, error: `call is ${this.deps.store.get(mine.id)?.status}` }\n this.setWaiting(mine.id, null)\n this.deps.log?.(`[calls] ${mine.id} withdrawn by ${agentId}`)\n return { ok: true, call: ended }\n }\n\n /** Ring now, wait for a busy widget, or hold as missed in Focus.\n * Returns how it rang. */\n private async ring(call: Call): Promise<Rang> {\n const focus = (this.deps.focus ?? readFocus)()\n if (focus.active && call.urgency !== \"urgent\") {\n if (!this.deps.store.transition(call.id, \"ringing\", { status: \"missed\", ringingSince: null, note: focusLabel(focus) })) return false\n await this.notice(call, this.missedTitle(call))\n return false\n }\n // A camera ask is answered on the phone, never on the widget.\n if (call.kind === \"camera\") {\n await this.notice(call, `${this.name(call.agentId)} wants to see through your camera`,\n `Open the phone app and tap Show within ${this.deps.config().ringSeconds} s.`)\n return \"notify\"\n }\n if (this.widgetAlive()) {\n // Busy by its last poll, or by a call placed since that it will show first.\n if (!this.widget.busy && !this.otherCalls(call).length) return \"widget\"\n this.startWaiting(call)\n await this.callWaiting(call)\n return \"waiting\"\n }\n await this.notice(call, `${this.name(call.agentId)} is calling`,\n `Open AgentX Voice to answer within ${this.deps.config().ringSeconds} s.`)\n return \"notify\"\n }\n\n private async notice(call: Call, title: string, extra?: string): Promise<void> {\n try {\n await this.deps.alert({\n title, message: extra ? `${call.reason}\\n${extra}` : call.reason,\n urgent: call.urgency === \"urgent\", from: call.agentId,\n })\n } catch (e: any) {\n this.deps.log?.(`[calls] notice for ${call.id} failed: ${e?.message ?? e}`)\n }\n }\n\n /** The call starts waiting for the busy widget. */\n private startWaiting(call: Call): void {\n const now = this.now()\n this.setWaiting(call.id, { since: now, toldAt: now })\n this.deps.log?.(`[calls] ${call.id} from ${call.agentId} waits: the widget is busy`)\n }\n\n /** Voice calls ringing or answered other than `call`, oldest first. */\n private otherCalls(call: Call): Call[] {\n return this.deps.store.list({ status: [\"ringing\", \"answered\"], kind: \"voice\", limit: 200 }).filter((c) => c.id !== call.id).reverse()\n }\n\n private async callWaiting(call: Call): Promise<void> {\n const busy = this.deps.config().busy\n await this.notice(call, busy.title.replaceAll(\"{agent}\", this.name(call.agentId)), busy.notice)\n }\n\n /** Why the owner cannot take `call` now, and how long it waits. */\n private busyInfo(call: Call): BusyInfo {\n const cfg = this.deps.config().busy\n const { since, toldAt } = this.waiting.get(call.id) ?? { since: this.now(), toldAt: this.now() }\n const others = this.otherCalls(call)\n const talking = others.find((c) => c.status === \"answered\")\n // The call on the pill, or the oldest one it has yet to show.\n const showing = others.find((c) => c.id === this.widget.showing) ?? others.find((c) => !this.waiting.has(c.id))\n // After the wait the ring time runs once more before it is missed.\n const waitsUntil = since + cfg.maxWaitMinutes * 60_000 + this.deps.config().ringSeconds * 1000\n const remindAt = toldAt + cfg.retryMinutes * 60_000\n const stopsWaiting = since + cfg.maxWaitMinutes * 60_000\n return {\n why: talking ? \"call\" : showing ? \"ringing\" : \"voice\",\n with: talking ? this.name(talking.agentId) : showing ? this.name(showing.agentId) : null,\n remindAt: remindAt < stopsWaiting ? remindAt : null,\n waitsUntil,\n }\n }\n\n private missedTitle(call: Call): string {\n return call.kind === \"camera\" ? `${this.name(call.agentId)} asked to see through your camera` : `Missed call from ${this.name(call.agentId)}`\n }\n\n /** Why the owner is in a call now, or null (#394, the call check): a\n * voice call answered in the last two hours and not ended, or a live\n * widget that says it is busy (a call, the microphone, a turn). */\n inCall(): string | null {\n if (this.widgetAlive() && this.widget.busy) return \"the widget is busy\"\n const since = this.now() - CALL_HOLD_MAX_MS\n const live = this.deps.store.list({ status: [\"answered\"], limit: 5 }).find((c) => (c.answeredAt ?? 0) >= since)\n return live ? `on a call with ${this.name(live.agentId)}` : null\n }\n\n /** True when the widget polled recently. */\n widgetAlive(): boolean {\n return this.now() - this.widgetSeenAt < WIDGET_FRESH_MS\n }\n\n /** The widget's poll: voice calls ringing now, oldest first. Marks it alive. */\n async ringing(widget: WidgetState = {}): Promise<Call[]> {\n this.widgetSeenAt = this.now()\n this.widget = widget\n await this.hold(widget)\n await this.sweep()\n return this.deps.store.list({ status: [\"ringing\"], kind: \"voice\", limit: 20 }).reverse()\n }\n\n /** A busy widget cannot ring: the ring time of each call it is not\n * showing starts over at every poll, for calls.busy.maxWaitMinutes at\n * most. The owner is told that the call waits, and again every\n * calls.busy.retryMinutes. The ring time starts over once more when the\n * widget first shows a call that waited. */\n private async hold(widget: WidgetState): Promise<void> {\n const now = this.now()\n const busy = this.deps.config().busy\n // Told after the loop: a poll that overlaps a slow notice must not\n // carry on from a list and a widget state that are out of date.\n const tell: Call[] = []\n const live = this.deps.store.list({ status: [\"ringing\"], kind: \"voice\", limit: 20 })\n for (const id of [...this.waiting.keys()]) if (!live.some((c) => c.id === id)) this.setWaiting(id, null)\n for (const call of live) {\n if (call.id === widget.showing) {\n if (this.waiting.has(call.id)) {\n this.setWaiting(call.id, null)\n this.deps.store.transition(call.id, \"ringing\", { ringingSince: now })\n }\n continue\n }\n if (!widget.busy) continue\n const held = this.waiting.get(call.id)\n if (held && now - held.since >= busy.maxWaitMinutes * 60_000) continue\n if (!this.deps.store.transition(call.id, \"ringing\", { ringingSince: now })) continue\n if (!held) this.startWaiting(call)\n else if (now - held.toldAt >= busy.retryMinutes * 60_000) this.setWaiting(call.id, { since: held.since, toldAt: now })\n else continue\n tell.push(call)\n }\n for (const call of tell) await this.callWaiting(call)\n }\n\n /** The phone app's poll: camera asks waiting for the owner, oldest first.\n * Does not count as the widget being up. */\n async asking(): Promise<Call[]> {\n await this.sweep()\n return this.deps.store.list({ status: [\"ringing\"], kind: \"camera\", limit: 20 }).reverse()\n }\n\n /** Unanswered calls become missed; \"later\" calls that came due ring\n * again; answered calls nobody hung up end after calls.maxCallMinutes. */\n async sweep(): Promise<void> {\n const now = this.now()\n const ringFor = this.deps.config().ringSeconds * 1000\n const talkFor = this.deps.config().maxCallMinutes * 60_000\n for (const call of this.deps.store.list({ status: [\"ringing\", \"later\", \"answered\"], limit: 200 })) {\n // The list was read before any await below: each move re-checks the\n // status in the store, so a call answered meanwhile is left alone.\n if (call.status === \"ringing\" && call.ringingSince !== null && now - call.ringingSince >= ringFor) {\n if (!this.deps.store.transition(call.id, \"ringing\", { status: \"missed\", ringingSince: null, note: this.waiting.has(call.id) ? \"widget busy\" : \"not answered\" })) continue\n this.deps.log?.(`[calls] ${call.id} from ${call.agentId} missed`)\n await this.notice(call, this.missedTitle(call))\n } else if (call.status === \"answered\" && call.answeredAt !== null && now - call.answeredAt >= talkFor) {\n if (!this.deps.store.transition(call.id, \"answered\", { status: \"ended\", endedAt: now, note: \"no hang-up\" })) continue\n this.deps.log?.(`[calls] ${call.id} from ${call.agentId} ended: no hang-up in ${this.deps.config().maxCallMinutes} min`)\n } else if (call.status === \"later\" && call.ringAgainAt !== null && now >= call.ringAgainAt) {\n const again = this.deps.store.transition(call.id, \"later\", { status: \"ringing\", ringingSince: now, ringAgainAt: null })\n if (!again) continue\n // A wait from before the snooze does not carry over.\n this.setWaiting(call.id, null)\n await this.ring(again)\n }\n }\n }\n\n /** The owner picked up, or tapped Show. Returns the call and, for a\n * voice call, the opener for /ask. */\n answer(id: string): CallResult & { opener?: string } {\n const call = this.deps.store.get(id)\n if (!call) return unknown(id)\n const answered = this.deps.store.transition(id, \"ringing\", { status: \"answered\", answeredAt: this.now(), ringingSince: null })\n if (!answered) return { ok: false, status: 409, error: `call is ${this.deps.store.get(id)?.status}, not ringing` }\n this.deps.log?.(`[calls] ${id} answered (${call.agentId})`)\n return answered.kind === \"camera\" ? { ok: true, call: answered } : { ok: true, call: answered, opener: openerPrompt(answered) }\n }\n\n decline(id: string): CallResult {\n const call = this.deps.store.get(id)\n if (!call) return unknown(id)\n const declined = this.deps.store.transition(id, [\"ringing\", \"later\"], { status: \"declined\", endedAt: this.now(), ringingSince: null, ringAgainAt: null })\n return declined ? { ok: true, call: declined } : { ok: false, status: 409, error: `call is ${this.deps.store.get(id)?.status}` }\n }\n\n /** \"Call back in N min\": it rings again then. */\n later(id: string, minutes?: unknown): CallResult {\n const call = this.deps.store.get(id)\n if (!call) return unknown(id)\n const n = minutes === undefined ? LATER_DEFAULT_MINUTES : Number(minutes)\n if (!Number.isInteger(n) || n < 1 || n > LATER_MAX_MINUTES) {\n return { ok: false, status: 400, error: `minutes must be a whole number from 1 to ${LATER_MAX_MINUTES}` }\n }\n const later = this.deps.store.transition(id, \"ringing\", { status: \"later\", ringingSince: null, ringAgainAt: this.now() + n * 60_000 })\n return later ? { ok: true, call: later } : { ok: false, status: 409, error: `call is ${this.deps.store.get(id)?.status}, not ringing` }\n }\n\n /** End an answered call. The summary is written in the background;\n * `summarized` settles when it is filed (tests wait on it). */\n hangup(id: string): CallResult & { summarized?: Promise<void> } {\n const call = this.deps.store.get(id)\n if (!call) return unknown(id)\n const ended = this.deps.store.transition(id, \"answered\", { status: \"ended\", endedAt: this.now() })\n if (!ended) return { ok: false, status: 409, error: `call is ${call.status}, not answered` }\n this.deps.log?.(`[calls] ${id} ended (${call.agentId})`)\n const summarize = this.deps.summarize\n // What the agent saw is already its answer; only a voice call is summarised.\n if (ended.kind === \"camera\" || !this.deps.config().summary || !summarize) return { ok: true, call: ended }\n const summarized = (async () => {\n try {\n const text = (await summarize(ended))?.trim()\n if (!text) return\n const done = this.deps.store.update(id, { summary: text })!\n this.deps.file?.(done, text)\n } catch (e: any) {\n this.deps.log?.(`[calls] summary for ${id} failed: ${e?.message ?? e}`)\n }\n })()\n return { ok: true, call: ended, summarized }\n }\n\n /** A camera ask ends when the share ends; no summary. Only an answered\n * ask ends this way, so a stop before Show leaves it ringing. */\n endCamera(id: string, note?: string): boolean {\n const call = this.deps.store.get(id)\n if (!call || call.kind !== \"camera\") return false\n const ended = this.deps.store.transition(id, \"answered\", { status: \"ended\", endedAt: this.now(), note: note ?? null })\n if (ended) this.deps.log?.(`[calls] ${id} ended (${call.agentId}): ${note ?? \"share over\"}`)\n return !!ended\n }\n\n get(id: string): Call | undefined { return this.deps.store.get(id) }\n\n list(opts: Parameters<CallStore[\"list\"]>[0] = {}): Call[] { return this.deps.store.list(opts) }\n}\n\n/** How a call reached the owner: on the widget, through notify, waiting\n * for a busy widget, or not at all (held as missed). */\nexport type Rang = \"widget\" | \"notify\" | \"waiting\" | false\n\n/** The note on a call its caller withdrew. */\nexport const CANCELLED_NOTE = \"withdrawn by the caller\"\n\n/** What the caller reads when its call waits for a busy owner. Times are\n * in the host's time zone (or `timeZone`), and say which one. */\nexport function busyText(busy: BusyInfo, now: number = Date.now(), timeZone?: string): string {\n const at = (ms: number) => `${new Date(ms).toLocaleTimeString(\"en-GB\", { hour: \"2-digit\", minute: \"2-digit\", hour12: false, timeZoneName: \"short\", timeZone })} (in ${Math.max(1, Math.round((ms - now) / 60_000))} min)`\n const why = busy.why === \"call\" ? `The owner is busy in another call${busy.with ? ` (with ${busy.with})` : \"\"}.`\n : busy.why === \"ringing\" ? `The owner is busy: another call${busy.with ? ` (from ${busy.with})` : \"\"} is ringing.`\n : \"The owner is busy on the voice widget.\"\n return `${why} They were told you are calling, and why. Your call waits and rings by itself as soon as they are free: do not call again. ` +\n (busy.remindAt !== null ? `Still busy at ${at(busy.remindAt)}, they are told again that you are waiting. ` : \"\") +\n `It waits until ${at(busy.waitsUntil)} at most, then counts as a missed call.`\n}\n\nconst unknown = (id: string): CallResult => ({ ok: false, status: 404, error: `No such call: ${id}` })\n","// --- Calls an agent placed to the owner, kept in SQLite ---\n//\n// One row per call, so a missed call is still there after a restart and\n// the dashboard can list them. Nothing else reads this table. A camera\n// ask (#325 phase 3: \"show me\") is a call of kind \"camera\": same\n// allowlist, same hourly limit, same lifecycle, answered on the phone\n// instead of the voice widget.\n\nimport type Database from \"better-sqlite3\"\n\nexport type CallStatus = \"ringing\" | \"answered\" | \"ended\" | \"declined\" | \"missed\" | \"later\"\nexport type CallUrgency = \"normal\" | \"urgent\"\n/** \"voice\": a live voice call on the widget. \"camera\": the agent asks to see through the phone camera. */\nexport type CallKind = \"voice\" | \"camera\"\n\nexport interface Call {\n id: string\n agentId: string\n kind: CallKind\n reason: string\n urgency: CallUrgency\n status: CallStatus\n /** The channel and chat the asking turn ran in, so what the agent sees\n * is answered there; null when the turn had none (a task run). */\n channel: string | null\n chatId: string | null\n /** When the agent asked, ms since the epoch. */\n createdAt: number\n /** When it last started ringing: at the request, or when \"later\" came due. */\n ringingSince: number | null\n answeredAt: number | null\n endedAt: number | null\n /** Status \"later\": when it rings again. */\n ringAgainAt: number | null\n /** Why it ended the way it did, in a few words (\"in Focus\", \"not answered\"). */\n note: string | null\n /** The agent's own summary, written after hang-up. */\n summary: string | null\n}\n\n/** A call waiting for a busy owner: since when, and when the owner was\n * last told. Ms since the epoch. */\nexport interface Waiting {\n since: number\n toldAt: number\n}\n\n/** A call that stops ringing (answered, snoozed, missed...) is no longer\n * waiting: the mark goes in the same write, so a call that rings again\n * later starts a fresh wait, even across a restart. */\nfunction leaving(patch: { status?: CallStatus }): string {\n return patch.status && patch.status !== \"ringing\" ? \", waiting_since=NULL, told_at=NULL\" : \"\"\n}\n\n/** Calls still in progress: an agent has at most one of these. */\nexport const LIVE: CallStatus[] = [\"ringing\", \"answered\", \"later\"]\n\nconst COLUMNS = `id, agent_id AS agentId, kind, reason, urgency, status, created_at AS createdAt,\n ringing_since AS ringingSince, answered_at AS answeredAt, ended_at AS endedAt,\n ring_again_at AS ringAgainAt, note, summary, channel, chat_id AS chatId`\n\nexport class CallStore {\n constructor(private db: Database.Database) {\n db.exec(`CREATE TABLE IF NOT EXISTS calls (\n id TEXT PRIMARY KEY, agent_id TEXT NOT NULL, reason TEXT NOT NULL,\n urgency TEXT NOT NULL, status TEXT NOT NULL, created_at INTEGER NOT NULL,\n ringing_since INTEGER, answered_at INTEGER, ended_at INTEGER,\n ring_again_at INTEGER, note TEXT, summary TEXT);\n CREATE INDEX IF NOT EXISTS calls_recent ON calls(created_at DESC);`)\n // Columns added since the table first shipped (#325 phase 3). A row\n // from before is a voice call from a turn whose chat was not kept.\n const have = new Set((db.prepare(\"PRAGMA table_info(calls)\").all() as Array<{ name: string }>).map((c) => c.name))\n if (!have.has(\"kind\")) db.exec(\"ALTER TABLE calls ADD COLUMN kind TEXT NOT NULL DEFAULT 'voice'\")\n if (!have.has(\"channel\")) db.exec(\"ALTER TABLE calls ADD COLUMN channel TEXT\")\n if (!have.has(\"chat_id\")) db.exec(\"ALTER TABLE calls ADD COLUMN chat_id TEXT\")\n // A call waiting for a busy owner (#969), kept so a restart does not\n // forget it: since when it waits, and when the owner was last told.\n if (!have.has(\"waiting_since\")) db.exec(\"ALTER TABLE calls ADD COLUMN waiting_since INTEGER\")\n if (!have.has(\"told_at\")) db.exec(\"ALTER TABLE calls ADD COLUMN told_at INTEGER\")\n }\n\n /** Ringing calls marked as waiting for a busy owner, by id. */\n waiting(): Map<string, Waiting> {\n const rows = this.db.prepare(\"SELECT id, waiting_since AS since, told_at AS toldAt FROM calls WHERE waiting_since IS NOT NULL AND status='ringing'\")\n .all() as Array<{ id: string } & Waiting>\n return new Map(rows.map(({ id, since, toldAt }) => [id, { since, toldAt: toldAt ?? since }]))\n }\n\n /** Mark a call as waiting, or clear the mark with null. */\n setWaiting(id: string, w: Waiting | null): void {\n this.db.prepare(\"UPDATE calls SET waiting_since=?, told_at=? WHERE id=?\").run(w?.since ?? null, w?.toldAt ?? null, id)\n }\n\n insert(call: Call): void {\n this.db.prepare(`INSERT INTO calls (id, agent_id, kind, reason, urgency, status, created_at, ringing_since,\n answered_at, ended_at, ring_again_at, note, summary, channel, chat_id) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(\n call.id, call.agentId, call.kind, call.reason, call.urgency, call.status, call.createdAt,\n call.ringingSince, call.answeredAt, call.endedAt, call.ringAgainAt, call.note, call.summary, call.channel, call.chatId)\n }\n\n get(id: string): Call | undefined {\n return this.db.prepare(`SELECT ${COLUMNS} FROM calls WHERE id=?`).get(id) as Call | undefined\n }\n\n /** Write the changed fields of one call. */\n update(id: string, patch: Partial<Omit<Call, \"id\" | \"agentId\" | \"createdAt\">>): Call | undefined {\n const cols: Record<string, string> = {\n reason: \"reason\", urgency: \"urgency\", status: \"status\", ringingSince: \"ringing_since\",\n answeredAt: \"answered_at\", endedAt: \"ended_at\", ringAgainAt: \"ring_again_at\", note: \"note\", summary: \"summary\",\n }\n const keys = Object.keys(patch).filter((k) => k in cols)\n if (keys.length) {\n const set = keys.map((k) => `${cols[k]}=?`).join(\", \") + leaving(patch)\n this.db.prepare(`UPDATE calls SET ${set} WHERE id=?`).run(...keys.map((k) => (patch as any)[k] ?? null), id)\n }\n return this.get(id)\n }\n\n /** Change a call only if it is still `from`: the check and the write are\n * one statement, so two sweeps, or a sweep and the owner answering,\n * cannot both move the same call. Undefined when it had moved on. */\n transition(id: string, from: CallStatus | CallStatus[], patch: Partial<Omit<Call, \"id\" | \"agentId\" | \"createdAt\">>): Call | undefined {\n const statuses = Array.isArray(from) ? from : [from]\n const cols: Record<string, string> = {\n status: \"status\", ringingSince: \"ringing_since\", answeredAt: \"answered_at\", endedAt: \"ended_at\",\n ringAgainAt: \"ring_again_at\", note: \"note\", summary: \"summary\",\n }\n const keys = Object.keys(patch).filter((k) => k in cols)\n const set = keys.map((k) => `${cols[k]}=?`).join(\", \") + leaving(patch)\n const marks = statuses.map(() => \"?\").join(\",\")\n const changed = this.db.prepare(`UPDATE calls SET ${set} WHERE id=? AND status IN (${marks})`)\n .run(...keys.map((k) => (patch as any)[k] ?? null), id, ...statuses).changes\n return changed ? this.get(id) : undefined\n }\n\n /** Newest first; only these statuses, and only this kind, when given. */\n list(opts: { status?: CallStatus[]; kind?: CallKind; limit?: number } = {}): Call[] {\n const limit = Math.min(Math.max(opts.limit ?? 50, 1), 200)\n const where: string[] = []\n const args: unknown[] = []\n if (opts.status?.length) {\n where.push(`status IN (${opts.status.map(() => \"?\").join(\",\")})`)\n args.push(...opts.status)\n }\n if (opts.kind) { where.push(\"kind=?\"); args.push(opts.kind) }\n const filter = where.length ? `WHERE ${where.join(\" AND \")}` : \"\"\n return this.db.prepare(`SELECT ${COLUMNS} FROM calls ${filter} ORDER BY created_at DESC, rowid DESC LIMIT ?`)\n .all(...args, limit) as Call[]\n }\n\n /** Calls this agent placed after `since` (ms), for the hourly limit. */\n countSince(agentId: string, since: number): number {\n return (this.db.prepare(\"SELECT COUNT(*) AS n FROM calls WHERE agent_id=? AND created_at>?\")\n .get(agentId, since) as { n: number }).n\n }\n}\n"],"mappings":"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","names":["randomUUID","leaving","patch","LIVE","COLUMNS","CallStore","db","have","c","rows","id","since","toldAt","w","call","cols","keys","k","set","from","statuses","marks","opts","limit","where","args","filter","agentId","REASON_MAX","WIDGET_FRESH_MS","CALL_HOLD_MAX_MS","LATER_DEFAULT_MINUTES","LATER_MAX_MINUTES","callerHeaders","env","openerPrompt","call","SUMMARY_PROMPT","CallService","deps","id","w","agentId","input","proof","reason","urgency","kind","cfg","LIVE","c","now","session","randomUUID","rang","saved","mine","ended","CANCELLED_NOTE","focus","readFocus","focusLabel","title","extra","e","busy","since","toldAt","others","talking","showing","waitsUntil","remindAt","stopsWaiting","live","widget","tell","held","ringFor","talkFor","again","unknown","answered","declined","minutes","n","later","summarize","summarized","text","done","note","opts","busyText","timeZone","at","ms"]}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import{b as oe,d as ae,h as Wt}from"./chunk-BRK6M6LA.js";import{l as ie,m as se,n as zt}from"./chunk-UIBUZ2N3.js";import{a as re,e as Bt}from"./chunk-2QNT5JDS.js";import{a as f,b as Ht}from"./chunk-QIJLFPWE.js";import{z as l}from"zod";var Kt,Xt,Gt,Vt,Zt,Yt,Jt,Qt,en,le,ue=f(()=>{"use strict";Kt=l.enum(["mon","tue","wed","thu","fri","sat","sun"]),Xt=l.object({days:l.array(Kt).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()}),Gt=l.object({title:l.string(),responsibilities:l.array(l.string()).default([]),sopPath:l.string().optional(),kpis:l.array(l.string()).default([])}),Vt=l.object({role:l.string(),reportsTo:l.string().optional(),schedule:Xt,utilizationTarget:l.number().min(0).max(1).default(.8)}),Zt=l.object({channel:l.string(),chatId:l.string(),accountId:l.string().optional()}),Yt=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")})]),Jt=l.object({id:l.string(),pm:l.string().optional(),client:l.string().optional()}),Qt=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()}),en=l.object({name:l.string().optional(),kind:l.enum(["client","internal","own"]).optional(),respondWithin:l.string().optional(),standing:l.array(l.string()).default([])}),le=l.object({enabled:l.boolean().default(!1),timezone:l.string().default("UTC"),mainChannel:Zt,workSource:Yt,roles:l.record(l.string(),Gt).default({}),orgChart:l.record(l.string(),Vt).default({}),projects:l.array(Jt).default([]),contactMap:l.array(Qt).default([]),clients:l.record(l.string(),en).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 d}from"zod";function Hr(r,t){let n=new Set(r.labels||[]);if(r.state==="closed"){let a=t.find(i=>i.kind==="closed");if(a)return a.id}for(let a of t)if(a.kind==="scoped-label"&&a.scopedLabel&&n.has(a.scopedLabel))return a.id;for(let a of t)if(a.kind==="label"&&a.mapsToLabel&&n.has(a.mapsToLabel))return a.id;let o=t.find(a=>a.kind==="open-backlog");if(o){let a=o.scopedPrefix+"::";return[...n].some(s=>s.startsWith(a))?null:o.id}return t[0]?.id??null}function Br(r,t){let n=[],o=[],a=!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"&&(a=!0),r&&(r.kind==="scoped-label"&&r.scopedLabel?o.push(r.scopedLabel):r.kind==="label"&&r.mapsToLabel?o.push(r.mapsToLabel):r.kind==="closed"&&t.kind!=="closed"&&(i=!0)),{addLabels:n.length?n:void 0,removeLabels:o.length?o:void 0,closeIssue:a||void 0,reopen:i||void 0}}var tn,nn,rn,on,an,sn,de,ln,ce,me=f(()=>{"use strict";tn=d.object({name:d.string(),color:d.string().regex(/^#[0-9a-f]{6}$/i).default("#6366f1"),description:d.string().optional()}),nn=d.object({id:d.string().regex(/^[a-z0-9_-]+$/,"column id must be lowercase slug"),title:d.string(),kind:d.enum(["open-backlog","scoped-label","closed","label"]).default("label"),mapsToLabel:d.string().optional(),scopedLabel:d.string().optional(),scopedPrefix:d.string().default("Status"),accent:d.string().optional()}),rn=d.object({enabled:d.boolean().default(!0),staleDoingMinutes:d.number().int().min(1).default(45),respectLunchBreak:d.boolean().default(!0),respectSchedule:d.boolean().default(!0),action:d.enum(["badge","notify"]).default("badge")}),on=d.discriminatedUnion("type",[d.object({type:d.literal("gitlab"),projects:d.array(d.string()).min(1)})]),an=[{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"}],sn=d.object({id:d.string().regex(/^[a-z0-9_-]+$/,"board id must be lowercase slug"),name:d.string(),source:on,primaryToolLabel:d.string().optional(),labels:d.array(tn).default([]),columns:d.array(nn).min(1).default(an),timeRangeDays:d.number().int().min(1).max(365).default(30),closedWindowDays:d.number().int().min(1).max(365).default(30),reconciliation:rn.default({})}),de=d.array(sn).default([]),ln=d.object({name:d.string(),url:d.string(),dashboardUrl:d.string().optional(),token:d.string().optional(),dashboardToken:d.string().optional()}),ce=d.object({enabled:d.boolean().default(!1),port:d.number().int().default(4202),bind:d.string().default("127.0.0.1"),token:d.string().optional(),daemonUrl:d.string().default("http://localhost:18800"),daemons:d.array(ln).default([]),draftAgent:d.string().optional()}).default({})});function gn(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 o=mn[n[n.length-1]]??n[n.length-1],a=new Set;for(let i of n.slice(0,-1)){let s=un[i];if(!s)return{ok:!1,error:`"${i}" is not a modifier; use ctrl, opt, shift or cmd`};if(a.has(s))return{ok:!1,error:`"${r}" names ${s} twice`};a.add(s)}return fn(o)?!fe(o)&&!["ctrl","opt","cmd"].some(i=>a.has(i))?{ok:!1,error:`"${r}" needs ctrl, opt or cmd, or it would take that key away from your typing`}:{ok:!0,value:[...dn.filter(i=>a.has(i)),o].join("+")}:{ok:!1,error:`"${o}" is not a key AgentX Voice can use; use a letter, a digit, f1 to f20, or space, period, comma, slash, return, tab`}}function ge(r){let t=gn(r);return t.ok?void 0:t.error}function zr(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"},o=r.split("+"),a=o.pop()??"";return o.map(i=>t[i]??i).join("")+(n[a]??a.toUpperCase())}var un,dn,cn,mn,fe,fn,O,pe=f(()=>{"use strict";un={ctrl:"ctrl",control:"ctrl",opt:"opt",option:"opt",alt:"opt",shift:"shift",cmd:"cmd",command:"cmd"},dn=["ctrl","opt","shift","cmd"],cn=["space","period","comma","slash","semicolon","quote","minus","equal","leftbracket","rightbracket","backslash","grave","return","tab"],mn={".":"period",",":"comma","/":"slash",";":"semicolon","'":"quote","-":"minus","=":"equal","`":"grave",enter:"return"},fe=r=>/^f([1-9]|1[0-9]|20)$/.test(r),fn=r=>/^[a-z0-9]$/.test(r)||cn.includes(r)||fe(r);O={talk:"opt+space",stop:"cmd+opt+period",paste:"cmd+opt+v"}});function xe(r){let t=/^#([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})$/i.exec(r);if(!t)return null;let[n,o,a]=t.slice(1).map(g=>parseInt(g,16)/255),i=Math.max(n,o,a),s=Math.min(n,o,a),u=i-s;return u<.04?null:((i===n?(o-a)/u%6:i===o?(a-n)/u+2:(n-o)/u+4)*60+360)%360}function pn(r){let t=xe(r);if(t===null)return T.find(a=>a.id==="lagoon");let n=T[0],o=360;for(let a of T){let i=Math.abs(a.hue-t),s=Math.min(i,360-i);s<o&&(n=a,o=s)}return n}function Xr(r,t=L){let n=s=>T.find(u=>u.id===s),o=n(r?.palette);if(o)return{...o,set:!0};let a=r?.color;return{...a&&xe(a)!==null?pn(a):n(t)??n(L),set:!1}}var T,he,be,G,L,ye=f(()=>{"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"]}],he=["orb","character"],be=["off","rarely","sometimes","often"],G=T.map(r=>r.id);L="lagoon"});function ke(r){let t=(r.match(/[-ۿ]/g)??[]).length,n=(r.match(/[a-zA-ZÀ-ÿ]/g)??[]).length;if(t&&t>=n)return"ar";let o=0,a=0;for(let i of r.toLowerCase().replace(/[’]/g,"'").match(/[a-zà-ÿœ']+/g)??[])hn.has(i)&&o++,bn.has(i)||i.startsWith("l'")||i.startsWith("d'")||i.startsWith("qu'")?a++:xn.test(i)&&(a+=.5);return o===a?null:o>a?"en":"fr"}var hn,bn,xn,we=f(()=>{"use strict";hn=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(" ")),bn=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(" ")),xn=/[éèêëàâçùûîïôœ]/});function Yr(r,t=V){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,o=>o.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 o=n.slice(0,t),a=([...n.slice(0,t+1).matchAll(/[.!?؟](?=\s)/g)].at(-1)?.index??-1)+1;n=(a>=t/2?o.slice(0,a):o.replace(/\s+\S*$/,"")+"...")+" "+yn[ke(o)??"en"]}return n}var V,yn,Se=f(()=>{"use strict";we();V=500,yn={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 eo(r,t=Z){let n=new Set(t.map(_e)),o=r.replace(/\[([^\][]*)\]|\(([^()]*)\)/g,(a,i,s)=>n.has(_e(i??s))?"":a);return!/[\p{L}\p{N}]/u.test(o)}var Z,Qr,_e,Ee=f(()=>{"use strict";Z=["background noise","noise","mumbling","babbling","unintelligible","inaudible","muffled speech","pause","silence","music","outro jingle","blank audio"],Qr="I didn't catch that.",_e=r=>r.toLowerCase().replace(/[_\s]+/g," ").trim()});import{randomBytes as kn}from"crypto";function Tn(r){try{return process.kill(r,0),!0}catch(t){return t?.code==="EPERM"}}function ro(r,t=Date.now()){if(!r.held)return"The floor is free.";let n=Math.max(0,Math.round(((r.expiresAt??t)-t)/1e3)),o=[r.sound?"plays sound":"",r.hide?"character and bubbles hidden":""].filter(Boolean).join(", ");return`The floor is held by ${r.holder}${o?` (${o})`:""} for up to ${n} more seconds: other lines wait until it is free.`}var N,wn,Sn,_n,En,Te,ve=f(()=>{"use strict";N={enabled:!0,defaultSeconds:120,maxSeconds:1800},wn=80,Sn=/^[a-f0-9]{24}$/,_n=25e3,En=2e3;Te=class{constructor(t={}){this.deps=t;this.now=t.now??Date.now,this.alive=t.alive??Tn}hold=null;seq=0;ended=null;expiry=null;watch=null;waiters=new Set;now;alive;get settings(){return{...N,...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,o=(A,w)=>({ok:!1,reason:A,error:w,view:this.view()});if(!n.enabled)return o("off","The floor is off on this computer (voice.floor.enabled)");let a=String(t.holder??"").replace(/\s+/g," ").trim().slice(0,wn),i=t.seconds??Math.min(n.defaultSeconds,n.maxSeconds),s=t.pid??null,u=t.id??kn(12).toString("hex");if(!Sn.test(u))return o("invalid","id is 24 hex characters, as a take returned it");if(!a||!Number.isFinite(i)||i<=0||i>n.maxSeconds||s!==null&&!(Number.isInteger(s)&&s>1))return o("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 o("held",`The floor is held by ${c.holder}`);let g=this.deps.busy?.();if(g)return o("busy",`The floor is taken by ${g}`);if(s!==null&&!this.alive(s))return o("invalid",`No process ${s} runs on this computer`);let b=this.now(),k={id:u,holder:a,agentId:t.agentId||null,sound:t.sound===!0,hide:t.hide===!0,pid:s,since:b,expiresAt:b+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 o=this.settings,a=n??Math.min(o.defaultSeconds,o.maxSeconds);return!Number.isFinite(a)||a<=0||a>o.maxSeconds?null:(this.hold.expiresAt=this.now()+a*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=_n,o){return t!==this.view().seq?Promise.resolve(this.view()):new Promise(a=>{let i=()=>{clearTimeout(s),this.waiters.delete(i),a(this.view())},s=setTimeout(i,n);s.unref?.(),this.waiters.add(i),o?.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")},En),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 Re,vn,Ae,Ie=f(()=>{"use strict";Re=m.string().regex(/^([01]\d|2[0-3]):[0-5]\d$/,"use HH:MM, 24-hour"),vn=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:Re,end:Re}).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"}),Ae=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(vn).default([])}).default({})});function $e(r){let t=Ce(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=Ce(r.contains);return n.length>200?"contains is longer than 200 characters":{kind:"url",url:t,...n?{contains:n}:{}}}async function Oe(r,t){let n=t.fetch??fetch,o;try{o=await n(r.url,{signal:AbortSignal.timeout(1e4),redirect:"follow"})}catch(a){return{verdict:"unverifiable",detail:`${r.url} could not be reached: ${String(a?.message??a).slice(0,200)}`}}return o.ok?r.contains&&!(await o.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 ${o.status}`}}var Ce,Le=f(()=>{"use strict";Ce=r=>typeof r=="string"?r.trim():""});function je(r,t){let n=Rn(r,t);return n&&n.project.split("/").every(o=>o!=="."&&o!=="..")?n:null}function Rn(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 o;try{o=new URL(t)}catch{return null}let a=o.pathname.replace(/\/+$/,"");if(r==="github"){let s=a.match(/^\/([\w.-]+\/[\w.-]+)\/(issues|pull)\/(\d+)$/);return s?{project:s[1],number:Number(s[3]),mr:s[2]==="pull"}:null}let i=a.match(/^\/(.+?)\/-\/(issues|merge_requests)\/(\d+)$/);return i?{project:i[1],number:Number(i[3]),mr:i[2]==="merge_requests"}:null}function De(r){let t=j(r.provider).toLowerCase();if(t!=="github"&&t!=="gitlab")return"provider must be github or gitlab";let n=j(r.ref);if(!n||n.length>500||!je(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 o=j(r.state).toLowerCase();if(o&&!Ne.includes(o))return`state must be one of ${Ne.join(", ")}`;let a=j(r.notePattern);return a.length>200?"notePattern is longer than 200 characters":{kind:"issue",provider:t,ref:n,...o?{state:o}:{},...a?{notePattern:a}:{}}}async function Pe(r,t){if(!t.forge)return{verdict:"unverifiable",detail:`no ${r.provider} connector on this node`};let n=je(r.provider,r.ref);if(!n)return{verdict:"unverifiable",detail:`${r.ref} is not an issue reference`};let o=await t.forge(r.provider,n,{notes:!!r.notePattern});if("error"in o)return{verdict:"unverifiable",detail:`${r.ref}: ${o.error}`};let a=r.state??(r.notePattern?null:"closed");if(a&&An(a,o.state))return{verdict:"met",detail:`${o.url} is ${o.state}`};if(r.notePattern){let i=r.notePattern.toLowerCase();if((o.notes??[]).some(s=>s.slice(0,1e4).toLowerCase().includes(i)))return{verdict:"met",detail:`${o.url} has a note with "${r.notePattern}"`}}return{verdict:"not_met",detail:`${o.url} is ${o.state}${r.notePattern?`, no note has "${r.notePattern}"`:""}`}}function lo(r,t=fetch){let n=async(o,a)=>{let i;try{i=await t(o,{headers:a,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(o,a,i)=>{if(o==="github"){let S=r.github;if(!S)return{error:"no github connector on this node (channels.github or requests.checkers.github)"};let Ut=S.apiUrl.replace(/\/$/,""),K={Accept:"application/vnd.github+json",...S.token?{Authorization:`Bearer ${S.token}`}:{}},X=`${Ut}/repos/${a.project}`,E=await n(`${X}/issues/${a.number}`,K);if(!E.ok)return{error:E.error};let I=E.body?.state==="closed"?"closed":"open";if(E.body?.pull_request&&I==="closed"){let $=await n(`${X}/pulls/${a.number}`,K);$.ok&&$.body?.merged&&(I="merged")}let ne=String(E.body?.html_url??`${a.project}#${a.number}`);if(!i.notes)return{state:I,url:ne};let C=await n(`${X}/issues/${a.number}/comments?per_page=100`,K);return C.ok?{state:I,url:ne,notes:(Array.isArray(C.body)?C.body:[]).map($=>String($?.body??""))}:{error:`its notes: ${C.error}`}}let s=r.gitlab;if(!s)return{error:"no gitlab connector on this node (channels.gitlab or requests.checkers.gitlab)"};let u=s.token?{"PRIVATE-TOKEN":s.token}:{},c=`${s.url.replace(/\/$/,"")}/api/v4/projects/${encodeURIComponent(a.project)}/${a.mr?"merge_requests":"issues"}/${a.number}`,g=await n(c,u);if(!g.ok)return{error:g.error};let b=String(g.body?.state??""),k=b==="merged"?"merged":b==="closed"?"closed":"open",A=String(g.body?.web_url??`${a.project}${a.mr?"!":"#"}${a.number}`);if(!i.notes)return{state:k,url:A};let w=await n(`${c}/notes?per_page=100&sort=desc&order_by=created_at`,u);return w.ok?{state:k,url:A,notes:(Array.isArray(w.body)?w.body:[]).map(S=>String(S?.body??"")).reverse()}:{error:`its notes: ${w.error}`}}}var Ne,j,An,Me=f(()=>{"use strict";Ne=["open","closed","merged"],j=r=>typeof r=="string"?r.trim():"";An=(r,t)=>r===t||r==="closed"&&t==="merged"});import{execFile as In}from"child_process";function qe(r,t){let n=Y(r.repo),o=Object.keys(t.repos);if(!n||!Object.prototype.hasOwnProperty.call(t.repos,n))return o.length?`repo must be one of ${o.join(", ")} (requests.checkers.repos)`:"no repos are set up for this check on this node (requests.checkers.repos)";let a=Y(r.commit);if(!$n.test(a))return"commit must be a commit hash (7 to 64 hex characters)";let i=Y(r.ref);return i&&!On.test(i)?"ref must be a branch or tag name, for example main or origin/main":{kind:"git-ancestor",repo:n,commit:a,...i?{ref:i}:{}}}async function Fe(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 o=r.ref??"HEAD",{code:a,stderr:i}=await(t.git??Ln)(n,["merge-base","--is-ancestor",r.commit,o]);if(a===0)return{verdict:"met",detail:`commit ${r.commit} is in ${o} of ${r.repo}`};if(a===1)return{verdict:"not_met",detail:`commit ${r.commit} is not in ${o} of ${r.repo} yet`};let s=i.replace(/\s+/g," ").trim().slice(0,200)||`git exited with ${a}`;return{verdict:"unverifiable",detail:`could not check ${r.commit} in ${r.repo}: ${s}`}}var Cn,$n,On,Y,Ln,Ue=f(()=>{"use strict";Cn=1e4,$n=/^[0-9a-f]{7,64}$/i,On=/^(?!-)(?!.*\.\.)[A-Za-z0-9._\/-]{1,200}$/,Y=r=>typeof r=="string"?r.trim():"";Ln=(r,t)=>new Promise(n=>{In("git",["-C",r,...t],{timeout:Cn,windowsHide:!0},(o,a,i)=>{n({code:o?typeof o.code=="number"?o.code:-1:0,stderr:String(i??"")})})})});function He(r,t){let n=D(r.channel).toLowerCase(),o=D(r.chatId),a=D(r.from);if(!n||!o)return"a reply check needs channel and chatId: the chat the answer arrives in";if(!a)return"a reply check needs from: the id of the person who answers (people)";if([n,o,a].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(D(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:o,from:a,after:i}}async function Be(r,t){if(!t.inbound)return{verdict:"unverifiable",detail:"this node keeps no record of inbound messages"};let n=r.after??0,o=t.inbound(r.channel,r.chatId,n,r.from);if(!o)return{verdict:"unverifiable",detail:"this node keeps no record of inbound messages"};let a=r.from.toLowerCase(),i=o.find(s=>s.at>n&&(s.person??"").toLowerCase()===a);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 fo(r){return(t,n,o,a)=>{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,o,a,Nn)}catch{return null}}}var D,Nn,We=f(()=>{"use strict";D=r=>typeof r=="string"?r.trim():"";Nn=5});function yo(r,t=jn,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 o=r,a=typeof o.kind=="string"?o.kind.trim().toLowerCase():"";if(!P.includes(a))return`closeWhen.kind must be one of ${P.join(", ")}`;if(!t.enabled.includes(a))return`the ${a} check is turned off on this node (requests.checkers.enabled)`;let i=a==="url"?$e(o):a==="issue"?De(o):a==="git-ancestor"?qe(o,t):He(o,n);return typeof i=="string"?`closeWhen: ${i}`:JSON.stringify(i).length>ze?`closeWhen is longer than ${ze} characters`:i}async function ko(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 Oe(r,t);case"issue":return await Pe(r,t);case"git-ancestor":return await Fe(r,t);case"reply":return await Be(r,t)}}catch(n){return{verdict:"unverifiable",detail:`the ${r.kind} check failed: ${String(n?.message??n).slice(0,200)}`}}}function wo(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 Ke(r){if(!r)return null;try{let t=JSON.parse(r);return t&&typeof t=="object"&&P.includes(t.kind)?t:null}catch{return null}}var P,jn,ze,Xe=f(()=>{"use strict";Le();Me();Ue();We();P=["url","issue","git-ancestor","reply"],jn={enabled:[...P],checkEveryMinutes:10,repos:{}},ze=2e3});function Pn(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
|
+
`),se(r,"requests",ie);let t=r.prepare("PRAGMA table_info(requests)").all();for(let[n,o]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"]])if(!t.some(a=>a.name===n))try{r.exec(`ALTER TABLE requests ADD COLUMN ${n} ${o}`)}catch(a){if(!/duplicate column name/i.test(String(a?.message)))throw a}}function Ro(r){return!!r.prepare("SELECT 1 FROM sqlite_master WHERE type = 'table' AND name = 'requests'").get()}function x(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,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:Ke(r.close_when),checkedAt:r.checked_at??null,verifiedAt:r.verified_at??null,verifyState:r.verify_state??null,verifyDetail:r.verify_detail??null}}var y,p,To,Dn,J,vo,M,v,h,Ge,Ve=f(()=>{"use strict";zt();Xe();y=["in_progress","waiting_owner","waiting_other","needs_attention"],p=["done","declined","dropped"],To=["issue","idea","approval","erp","card"],Dn=["issue","idea","approval","erp"],J=500,vo="owner",M=4e3,v=500;h=r=>Array(r).fill("?").join(","),Ge=class{constructor(t){this.db=t;Pn(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, owed_to)
|
|
33
|
+
VALUES (?, 'candidate', ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(t.id,t.channel,t.chatId,t.sender??null,t.person??null,t.agentId,t.text.slice(0,M),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, 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,M),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,J):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(y.length)})`).get(t,...y).n}setLoop(t,n,o){n.dueAt!==void 0&&this.db.prepare("UPDATE requests SET due_at = ?, updated_at = ? WHERE id = ?").run(n.dueAt,o,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,o,t)}markChecked(t,n,o,a){this.db.prepare("UPDATE requests SET checked_at = ?, verify_state = ?, verify_detail = ? WHERE id = ?").run(a,n,o.slice(0,v)||null,t)}closeVerified(t,n,o){let a=n.trim().slice(0,v)||"the check was met";return this.db.prepare(`UPDATE requests SET state = 'done', closed_at = ?, updated_at = ?, evidence = ?, close_reason = NULL,
|
|
35
|
+
checked_at = ?, verified_at = ?, verify_state = 'verified', verify_detail = ?
|
|
36
|
+
WHERE id = ? AND state NOT IN (${h(p.length)})`).run(o,o,a,o,o,a,t,...p).changes>0}dueForCheck(t,n=50){return this.db.prepare(`SELECT * FROM requests WHERE state IN (${h(y.length)}) AND close_when IS NOT NULL
|
|
37
|
+
AND (checked_at IS NULL OR checked_at < ?) ORDER BY COALESCE(checked_at, 0), created_at LIMIT ?`).all(...y,t,n).map(x)}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 < ?
|
|
38
|
+
AND (updated_at <= due_at OR updated_at < ?)
|
|
39
|
+
ORDER BY due_at, rowid`).all(t,n).map(x)}get(t){let n=this.db.prepare("SELECT * FROM requests WHERE id = ?").get(t);return n?x(n):null}byLink(t,n){let o=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 o?x(o):null}link(t,n,o,a){this.db.prepare("INSERT OR IGNORE INTO request_links (request_id, kind, ref, at) VALUES (?, ?, ?, ?)").run(t,n,o,a)}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
|
|
40
|
+
FROM request_links l JOIN task_traces t ON t.task_id = l.ref
|
|
41
|
+
WHERE l.request_id = ? AND l.kind = 'run' AND t.final_response IS NOT NULL AND t.final_response != ''
|
|
42
|
+
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,o="in_progress"){this.db.prepare(`UPDATE requests SET state = ?, updated_at = ?, attention_reason = NULL, notified_at = NULL, question = NULL
|
|
43
|
+
WHERE id = ? AND state NOT IN (${h(p.length)})`).run(o,n,t,...p)}waitOnOwner(t,n,o){this.db.prepare(`UPDATE requests SET state = 'waiting_owner', updated_at = ?, question = ?, attention_reason = NULL, notified_at = NULL
|
|
44
|
+
WHERE id = ? AND state NOT IN (${h(p.length)})`).run(o,n.slice(0,M),t,...p)}needsAttention(t,n,o){return this.db.prepare(`UPDATE requests SET state = 'needs_attention', updated_at = ?, attention_reason = ?, notified_at = NULL
|
|
45
|
+
WHERE id = ? AND state NOT IN ('needs_attention', ${h(p.length)})`).run(o,n.replace(/\s+/g," ").trim().slice(0,v),t,...p).changes>0}close(t,n,o,a){let i=o.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 = ?, evidence = ?, close_reason = ?
|
|
46
|
+
WHERE id = ? AND state NOT IN (${h(p.length)})`).run(n,a,a,n==="done"?i.slice(0,v):null,n==="done"?null:i.slice(0,v),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(y.length)}) ORDER BY created_at, rowid`).all(...y).map(x)}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(x)}awaitingNotice(){return this.db.prepare("SELECT * FROM requests WHERE state = 'needs_attention' AND notified_at IS NULL ORDER BY created_at, rowid").all().map(x)}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(x)}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(x)}setHome(t,n,o,a={}){let i=n.link.slice(0,J);a.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,o,t),this.linkHome(t,n,o)}linkHome(t,n,o){Dn.includes(n.kind)&&this.link(t,n.kind,n.link.slice(0,J),o)}query(t){let n=[`state IN (${h(t.states.length)})`],o=[...t.states];t.since!==void 0&&(n.push("created_at >= ?"),o.push(t.since)),t.closedSince!==void 0&&(n.push("closed_at >= ?"),o.push(t.closedSince));let a=n.join(" AND "),i=this.db.prepare(`SELECT COUNT(*) AS n FROM requests WHERE ${a}`).get(...o).n,s=t.newest?"DESC":"ASC",u=this.db.prepare(`SELECT * FROM requests WHERE ${a} ORDER BY created_at ${s}, rowid ${s} LIMIT ? OFFSET ?`).all(...o,t.limit,t.offset??0).map(x);return{items:t.newest?u.reverse():u,total:i}}requestPickup(t,n){return this.db.prepare(`UPDATE requests SET state = 'in_progress', updated_at = ?, attention_reason = NULL, notified_at = NULL, pickup_at = ?
|
|
47
|
+
WHERE id = ? AND state = 'needs_attention'`).run(n,n,t).changes>0}handOff(t,n,o,a){return this.db.prepare(`UPDATE requests SET agent_id = ?, state = 'in_progress', updated_at = ?, attention_reason = NULL, notified_at = NULL,
|
|
48
|
+
question = NULL, pickup_at = ?, owner_note = ?, note_said = NULL, asked_agent = COALESCE(asked_agent, agent_id)
|
|
49
|
+
WHERE id = ? AND state IN (${h(y.length)})`).run(n,a,a,o.trim().slice(0,M)||null,t,...y).changes>0}setNoteSaid(t,n,o){this.db.prepare(`UPDATE requests SET note_said = ? WHERE id = ? AND owner_note = ?${o?" AND pickup_at IS NULL":""}`).run(o?1:null,t,n)}takePickups(){return this.db.prepare("UPDATE requests SET pickup_at = NULL WHERE pickup_at IS NOT NULL RETURNING *").all().map(x).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 Co(r){return Object.assign({channel:Je,chatId:r,sender:"operator"},{[Qe]:!0})}function $o(r){return!!r&&r[Qe]===!0}function Fn(r,t,n,o=!1){let a=q(t);if(r.channels.length&&!r.channels.map(q).includes(a))return!1;if(F.has(a))return o&&!Mn.has((n?.name??"").toLowerCase());let i=qn.has(a)?n?.username:n?.id,s=i?Ze(a,i):"";return s?r.from.some(u=>{let c=u.trim().toLowerCase(),g=c.indexOf(":");return g>0&&c.slice(0,g)===a&&Ze(a,c.slice(g+1))===s}):!1}function Oo(r){if(!r)return;let t=o=>typeof o=="string"&&o?o.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 Un(r){return/\btimed? ?out\b|timeout/i.test(r)?"timed out":"failed"}var Je,Qe,F,Mn,q,qn,Ze,_,R,Ye,et=f(()=>{"use strict";Ve();Je="requests",Qe=Symbol("requests.pickup");F=new Set(["voice","app","dashboard","webrtc"]),Mn=new Set(["camera"]),q=r=>r.toLowerCase().split("@")[0],qn=new Set(["github","gitlab"]),Ze=(r,t)=>r==="whatsapp"?t.replace(/@.*$/,"").replace(/\D/g,""):t.trim().toLowerCase().replace(/^@/,"");_=(r,t=200)=>{let n=r.replace(/\s+/g," ").trim();return n.length>t?`${n.slice(0,t-1)}\u2026`:n},R=(r,t,n)=>`${r}\0${t}\0${n}`,Ye=class{constructor(t,n,o,a=Date.now){this.store=t;this.settings=n;this.log=o;this.now=a}live=new Map;guard(t,n){try{n()}catch(o){this.log(`[requests] ${t} failed: ${o?.message??o}`)}}isListedOwner(t){let n=q(t.channel),o=this.settings().channels;return o.length&&!o.map(q).includes(n)?!1:t.person?.role==="owner"&&!F.has(n)}liveRequestId(t,n,o){return this.live.get(R(t,n,o))?.requestId??null}taskStarted(t){t.taskId&&this.guard("capture",()=>{if(!this.settings().enabled)return;let n=this.now(),o=t.resumedFrom?this.store.byLink("run",t.resumedFrom):null,a=R(t.agentId,t.channel,t.chatId);if(o){this.store.link(o.id,"run",t.taskId,n),o.state==="candidate"?this.store.touch(o.id,n):this.store.progress(o.id,n),this.live.set(a,{requestId:o.id,runId:t.taskId});return}let i=t.pickup&&t.channel===Je?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(a,{requestId:i.id,runId:t.taskId});return}!t.humanRoot||!(Fn(this.settings(),t.channel,t.sender,t.operator===!0)||this.isListedOwner(t))||(this.live.set(a,{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=R(t.agentId,t.channel,t.chatId);this.live.get(n)?.runId===t.taskId&&this.live.delete(n);let o=this.store.byLink("run",t.taskId);if(!o)return;let a=this.now();if(!t.interrupted){if(t.error&&t.errorKind!=="cancelled"&&!t.stopped&&t.errorKind!=="stopped"){let i=Un(t.error);this.store.needsAttention(o.id,`${t.agentId} ${i}: ${_(t.error)}`,a)&&this.log(`[requests] ${o.id} needs attention: run ${t.taskId} ${i}`);return}o.state==="candidate"?this.store.discardCandidate(o.id):this.store.touch(o.id,a)}})}delegationStarted(t){this.guard("delegation start",()=>{if(!this.settings().enabled)return;let n=this.live.get(R(t.caller,t.origin.channel,t.origin.chatId));if(!n)return;let o=this.now();this.store.link(n.requestId,"delegation",t.id,o),this.store.progress(n.requestId,o,"waiting_other")})}delegationDone(t,n,o){this.guard("delegation end",()=>{let a=this.store.byLink("delegation",t.id);if(!a)return;let i=this.now(),s=t.peer?`${t.callee} on ${t.peer}`:t.callee;if(n==="done"){this.store.progress(a.id,i);return}let u={error:"failed",timeout:"did not answer in time",lost:"was lost in a restart"}[n];this.store.needsAttention(a.id,`The work handed to ${s} ${u}: ${_(o)}`,i)&&this.log(`[requests] ${a.id} needs attention: delegation ${t.id} ${n}`)})}relayFailed(t,n,o){this.guard("delegation relay",()=>{let a=this.store.byLink("delegation",t.id);if(!a)return;let i=t.peer?`${t.callee} on ${t.peer}`:t.callee;this.store.needsAttention(a.id,`The answer from ${i} was not passed on after a restart (${_(o)}). It said: ${_(n)}`,this.now())&&this.log(`[requests] ${a.id} needs attention: delegation ${t.id} answer not relayed`)})}cardRaised(t,n){n&&this.guard("card",()=>{if(!this.settings().enabled)return;let o=this.live.get(R(t.raised_by,n.channel,n.chatId))?.requestId;if(!o)return;let a=this.now();this.store.link(o,"card",t.id,a),this.store.waitOnOwner(o,t.ask,a)})}cardResolved(t){this.guard("card result",()=>{let n=this.store.byLink("card",t.id);if(!n)return;let o=this.now(),a=t.outcome??t.if_silent;t.status==="decided"||t.status==="resolved"?n.state==="waiting_owner"&&this.store.progress(n.id,o):this.store.needsAttention(n.id,`Your answer did not come before the card expired (${_(t.ask)}); "${a}" was applied`,o)&&this.log(`[requests] ${n.id} needs attention: card ${t.id} expired unanswered`)})}closingNote(t){try{let n=this.store.byLink("delegation",t);return!n||!y.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, leave it open.`}catch{return}}resumeOutcome(t){this.guard("restart",()=>{let n=this.store.byLink("run",t.taskId);if(!n)return;let o=this.now();t.decision==="resumed"?n.state==="candidate"?this.store.touch(n.id,o):this.store.progress(n.id,o):(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 (${_(t.reason)})`,o)&&this.log(`[requests] ${n.id} needs attention: run ${t.taskId} cut off by a restart`)})}}});import{randomBytes as Hn,timingSafeEqual as nt}from"crypto";import{chmodSync as Bn,existsSync as Wn,mkdirSync as zn,readFileSync as Kn,writeFileSync as Xn}from"fs";import{networkInterfaces as Gn}from"os";import{dirname as Vn,resolve as Zn}from"path";function Mo(r){return Object.assign(r,{[rt]:!0})}function ot(r){return!!r&&r[rt]===!0}function Jn(r=process.cwd()){return Zn(r,".agentx","operator.key")}function qo(r=process.cwd(),t={}){let n=Jn(r);try{if(Wn(n)){let a=Kn(n,"utf-8").trim();if(a)return a}if(!t.create)return null;let o=Hn(32).toString("hex");return zn(Vn(n),{recursive:!0}),Xn(n,o+`
|
|
50
|
+
`,{mode:384}),Bn(n,384),o}catch{return null}}function Qn(r,t){let n=Array.isArray(t)?t[0]:t;if(!r||!n)return!1;let o=Buffer.from(r),a=Buffer.from(n.trim());return o.length===a.length&&nt(o,a)}function tr(r,t,n,o){if(!r||typeof r!="object")return!1;let a=String(r.channel??"").toLowerCase().split("@")[0];return o.has(a)?Qn(n,t[Yn]):!1}function nr(r,t){let n=r[er];if((Array.isArray(n)?n[0]:n)!=="1")return!1;let o=r.authorization,a=(Array.isArray(o)?o[0]:o)||"",i=a.toLowerCase().startsWith("bearer ")?a.slice(7).trim():"";if(!i)return!1;let s=Buffer.from(i);for(let u of t){if(!u)continue;let c=Buffer.from(u);if(c.length===s.length&&nt(c,s))return!0}return!1}function rr(r,t=or()){let n=tt(r??"");if(!n||n==="::1"||n.startsWith("127."))return!0;for(let o of t)if(tt(o)===n)return!0;return!1}function or(){return Object.values(Gn()).flatMap(r=>(r??[]).map(t=>t.address))}function Fo(r,t,n){if(tr(r,t.headers,n.key,n.operatorChannels))return!0;if(!r||typeof r!="object")return!1;let o=String(r.channel??"").toLowerCase().split("@")[0];return!n.operatorChannels.has(o)||rr(t.remoteAddress,n.ownAddresses)?!1:nr(t.headers,n.peerTokens)}var Yn,rt,er,tt,at=f(()=>{"use strict";Yn="x-agentx-operator",rt=Symbol("requests.operator");er="x-agentx-operator-vouch";tt=r=>r.toLowerCase().replace(/^::ffff:/,"").replace(/%.*$/,"")});function Ko(r,t){return r.find(n=>n.id===t)?.name??(t===U?"Owner":t)}function it(r,t){let n=t.trim().toLowerCase();return r==="whatsapp"?n.replace(/@.*$/,"").replace(/\D/g,""):n.replace(/^@/,"")}function st(r){let t=r.indexOf(":");if(t<=0||t===r.length-1)return null;let n=Q(r.slice(0,t).trim()),o=it(n,r.slice(t+1));return o?{channel:n,value:o}:null}function lt(r){let t=new Set,n=new Map;for(let o of r){if(t.has(o.id))return`person "${o.id}" is listed twice`;if(t.add(o.id),o.id===U&&o.role!=="owner")return`the id "${U}" is kept for a person with the owner role`;for(let a of o.identities){let i=st(a);if(!i)return`identity "${a}" of "${o.id}" must be written channel:id`;let s=`${i.channel}:${i.value}`,u=n.get(s);if(u&&u!==o.id)return`identity "${a}" belongs to both "${u}" and "${o.id}"`;n.set(s,o.id)}}return null}function ar(r,t,n){let o=Q(t),i=(o==="whatsapp"?[n?.id]:[n?.id,n?.username]).filter(s=>typeof s=="string"&&!!s).map(s=>it(o,s)).filter(Boolean);return i.length?r.find(s=>s.identities.some(u=>{let c=st(u);return!!c&&c.channel===o&&i.includes(c.value)}))??null:null}function ir(r){let t=r.filter(n=>n.role==="owner");return t.length===0?{id:U,role:"owner"}:t.length===1?{id:t[0].id,role:"owner"}:null}function ut(r,t){let n=oe(t);if(n)return n.person?{id:n.person}:null;if(!t||ae(t)!=="human")return null;let o=String(t.channel??"");if(F.has(Q(o)))return ot(t)?ir(r):null;let a=s=>typeof s=="string"?s:void 0,i=ar(r,o,{id:a(t.senderId),username:a(t.senderUsername)});return i?{id:i.id,role:i.role}:null}function sr(r,t){let n=r.agents??[];return r.role==="owner"||n.length===0||n.includes(t)}function Xo(r,t,n){let o=ut(r,n);if(!o)return null;let a=r.find(s=>s.id===o.id);if(!a||sr(a,t))return null;let i=(a.agents??[]).join(", ");return{person:a,note:`${a.name}, you can reach ${i} here, not ${t}. Ask the owner if you need ${t}.`}}function Go(r,t){let n=r.trim().split("*").map(o=>o.replace(/[.+?^${}()|[\]\\]/g,"\\$&")).join(".*");return new RegExp(`^${n}$`,"i").test(t.trim())}function Vo(r,t){let n=ut(r,t);if(!n)return null;let o=r.find(s=>s.id===n.id);if(!o||o.role==="owner")return null;let a=o.deny?.tools??[],i=o.deny?.skills??[];return!a.length&&!i.length?null:{personId:o.id,name:o.name,tools:[...a],skills:[...i]}}var zo,U,Q,dt=f(()=>{"use strict";Wt();et();at();zo=["owner","member","client","guest"],U="owner";Q=r=>r.toLowerCase().split("@")[0]});function Jo(r){return typeof r=="string"&&ur.includes(r)}function ct(r){let t=new Set(lr);return r.map(n=>n.trim()).filter(n=>n&&!t.has(n))}var lr,ur,Yo,mt=f(()=>{"use strict";lr=["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_send_contact","agentx_task","agentx_agents","agentx_voice_queue","agentx_call_owner","agentx_camera_ask","agentx_camera_look","agentx_health","agentx_attach_next","agentx_attach_answer","agentx_crons","agentx_schedule","agentx_approval","agentx_request","agentx_workflow","agentx_debug","agentx_wiki_query","agentx_wiki_patch","agentx_wiki_interview","agentx_graph_review"],ur=["read","full"],Yo=["agentx_inspect","agentx_skill_match","agentx_detect_output_type","agentx_recent","agentx_events","agentx_agents","agentx_voice_queue","agentx_health","agentx_crons","agentx_wiki_query"]});var Pr={};Ht(Pr,{INTEGRATION_KINDS:()=>br,WIKI_CONTRIBUTE_JOB:()=>Dt,WIKI_CONTRIBUTE_MERGE_JOB:()=>Pt,WIKI_SUMMARIZE_JOB:()=>ee,approvalsConfigSchema:()=>Nt,callsSchema:()=>Tt,cameraBotSchema:()=>_t,daemonConfigSchema:()=>jt,eventSubscriptionSchema:()=>St,expandEnvVars:()=>z,isCronExpression:()=>Et,loadDaemonConfig:()=>jr,membersConfigSchema:()=>$t,peopleConfigSchema:()=>Ct,personSchema:()=>It,qualityReportConfigSchema:()=>Lt,requestStatusConfigSchema:()=>Ot,requestsConfigSchema:()=>At,reviewGateConfigSchema:()=>vt,validateWorkspaces:()=>Dr,watchersConfigSchema:()=>Rt,withContributionJobs:()=>Mt,withSummariesJob:()=>qt});import{z as e}from"zod";import{readFileSync as xt,existsSync as B}from"fs";import{resolve as W}from"path";function dr(r){let t=W(r,".env");if(!B(t))return;let n=xt(t,"utf-8");for(let o of n.split(`
|
|
51
|
+
`)){let a=o.trim();if(!a||a.startsWith("#"))continue;let i=a.indexOf("=");if(i===-1)continue;let s=a.slice(0,i).trim(),u=a.slice(i+1).trim();process.env[s]||(process.env[s]=u)}}function Et(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((o,a)=>o.split(",").every(i=>{let s=/^(\*|(\d+)(?:-(\d+))?)(?:\/(\d+))?$/.exec(i);if(!s)return!1;let[u,c]=t[a],g=s[2]===void 0?u:Number(s[2]),b=s[3]===void 0?g:Number(s[3]),k=s[4]===void 0?1:Number(s[4]);return g>=u&&b<=c&&g<=b&&k>=1}))}function wr(r){try{return new Intl.DateTimeFormat("en-US",{timeZone:r}),!0}catch{return!1}}function z(r){if(typeof r=="string")return r.replace(/\$\{(\w+)\}/g,(t,n)=>process.env[n]||"");if(Array.isArray(r))return r.map(z);if(r!==null&&typeof r=="object"){let t={};for(let[n,o]of Object.entries(r))t[n]=z(o);return t}return r}function Nr(r,t){let n=[],o=new Set;for(let a of r){let i=a.path.join("."),s=/^channels\.telegram\.accounts\.([^.]+)\.token$/.exec(i);if(s&&a.message.toLowerCase().includes("required")){let u=s[1],c=`tg:${u}`;if(o.has(c))continue;o.add(c),n.push(`Hint: Telegram account "${u}" is enabled but has no bot token.
|
|
52
|
+
Either:
|
|
53
|
+
A) Open ${t} and set channels.telegram.accounts.${u}.token to your bot token (or "\${TG_${u.toUpperCase()}_BOT_TOKEN}" + the matching .env entry).
|
|
54
|
+
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.${u}).`)}}return n}function jr(r){let t=r?[r]:[W(process.cwd(),"agentx.json"),W(process.cwd(),".agentx/config.json")];dr(process.cwd());let n,o;for(let u of t)if(B(u)){n=xt(u,"utf-8"),o=u;break}if(!n||!o)throw new Error(`No config found. Create agentx.json or .agentx/config.json
|
|
55
|
+
Searched: ${t.join(", ")}`);let a;try{a=JSON.parse(n)}catch(u){throw new Error(`Invalid JSON in ${o}: ${u.message}`)}let i=z(a),s=jt.safeParse(i);if(!s.success){let u=s.error.issues.map(b=>` ${b.path.join(".")}: ${b.message}`).join(`
|
|
56
|
+
`),c=Nr(s.error.issues,o),g=c.length?`
|
|
57
|
+
|
|
58
|
+
${c.join(`
|
|
59
|
+
|
|
60
|
+
`)}`:"";throw new Error(`Config validation failed (${o}):
|
|
61
|
+
${u}${g}`)}return qt(Mt(s.data))}function Dr(r){let t=[];for(let[n,o]of Object.entries(r.agents)){if(!B(o.workspace)){t.push(`Agent "${n}": workspace not found at ${o.workspace}`);continue}if(o.tier==="claude-code"){let s=W(o.workspace,".claude");B(s)||t.push(`Agent "${n}": no .claude/ directory in workspace ${o.workspace}. Claude Code native features (hooks, MCP, skills) won't be available.`)}let a=o.provider||"claude",i=r.providers[a];!["claude-code","codex-cli","opencode"].includes(o.tier)&&(!i||!i.apiKey)&&t.push(`Agent "${n}": provider "${a}" has no API key configured. Set providers.${a}.apiKey in config or use a CLI-backed tier ("claude-code", "codex-cli", or "opencode").`)}for(let[n,o]of Object.entries(r.crons))r.agents[o.agent]||t.push(`Cron "${n}": references unknown agent "${o.agent}"`);if(r.channels.telegram.enabled)for(let[n,o]of Object.entries(r.channels.telegram.accounts))r.agents[o.agentBinding]||t.push(`Telegram account "${n}": references unknown agent "${o.agentBinding}"`);return t}function Mt(r,t=Ft()){let n=Object.entries(r.agents).filter(([,s])=>s.wiki?.contribute?.enabled).map(([s])=>s).sort();if(n.length===0)return r;let o=r.wiki.contributions,a=(s,u)=>te.parse({schedule:s,timezone:o.timezone,agent:n[0],command:u,timeout:3600,onError:"log"}),i={[Dt]:a(o.schedule,`${t} wiki contribute --all`),[Pt]:a(o.mergeSchedule,`${t} wiki contributions merge`)};return{...r,crons:{...i,...r.crons}}}function qt(r,t=Ft()){let n=r.wiki.summaries,o=n.agent??r.node.defaultAgent??Object.keys(r.agents).sort()[0];if(!n.schedule?.trim()||!o)return r;let a=te.safeParse({schedule:n.schedule,timezone:n.timezone,agent:o,command:`${t} wiki summarize --all`,timeout:3600,onError:"log"});if(!a.success)throw new Error(`wiki.summaries: the ${ee} job can't be built: ${a.error.issues.map(i=>i.message).join("; ")}`);return{...r,crons:{[ee]:a.data,...r.crons}}}function Ft(){let r=process.argv[1]??"";return/[\\/]cli\.(?:c|m)?js$/.test(r)?`"${process.execPath}" "${r}"`:"agentx"}var ft,cr,mr,fr,gr,pr,hr,br,yt,kt,H,wt,St,xr,yr,_t,kr,te,gt,pt,ht,Sr,_r,Er,Tr,vr,Rr,Ar,Ir,Cr,Tt,$r,Or,Lr,vt,Rt,At,It,Ct,$t,Ot,bt,Lt,Nt,jt,Dt,Pt,ee,Mr=f(()=>{ue();me();Bt();pe();ye();Se();Ee();ve();Ie();dt();mt();ft=e.array(e.string().min(1)).superRefine((r,t)=>{let n=ct(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__".`})}),cr=e.object({apiKey:e.string().optional(),defaultModel:e.string().optional(),baseUrl:e.string().optional(),thinking:e.boolean().optional()}),mr=e.object({type:e.literal("stdio").optional(),command:e.string(),args:e.array(e.string()).default([]),env:e.record(e.string(),e.string()).optional()}),fr=e.object({type:e.literal("http"),url:e.string().url(),headers:e.record(e.string(),e.string()).optional()}),gr=e.union([fr,mr]),pr=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(),hr=e.object({kind:e.string().min(1),label:e.string().min(1),credentials:pr.default({}),metadata:e.record(e.string(),e.union([e.string(),e.number(),e.boolean()])).default({}),enabled:e.boolean().default(!0)}),br=["telegram-bot","whatsapp","gitlab-user","github-user","gmail","hotmail","smtp","hubspot","salesforce","pipedrive","odoo","trello","linear","asana","jira","notion","stripe","sentry","vercel","twilio","custom"],yt=e.enum(["system","elevenlabs"]),kt=e.union([e.string(),e.record(e.string(),e.string())]),H=e.string().superRefine((r,t)=>{let n=ge(r);n&&t.addIssue({code:e.ZodIssueCode.custom,message:`shortcut ${n}`})}),wt=e.object({provider:yt.optional(),system:kt.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:H.optional()}),St=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)}),xr=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(),gr).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(St).default([]),integrations:e.array(hr).default([]),voice:wt.optional(),presence:e.object({color:e.string().regex(/^#[0-9a-fA-F]{6}$/).optional(),palette:e.enum(G).optional(),initial:e.string().max(2).optional(),label:e.string().optional(),allowActions:e.boolean().optional()}).optional()}),yr=e.object({token:e.string(),agentBinding:e.string(),allowFrom:e.array(e.string()).optional(),pollInbound:e.boolean().default(!0)}),_t=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({}),kr=e.object({telegram:e.object({enabled:e.boolean().default(!1),accounts:e.record(e.string(),yr).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:_t}).default({})}).default({})}),te=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(),autonomy:re.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)"});gt=e.string().regex(/^(?!.*(?:^|\/)\.+(?:\/|$))[\w.-]+(\/[\w.-]+)+$/,"expected owner/name"),pt=e.union([gt,e.object({repo:gt,about:e.string().max(200).optional()})]),ht={tokenEnv:e.string().regex(/^[A-Za-z_][A-Za-z0-9_]*$/).optional(),tokenFile:e.string().optional()},Sr=e.discriminatedUnion("type",[e.object({type:e.literal("github"),apiUrl:e.string().url().default("https://api.github.com"),...ht,repos:e.array(pt).min(1)}),e.object({type:e.literal("gitlab"),url:e.string().url().optional(),...ht,repos:e.array(pt).min(1)}),e.object({type:e.literal("agentx"),url:e.string().url().optional(),peers:e.boolean().default(!0)})]),_r=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"}),Er=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()}),Tr=e.object({url:e.string(),name:e.string(),token:e.string().optional()}),vr=e.object({name:e.string(),agent:e.string(),enabled:e.boolean().default(!0)}),Rr=e.object({enabled:e.boolean().default(!1),peers:e.array(Tr).default([]),inboxes:e.array(vr).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)}).default({})}),Ar=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({}),Ir=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({}),Cr=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(),Ir).default({}),routing:e.object({cheapModel:e.string().optional(),cheapModels:e.object({"claude-code":e.string().optional(),"codex-cli":e.string().optional()}).optional()}).default({})}).default({}),Tt=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({}),$r=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()}).default({})}).default({}),Or=e.object({x:e.number(),y:e.number(),width:e.number().positive(),height:e.number().positive()}),Lr=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-]*$/),Or).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({}),vt=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({}),Rt=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")}).default({}).refine(r=>r.noBelow<=r.yesAt,{message:"watchers.noBelow must not be above watchers.yesAt"}),At=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),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([])}).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({})}).default({}),It=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({})}),Ct=e.array(It).default([]).superRefine((r,t)=>{let n=lt(r);n&&t.addIssue({code:e.ZodIssueCode.custom,message:n})}),$t=e.object({logRetentionDays:e.number().int().min(1).max(3650).default(90)}).default({}),Ot=e.object({channels:e.array(e.string().min(1)).default([])}).default({}),bt=e.string().regex(/^\d{4}-\d{2}-\d{2}$/,"use YYYY-MM-DD"),Lt=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:bt,until:bt.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"})}),Nt=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({}),jt=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(),cr).default({}),agents:e.record(e.string(),xr).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(Sr).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()===""||Et(r),'expected a cron of 5 fields, such as "30 23 * * *"').optional(),timezone:e.string().refine(wr,'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:kr.default({}),crons:e.record(e.string(),te).default({}),wikiNotes:_r.default({}),services:e.record(e.string(),Er).default({}),notifications:$r,calls:Tt,whatsappTriage:Ae,approvals:Nt,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:vt}).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:At,watchers:Rt,people:Ct,members:$t,requestStatus:Ot,qualityReport:Lt,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({}),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({})}).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:Lr,mesh:Rr.default({}),meshVoices:e.record(e.string(),wt.omit({rate:!0,priority:!0,hotkey:!0}).extend({name:e.string().optional()})).default({}),voice:e.object({provider:yt.default("system"),fallback:e.enum(["system","none"]).default("system"),system:kt.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(V),noiseFilter:e.object({enabled:e.boolean().default(!0),markers:e.array(e.string()).default(Z)}).default({}),localStt:e.enum(["mlx-whisper","parakeet"]).default("mlx-whisper"),endOfTurn:e.enum(["vad","volume"]).default("vad"),hotkeys:e.object({talk:H.default(O.talk),stop:H.default(O.stop),paste:H.default(O.paste)}).default({}),look:e.enum(he).default("orb"),startReduced:e.boolean().default(!1),stroll:e.boolean().default(!1),animations:e.enum(be).default("sometimes"),palette:e.enum(G).default(L),floor:e.object({enabled:e.boolean().default(!0),defaultSeconds:e.number().int().min(5).max(3600).default(N.defaultSeconds),maxSeconds:e.number().int().min(5).max(14400).default(N.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:le.optional(),boards:de,dashboard:ce,graph:Ar,decisions:Cr,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:ft.default([]),agentxToolsByChannel:e.record(ft).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({})});Dt="wiki-contribute",Pt="wiki-contribute-merge";ee="wiki-summarize"});export{Hr as a,Br as b,me as c,gn as d,zr as e,O as f,pe as g,T as h,he as i,be as j,G as k,Xr as l,ye as m,ke as n,we as o,Yr as p,Se as q,Qr as r,eo as s,Ee as t,Te as u,ro as v,ve as w,Ae as x,Ie as y,Oe as z,Le as A,lo as B,Me as C,fo as D,We as E,P as F,jn as G,yo as H,ko as I,wo as J,Xe as K,y as L,p as M,To as N,J as O,vo as P,M as Q,Ro as R,Ge as S,Ve as T,Co as U,$o as V,F as W,Oo as X,Ye as Y,et as Z,Yn as _,Mo as $,ot as aa,qo as ba,tr as ca,Fo as da,at as ea,zo as fa,U as ga,Ko as ha,st as ia,ar as ja,ir as ka,ut as la,Xo as ma,Go as na,Vo as oa,dt as pa,ur as qa,Yo as ra,Jo as sa,mt as ta,br as ua,St as va,_t as wa,Et as xa,Tt as ya,vt as za,Rt as Aa,At as Ba,It as Ca,Ct as Da,$t as Ea,Ot as Fa,Lt as Ga,Nt as Ha,jt as Ia,z as Ja,jr as Ka,Dr as La,Dt as Ma,Pt as Na,Mt as Oa,ee as Pa,qt as Qa,Pr as Ra,Mr as Sa};
|
|
62
|
+
//# sourceMappingURL=chunk-DPIQCHXB.js.map
|