agentix-cli 0.16.0 → 0.18.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/agent-RDVGL2TW.js +2 -0
- package/dist/audit-Z47AQFFD.js +3 -0
- package/dist/audit-Z47AQFFD.js.map +1 -0
- package/dist/board-dashboard-KX2CNOSY.js +7282 -0
- package/dist/board-dashboard-KX2CNOSY.js.map +1 -0
- package/dist/chunk-34NG7UDX.js +11 -0
- package/dist/chunk-34NG7UDX.js.map +1 -0
- package/dist/{chunk-6MKAXEE5.js → chunk-4ITQP3XL.js} +2 -2
- package/dist/{chunk-6MKAXEE5.js.map → chunk-4ITQP3XL.js.map} +1 -1
- package/dist/chunk-53NRX2NA.js +2 -0
- package/dist/chunk-53NRX2NA.js.map +1 -0
- package/dist/chunk-6PPIFDOQ.js +1742 -0
- package/dist/chunk-6PPIFDOQ.js.map +1 -0
- package/dist/chunk-AFRHKA7B.js +2 -0
- package/dist/chunk-AFRHKA7B.js.map +1 -0
- package/dist/chunk-ASSJ4Z6Z.js +164 -0
- package/dist/chunk-ASSJ4Z6Z.js.map +1 -0
- package/dist/chunk-BLUXL6HR.js +854 -0
- package/dist/chunk-BLUXL6HR.js.map +1 -0
- package/dist/chunk-BTAEYL3P.js +72 -0
- package/dist/chunk-BTAEYL3P.js.map +1 -0
- package/dist/chunk-CDNWCYF7.js +2 -0
- package/dist/chunk-CDNWCYF7.js.map +1 -0
- package/dist/chunk-DJEZ2HVU.js +5 -0
- package/dist/chunk-DJEZ2HVU.js.map +1 -0
- package/dist/{chunk-FLOGK2U7.js → chunk-DSCMCVTV.js} +3 -3
- package/dist/chunk-DSCMCVTV.js.map +1 -0
- package/dist/chunk-EW27SCCC.js +2 -0
- package/dist/chunk-EW27SCCC.js.map +1 -0
- package/dist/chunk-HPDHNIA3.js +2 -0
- package/dist/chunk-IM376SD6.js +13 -0
- package/dist/chunk-IM376SD6.js.map +1 -0
- package/dist/chunk-J4WKMKTP.js +5 -0
- package/dist/{chunk-YI2T5QYB.js.map → chunk-J4WKMKTP.js.map} +1 -1
- package/dist/chunk-JGXE2YNV.js +46 -0
- package/dist/chunk-JGXE2YNV.js.map +1 -0
- package/dist/chunk-JXEPJ3YD.js +118 -0
- package/dist/chunk-JXEPJ3YD.js.map +1 -0
- package/dist/chunk-LY7JGDV7.js +6 -0
- package/dist/chunk-LY7JGDV7.js.map +1 -0
- package/dist/chunk-N67JZAXU.js +2 -0
- package/dist/chunk-N67JZAXU.js.map +1 -0
- package/dist/chunk-SR3X25TN.js +3 -0
- package/dist/chunk-SR3X25TN.js.map +1 -0
- package/dist/{chunk-7MBTMCKY.js → chunk-TRQSP67X.js} +2 -2
- package/dist/chunk-U364GNQX.js +719 -0
- package/dist/chunk-U364GNQX.js.map +1 -0
- package/dist/chunk-VJUTQAPJ.js +2 -0
- package/dist/chunk-VJUTQAPJ.js.map +1 -0
- package/dist/{chunk-HI24KAEY.js → chunk-WJXU5F4Z.js} +3 -4
- package/dist/{chunk-HI24KAEY.js.map → chunk-WJXU5F4Z.js.map} +1 -1
- package/dist/{chunk-QZP65I2H.js → chunk-ZE2YFX4K.js} +2 -2
- package/dist/cli.js +1 -320
- package/dist/cli.js.map +1 -1
- package/dist/{compaction-JBZZ7I7Y.js → compaction-S5H6DTOR.js} +6 -6
- package/dist/compaction-S5H6DTOR.js.map +1 -0
- package/dist/config-UOHEG4BW.js +2 -0
- package/dist/config-mutator-KSEBWKJH.js +2 -0
- package/dist/context-planner-2RGHJHSO.js +17 -0
- package/dist/context-planner-2RGHJHSO.js.map +1 -0
- package/dist/debug-TGAP7E7W.js +2 -0
- package/dist/discover-OATU7SHI.js +4 -0
- package/dist/discover-OATU7SHI.js.map +1 -0
- package/dist/editor-chat-FUBQLMAQ.js +137 -0
- package/dist/editor-chat-FUBQLMAQ.js.map +1 -0
- package/dist/heal-YP7QI7D3.js +2 -0
- package/dist/inbox-O5UB3YQN.js +315 -0
- package/dist/inbox-O5UB3YQN.js.map +1 -0
- package/dist/index.d.ts +3390 -1814
- package/dist/index.js +23 -53
- package/dist/index.js.map +1 -1
- package/dist/ingest-whatsapp-C3O5UPBX.js +6 -0
- package/dist/ingest-whatsapp-C3O5UPBX.js.map +1 -0
- package/dist/loader-BGWA7IRY.js +2 -0
- package/dist/loader-LBHKSXBK.js +2 -0
- package/dist/memory-extract-KKPKAOPH.js +2 -0
- package/dist/processes-7W27G3J3.js +256 -0
- package/dist/processes-7W27G3J3.js.map +1 -0
- package/dist/program-2MDELQLX.js +2 -0
- package/dist/providers-WCQSKTNP.js +2 -0
- package/dist/query-FZAL7AQD.js +2 -0
- package/dist/recipes-LZ56TA3X.js +2 -0
- package/dist/registry-ONJSILH4.js +2 -0
- package/dist/scheduler-XRM2RJIV.js +2 -0
- package/dist/slack-D27BE37Q.js +3 -0
- package/dist/slack-D27BE37Q.js.map +1 -0
- package/dist/store-4UCO56K2.js +2 -0
- package/dist/store-4UCO56K2.js.map +1 -0
- package/dist/{subagent-2SJHSSJS.js → subagent-EPOT3QDM.js} +2 -2
- package/dist/task-store-3NUWLYRY.js +2 -0
- package/dist/task-store-3NUWLYRY.js.map +1 -0
- package/dist/telegram-UWV67KWG.js +3 -0
- package/dist/telegram-UWV67KWG.js.map +1 -0
- package/dist/timers-QB4KYNAE.js +2 -0
- package/dist/timers-QB4KYNAE.js.map +1 -0
- package/dist/types-ECMEC5BZ.js +2 -0
- package/dist/types-ECMEC5BZ.js.map +1 -0
- package/dist/types-TTB6DJUE.js +2 -0
- package/dist/types-TTB6DJUE.js.map +1 -0
- package/dist/usage-dashboard-FQGPPABD.js +803 -0
- package/dist/usage-dashboard-FQGPPABD.js.map +1 -0
- package/dist/web/workflow-editor.global.js +1450 -0
- package/dist/web/workflow-editor.global.js.map +1 -0
- package/dist/whatsapp-KJ2NRKKQ.js +3 -0
- package/dist/whatsapp-KJ2NRKKQ.js.map +1 -0
- package/dist/{whatsapp-state-6D6FHHD2.js → whatsapp-state-3ZVQ6MQD.js} +2 -2
- package/dist/{wiki-UDPYFJBL.js → wiki-OUYKM7MB.js} +2 -2
- package/dist/wiki-OUYKM7MB.js.map +1 -0
- package/dist/workspace-setup-6GDBEORT.js +2 -0
- package/dist/workspace-setup-6GDBEORT.js.map +1 -0
- package/package.json +19 -2
- package/dist/agent-BCDPSPKU.js +0 -2
- package/dist/board-dashboard-LA7WW56M.js +0 -5668
- package/dist/board-dashboard-LA7WW56M.js.map +0 -1
- package/dist/chunk-6U2XOQHU.js +0 -7
- package/dist/chunk-6U2XOQHU.js.map +0 -1
- package/dist/chunk-7FJQGD6U.js +0 -6
- package/dist/chunk-7FJQGD6U.js.map +0 -1
- package/dist/chunk-ABDP7TDL.js +0 -71
- package/dist/chunk-ABDP7TDL.js.map +0 -1
- package/dist/chunk-CXY2RYNI.js +0 -11
- package/dist/chunk-CXY2RYNI.js.map +0 -1
- package/dist/chunk-FIOB3MEX.js +0 -2
- package/dist/chunk-FLOGK2U7.js.map +0 -1
- package/dist/chunk-HVC6FJNP.js +0 -108
- package/dist/chunk-HVC6FJNP.js.map +0 -1
- package/dist/chunk-K7BE4E35.js +0 -138
- package/dist/chunk-K7BE4E35.js.map +0 -1
- package/dist/chunk-KXJSLLSQ.js +0 -199
- package/dist/chunk-KXJSLLSQ.js.map +0 -1
- package/dist/chunk-KXZMYLHQ.js +0 -46
- package/dist/chunk-KXZMYLHQ.js.map +0 -1
- package/dist/chunk-M7HKBG3V.js +0 -2
- package/dist/chunk-M7HKBG3V.js.map +0 -1
- package/dist/chunk-MCJUAE6V.js +0 -6
- package/dist/chunk-MCJUAE6V.js.map +0 -1
- package/dist/chunk-RB7265LO.js +0 -12
- package/dist/chunk-RB7265LO.js.map +0 -1
- package/dist/chunk-YI2T5QYB.js +0 -5
- package/dist/compaction-JBZZ7I7Y.js.map +0 -1
- package/dist/config-LN6JEHMF.js +0 -2
- package/dist/config-mutator-24TH7PHM.js +0 -2
- package/dist/debug-3MEUO2P2.js +0 -2
- package/dist/heal-WKY5I4KB.js +0 -2
- package/dist/loader-PTCXLJEX.js +0 -2
- package/dist/memory-extract-AD7KMRPC.js +0 -2
- package/dist/providers-VUIWO3ZK.js +0 -2
- package/dist/query-PKGYE5HR.js +0 -2
- package/dist/registry-ASC2QS5K.js +0 -2
- package/dist/scheduler-A3TQOCKT.js +0 -2
- package/dist/types-OIFU3KSQ.js +0 -2
- package/dist/usage-dashboard-QX3CF6KK.js +0 -404
- package/dist/usage-dashboard-QX3CF6KK.js.map +0 -1
- package/dist/workspace-setup-ZWTT42XT.js +0 -2
- /package/dist/{agent-BCDPSPKU.js.map → agent-RDVGL2TW.js.map} +0 -0
- /package/dist/{chunk-FIOB3MEX.js.map → chunk-HPDHNIA3.js.map} +0 -0
- /package/dist/{chunk-7MBTMCKY.js.map → chunk-TRQSP67X.js.map} +0 -0
- /package/dist/{chunk-QZP65I2H.js.map → chunk-ZE2YFX4K.js.map} +0 -0
- /package/dist/{config-LN6JEHMF.js.map → config-UOHEG4BW.js.map} +0 -0
- /package/dist/{config-mutator-24TH7PHM.js.map → config-mutator-KSEBWKJH.js.map} +0 -0
- /package/dist/{debug-3MEUO2P2.js.map → debug-TGAP7E7W.js.map} +0 -0
- /package/dist/{heal-WKY5I4KB.js.map → heal-YP7QI7D3.js.map} +0 -0
- /package/dist/{loader-PTCXLJEX.js.map → loader-BGWA7IRY.js.map} +0 -0
- /package/dist/{memory-extract-AD7KMRPC.js.map → loader-LBHKSXBK.js.map} +0 -0
- /package/dist/{providers-VUIWO3ZK.js.map → memory-extract-KKPKAOPH.js.map} +0 -0
- /package/dist/{query-PKGYE5HR.js.map → program-2MDELQLX.js.map} +0 -0
- /package/dist/{registry-ASC2QS5K.js.map → providers-WCQSKTNP.js.map} +0 -0
- /package/dist/{scheduler-A3TQOCKT.js.map → query-FZAL7AQD.js.map} +0 -0
- /package/dist/{types-OIFU3KSQ.js.map → recipes-LZ56TA3X.js.map} +0 -0
- /package/dist/{wiki-UDPYFJBL.js.map → registry-ONJSILH4.js.map} +0 -0
- /package/dist/{workspace-setup-ZWTT42XT.js.map → scheduler-XRM2RJIV.js.map} +0 -0
- /package/dist/{subagent-2SJHSSJS.js.map → subagent-EPOT3QDM.js.map} +0 -0
- /package/dist/{whatsapp-state-6D6FHHD2.js.map → whatsapp-state-3ZVQ6MQD.js.map} +0 -0
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import"./chunk-HPDHNIA3.js";import{createHash as C}from"crypto";function $(e,n){if(!e.enabled)return[];let t=n.listContacts(),d=n.listChats(),i=[],a=r=>r.replace(/^\+/,"").toLowerCase(),s=e.allowContacts.map(a),o=e.allowGroups.map(a),c=e.denyContacts.map(a),u=e.denyGroups.map(a),m=(r,g)=>{if(g.length===0)return!1;let f=a(r);return g.some(p=>f.includes(p)||p.includes(f))};for(let r of t){let g=`${r.phone}|${r.jid}`;if(m(g,c)||!m(g,s))continue;let f=d.find(p=>!p.isGroup&&p.jid===r.jid);i.push({kind:"contact",jid:r.jid,contact:r,chat:f})}for(let r of d)r.isGroup&&(m(r.jid,u)||m(r.jid,o)&&i.push({kind:"group",jid:r.jid,chat:r}));return i}var h=(e=new Date)=>e.toISOString().slice(0,10),l=(e=new Date)=>h(e).replace(/-/g,"");function w(e){return C("sha1").update(e).digest("hex").slice(0,10)}function R(e,n,t=new Date){let d=h(t),i=`wa-contact-${w(e.jid)}-${l(t)}`,s=[`# Contact: ${e.savedName||e.pushName||e.phone||e.jid}`,""];return s.push(`- **JID**: ${e.jid}`),e.phone&&s.push(`- **Phone**: +${e.phone}`),e.savedName&&s.push(`- **Saved name**: ${e.savedName}`),e.pushName&&e.pushName!==e.savedName&&s.push(`- **Push name**: ${e.pushName}`),e.status&&s.push(`- **Status**: ${e.status}`),e.updatedAt&&s.push(`- **Last observed**: ${e.updatedAt}`),{id:i,date:d,agentId:n,source:"whatsapp:contact",sourceContext:e.jid,content:s.join(`
|
|
2
|
+
`),meta:{jid:e.jid,phone:e.phone,kind:"contact",profileHash:x(e)}}}function k(e,n,t=new Date){let d=h(t),i=`wa-group-meta-${w(e.jid)}-${l(t)}`,a=[`# Group: ${e.subject||"(unnamed)"}`,""];a.push(`- **JID**: ${e.jid}`),e.description&&a.push(`- **Description**: ${e.description}`),e.owner&&a.push(`- **Owner**: ${e.owner}`),a.push(`- **Members (${e.memberCount})**:`);let s=e.members.slice(0,50);for(let o of s){let c=o.admin?` (${o.admin})`:"";a.push(` - ${o.jid}${c}`)}return e.members.length>s.length&&a.push(` - \u2026and ${e.members.length-s.length} more`),{id:i,date:d,agentId:n,source:"whatsapp:group-meta",sourceContext:e.jid,content:a.join(`
|
|
3
|
+
`),meta:{jid:e.jid,subject:e.subject,memberCount:e.memberCount,kind:"group-meta"}}}function D(e,n,t,d,i=new Date){if(t.length===0)return null;let a=t[t.length-1].id,s=h(i),o=`wa-dm-${w(e.jid)}-${l(i)}-${b(a)}`,c=n?.savedName||n?.pushName||e.name||e.jid,u=[`# Chat with ${c}`,""];u.push(`- **JID**: ${e.jid}`),n?.phone&&u.push(`- **Phone**: +${n.phone}`),u.push(`- **Messages**: ${t.length}`),u.push("");for(let m of t)u.push(y(m,c));return{id:o,date:s,agentId:d,source:"whatsapp:dm",sourceContext:e.jid,content:u.join(`
|
|
4
|
+
`),meta:{jid:e.jid,kind:"dm",messageCount:t.length,oldestMsgTs:t[0]?.timestamp,newestMsgTs:t[t.length-1]?.timestamp,lastMsgId:a}}}function M(e,n,t,d,i=new Date){if(t.length===0)return null;let a=t[t.length-1].id,s=h(i),o=`wa-group-${w(e.jid)}-${l(i)}-${b(a)}`,c=[`# Group chat: ${n.subject||e.name}`,""];c.push(`- **JID**: ${e.jid}`),c.push(`- **Members**: ${n.memberCount}`),c.push(`- **Messages**: ${t.length}`),c.push("");for(let u of t)c.push(y(u,u.fromJid));return{id:o,date:s,agentId:d,source:"whatsapp:group",sourceContext:e.jid,content:c.join(`
|
|
5
|
+
`),meta:{jid:e.jid,kind:"group",messageCount:t.length,lastMsgId:a}}}function y(e,n){let t=e.timestamp?G(e.timestamp):"",d=e.fromMe?"me":n,i=e.text;return!i&&e.media&&(e.media.kind==="image"?i=`[image${e.media.caption?`: ${e.media.caption}`:""}]`:e.media.kind==="audio"?i="[voice message]":e.media.kind==="video"?i=`[video${e.media.caption?`: ${e.media.caption}`:""}]`:e.media.kind==="document"?i=`[document: ${e.media.filename??"unnamed"}]`:e.media.kind==="sticker"&&(i="[sticker]")),`[${t}] ${d}: ${i}`}function G(e){try{return new Date(e*1e3).toISOString().replace("T"," ").slice(0,16)}catch{return String(e)}}function b(e){return e.replace(/[^A-Za-z0-9]/g,"").slice(-10)}function x(e){let n=C("sha1");return n.update(e.jid),n.update("|"),n.update(e.savedName||""),n.update("|"),n.update(e.pushName||""),n.update("|"),n.update(e.status||""),n.digest("hex").slice(0,10)}async function I(e){let n=e.now??new Date,t={scannedContacts:0,scannedGroups:0,wroteContacts:0,wroteGroups:0,wroteDmWindows:0,wroteGroupWindows:0,skippedUnchanged:0,errors:[]},d=[],i=s=>{if(e.dryRun){d.push(s);return}e.store.addEntry(s)};if(!e.source.isConnected()&&!e.dryRun)return t.errors.push({jid:"-",kind:"source",message:"WhatsApp socket not connected"}),{...t,dryRunEntries:e.dryRun?d:void 0};let a=$(e.config,e.source);a.length>e.config.throttle.maxChatsPerSweep&&(a=a.slice(0,e.config.throttle.maxChatsPerSweep));for(let s of a)try{if(s.kind==="contact"){t.scannedContacts++;let o=s.contact??await e.source.getContactProfile(s.jid);if(!o)continue;let c=R(o,e.agentId,n);if(i(c),t.wroteContacts++,e.config.mode==="messages"){let u=await e.source.getHistory(s.jid,{limit:e.config.messageCap}),m=j(u,e.config.historyDays,n);if(m.length>0&&s.chat){let r=D(s.chat,o,m,e.agentId,n);r&&(i(r),t.wroteDmWindows++)}}}else{t.scannedGroups++;let o=await e.source.getGroupMetadata(s.jid);if(!o)continue;let c=k(o,e.agentId,n);if(i(c),t.wroteGroups++,e.config.mode==="messages"&&s.chat){let u=await e.source.getHistory(s.jid,{limit:e.config.messageCap}),m=j(u,e.config.historyDays,n);if(m.length>0){let r=M(s.chat,o,m,e.agentId,n);r&&(i(r),t.wroteGroupWindows++)}}}}catch(o){t.errors.push({jid:s.jid,kind:s.kind,message:o?.message??String(o)})}return{...t,dryRunEntries:e.dryRun?d:void 0}}function j(e,n,t){let d=Math.floor((t.getTime()-n*864e5)/1e3);return e.filter(i=>!i.timestamp||i.timestamp>=d)}export{x as hashProfile,$ as resolveScope,I as runSweep,R as transformContact,D as transformDm,M as transformGroupMessages,k as transformGroupMeta};
|
|
6
|
+
//# sourceMappingURL=ingest-whatsapp-C3O5UPBX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/wiki/ingest-whatsapp.ts"],"sourcesContent":["import { createHash } from \"crypto\"\nimport type { WikiEntry } from \"./types\"\n\n// --- WhatsApp → Wiki ingestor ---\n//\n// Pure transforms + orchestration. Takes a read-only WhatsApp source\n// (the adapter's read API, structurally typed below) and emits raw wiki\n// entries through the provided `addEntry` callback. Never imports\n// @whiskeysockets/baileys — keeps the transform layer testable with a\n// mock source and isolates us from Baileys version drift.\n//\n// Design notes\n// ------------\n// - One raw entry per WhatsApp primitive (contact, group-meta, dm-window,\n// group-window). The existing absorb pipeline at src/wiki/prompts.ts\n// handles typed article creation (person, project, event) — we don't\n// reinvent it here.\n// - Stable IDs are the dedup mechanism. Re-running an ingest emits the\n// SAME filename for unchanged data (filesystem = database), so repeated\n// sweeps are near-free.\n// - Default mode is \"metadata-only\" — message bodies are opt-in per chat\n// via `mode: \"messages\"`. Privacy surface stays narrow by default.\n\n// --- Source contract (structurally typed — mock-friendly) ---\n\nexport interface ContactRead {\n jid: string\n phone: string\n name?: string\n pushName?: string\n savedName?: string\n status?: string\n updatedAt?: string\n}\nexport interface ChatRead {\n jid: string\n name: string\n isGroup: boolean\n lastMessageAt?: number\n unreadCount?: number\n}\nexport interface GroupRead {\n jid: string\n subject: string\n description?: string\n owner?: string\n members: Array<{ jid: string; admin?: \"admin\" | \"superadmin\" }>\n memberCount: number\n}\nexport interface MessageRead {\n id: string\n fromJid: string\n fromMe: boolean\n timestamp?: number\n text: string\n media?: { kind: \"image\" | \"audio\" | \"video\" | \"document\" | \"sticker\"; caption?: string; filename?: string }\n}\n\nexport interface WhatsAppSource {\n isConnected(): boolean\n listContacts(): ContactRead[]\n listChats(): ChatRead[]\n getContactProfile(jid: string): Promise<ContactRead | null>\n getGroupMetadata(jid: string): Promise<GroupRead | null>\n getHistory(jid: string, opts: { limit?: number; before?: string }): Promise<MessageRead[]>\n}\n\n// --- Config (mirror of channels.whatsapp.ingest in src/daemon/config.ts) ---\n\nexport interface IngestConfig {\n enabled: boolean\n mode: \"metadata-only\" | \"messages\"\n allowContacts: string[]\n allowGroups: string[]\n denyContacts: string[]\n denyGroups: string[]\n messageCap: number\n historyDays: number\n contactRefreshDays: number\n throttle: {\n minMsBetweenCalls: number\n maxCallsPerMinute: number\n maxChatsPerSweep: number\n }\n retentionDays: number\n}\n\n// --- Scope resolution ---\n\nexport interface IngestTarget {\n kind: \"contact\" | \"group\"\n jid: string\n /** The resolved cached record for reference during transform. */\n contact?: ContactRead\n chat?: ChatRead\n}\n\n/** Apply allow/deny rules to the cached catalog. Rules:\n * 1. If not `enabled`, return empty (master switch).\n * 2. Deny precedence: a JID matching any `denyContacts` / `denyGroups`\n * entry is dropped regardless of other matches.\n * 3. Empty allowlists = ingest NOTHING (defensive — avoids accidental\n * full-sweep when someone enables the feature without scoping).\n * 4. Phone/JID match is substring-based (matches `allowFrom` semantics\n * at src/channels/whatsapp.ts). `+`-prefixed numbers are normalized.\n */\nexport function resolveScope(config: IngestConfig, source: WhatsAppSource): IngestTarget[] {\n if (!config.enabled) return []\n\n const contacts = source.listContacts()\n const chats = source.listChats()\n const targets: IngestTarget[] = []\n\n const normalize = (s: string) => s.replace(/^\\+/, \"\").toLowerCase()\n const allowC = config.allowContacts.map(normalize)\n const allowG = config.allowGroups.map(normalize)\n const denyC = config.denyContacts.map(normalize)\n const denyG = config.denyGroups.map(normalize)\n\n const matchesAny = (value: string, patterns: string[]) => {\n if (patterns.length === 0) return false\n const v = normalize(value)\n return patterns.some((p) => v.includes(p) || p.includes(v))\n }\n\n for (const c of contacts) {\n const needle = `${c.phone}|${c.jid}`\n if (matchesAny(needle, denyC)) continue\n if (!matchesAny(needle, allowC)) continue\n // Find the chat record for this contact if one exists (optional).\n const chat = chats.find((x) => !x.isGroup && x.jid === c.jid)\n targets.push({ kind: \"contact\", jid: c.jid, contact: c, chat })\n }\n\n for (const ch of chats) {\n if (!ch.isGroup) continue\n if (matchesAny(ch.jid, denyG)) continue\n if (!matchesAny(ch.jid, allowG)) continue\n targets.push({ kind: \"group\", jid: ch.jid, chat: ch })\n }\n\n return targets\n}\n\n// --- Transforms (pure, testable) ---\n\nconst ISO_DAY = (d: Date = new Date()) => d.toISOString().slice(0, 10)\nconst YYYYMMDD = (d: Date = new Date()) => ISO_DAY(d).replace(/-/g, \"\")\n\n/** Short, stable JID hash — keeps filenames readable while still unique\n * across different JIDs that share a prefix. 10 hex chars is plenty for\n * a personal account's contact set. */\nfunction jidHash(jid: string): string {\n return createHash(\"sha1\").update(jid).digest(\"hex\").slice(0, 10)\n}\n\nexport function transformContact(profile: ContactRead, agentId: string, now: Date = new Date()): WikiEntry {\n const date = ISO_DAY(now)\n const id = `wa-contact-${jidHash(profile.jid)}-${YYYYMMDD(now)}`\n const displayName = profile.savedName || profile.pushName || profile.phone || profile.jid\n const lines: string[] = [`# Contact: ${displayName}`, \"\"]\n lines.push(`- **JID**: ${profile.jid}`)\n if (profile.phone) lines.push(`- **Phone**: +${profile.phone}`)\n if (profile.savedName) lines.push(`- **Saved name**: ${profile.savedName}`)\n if (profile.pushName && profile.pushName !== profile.savedName) lines.push(`- **Push name**: ${profile.pushName}`)\n if (profile.status) lines.push(`- **Status**: ${profile.status}`)\n if (profile.updatedAt) lines.push(`- **Last observed**: ${profile.updatedAt}`)\n\n return {\n id,\n date,\n agentId,\n source: \"whatsapp:contact\",\n sourceContext: profile.jid,\n content: lines.join(\"\\n\"),\n meta: {\n jid: profile.jid,\n phone: profile.phone,\n kind: \"contact\",\n profileHash: hashProfile(profile),\n },\n }\n}\n\nexport function transformGroupMeta(group: GroupRead, agentId: string, now: Date = new Date()): WikiEntry {\n const date = ISO_DAY(now)\n const id = `wa-group-meta-${jidHash(group.jid)}-${YYYYMMDD(now)}`\n const lines: string[] = [`# Group: ${group.subject || \"(unnamed)\"}`, \"\"]\n lines.push(`- **JID**: ${group.jid}`)\n if (group.description) lines.push(`- **Description**: ${group.description}`)\n if (group.owner) lines.push(`- **Owner**: ${group.owner}`)\n lines.push(`- **Members (${group.memberCount})**:`)\n // Cap roster at 50 so a 500-member group doesn't explode the entry.\n // The absorb pipeline cares more about who's there than an exhaustive list.\n const topMembers = group.members.slice(0, 50)\n for (const m of topMembers) {\n const adminTag = m.admin ? ` (${m.admin})` : \"\"\n lines.push(` - ${m.jid}${adminTag}`)\n }\n if (group.members.length > topMembers.length) {\n lines.push(` - …and ${group.members.length - topMembers.length} more`)\n }\n\n return {\n id,\n date,\n agentId,\n source: \"whatsapp:group-meta\",\n sourceContext: group.jid,\n content: lines.join(\"\\n\"),\n meta: {\n jid: group.jid,\n subject: group.subject,\n memberCount: group.memberCount,\n kind: \"group-meta\",\n },\n }\n}\n\nexport function transformDm(\n chat: ChatRead,\n contact: ContactRead | undefined,\n messages: MessageRead[],\n agentId: string,\n now: Date = new Date(),\n): WikiEntry | null {\n if (messages.length === 0) return null\n const lastMsgId = messages[messages.length - 1]!.id\n const date = ISO_DAY(now)\n const id = `wa-dm-${jidHash(chat.jid)}-${YYYYMMDD(now)}-${shortId(lastMsgId)}`\n const displayName = contact?.savedName || contact?.pushName || chat.name || chat.jid\n const lines: string[] = [`# Chat with ${displayName}`, \"\"]\n lines.push(`- **JID**: ${chat.jid}`)\n if (contact?.phone) lines.push(`- **Phone**: +${contact.phone}`)\n lines.push(`- **Messages**: ${messages.length}`)\n lines.push(\"\")\n for (const m of messages) {\n lines.push(renderMessageLine(m, displayName))\n }\n\n return {\n id,\n date,\n agentId,\n source: \"whatsapp:dm\",\n sourceContext: chat.jid,\n content: lines.join(\"\\n\"),\n meta: {\n jid: chat.jid,\n kind: \"dm\",\n messageCount: messages.length,\n oldestMsgTs: messages[0]?.timestamp,\n newestMsgTs: messages[messages.length - 1]?.timestamp,\n lastMsgId,\n },\n }\n}\n\nexport function transformGroupMessages(\n chat: ChatRead,\n group: GroupRead,\n messages: MessageRead[],\n agentId: string,\n now: Date = new Date(),\n): WikiEntry | null {\n if (messages.length === 0) return null\n const lastMsgId = messages[messages.length - 1]!.id\n const date = ISO_DAY(now)\n const id = `wa-group-${jidHash(chat.jid)}-${YYYYMMDD(now)}-${shortId(lastMsgId)}`\n const lines: string[] = [`# Group chat: ${group.subject || chat.name}`, \"\"]\n lines.push(`- **JID**: ${chat.jid}`)\n lines.push(`- **Members**: ${group.memberCount}`)\n lines.push(`- **Messages**: ${messages.length}`)\n lines.push(\"\")\n for (const m of messages) {\n lines.push(renderMessageLine(m, m.fromJid))\n }\n\n return {\n id,\n date,\n agentId,\n source: \"whatsapp:group\",\n sourceContext: chat.jid,\n content: lines.join(\"\\n\"),\n meta: {\n jid: chat.jid,\n kind: \"group\",\n messageCount: messages.length,\n lastMsgId,\n },\n }\n}\n\nfunction renderMessageLine(m: MessageRead, senderLabel: string): string {\n const ts = m.timestamp ? formatTs(m.timestamp) : \"\"\n const who = m.fromMe ? \"me\" : senderLabel\n let body = m.text\n if (!body && m.media) {\n if (m.media.kind === \"image\") body = `[image${m.media.caption ? `: ${m.media.caption}` : \"\"}]`\n else if (m.media.kind === \"audio\") body = \"[voice message]\"\n else if (m.media.kind === \"video\") body = `[video${m.media.caption ? `: ${m.media.caption}` : \"\"}]`\n else if (m.media.kind === \"document\") body = `[document: ${m.media.filename ?? \"unnamed\"}]`\n else if (m.media.kind === \"sticker\") body = \"[sticker]\"\n }\n return `[${ts}] ${who}: ${body}`\n}\n\nfunction formatTs(seconds: number): string {\n try { return new Date(seconds * 1000).toISOString().replace(\"T\", \" \").slice(0, 16) }\n catch { return String(seconds) }\n}\n\nfunction shortId(id: string): string {\n return id.replace(/[^A-Za-z0-9]/g, \"\").slice(-10)\n}\n\n/** Hash of the fields we consider when deciding whether a contact entry\n * needs re-writing. Used by the sweep to skip unchanged contacts inside\n * the refresh window. */\nexport function hashProfile(profile: ContactRead): string {\n const h = createHash(\"sha1\")\n h.update(profile.jid)\n h.update(\"|\")\n h.update(profile.savedName || \"\")\n h.update(\"|\")\n h.update(profile.pushName || \"\")\n h.update(\"|\")\n h.update(profile.status || \"\")\n return h.digest(\"hex\").slice(0, 10)\n}\n\n// --- Sweep orchestration ---\n\nexport interface SweepReport {\n scannedContacts: number\n scannedGroups: number\n wroteContacts: number\n wroteGroups: number\n wroteDmWindows: number\n wroteGroupWindows: number\n skippedUnchanged: number\n errors: Array<{ jid: string; kind: string; message: string }>\n}\n\n/** Lightweight store interface the sweep calls against. Accepts anything\n * that can `addEntry(WikiEntry)` — the real `WikiStore.addEntry` at\n * src/wiki/store.ts:67-89 matches. */\nexport interface EntryStore {\n addEntry(entry: WikiEntry): string\n}\n\n/** Orchestrates a full ingest pass. Iterates scope → fetches metadata →\n * (optionally) fetches history → writes raw entries. Per-target\n * try/catch so one failure doesn't abort the pass.\n *\n * `dryRun`: compute transforms but skip `addEntry`. Returns the would-\n * have-been entries in `dryRunEntries` for the caller to display. */\nexport async function runSweep(opts: {\n source: WhatsAppSource\n store: EntryStore\n config: IngestConfig\n agentId: string\n now?: Date\n dryRun?: boolean\n}): Promise<SweepReport & { dryRunEntries?: WikiEntry[] }> {\n const now = opts.now ?? new Date()\n const report: SweepReport = {\n scannedContacts: 0,\n scannedGroups: 0,\n wroteContacts: 0,\n wroteGroups: 0,\n wroteDmWindows: 0,\n wroteGroupWindows: 0,\n skippedUnchanged: 0,\n errors: [],\n }\n const dryRunEntries: WikiEntry[] = []\n const writeOrStage = (entry: WikiEntry) => {\n if (opts.dryRun) { dryRunEntries.push(entry); return }\n opts.store.addEntry(entry)\n }\n\n if (!opts.source.isConnected() && !opts.dryRun) {\n report.errors.push({ jid: \"-\", kind: \"source\", message: \"WhatsApp socket not connected\" })\n return { ...report, dryRunEntries: opts.dryRun ? dryRunEntries : undefined }\n }\n\n let targets = resolveScope(opts.config, opts.source)\n // Cap per-sweep to protect the Baileys socket from burst reads.\n if (targets.length > opts.config.throttle.maxChatsPerSweep) {\n targets = targets.slice(0, opts.config.throttle.maxChatsPerSweep)\n }\n\n for (const t of targets) {\n try {\n if (t.kind === \"contact\") {\n report.scannedContacts++\n const profile = t.contact ?? (await opts.source.getContactProfile(t.jid))\n if (!profile) continue\n const entry = transformContact(profile, opts.agentId, now)\n writeOrStage(entry)\n report.wroteContacts++\n\n // DM message window only when operator opts in.\n if (opts.config.mode === \"messages\") {\n const msgs = await opts.source.getHistory(t.jid, { limit: opts.config.messageCap })\n const bounded = bounded_by_history_days(msgs, opts.config.historyDays, now)\n if (bounded.length > 0 && t.chat) {\n const dm = transformDm(t.chat, profile, bounded, opts.agentId, now)\n if (dm) { writeOrStage(dm); report.wroteDmWindows++ }\n }\n }\n } else {\n report.scannedGroups++\n const group = await opts.source.getGroupMetadata(t.jid)\n if (!group) continue\n const entry = transformGroupMeta(group, opts.agentId, now)\n writeOrStage(entry)\n report.wroteGroups++\n\n if (opts.config.mode === \"messages\" && t.chat) {\n const msgs = await opts.source.getHistory(t.jid, { limit: opts.config.messageCap })\n const bounded = bounded_by_history_days(msgs, opts.config.historyDays, now)\n if (bounded.length > 0) {\n const window = transformGroupMessages(t.chat, group, bounded, opts.agentId, now)\n if (window) { writeOrStage(window); report.wroteGroupWindows++ }\n }\n }\n }\n } catch (e: any) {\n report.errors.push({ jid: t.jid, kind: t.kind, message: e?.message ?? String(e) })\n }\n }\n\n return { ...report, dryRunEntries: opts.dryRun ? dryRunEntries : undefined }\n}\n\n/** Keep only messages within the `historyDays` window ending at `now`.\n * Messages without a timestamp are kept (they're typically recent). */\nfunction bounded_by_history_days(msgs: MessageRead[], historyDays: number, now: Date): MessageRead[] {\n const cutoff = Math.floor((now.getTime() - historyDays * 86400_000) / 1000)\n return msgs.filter((m) => !m.timestamp || m.timestamp >= cutoff)\n}\n"],"mappings":"4BAAA,OAAS,cAAAA,MAAkB,SA0GpB,SAASC,EAAaC,EAAsBC,EAAwC,CACzF,GAAI,CAACD,EAAO,QAAS,MAAO,CAAC,EAE7B,IAAME,EAAWD,EAAO,aAAa,EAC/BE,EAAQF,EAAO,UAAU,EACzBG,EAA0B,CAAC,EAE3BC,EAAaC,GAAcA,EAAE,QAAQ,MAAO,EAAE,EAAE,YAAY,EAC5DC,EAASP,EAAO,cAAc,IAAIK,CAAS,EAC3CG,EAASR,EAAO,YAAY,IAAIK,CAAS,EACzCI,EAAQT,EAAO,aAAa,IAAIK,CAAS,EACzCK,EAAQV,EAAO,WAAW,IAAIK,CAAS,EAEvCM,EAAa,CAACC,EAAeC,IAAuB,CACxD,GAAIA,EAAS,SAAW,EAAG,MAAO,GAClC,IAAMC,EAAIT,EAAUO,CAAK,EACzB,OAAOC,EAAS,KAAM,GAAMC,EAAE,SAAS,CAAC,GAAK,EAAE,SAASA,CAAC,CAAC,CAC5D,EAEA,QAAWC,KAAKb,EAAU,CACxB,IAAMc,EAAS,GAAGD,EAAE,SAASA,EAAE,MAE/B,GADIJ,EAAWK,EAAQP,CAAK,GACxB,CAACE,EAAWK,EAAQT,CAAM,EAAG,SAEjC,IAAMU,EAAOd,EAAM,KAAMe,GAAM,CAACA,EAAE,SAAWA,EAAE,MAAQH,EAAE,GAAG,EAC5DX,EAAQ,KAAK,CAAE,KAAM,UAAW,IAAKW,EAAE,IAAK,QAASA,EAAG,KAAAE,CAAK,CAAC,EAGhE,QAAWE,KAAMhB,EACVgB,EAAG,UACJR,EAAWQ,EAAG,IAAKT,CAAK,GACvBC,EAAWQ,EAAG,IAAKX,CAAM,GAC9BJ,EAAQ,KAAK,CAAE,KAAM,QAAS,IAAKe,EAAG,IAAK,KAAMA,CAAG,CAAC,GAGvD,OAAOf,CACT,CAIA,IAAMgB,EAAU,CAACC,EAAU,IAAI,OAAWA,EAAE,YAAY,EAAE,MAAM,EAAG,EAAE,EAC/DC,EAAW,CAACD,EAAU,IAAI,OAAWD,EAAQC,CAAC,EAAE,QAAQ,KAAM,EAAE,EAKtE,SAASE,EAAQC,EAAqB,CACpC,OAAO1B,EAAW,MAAM,EAAE,OAAO0B,CAAG,EAAE,OAAO,KAAK,EAAE,MAAM,EAAG,EAAE,CACjE,CAEO,SAASC,EAAiBC,EAAsBC,EAAiBC,EAAY,IAAI,KAAmB,CACzG,IAAMC,EAAOT,EAAQQ,CAAG,EAClBE,EAAK,cAAcP,EAAQG,EAAQ,GAAG,KAAKJ,EAASM,CAAG,IAEvDG,EAAkB,CAAC,cADLL,EAAQ,WAAaA,EAAQ,UAAYA,EAAQ,OAASA,EAAQ,MAChC,EAAE,EACxD,OAAAK,EAAM,KAAK,cAAcL,EAAQ,KAAK,EAClCA,EAAQ,OAAOK,EAAM,KAAK,iBAAiBL,EAAQ,OAAO,EAC1DA,EAAQ,WAAWK,EAAM,KAAK,qBAAqBL,EAAQ,WAAW,EACtEA,EAAQ,UAAYA,EAAQ,WAAaA,EAAQ,WAAWK,EAAM,KAAK,oBAAoBL,EAAQ,UAAU,EAC7GA,EAAQ,QAAQK,EAAM,KAAK,iBAAiBL,EAAQ,QAAQ,EAC5DA,EAAQ,WAAWK,EAAM,KAAK,wBAAwBL,EAAQ,WAAW,EAEtE,CACL,GAAAI,EACA,KAAAD,EACA,QAAAF,EACA,OAAQ,mBACR,cAAeD,EAAQ,IACvB,QAASK,EAAM,KAAK;AAAA,CAAI,EACxB,KAAM,CACJ,IAAKL,EAAQ,IACb,MAAOA,EAAQ,MACf,KAAM,UACN,YAAaM,EAAYN,CAAO,CAClC,CACF,CACF,CAEO,SAASO,EAAmBC,EAAkBP,EAAiBC,EAAY,IAAI,KAAmB,CACvG,IAAMC,EAAOT,EAAQQ,CAAG,EAClBE,EAAK,iBAAiBP,EAAQW,EAAM,GAAG,KAAKZ,EAASM,CAAG,IACxDG,EAAkB,CAAC,YAAYG,EAAM,SAAW,cAAe,EAAE,EACvEH,EAAM,KAAK,cAAcG,EAAM,KAAK,EAChCA,EAAM,aAAaH,EAAM,KAAK,sBAAsBG,EAAM,aAAa,EACvEA,EAAM,OAAOH,EAAM,KAAK,gBAAgBG,EAAM,OAAO,EACzDH,EAAM,KAAK,gBAAgBG,EAAM,iBAAiB,EAGlD,IAAMC,EAAaD,EAAM,QAAQ,MAAM,EAAG,EAAE,EAC5C,QAAWE,KAAKD,EAAY,CAC1B,IAAME,EAAWD,EAAE,MAAQ,KAAKA,EAAE,SAAW,GAC7CL,EAAM,KAAK,OAAOK,EAAE,MAAMC,GAAU,EAEtC,OAAIH,EAAM,QAAQ,OAASC,EAAW,QACpCJ,EAAM,KAAK,iBAAYG,EAAM,QAAQ,OAASC,EAAW,aAAa,EAGjE,CACL,GAAAL,EACA,KAAAD,EACA,QAAAF,EACA,OAAQ,sBACR,cAAeO,EAAM,IACrB,QAASH,EAAM,KAAK;AAAA,CAAI,EACxB,KAAM,CACJ,IAAKG,EAAM,IACX,QAASA,EAAM,QACf,YAAaA,EAAM,YACnB,KAAM,YACR,CACF,CACF,CAEO,SAASI,EACdrB,EACAsB,EACAC,EACAb,EACAC,EAAY,IAAI,KACE,CAClB,GAAIY,EAAS,SAAW,EAAG,OAAO,KAClC,IAAMC,EAAYD,EAASA,EAAS,OAAS,CAAC,EAAG,GAC3CX,EAAOT,EAAQQ,CAAG,EAClBE,EAAK,SAASP,EAAQN,EAAK,GAAG,KAAKK,EAASM,CAAG,KAAKc,EAAQD,CAAS,IACrEE,EAAcJ,GAAS,WAAaA,GAAS,UAAYtB,EAAK,MAAQA,EAAK,IAC3Ec,EAAkB,CAAC,eAAeY,IAAe,EAAE,EACzDZ,EAAM,KAAK,cAAcd,EAAK,KAAK,EAC/BsB,GAAS,OAAOR,EAAM,KAAK,iBAAiBQ,EAAQ,OAAO,EAC/DR,EAAM,KAAK,mBAAmBS,EAAS,QAAQ,EAC/CT,EAAM,KAAK,EAAE,EACb,QAAW,KAAKS,EACdT,EAAM,KAAKa,EAAkB,EAAGD,CAAW,CAAC,EAG9C,MAAO,CACL,GAAAb,EACA,KAAAD,EACA,QAAAF,EACA,OAAQ,cACR,cAAeV,EAAK,IACpB,QAASc,EAAM,KAAK;AAAA,CAAI,EACxB,KAAM,CACJ,IAAKd,EAAK,IACV,KAAM,KACN,aAAcuB,EAAS,OACvB,YAAaA,EAAS,CAAC,GAAG,UAC1B,YAAaA,EAASA,EAAS,OAAS,CAAC,GAAG,UAC5C,UAAAC,CACF,CACF,CACF,CAEO,SAASI,EACd5B,EACAiB,EACAM,EACAb,EACAC,EAAY,IAAI,KACE,CAClB,GAAIY,EAAS,SAAW,EAAG,OAAO,KAClC,IAAMC,EAAYD,EAASA,EAAS,OAAS,CAAC,EAAG,GAC3CX,EAAOT,EAAQQ,CAAG,EAClBE,EAAK,YAAYP,EAAQN,EAAK,GAAG,KAAKK,EAASM,CAAG,KAAKc,EAAQD,CAAS,IACxEV,EAAkB,CAAC,iBAAiBG,EAAM,SAAWjB,EAAK,OAAQ,EAAE,EAC1Ec,EAAM,KAAK,cAAcd,EAAK,KAAK,EACnCc,EAAM,KAAK,kBAAkBG,EAAM,aAAa,EAChDH,EAAM,KAAK,mBAAmBS,EAAS,QAAQ,EAC/CT,EAAM,KAAK,EAAE,EACb,QAAWK,KAAKI,EACdT,EAAM,KAAKa,EAAkBR,EAAGA,EAAE,OAAO,CAAC,EAG5C,MAAO,CACL,GAAAN,EACA,KAAAD,EACA,QAAAF,EACA,OAAQ,iBACR,cAAeV,EAAK,IACpB,QAASc,EAAM,KAAK;AAAA,CAAI,EACxB,KAAM,CACJ,IAAKd,EAAK,IACV,KAAM,QACN,aAAcuB,EAAS,OACvB,UAAAC,CACF,CACF,CACF,CAEA,SAASG,EAAkBR,EAAgBU,EAA6B,CACtE,IAAMC,EAAKX,EAAE,UAAYY,EAASZ,EAAE,SAAS,EAAI,GAC3Ca,EAAMb,EAAE,OAAS,KAAOU,EAC1BI,EAAOd,EAAE,KACb,MAAI,CAACc,GAAQd,EAAE,QACTA,EAAE,MAAM,OAAS,QAASc,EAAO,SAASd,EAAE,MAAM,QAAU,KAAKA,EAAE,MAAM,UAAY,MAChFA,EAAE,MAAM,OAAS,QAASc,EAAO,kBACjCd,EAAE,MAAM,OAAS,QAASc,EAAO,SAASd,EAAE,MAAM,QAAU,KAAKA,EAAE,MAAM,UAAY,MACrFA,EAAE,MAAM,OAAS,WAAYc,EAAO,cAAcd,EAAE,MAAM,UAAY,aACtEA,EAAE,MAAM,OAAS,YAAWc,EAAO,cAEvC,IAAIH,MAAOE,MAAQC,GAC5B,CAEA,SAASF,EAASG,EAAyB,CACzC,GAAI,CAAE,OAAO,IAAI,KAAKA,EAAU,GAAI,EAAE,YAAY,EAAE,QAAQ,IAAK,GAAG,EAAE,MAAM,EAAG,EAAE,CAAE,MACnF,CAAQ,OAAO,OAAOA,CAAO,CAAE,CACjC,CAEA,SAAST,EAAQZ,EAAoB,CACnC,OAAOA,EAAG,QAAQ,gBAAiB,EAAE,EAAE,MAAM,GAAG,CAClD,CAKO,SAASE,EAAYN,EAA8B,CACxD,IAAM0B,EAAItD,EAAW,MAAM,EAC3B,OAAAsD,EAAE,OAAO1B,EAAQ,GAAG,EACpB0B,EAAE,OAAO,GAAG,EACZA,EAAE,OAAO1B,EAAQ,WAAa,EAAE,EAChC0B,EAAE,OAAO,GAAG,EACZA,EAAE,OAAO1B,EAAQ,UAAY,EAAE,EAC/B0B,EAAE,OAAO,GAAG,EACZA,EAAE,OAAO1B,EAAQ,QAAU,EAAE,EACtB0B,EAAE,OAAO,KAAK,EAAE,MAAM,EAAG,EAAE,CACpC,CA4BA,eAAsBC,EAASC,EAO4B,CACzD,IAAM1B,EAAM0B,EAAK,KAAO,IAAI,KACtBC,EAAsB,CAC1B,gBAAiB,EACjB,cAAe,EACf,cAAe,EACf,YAAa,EACb,eAAgB,EAChB,kBAAmB,EACnB,iBAAkB,EAClB,OAAQ,CAAC,CACX,EACMC,EAA6B,CAAC,EAC9BC,EAAgBC,GAAqB,CACzC,GAAIJ,EAAK,OAAQ,CAAEE,EAAc,KAAKE,CAAK,EAAG,OAC9CJ,EAAK,MAAM,SAASI,CAAK,CAC3B,EAEA,GAAI,CAACJ,EAAK,OAAO,YAAY,GAAK,CAACA,EAAK,OACtC,OAAAC,EAAO,OAAO,KAAK,CAAE,IAAK,IAAK,KAAM,SAAU,QAAS,+BAAgC,CAAC,EAClF,CAAE,GAAGA,EAAQ,cAAeD,EAAK,OAASE,EAAgB,MAAU,EAG7E,IAAIpD,EAAUL,EAAauD,EAAK,OAAQA,EAAK,MAAM,EAE/ClD,EAAQ,OAASkD,EAAK,OAAO,SAAS,mBACxClD,EAAUA,EAAQ,MAAM,EAAGkD,EAAK,OAAO,SAAS,gBAAgB,GAGlE,QAAWK,KAAKvD,EACd,GAAI,CACF,GAAIuD,EAAE,OAAS,UAAW,CACxBJ,EAAO,kBACP,IAAM7B,EAAUiC,EAAE,SAAY,MAAML,EAAK,OAAO,kBAAkBK,EAAE,GAAG,EACvE,GAAI,CAACjC,EAAS,SACd,IAAMgC,EAAQjC,EAAiBC,EAAS4B,EAAK,QAAS1B,CAAG,EAKzD,GAJA6B,EAAaC,CAAK,EAClBH,EAAO,gBAGHD,EAAK,OAAO,OAAS,WAAY,CACnC,IAAMM,EAAO,MAAMN,EAAK,OAAO,WAAWK,EAAE,IAAK,CAAE,MAAOL,EAAK,OAAO,UAAW,CAAC,EAC5EO,EAAUC,EAAwBF,EAAMN,EAAK,OAAO,YAAa1B,CAAG,EAC1E,GAAIiC,EAAQ,OAAS,GAAKF,EAAE,KAAM,CAChC,IAAMI,EAAKzB,EAAYqB,EAAE,KAAMjC,EAASmC,EAASP,EAAK,QAAS1B,CAAG,EAC9DmC,IAAMN,EAAaM,CAAE,EAAGR,EAAO,wBAGlC,CACLA,EAAO,gBACP,IAAMrB,EAAQ,MAAMoB,EAAK,OAAO,iBAAiBK,EAAE,GAAG,EACtD,GAAI,CAACzB,EAAO,SACZ,IAAMwB,EAAQzB,EAAmBC,EAAOoB,EAAK,QAAS1B,CAAG,EAIzD,GAHA6B,EAAaC,CAAK,EAClBH,EAAO,cAEHD,EAAK,OAAO,OAAS,YAAcK,EAAE,KAAM,CAC7C,IAAMC,EAAO,MAAMN,EAAK,OAAO,WAAWK,EAAE,IAAK,CAAE,MAAOL,EAAK,OAAO,UAAW,CAAC,EAC5EO,EAAUC,EAAwBF,EAAMN,EAAK,OAAO,YAAa1B,CAAG,EAC1E,GAAIiC,EAAQ,OAAS,EAAG,CACtB,IAAMG,EAASnB,EAAuBc,EAAE,KAAMzB,EAAO2B,EAASP,EAAK,QAAS1B,CAAG,EAC3EoC,IAAUP,EAAaO,CAAM,EAAGT,EAAO,uBAInD,OAASU,EAAP,CACAV,EAAO,OAAO,KAAK,CAAE,IAAKI,EAAE,IAAK,KAAMA,EAAE,KAAM,QAASM,GAAG,SAAW,OAAOA,CAAC,CAAE,CAAC,CACnF,CAGF,MAAO,CAAE,GAAGV,EAAQ,cAAeD,EAAK,OAASE,EAAgB,MAAU,CAC7E,CAIA,SAASM,EAAwBF,EAAqBM,EAAqBtC,EAA0B,CACnG,IAAMuC,EAAS,KAAK,OAAOvC,EAAI,QAAQ,EAAIsC,EAAc,OAAa,GAAI,EAC1E,OAAON,EAAK,OAAQxB,GAAM,CAACA,EAAE,WAAaA,EAAE,WAAa+B,CAAM,CACjE","names":["createHash","resolveScope","config","source","contacts","chats","targets","normalize","s","allowC","allowG","denyC","denyG","matchesAny","value","patterns","v","c","needle","chat","x","ch","ISO_DAY","d","YYYYMMDD","jidHash","jid","transformContact","profile","agentId","now","date","id","lines","hashProfile","transformGroupMeta","group","topMembers","m","adminTag","transformDm","contact","messages","lastMsgId","shortId","displayName","renderMessageLine","transformGroupMessages","senderLabel","ts","formatTs","who","body","seconds","h","runSweep","opts","report","dryRunEntries","writeOrStage","entry","t","msgs","bounded","bounded_by_history_days","dm","window","e","historyDays","cutoff"]}
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import{a as e}from"./chunk-6PPIFDOQ.js";import"./chunk-HPDHNIA3.js";function i(t={}){let r=`<div class="ax-proc__kpis" id="proc-kpis" hidden></div>
|
|
2
|
+
<div class="ax-proc__root">
|
|
3
|
+
<aside class="ax-proc__list">
|
|
4
|
+
<header>
|
|
5
|
+
<h2>Processes</h2>
|
|
6
|
+
<button id="proc-refresh" class="ax-proc__icon" title="Refresh (r)">\u21BB</button>
|
|
7
|
+
</header>
|
|
8
|
+
<div class="ax-proc__filter">
|
|
9
|
+
<label>Status
|
|
10
|
+
<select id="proc-status">
|
|
11
|
+
<option value="">All</option>
|
|
12
|
+
<option value="running">Running</option>
|
|
13
|
+
<option value="paused">Paused</option>
|
|
14
|
+
<option value="completed">Completed</option>
|
|
15
|
+
<option value="failed">Failed</option>
|
|
16
|
+
</select>
|
|
17
|
+
</label>
|
|
18
|
+
</div>
|
|
19
|
+
<ul id="proc-runs" class="ax-proc__cards" aria-live="polite"></ul>
|
|
20
|
+
<div id="proc-empty" class="ax-proc__empty" hidden>
|
|
21
|
+
<p>No runs yet.</p>
|
|
22
|
+
</div>
|
|
23
|
+
</aside>
|
|
24
|
+
|
|
25
|
+
<section class="ax-proc__detail">
|
|
26
|
+
<header id="proc-detail-head">
|
|
27
|
+
<span class="hint">Select a process to see its composition tree and history.</span>
|
|
28
|
+
</header>
|
|
29
|
+
<div id="proc-detail-body" class="ax-proc__detail-body"></div>
|
|
30
|
+
</section>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<div id="proc-toast" class="ax-proc__toast" hidden></div>`;return e({title:"AgentX \xB7 Processes",activeTab:"workflows",subtitle:"Processes",peers:t.peers,currentPeerId:t.currentPeerId,body:r,css:a,scripts:`<script>${s}</script>`})}var a=`
|
|
34
|
+
.ax-proc__root {
|
|
35
|
+
display: grid; grid-template-columns: 360px 1fr;
|
|
36
|
+
height: calc(100vh - var(--ax-topbar-h, 48px));
|
|
37
|
+
background: var(--ax-bg);
|
|
38
|
+
}
|
|
39
|
+
.ax-proc__list { border-right: 1px solid var(--ax-border); display: flex; flex-direction: column; min-height: 0; }
|
|
40
|
+
.ax-proc__list > header { display: flex; justify-content: space-between; align-items: center; padding: 12px 14px; border-bottom: 1px solid var(--ax-border); }
|
|
41
|
+
.ax-proc__list > header h2 { margin: 0; font-size: 14px; font-weight: 600; }
|
|
42
|
+
.ax-proc__filter { padding: 10px 14px; border-bottom: 1px solid var(--ax-border); }
|
|
43
|
+
.ax-proc__filter label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--ax-muted); }
|
|
44
|
+
.ax-proc__filter select { font: inherit; padding: 6px 8px; border: 1px solid var(--ax-border); border-radius: 6px; background: var(--ax-bg); color: var(--ax-fg); }
|
|
45
|
+
.ax-proc__cards { list-style: none; margin: 0; padding: 6px; overflow-y: auto; flex: 1; }
|
|
46
|
+
.ax-proc__card { padding: 10px 12px; border-radius: 6px; margin-bottom: 4px; cursor: pointer; border: 1px solid transparent; }
|
|
47
|
+
.ax-proc__card:hover { background: var(--ax-surface-2, rgba(127,127,127,0.08)); }
|
|
48
|
+
.ax-proc__card.is-active { background: var(--ax-surface-2); border-color: var(--ax-border); }
|
|
49
|
+
.ax-proc__card-title { font-weight: 600; font-size: 13px; margin-bottom: 2px; display: flex; justify-content: space-between; align-items: center; gap: 6px; }
|
|
50
|
+
.ax-proc__card-meta { font-size: 11px; color: var(--ax-muted); }
|
|
51
|
+
.ax-proc__status-pill { display: inline-block; padding: 1px 8px; border-radius: 10px; font-size: 10px; font-weight: 600; text-transform: uppercase; }
|
|
52
|
+
.ax-proc__status-running { background: #3498db; color: white; }
|
|
53
|
+
.ax-proc__status-paused { background: #f1c40f; color: #333; }
|
|
54
|
+
.ax-proc__status-completed { background: #27ae60; color: white; }
|
|
55
|
+
.ax-proc__status-failed { background: #e74c3c; color: white; }
|
|
56
|
+
.ax-proc__status-canceled { background: #95a5a6; color: white; }
|
|
57
|
+
.ax-proc__sla { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
|
|
58
|
+
.ax-proc__sla-green { background: #27ae60; }
|
|
59
|
+
.ax-proc__sla-yellow { background: #f1c40f; }
|
|
60
|
+
.ax-proc__sla-red { background: #e74c3c; }
|
|
61
|
+
.ax-proc__empty { padding: 20px; color: var(--ax-muted); text-align: center; font-size: 13px; }
|
|
62
|
+
.ax-proc__detail { display: flex; flex-direction: column; min-height: 0; }
|
|
63
|
+
.ax-proc__detail > header { padding: 14px 18px; border-bottom: 1px solid var(--ax-border); }
|
|
64
|
+
.ax-proc__detail-body { overflow-y: auto; padding: 18px; flex: 1; }
|
|
65
|
+
.ax-proc__tree { margin: 0 0 16px; font-family: var(--ax-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; line-height: 1.6; }
|
|
66
|
+
.ax-proc__tree-row { padding: 2px 0; }
|
|
67
|
+
.ax-proc__tree-depth { display: inline-block; color: var(--ax-muted); }
|
|
68
|
+
.ax-proc__history { border-top: 1px solid var(--ax-border); padding-top: 12px; }
|
|
69
|
+
.ax-proc__history table { width: 100%; border-collapse: collapse; font-size: 12px; }
|
|
70
|
+
.ax-proc__history th, .ax-proc__history td { text-align: left; padding: 4px 8px; border-bottom: 1px solid var(--ax-border); }
|
|
71
|
+
.ax-proc__icon { background: none; border: 1px solid transparent; color: var(--ax-fg); cursor: pointer; font-size: 14px; padding: 4px 8px; border-radius: 4px; }
|
|
72
|
+
.ax-proc__icon:hover { background: var(--ax-surface-2); }
|
|
73
|
+
.ax-proc__toast { position: fixed; bottom: 16px; right: 16px; padding: 10px 14px; background: var(--ax-fg); color: var(--ax-bg); border-radius: 6px; font-size: 13px; z-index: 100; }
|
|
74
|
+
.ax-proc__kpis {
|
|
75
|
+
display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));
|
|
76
|
+
gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--ax-border);
|
|
77
|
+
background: var(--ax-surface-2, rgba(127,127,127,0.04));
|
|
78
|
+
}
|
|
79
|
+
.ax-proc__kpi { padding: 6px 10px; border-radius: 6px; background: var(--ax-bg); border: 1px solid var(--ax-border); }
|
|
80
|
+
.ax-proc__kpi-label { font-size: 11px; color: var(--ax-muted); text-transform: uppercase; letter-spacing: 0.04em; }
|
|
81
|
+
.ax-proc__kpi-value { font-size: 20px; font-weight: 600; }
|
|
82
|
+
.ax-proc__kpi-breakdown { font-size: 11px; color: var(--ax-muted); margin-top: 2px; }
|
|
83
|
+
`,s=`
|
|
84
|
+
(function(){
|
|
85
|
+
const $ = (sel) => document.querySelector(sel);
|
|
86
|
+
const state = { runs: [], tasks: [], workflows: [], selected: null, filter: '' };
|
|
87
|
+
const toast = (msg) => { const el = $('#proc-toast'); el.textContent = msg; el.hidden = false; setTimeout(() => el.hidden = true, 2500); };
|
|
88
|
+
|
|
89
|
+
async function refresh() {
|
|
90
|
+
try {
|
|
91
|
+
const [r1, r2, r3, r4] = await Promise.all([
|
|
92
|
+
fetch('/api/workflows/runs?limit=200', { credentials: 'same-origin' }).then(r => r.json()),
|
|
93
|
+
fetch('/api/workflows/tasks', { credentials: 'same-origin' }).then(r => r.json()).catch(() => ({ tasks: [] })),
|
|
94
|
+
fetch('/api/workflows', { credentials: 'same-origin' }).then(r => r.json()),
|
|
95
|
+
fetch('/api/workflows/kpis', { credentials: 'same-origin' }).then(r => r.json()).catch(() => null),
|
|
96
|
+
]);
|
|
97
|
+
state.runs = Array.isArray(r1.runs) ? r1.runs : [];
|
|
98
|
+
state.tasks = Array.isArray(r2.tasks) ? r2.tasks : [];
|
|
99
|
+
state.workflows = Array.isArray(r3.workflows) ? r3.workflows : [];
|
|
100
|
+
renderKpis(r4);
|
|
101
|
+
renderList();
|
|
102
|
+
if (state.selected) renderDetail();
|
|
103
|
+
} catch (e) { toast('refresh failed: ' + e.message); }
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function formatDuration(ms) {
|
|
107
|
+
if (ms == null) return '\u2014';
|
|
108
|
+
if (ms < 1000) return ms + 'ms';
|
|
109
|
+
if (ms < 60000) return Math.round(ms/100)/10 + 's';
|
|
110
|
+
if (ms < 3600000) return Math.round(ms/6000)/10 + 'm';
|
|
111
|
+
return Math.round(ms/360000)/10 + 'h';
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function renderKpis(kpis) {
|
|
115
|
+
const el = $('#proc-kpis');
|
|
116
|
+
if (!kpis || !kpis.totals) { el.hidden = true; return; }
|
|
117
|
+
const t = kpis.totals;
|
|
118
|
+
const breachPct = t.slaBreachRate == null ? '\u2014' : Math.round(t.slaBreachRate * 100) + '%';
|
|
119
|
+
const topActors = (kpis.byActor || []).slice(0, 3);
|
|
120
|
+
el.hidden = false;
|
|
121
|
+
el.innerHTML =
|
|
122
|
+
kpiCard('Open tasks', t.openTasks, topActors.map(a => a.actorId + ': ' + a.openTasks).join(' \xB7 ')) +
|
|
123
|
+
kpiCard('Completed', t.completedTasks, '') +
|
|
124
|
+
kpiCard('Avg duration', formatDuration(t.avgDurationMs), '') +
|
|
125
|
+
kpiCard('SLA breach', breachPct, t.breachedCount + ' tasks');
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function kpiCard(label, value, breakdown) {
|
|
129
|
+
return '<div class="ax-proc__kpi">' +
|
|
130
|
+
'<div class="ax-proc__kpi-label">' + escHtml(label) + '</div>' +
|
|
131
|
+
'<div class="ax-proc__kpi-value">' + escHtml(value) + '</div>' +
|
|
132
|
+
(breakdown ? '<div class="ax-proc__kpi-breakdown">' + escHtml(breakdown) + '</div>' : '') +
|
|
133
|
+
'</div>';
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function workflowTitle(id) {
|
|
137
|
+
const wf = state.workflows.find(w => w.id === id);
|
|
138
|
+
return wf?.title || id;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function slaForRun(runId) {
|
|
142
|
+
// Worst SLA across open tasks on this run: red > yellow > green > none.
|
|
143
|
+
const tasks = state.tasks.filter(t => t.runId === runId && t.dueAt);
|
|
144
|
+
if (!tasks.length) return null;
|
|
145
|
+
const now = Date.now();
|
|
146
|
+
let worst = 'green';
|
|
147
|
+
for (const t of tasks) {
|
|
148
|
+
const remaining = Date.parse(t.dueAt) - now;
|
|
149
|
+
const windowMs = Math.max(1, Date.parse(t.dueAt) - Date.parse(t.createdAt));
|
|
150
|
+
if (remaining <= 0) { worst = 'red'; break; }
|
|
151
|
+
if (remaining / windowMs < 0.25 && worst !== 'red') worst = 'yellow';
|
|
152
|
+
}
|
|
153
|
+
return worst;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function renderList() {
|
|
157
|
+
const ul = $('#proc-runs');
|
|
158
|
+
const empty = $('#proc-empty');
|
|
159
|
+
ul.innerHTML = '';
|
|
160
|
+
const filter = state.filter;
|
|
161
|
+
// Only show root runs in the list; children show inside the composition tree.
|
|
162
|
+
const roots = state.runs.filter(r => !r.parentRunId && (!filter || r.status === filter));
|
|
163
|
+
if (!roots.length) { empty.hidden = false; return; }
|
|
164
|
+
empty.hidden = true;
|
|
165
|
+
for (const r of roots) {
|
|
166
|
+
const sla = slaForRun(r.id);
|
|
167
|
+
const li = document.createElement('li');
|
|
168
|
+
li.className = 'ax-proc__card' + (r.id === state.selected ? ' is-active' : '');
|
|
169
|
+
li.innerHTML =
|
|
170
|
+
'<div class="ax-proc__card-title">' +
|
|
171
|
+
'<span>' + escHtml(workflowTitle(r.workflowId)) + '</span>' +
|
|
172
|
+
'<span>' +
|
|
173
|
+
(sla ? '<span class="ax-proc__sla ax-proc__sla-' + sla + '" title="SLA"></span> ' : '') +
|
|
174
|
+
'<span class="ax-proc__status-pill ax-proc__status-' + r.status + '">' + r.status + '</span>' +
|
|
175
|
+
'</span></div>' +
|
|
176
|
+
'<div class="ax-proc__card-meta">' + escHtml(r.id.slice(0, 8)) + ' \xB7 depth ' + (r.depth ?? 0) +
|
|
177
|
+
(r.pausedAt ? ' \xB7 paused on ' + escHtml(r.pausedAt.kind) : '') + '</div>';
|
|
178
|
+
li.addEventListener('click', () => { state.selected = r.id; renderList(); renderDetail(); });
|
|
179
|
+
ul.appendChild(li);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function renderDetail() {
|
|
184
|
+
const head = $('#proc-detail-head');
|
|
185
|
+
const body = $('#proc-detail-body');
|
|
186
|
+
const root = state.runs.find(r => r.id === state.selected);
|
|
187
|
+
if (!root) { head.innerHTML = '<span class="hint">Select a process.</span>'; body.innerHTML = ''; return; }
|
|
188
|
+
|
|
189
|
+
head.innerHTML =
|
|
190
|
+
'<h2 style="margin:0;font-size:15px">' + escHtml(workflowTitle(root.workflowId)) + '</h2>' +
|
|
191
|
+
'<p class="hint" style="margin:6px 0 0">Run ' + escHtml(root.id) + ' \xB7 status <span class="ax-proc__status-pill ax-proc__status-' + root.status + '">' + root.status + '</span></p>';
|
|
192
|
+
|
|
193
|
+
// Composition tree \u2014 walk all runs whose rootRunId equals root.id.
|
|
194
|
+
const tree = state.runs.filter(r => (r.rootRunId || r.id) === root.id);
|
|
195
|
+
// Build adjacency: parentRunId \u2192 children.
|
|
196
|
+
const byParent = new Map();
|
|
197
|
+
for (const r of tree) {
|
|
198
|
+
const key = r.parentRunId || null;
|
|
199
|
+
if (!byParent.has(key)) byParent.set(key, []);
|
|
200
|
+
byParent.get(key).push(r);
|
|
201
|
+
}
|
|
202
|
+
const render = (parentId, depth) => {
|
|
203
|
+
const kids = byParent.get(parentId) || [];
|
|
204
|
+
return kids.map(k => {
|
|
205
|
+
const pad = ' '.repeat(depth);
|
|
206
|
+
const sla = slaForRun(k.id);
|
|
207
|
+
return '<div class="ax-proc__tree-row"><span class="ax-proc__tree-depth">' + pad + '\u2514\u2500 </span>' +
|
|
208
|
+
'<a href="#" data-run="' + k.id + '">' + escHtml(workflowTitle(k.workflowId)) + '</a>' +
|
|
209
|
+
' <span class="ax-proc__status-pill ax-proc__status-' + k.status + '">' + k.status + '</span>' +
|
|
210
|
+
(sla ? ' <span class="ax-proc__sla ax-proc__sla-' + sla + '" title="SLA"></span>' : '') +
|
|
211
|
+
(k.pausedAt ? ' <span class="hint">paused: ' + escHtml(k.pausedAt.kind) + '</span>' : '') +
|
|
212
|
+
'</div>' + render(k.id, depth + 1);
|
|
213
|
+
}).join('');
|
|
214
|
+
};
|
|
215
|
+
|
|
216
|
+
const rootSla = slaForRun(root.id);
|
|
217
|
+
const treeHtml =
|
|
218
|
+
'<div class="ax-proc__tree"><div class="ax-proc__tree-row">' +
|
|
219
|
+
escHtml(workflowTitle(root.workflowId)) +
|
|
220
|
+
' <span class="ax-proc__status-pill ax-proc__status-' + root.status + '">' + root.status + '</span>' +
|
|
221
|
+
(rootSla ? ' <span class="ax-proc__sla ax-proc__sla-' + rootSla + '"></span>' : '') +
|
|
222
|
+
'</div>' + render(root.id, 1) + '</div>';
|
|
223
|
+
|
|
224
|
+
const openTasks = state.tasks.filter(t => (tree.some(r => r.id === t.runId)));
|
|
225
|
+
const tasksHtml = openTasks.length ? ('<h3 style="margin:0 0 6px; font-size: 13px">Open tasks</h3><ul>' +
|
|
226
|
+
openTasks.map(t => {
|
|
227
|
+
const sla = t.dueAt ? (Date.parse(t.dueAt) < Date.now() ? 'red' : 'green') : null;
|
|
228
|
+
return '<li>' + escHtml(t.title) + ' \u2014 assignee ' + escHtml(t.assignee) +
|
|
229
|
+
(t.dueAt ? ' \xB7 due ' + new Date(t.dueAt).toLocaleString() : '') +
|
|
230
|
+
(sla ? ' <span class="ax-proc__sla ax-proc__sla-' + sla + '"></span>' : '') +
|
|
231
|
+
'</li>';
|
|
232
|
+
}).join('') + '</ul>') : '';
|
|
233
|
+
|
|
234
|
+
const historyHtml = '<div class="ax-proc__history"><h3 style="margin:0 0 6px; font-size: 13px">Root run history</h3>' +
|
|
235
|
+
'<table><thead><tr><th>At</th><th>Node</th><th>Status</th><th>Note</th></tr></thead><tbody>' +
|
|
236
|
+
(root.history || []).map(h =>
|
|
237
|
+
'<tr><td>' + escHtml(new Date(h.at).toLocaleTimeString()) + '</td><td>' + escHtml(h.nodeId) + '</td><td>' + escHtml(h.status) + '</td><td>' + escHtml(h.note || '') + '</td></tr>'
|
|
238
|
+
).join('') +
|
|
239
|
+
'</tbody></table></div>';
|
|
240
|
+
|
|
241
|
+
body.innerHTML = treeHtml + tasksHtml + historyHtml;
|
|
242
|
+
|
|
243
|
+
body.querySelectorAll('a[data-run]').forEach(a => {
|
|
244
|
+
a.addEventListener('click', (e) => { e.preventDefault(); state.selected = a.dataset.run; renderList(); renderDetail(); });
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function escHtml(s) { return String(s ?? '').replace(/[&<>"']/g, c => ({'&':'&','<':'<','>':'>','"':'"',"'":'''})[c]); }
|
|
249
|
+
|
|
250
|
+
$('#proc-refresh').addEventListener('click', refresh);
|
|
251
|
+
$('#proc-status').addEventListener('change', (e) => { state.filter = e.target.value; renderList(); });
|
|
252
|
+
refresh();
|
|
253
|
+
setInterval(refresh, 5000);
|
|
254
|
+
})();
|
|
255
|
+
`;export{i as renderProcessesPage};
|
|
256
|
+
//# sourceMappingURL=processes-7W27G3J3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/daemon/ui/pages/processes.ts"],"sourcesContent":["import { renderShell, esc, type TopbarPeer } from \"..\"\n\n// --- Processes dashboard ---\n//\n// Live view over workflow runs with composition-tree rendering and SLA\n// indicators computed from userTasks' dueAt. Reads:\n// GET /api/workflows/runs — list of recent runs\n// GET /api/workflows/tasks — open user tasks\n// GET /api/workflows — workflow definitions (for titles)\n\nexport interface ProcessesPageOpts {\n peers?: TopbarPeer[]\n currentPeerId?: string\n}\n\nexport function renderProcessesPage(opts: ProcessesPageOpts = {}): string {\n const body = `<div class=\"ax-proc__kpis\" id=\"proc-kpis\" hidden></div>\n<div class=\"ax-proc__root\">\n <aside class=\"ax-proc__list\">\n <header>\n <h2>Processes</h2>\n <button id=\"proc-refresh\" class=\"ax-proc__icon\" title=\"Refresh (r)\">↻</button>\n </header>\n <div class=\"ax-proc__filter\">\n <label>Status\n <select id=\"proc-status\">\n <option value=\"\">All</option>\n <option value=\"running\">Running</option>\n <option value=\"paused\">Paused</option>\n <option value=\"completed\">Completed</option>\n <option value=\"failed\">Failed</option>\n </select>\n </label>\n </div>\n <ul id=\"proc-runs\" class=\"ax-proc__cards\" aria-live=\"polite\"></ul>\n <div id=\"proc-empty\" class=\"ax-proc__empty\" hidden>\n <p>No runs yet.</p>\n </div>\n </aside>\n\n <section class=\"ax-proc__detail\">\n <header id=\"proc-detail-head\">\n <span class=\"hint\">Select a process to see its composition tree and history.</span>\n </header>\n <div id=\"proc-detail-body\" class=\"ax-proc__detail-body\"></div>\n </section>\n</div>\n\n<div id=\"proc-toast\" class=\"ax-proc__toast\" hidden></div>`\n\n return renderShell({\n title: \"AgentX · Processes\",\n activeTab: \"workflows\",\n subtitle: \"Processes\",\n peers: opts.peers,\n currentPeerId: opts.currentPeerId,\n body,\n css: PROC_PAGE_CSS,\n scripts: `<script>${PROC_PAGE_SCRIPT}</script>`,\n })\n}\n\nconst PROC_PAGE_CSS = `\n.ax-proc__root {\n display: grid; grid-template-columns: 360px 1fr;\n height: calc(100vh - var(--ax-topbar-h, 48px));\n background: var(--ax-bg);\n}\n.ax-proc__list { border-right: 1px solid var(--ax-border); display: flex; flex-direction: column; min-height: 0; }\n.ax-proc__list > header { display: flex; justify-content: space-between; align-items: center; padding: 12px 14px; border-bottom: 1px solid var(--ax-border); }\n.ax-proc__list > header h2 { margin: 0; font-size: 14px; font-weight: 600; }\n.ax-proc__filter { padding: 10px 14px; border-bottom: 1px solid var(--ax-border); }\n.ax-proc__filter label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--ax-muted); }\n.ax-proc__filter select { font: inherit; padding: 6px 8px; border: 1px solid var(--ax-border); border-radius: 6px; background: var(--ax-bg); color: var(--ax-fg); }\n.ax-proc__cards { list-style: none; margin: 0; padding: 6px; overflow-y: auto; flex: 1; }\n.ax-proc__card { padding: 10px 12px; border-radius: 6px; margin-bottom: 4px; cursor: pointer; border: 1px solid transparent; }\n.ax-proc__card:hover { background: var(--ax-surface-2, rgba(127,127,127,0.08)); }\n.ax-proc__card.is-active { background: var(--ax-surface-2); border-color: var(--ax-border); }\n.ax-proc__card-title { font-weight: 600; font-size: 13px; margin-bottom: 2px; display: flex; justify-content: space-between; align-items: center; gap: 6px; }\n.ax-proc__card-meta { font-size: 11px; color: var(--ax-muted); }\n.ax-proc__status-pill { display: inline-block; padding: 1px 8px; border-radius: 10px; font-size: 10px; font-weight: 600; text-transform: uppercase; }\n.ax-proc__status-running { background: #3498db; color: white; }\n.ax-proc__status-paused { background: #f1c40f; color: #333; }\n.ax-proc__status-completed { background: #27ae60; color: white; }\n.ax-proc__status-failed { background: #e74c3c; color: white; }\n.ax-proc__status-canceled { background: #95a5a6; color: white; }\n.ax-proc__sla { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }\n.ax-proc__sla-green { background: #27ae60; }\n.ax-proc__sla-yellow { background: #f1c40f; }\n.ax-proc__sla-red { background: #e74c3c; }\n.ax-proc__empty { padding: 20px; color: var(--ax-muted); text-align: center; font-size: 13px; }\n.ax-proc__detail { display: flex; flex-direction: column; min-height: 0; }\n.ax-proc__detail > header { padding: 14px 18px; border-bottom: 1px solid var(--ax-border); }\n.ax-proc__detail-body { overflow-y: auto; padding: 18px; flex: 1; }\n.ax-proc__tree { margin: 0 0 16px; font-family: var(--ax-mono, ui-monospace, SFMono-Regular, Menlo, monospace); font-size: 12px; line-height: 1.6; }\n.ax-proc__tree-row { padding: 2px 0; }\n.ax-proc__tree-depth { display: inline-block; color: var(--ax-muted); }\n.ax-proc__history { border-top: 1px solid var(--ax-border); padding-top: 12px; }\n.ax-proc__history table { width: 100%; border-collapse: collapse; font-size: 12px; }\n.ax-proc__history th, .ax-proc__history td { text-align: left; padding: 4px 8px; border-bottom: 1px solid var(--ax-border); }\n.ax-proc__icon { background: none; border: 1px solid transparent; color: var(--ax-fg); cursor: pointer; font-size: 14px; padding: 4px 8px; border-radius: 4px; }\n.ax-proc__icon:hover { background: var(--ax-surface-2); }\n.ax-proc__toast { position: fixed; bottom: 16px; right: 16px; padding: 10px 14px; background: var(--ax-fg); color: var(--ax-bg); border-radius: 6px; font-size: 13px; z-index: 100; }\n.ax-proc__kpis {\n display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr));\n gap: 8px; padding: 10px 14px; border-bottom: 1px solid var(--ax-border);\n background: var(--ax-surface-2, rgba(127,127,127,0.04));\n}\n.ax-proc__kpi { padding: 6px 10px; border-radius: 6px; background: var(--ax-bg); border: 1px solid var(--ax-border); }\n.ax-proc__kpi-label { font-size: 11px; color: var(--ax-muted); text-transform: uppercase; letter-spacing: 0.04em; }\n.ax-proc__kpi-value { font-size: 20px; font-weight: 600; }\n.ax-proc__kpi-breakdown { font-size: 11px; color: var(--ax-muted); margin-top: 2px; }\n`\n\nconst PROC_PAGE_SCRIPT = `\n(function(){\n const $ = (sel) => document.querySelector(sel);\n const state = { runs: [], tasks: [], workflows: [], selected: null, filter: '' };\n const toast = (msg) => { const el = $('#proc-toast'); el.textContent = msg; el.hidden = false; setTimeout(() => el.hidden = true, 2500); };\n\n async function refresh() {\n try {\n const [r1, r2, r3, r4] = await Promise.all([\n fetch('/api/workflows/runs?limit=200', { credentials: 'same-origin' }).then(r => r.json()),\n fetch('/api/workflows/tasks', { credentials: 'same-origin' }).then(r => r.json()).catch(() => ({ tasks: [] })),\n fetch('/api/workflows', { credentials: 'same-origin' }).then(r => r.json()),\n fetch('/api/workflows/kpis', { credentials: 'same-origin' }).then(r => r.json()).catch(() => null),\n ]);\n state.runs = Array.isArray(r1.runs) ? r1.runs : [];\n state.tasks = Array.isArray(r2.tasks) ? r2.tasks : [];\n state.workflows = Array.isArray(r3.workflows) ? r3.workflows : [];\n renderKpis(r4);\n renderList();\n if (state.selected) renderDetail();\n } catch (e) { toast('refresh failed: ' + e.message); }\n }\n\n function formatDuration(ms) {\n if (ms == null) return '—';\n if (ms < 1000) return ms + 'ms';\n if (ms < 60000) return Math.round(ms/100)/10 + 's';\n if (ms < 3600000) return Math.round(ms/6000)/10 + 'm';\n return Math.round(ms/360000)/10 + 'h';\n }\n\n function renderKpis(kpis) {\n const el = $('#proc-kpis');\n if (!kpis || !kpis.totals) { el.hidden = true; return; }\n const t = kpis.totals;\n const breachPct = t.slaBreachRate == null ? '—' : Math.round(t.slaBreachRate * 100) + '%';\n const topActors = (kpis.byActor || []).slice(0, 3);\n el.hidden = false;\n el.innerHTML =\n kpiCard('Open tasks', t.openTasks, topActors.map(a => a.actorId + ': ' + a.openTasks).join(' · ')) +\n kpiCard('Completed', t.completedTasks, '') +\n kpiCard('Avg duration', formatDuration(t.avgDurationMs), '') +\n kpiCard('SLA breach', breachPct, t.breachedCount + ' tasks');\n }\n\n function kpiCard(label, value, breakdown) {\n return '<div class=\"ax-proc__kpi\">' +\n '<div class=\"ax-proc__kpi-label\">' + escHtml(label) + '</div>' +\n '<div class=\"ax-proc__kpi-value\">' + escHtml(value) + '</div>' +\n (breakdown ? '<div class=\"ax-proc__kpi-breakdown\">' + escHtml(breakdown) + '</div>' : '') +\n '</div>';\n }\n\n function workflowTitle(id) {\n const wf = state.workflows.find(w => w.id === id);\n return wf?.title || id;\n }\n\n function slaForRun(runId) {\n // Worst SLA across open tasks on this run: red > yellow > green > none.\n const tasks = state.tasks.filter(t => t.runId === runId && t.dueAt);\n if (!tasks.length) return null;\n const now = Date.now();\n let worst = 'green';\n for (const t of tasks) {\n const remaining = Date.parse(t.dueAt) - now;\n const windowMs = Math.max(1, Date.parse(t.dueAt) - Date.parse(t.createdAt));\n if (remaining <= 0) { worst = 'red'; break; }\n if (remaining / windowMs < 0.25 && worst !== 'red') worst = 'yellow';\n }\n return worst;\n }\n\n function renderList() {\n const ul = $('#proc-runs');\n const empty = $('#proc-empty');\n ul.innerHTML = '';\n const filter = state.filter;\n // Only show root runs in the list; children show inside the composition tree.\n const roots = state.runs.filter(r => !r.parentRunId && (!filter || r.status === filter));\n if (!roots.length) { empty.hidden = false; return; }\n empty.hidden = true;\n for (const r of roots) {\n const sla = slaForRun(r.id);\n const li = document.createElement('li');\n li.className = 'ax-proc__card' + (r.id === state.selected ? ' is-active' : '');\n li.innerHTML =\n '<div class=\"ax-proc__card-title\">' +\n '<span>' + escHtml(workflowTitle(r.workflowId)) + '</span>' +\n '<span>' +\n (sla ? '<span class=\"ax-proc__sla ax-proc__sla-' + sla + '\" title=\"SLA\"></span> ' : '') +\n '<span class=\"ax-proc__status-pill ax-proc__status-' + r.status + '\">' + r.status + '</span>' +\n '</span></div>' +\n '<div class=\"ax-proc__card-meta\">' + escHtml(r.id.slice(0, 8)) + ' · depth ' + (r.depth ?? 0) +\n (r.pausedAt ? ' · paused on ' + escHtml(r.pausedAt.kind) : '') + '</div>';\n li.addEventListener('click', () => { state.selected = r.id; renderList(); renderDetail(); });\n ul.appendChild(li);\n }\n }\n\n function renderDetail() {\n const head = $('#proc-detail-head');\n const body = $('#proc-detail-body');\n const root = state.runs.find(r => r.id === state.selected);\n if (!root) { head.innerHTML = '<span class=\"hint\">Select a process.</span>'; body.innerHTML = ''; return; }\n\n head.innerHTML =\n '<h2 style=\"margin:0;font-size:15px\">' + escHtml(workflowTitle(root.workflowId)) + '</h2>' +\n '<p class=\"hint\" style=\"margin:6px 0 0\">Run ' + escHtml(root.id) + ' · status <span class=\"ax-proc__status-pill ax-proc__status-' + root.status + '\">' + root.status + '</span></p>';\n\n // Composition tree — walk all runs whose rootRunId equals root.id.\n const tree = state.runs.filter(r => (r.rootRunId || r.id) === root.id);\n // Build adjacency: parentRunId → children.\n const byParent = new Map();\n for (const r of tree) {\n const key = r.parentRunId || null;\n if (!byParent.has(key)) byParent.set(key, []);\n byParent.get(key).push(r);\n }\n const render = (parentId, depth) => {\n const kids = byParent.get(parentId) || [];\n return kids.map(k => {\n const pad = ' '.repeat(depth);\n const sla = slaForRun(k.id);\n return '<div class=\"ax-proc__tree-row\"><span class=\"ax-proc__tree-depth\">' + pad + '└─ </span>' +\n '<a href=\"#\" data-run=\"' + k.id + '\">' + escHtml(workflowTitle(k.workflowId)) + '</a>' +\n ' <span class=\"ax-proc__status-pill ax-proc__status-' + k.status + '\">' + k.status + '</span>' +\n (sla ? ' <span class=\"ax-proc__sla ax-proc__sla-' + sla + '\" title=\"SLA\"></span>' : '') +\n (k.pausedAt ? ' <span class=\"hint\">paused: ' + escHtml(k.pausedAt.kind) + '</span>' : '') +\n '</div>' + render(k.id, depth + 1);\n }).join('');\n };\n\n const rootSla = slaForRun(root.id);\n const treeHtml =\n '<div class=\"ax-proc__tree\"><div class=\"ax-proc__tree-row\">' +\n escHtml(workflowTitle(root.workflowId)) +\n ' <span class=\"ax-proc__status-pill ax-proc__status-' + root.status + '\">' + root.status + '</span>' +\n (rootSla ? ' <span class=\"ax-proc__sla ax-proc__sla-' + rootSla + '\"></span>' : '') +\n '</div>' + render(root.id, 1) + '</div>';\n\n const openTasks = state.tasks.filter(t => (tree.some(r => r.id === t.runId)));\n const tasksHtml = openTasks.length ? ('<h3 style=\"margin:0 0 6px; font-size: 13px\">Open tasks</h3><ul>' +\n openTasks.map(t => {\n const sla = t.dueAt ? (Date.parse(t.dueAt) < Date.now() ? 'red' : 'green') : null;\n return '<li>' + escHtml(t.title) + ' — assignee ' + escHtml(t.assignee) +\n (t.dueAt ? ' · due ' + new Date(t.dueAt).toLocaleString() : '') +\n (sla ? ' <span class=\"ax-proc__sla ax-proc__sla-' + sla + '\"></span>' : '') +\n '</li>';\n }).join('') + '</ul>') : '';\n\n const historyHtml = '<div class=\"ax-proc__history\"><h3 style=\"margin:0 0 6px; font-size: 13px\">Root run history</h3>' +\n '<table><thead><tr><th>At</th><th>Node</th><th>Status</th><th>Note</th></tr></thead><tbody>' +\n (root.history || []).map(h =>\n '<tr><td>' + escHtml(new Date(h.at).toLocaleTimeString()) + '</td><td>' + escHtml(h.nodeId) + '</td><td>' + escHtml(h.status) + '</td><td>' + escHtml(h.note || '') + '</td></tr>'\n ).join('') +\n '</tbody></table></div>';\n\n body.innerHTML = treeHtml + tasksHtml + historyHtml;\n\n body.querySelectorAll('a[data-run]').forEach(a => {\n a.addEventListener('click', (e) => { e.preventDefault(); state.selected = a.dataset.run; renderList(); renderDetail(); });\n });\n }\n\n function escHtml(s) { return String(s ?? '').replace(/[&<>\"']/g, c => ({'&':'&','<':'<','>':'>','\"':'"',\"'\":'''})[c]); }\n\n $('#proc-refresh').addEventListener('click', refresh);\n $('#proc-status').addEventListener('change', (e) => { state.filter = e.target.value; renderList(); });\n refresh();\n setInterval(refresh, 5000);\n})();\n`\n"],"mappings":"oEAeO,SAASA,EAAoBC,EAA0B,CAAC,EAAW,CACxE,IAAMC,EAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,2DAkCb,OAAOC,EAAY,CACjB,MAAO,wBACP,UAAW,YACX,SAAU,YACV,MAAOF,EAAK,MACZ,cAAeA,EAAK,cACpB,KAAAC,EACA,IAAKE,EACL,QAAS,WAAWC,YACtB,CAAC,CACH,CAEA,IAAMD,EAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoDhBC,EAAmB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;","names":["renderProcessesPage","opts","body","renderShell","PROC_PAGE_CSS","PROC_PAGE_SCRIPT"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{a}from"./chunk-BLUXL6HR.js";import"./chunk-ASSJ4Z6Z.js";import"./chunk-6PPIFDOQ.js";import"./chunk-U364GNQX.js";import"./chunk-IM376SD6.js";import"./chunk-ZE2YFX4K.js";import"./chunk-JXEPJ3YD.js";import"./chunk-DSCMCVTV.js";import"./chunk-J4WKMKTP.js";import"./chunk-LY7JGDV7.js";import"./chunk-N67JZAXU.js";import"./chunk-CDNWCYF7.js";import"./chunk-AFRHKA7B.js";import"./chunk-EW27SCCC.js";import"./chunk-BTAEYL3P.js";import"./chunk-34NG7UDX.js";import"./chunk-5F3J55LG.js";import"./chunk-WJXU5F4Z.js";import"./chunk-LEIYC3BF.js";import"./chunk-WZEYII2S.js";import"./chunk-64P4QHSZ.js";import"./chunk-BFZUG66X.js";import"./chunk-SR3X25TN.js";import"./chunk-53NRX2NA.js";import"./chunk-SFQUP3BP.js";import"./chunk-JGXE2YNV.js";import"./chunk-VJUTQAPJ.js";import"./chunk-DJEZ2HVU.js";import"./chunk-DSYKYZMT.js";import"./chunk-TRQSP67X.js";import"./chunk-QOYAX2FT.js";import"./chunk-HPDHNIA3.js";export{a as buildProgram};
|
|
2
|
+
//# sourceMappingURL=program-2MDELQLX.js.map
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{f as a,g as b,h as c}from"./chunk-BTAEYL3P.js";import"./chunk-34NG7UDX.js";import"./chunk-5F3J55LG.js";import"./chunk-WJXU5F4Z.js";import"./chunk-LEIYC3BF.js";import"./chunk-WZEYII2S.js";import"./chunk-64P4QHSZ.js";import"./chunk-BFZUG66X.js";import"./chunk-SR3X25TN.js";import"./chunk-53NRX2NA.js";import"./chunk-VJUTQAPJ.js";import"./chunk-TRQSP67X.js";import"./chunk-QOYAX2FT.js";import"./chunk-HPDHNIA3.js";export{c as AgentRegistry,b as getGlobalRegistry,a as setGlobalRegistry};
|
|
2
|
+
//# sourceMappingURL=registry-ONJSILH4.js.map
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import"./chunk-HPDHNIA3.js";function m(e){let r=e.log??(()=>{});return async function(o){let n=e.inboxBaseUrl??"",s=f(o);for(let d of o.assignedTo){let c=e.actors.channelFor(d,"slack");if(!c)continue;let a=u(o,d,n,s);try{let t=await e.adapter.send({channel:"slack",chatId:c,text:a,parseMode:"markdown"}),l=[...o.delivered??[],{channel:"slack",handle:c,messageId:typeof t=="string"?t:void 0,at:new Date().toISOString()}];e.tasks.save({...o,delivered:l})}catch(t){r(`[task:${o.id}] slack delivery to ${d}@${c} failed: ${t?.message??t}`)}}}}function f(e){if(!e.form.secondaryAction)return!1;for(let r of e.form.fields??[])if(r.required&&r.defaultValue===void 0)return!1;return!0}function u(e,r,i,o){let n=[];if(n.push(`*${e.title}*`),e.description&&n.push("",e.description),!o&&e.form.fields?.length){n.push("","*Fields*:");for(let s of e.form.fields)n.push(`\u2022 ${s.label}${s.required?" (required)":""}`)}return e.dueAt&&n.push("",`:clock3: Due: ${new Date(e.dueAt).toLocaleString()}`),o?(n.push("",`:white_check_mark: <${i}/t/${encodeURIComponent(e.id)}/primary?actor=${encodeURIComponent(r)}|${e.form.submitLabel||"Approve"}>`),e.form.secondaryAction&&n.push(`:x: <${i}/t/${encodeURIComponent(e.id)}/secondary?actor=${encodeURIComponent(r)}|${e.form.secondaryAction.label}>`)):n.push("",`<${i}/inbox?actor=${encodeURIComponent(r)}|Open form>`),n.join(`
|
|
2
|
+
`)}export{m as createSlackTaskRenderer};
|
|
3
|
+
//# sourceMappingURL=slack-D27BE37Q.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/forms/renderers/slack.ts"],"sourcesContent":["import type { UserTaskRecord, TaskStore } from \"../../workflows/task-store\"\nimport type { ActorStore } from \"../../actors/store\"\n\n// --- Slack user-task renderer ---\n//\n// Mirrors the Telegram/WhatsApp Phase 3 renderers. Delivers a plain-text\n// message to the assignee's Slack member id, with one-click approve/\n// reject URLs when the form is shaped that way, or a deep-link to the\n// web inbox for forms with required fields.\n//\n// Block Kit interactive elements (rich buttons with callback handling)\n// would need the adapter to surface Slack's interactive-message webhook.\n// For parity with the other Phase 3 renderers we stick with text + URLs,\n// which works everywhere Slack works and needs no extra plumbing.\n\ninterface SendingAdapter {\n send: (msg: { channel: string; chatId: string; text: string; parseMode?: \"markdown\" | \"html\" | \"plain\" }) => Promise<string | void>\n}\n\nexport interface SlackRendererOptions {\n actors: ActorStore\n tasks: TaskStore\n adapter: SendingAdapter\n inboxBaseUrl?: string\n log?: (msg: string) => void\n}\n\nexport function createSlackTaskRenderer(opts: SlackRendererOptions) {\n const log = opts.log ?? (() => {})\n return async function render(task: UserTaskRecord): Promise<void> {\n const baseUrl = opts.inboxBaseUrl ?? \"\"\n const oneClick = canOneClickSubmit(task)\n for (const actorId of task.assignedTo) {\n const handle = opts.actors.channelFor(actorId, \"slack\")\n if (!handle) continue\n const text = buildText(task, actorId, baseUrl, oneClick)\n try {\n const messageId = await opts.adapter.send({\n channel: \"slack\",\n chatId: handle,\n text,\n parseMode: \"markdown\",\n })\n const delivered = [\n ...(task.delivered ?? []),\n {\n channel: \"slack\",\n handle,\n messageId: typeof messageId === \"string\" ? messageId : undefined,\n at: new Date().toISOString(),\n },\n ]\n opts.tasks.save({ ...task, delivered })\n } catch (e: any) {\n log(`[task:${task.id}] slack delivery to ${actorId}@${handle} failed: ${e?.message ?? e}`)\n }\n }\n }\n}\n\nfunction canOneClickSubmit(task: UserTaskRecord): boolean {\n if (!task.form.secondaryAction) return false\n for (const f of task.form.fields ?? []) {\n if (f.required && f.defaultValue === undefined) return false\n }\n return true\n}\n\nfunction buildText(task: UserTaskRecord, actorId: string, baseUrl: string, oneClick: boolean): string {\n const lines: string[] = []\n lines.push(`*${task.title}*`)\n if (task.description) lines.push(\"\", task.description)\n if (!oneClick && task.form.fields?.length) {\n lines.push(\"\", \"*Fields*:\")\n for (const f of task.form.fields) lines.push(`• ${f.label}${f.required ? \" (required)\" : \"\"}`)\n }\n if (task.dueAt) lines.push(\"\", `:clock3: Due: ${new Date(task.dueAt).toLocaleString()}`)\n if (oneClick) {\n lines.push(\n \"\",\n `:white_check_mark: <${baseUrl}/t/${encodeURIComponent(task.id)}/primary?actor=${encodeURIComponent(actorId)}|${task.form.submitLabel || \"Approve\"}>`,\n )\n if (task.form.secondaryAction) {\n lines.push(\n `:x: <${baseUrl}/t/${encodeURIComponent(task.id)}/secondary?actor=${encodeURIComponent(actorId)}|${task.form.secondaryAction.label}>`,\n )\n }\n } else {\n lines.push(\"\", `<${baseUrl}/inbox?actor=${encodeURIComponent(actorId)}|Open form>`)\n }\n return lines.join(\"\\n\")\n}\n"],"mappings":"4BA2BO,SAASA,EAAwBC,EAA4B,CAClE,IAAMC,EAAMD,EAAK,MAAQ,IAAM,CAAC,GAChC,OAAO,eAAsBE,EAAqC,CAChE,IAAMC,EAAUH,EAAK,cAAgB,GAC/BI,EAAWC,EAAkBH,CAAI,EACvC,QAAWI,KAAWJ,EAAK,WAAY,CACrC,IAAMK,EAASP,EAAK,OAAO,WAAWM,EAAS,OAAO,EACtD,GAAI,CAACC,EAAQ,SACb,IAAMC,EAAOC,EAAUP,EAAMI,EAASH,EAASC,CAAQ,EACvD,GAAI,CACF,IAAMM,EAAY,MAAMV,EAAK,QAAQ,KAAK,CACxC,QAAS,QACT,OAAQO,EACR,KAAAC,EACA,UAAW,UACb,CAAC,EACKG,EAAY,CAChB,GAAIT,EAAK,WAAa,CAAC,EACvB,CACE,QAAS,QACT,OAAAK,EACA,UAAW,OAAOG,GAAc,SAAWA,EAAY,OACvD,GAAI,IAAI,KAAK,EAAE,YAAY,CAC7B,CACF,EACAV,EAAK,MAAM,KAAK,CAAE,GAAGE,EAAM,UAAAS,CAAU,CAAC,CACxC,OAASC,EAAP,CACAX,EAAI,SAASC,EAAK,yBAAyBI,KAAWC,aAAkBK,GAAG,SAAWA,GAAG,CAC3F,EAEJ,CACF,CAEA,SAASP,EAAkBH,EAA+B,CACxD,GAAI,CAACA,EAAK,KAAK,gBAAiB,MAAO,GACvC,QAAWW,KAAKX,EAAK,KAAK,QAAU,CAAC,EACnC,GAAIW,EAAE,UAAYA,EAAE,eAAiB,OAAW,MAAO,GAEzD,MAAO,EACT,CAEA,SAASJ,EAAUP,EAAsBI,EAAiBH,EAAiBC,EAA2B,CACpG,IAAMU,EAAkB,CAAC,EAGzB,GAFAA,EAAM,KAAK,IAAIZ,EAAK,QAAQ,EACxBA,EAAK,aAAaY,EAAM,KAAK,GAAIZ,EAAK,WAAW,EACjD,CAACE,GAAYF,EAAK,KAAK,QAAQ,OAAQ,CACzCY,EAAM,KAAK,GAAI,WAAW,EAC1B,QAAWD,KAAKX,EAAK,KAAK,OAAQY,EAAM,KAAK,UAAKD,EAAE,QAAQA,EAAE,SAAW,cAAgB,IAAI,EAE/F,OAAIX,EAAK,OAAOY,EAAM,KAAK,GAAI,iBAAiB,IAAI,KAAKZ,EAAK,KAAK,EAAE,eAAe,GAAG,EACnFE,GACFU,EAAM,KACJ,GACA,uBAAuBX,OAAa,mBAAmBD,EAAK,EAAE,mBAAmB,mBAAmBI,CAAO,KAAKJ,EAAK,KAAK,aAAe,YAC3I,EACIA,EAAK,KAAK,iBACZY,EAAM,KACJ,QAAQX,OAAa,mBAAmBD,EAAK,EAAE,qBAAqB,mBAAmBI,CAAO,KAAKJ,EAAK,KAAK,gBAAgB,QAC/H,GAGFY,EAAM,KAAK,GAAI,IAAIX,iBAAuB,mBAAmBG,CAAO,cAAc,EAE7EQ,EAAM,KAAK;AAAA,CAAI,CACxB","names":["createSlackTaskRenderer","opts","log","task","baseUrl","oneClick","canOneClickSubmit","actorId","handle","text","buildText","messageId","delivered","e","f","lines"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import"./chunk-
|
|
1
|
+
import"./chunk-HPDHNIA3.js";var g=class{registry;activeRuns=new Map;runCounter=0;log;depthMap=new Map;constructor(t,e=console.error.bind(console,"[subagent]")){this.registry=t,this.log=e}async spawn(t){let e=this.depthMap.get(t.parentAgentId)||0;if(e>=3)return{runId:"",targetAgentId:t.targetAgentId,success:!1,content:"",error:`Sub-agent spawn depth exceeded (max: 3). Cannot spawn "${t.targetAgentId}" from "${t.parentAgentId}".`,duration:0};let n=`sub-${++this.runCounter}-${Date.now().toString(36)}`,r={request:t,runId:n,startedAt:Date.now()};this.activeRuns.set(n,r),this.depthMap.set(t.targetAgentId,e+1),this.log(`Spawning sub-agent: ${t.targetAgentId} (parent: ${t.parentAgentId}, depth: ${e+1}, blocking: ${t.blocking??!1})`);let o={message:this.buildSubAgentPrompt(t),agentId:t.targetAgentId,context:{channel:"subagent",sender:`agent:${t.parentAgentId}`,chatId:`subagent:${n}`}},i=t.timeout??3e5;try{let a=await Promise.race([this.registry.execute(o),this.timeoutPromise(i,t.targetAgentId)]),s={runId:n,targetAgentId:t.targetAgentId,success:!a.error,content:a.content,error:a.error,duration:Date.now()-r.startedAt};return this.log(`Sub-agent ${t.targetAgentId} completed in ${s.duration}ms (${s.success?"success":"error"})`),s}catch(a){return{runId:n,targetAgentId:t.targetAgentId,success:!1,content:"",error:a.message,duration:Date.now()-r.startedAt}}finally{this.activeRuns.delete(n),this.depthMap.delete(t.targetAgentId)}}async spawnParallel(t){return Promise.all(t.map(e=>this.spawn(e)))}listActive(){return Array.from(this.activeRuns.values()).map(t=>({runId:t.runId,targetAgentId:t.request.targetAgentId,parentAgentId:t.request.parentAgentId,duration:Date.now()-t.startedAt}))}buildSubAgentPrompt(t){return[`[Sub-agent task from ${t.parentAgentId}]`,"You have been spawned as a background sub-agent to handle a specific task.","Focus on completing the task below and provide a clear, actionable response.","",t.prompt].join(`
|
|
2
2
|
`)}timeoutPromise(t,e){return new Promise((n,r)=>{setTimeout(()=>{r(new Error(`Sub-agent "${e}" timed out after ${Math.round(t/1e3)}s`))},t)})}};export{g as SubAgentManager};
|
|
3
|
-
//# sourceMappingURL=subagent-
|
|
3
|
+
//# sourceMappingURL=subagent-EPOT3QDM.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import"./chunk-HPDHNIA3.js";function p(e){let i=e.log??(()=>{});return async function(n){let d=e.inboxBaseUrl??"",r=g(n.form);for(let t of n.assignedTo){let o=e.actors.channelFor(t,"telegram");if(!o)continue;let l=m(n,t,d,r);try{let s;if(r&&e.adapter.sendWithInlineButtons){let a=[{label:n.form.submitLabel||"Approve",url:`${d}/t/${encodeURIComponent(n.id)}/primary?actor=${encodeURIComponent(t)}`},...n.form.secondaryAction?[{label:n.form.secondaryAction.label,url:`${d}/t/${encodeURIComponent(n.id)}/secondary?actor=${encodeURIComponent(t)}`}]:[]],f=await e.adapter.sendWithInlineButtons({chatId:o,text:l,parseMode:"markdown",buttons:a});s=typeof f=="string"?f:void 0}else{let a=await e.adapter.sendMessage({chatId:o,text:l,parseMode:"markdown"});s=typeof a=="string"?a:void 0}let u=[...n.delivered??[],{channel:"telegram",handle:o,messageId:s,at:new Date().toISOString()}];e.tasks.save({...n,delivered:u})}catch(s){i(`[task:${n.id}] telegram delivery to ${t}@${o} failed: ${s?.message??s}`)}}}}function g(e){if(!e.secondaryAction)return!1;for(let i of e.fields??[])if(i.required&&i.defaultValue===void 0)return!1;return!0}function m(e,i,c,n){let d=`${c}/inbox?actor=${encodeURIComponent(i)}`,r=[];if(r.push(`\u{1F4CB} **${e.title}**`),e.description&&r.push("",e.description),!n&&e.form.fields?.length){r.push("","_Fields_:");for(let t of e.form.fields){let o=t.required?" *(required)*":"";r.push(`\u2022 ${t.label}${o}`)}}return e.dueAt&&r.push("",`\u23F0 Due: ${new Date(e.dueAt).toLocaleString()}`),n||r.push("",`[Open form \u2192](${d})`),r.join(`
|
|
2
|
+
`)}export{p as createTelegramTaskRenderer};
|
|
3
|
+
//# sourceMappingURL=telegram-UWV67KWG.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/forms/renderers/telegram.ts"],"sourcesContent":["import type { UserTaskRecord, TaskStore } from \"../../workflows/task-store\"\nimport type { ActorStore } from \"../../actors/store\"\nimport type { FormSchema } from \"../types\"\n\n// --- Telegram user-task renderer ---\n//\n// Delivers a user-task notification to the assignee's Telegram chat.\n//\n// Two delivery shapes:\n// 1. Approve/reject (form has a secondaryAction AND no required fields\n// that lack defaults) — inline-keyboard with two URL buttons that\n// one-tap-submit via `GET /t/:taskId/:action`. No web visit required.\n// 2. Full-form (any form with required fields) — plain message with a\n// deep link to the web inbox where the user fills in values.\n//\n// The one-click URL pattern uses URL buttons (not callback_data) so we\n// don't need to wire callback_query handling into the adapter polling\n// loop for Phase 3 MVP.\n\ninterface SendingAdapter {\n sendMessage: (msg: { chatId: string; text: string; parseMode?: string; accountId?: string }) => Promise<string | undefined | void>\n sendWithInlineButtons?: (args: {\n chatId: string\n text: string\n buttons: Array<{ label: string; url: string }>\n parseMode?: \"markdown\" | \"html\" | \"plain\"\n accountId?: string\n }) => Promise<string | undefined | void>\n}\n\nexport interface TelegramRendererOptions {\n actors: ActorStore\n tasks: TaskStore\n inboxBaseUrl?: string\n adapter: SendingAdapter\n log?: (msg: string) => void\n}\n\nexport function createTelegramTaskRenderer(opts: TelegramRendererOptions) {\n const log = opts.log ?? (() => {})\n return async function render(task: UserTaskRecord): Promise<void> {\n const baseUrl = opts.inboxBaseUrl ?? \"\"\n const oneClick = canOneClickSubmit(task.form)\n\n for (const actorId of task.assignedTo) {\n const handle = opts.actors.channelFor(actorId, \"telegram\")\n if (!handle) continue\n const text = buildTaskText(task, actorId, baseUrl, oneClick)\n\n try {\n let messageId: string | undefined\n if (oneClick && opts.adapter.sendWithInlineButtons) {\n const buttons = [\n { label: task.form.submitLabel || \"Approve\", url: `${baseUrl}/t/${encodeURIComponent(task.id)}/primary?actor=${encodeURIComponent(actorId)}` },\n ...(task.form.secondaryAction ? [{\n label: task.form.secondaryAction.label,\n url: `${baseUrl}/t/${encodeURIComponent(task.id)}/secondary?actor=${encodeURIComponent(actorId)}`,\n }] : []),\n ]\n const r = await opts.adapter.sendWithInlineButtons({\n chatId: handle, text, parseMode: \"markdown\", buttons,\n })\n messageId = typeof r === \"string\" ? r : undefined\n } else {\n const r = await opts.adapter.sendMessage({ chatId: handle, text, parseMode: \"markdown\" })\n messageId = typeof r === \"string\" ? r : undefined\n }\n const delivered = [\n ...(task.delivered ?? []),\n { channel: \"telegram\", handle, messageId, at: new Date().toISOString() },\n ]\n opts.tasks.save({ ...task, delivered })\n } catch (e: any) {\n log(`[task:${task.id}] telegram delivery to ${actorId}@${handle} failed: ${e?.message ?? e}`)\n }\n }\n }\n}\n\n/** A form is one-click-submittable when it has a primary + secondary\n * action AND no required field without a defaultValue. Pure approve/\n * reject forms (no fields at all, or all fields optional with defaults)\n * fit this shape. */\nfunction canOneClickSubmit(form: FormSchema): boolean {\n if (!form.secondaryAction) return false\n for (const f of form.fields ?? []) {\n if (f.required && f.defaultValue === undefined) return false\n }\n return true\n}\n\nfunction buildTaskText(task: UserTaskRecord, actorId: string, baseUrl: string, oneClick: boolean): string {\n const inboxUrl = `${baseUrl}/inbox?actor=${encodeURIComponent(actorId)}`\n const lines: string[] = []\n lines.push(`📋 **${task.title}**`)\n if (task.description) lines.push(\"\", task.description)\n if (!oneClick && task.form.fields?.length) {\n lines.push(\"\", \"_Fields_:\")\n for (const f of task.form.fields) {\n const required = f.required ? \" *(required)*\" : \"\"\n lines.push(`• ${f.label}${required}`)\n }\n }\n if (task.dueAt) lines.push(\"\", `⏰ Due: ${new Date(task.dueAt).toLocaleString()}`)\n if (!oneClick) lines.push(\"\", `[Open form →](${inboxUrl})`)\n return lines.join(\"\\n\")\n}\n"],"mappings":"4BAsCO,SAASA,EAA2BC,EAA+B,CACxE,IAAMC,EAAMD,EAAK,MAAQ,IAAM,CAAC,GAChC,OAAO,eAAsBE,EAAqC,CAChE,IAAMC,EAAUH,EAAK,cAAgB,GAC/BI,EAAWC,EAAkBH,EAAK,IAAI,EAE5C,QAAWI,KAAWJ,EAAK,WAAY,CACrC,IAAMK,EAASP,EAAK,OAAO,WAAWM,EAAS,UAAU,EACzD,GAAI,CAACC,EAAQ,SACb,IAAMC,EAAOC,EAAcP,EAAMI,EAASH,EAASC,CAAQ,EAE3D,GAAI,CACF,IAAIM,EACJ,GAAIN,GAAYJ,EAAK,QAAQ,sBAAuB,CAClD,IAAMW,EAAU,CACd,CAAE,MAAOT,EAAK,KAAK,aAAe,UAAW,IAAK,GAAGC,OAAa,mBAAmBD,EAAK,EAAE,mBAAmB,mBAAmBI,CAAO,GAAI,EAC7I,GAAIJ,EAAK,KAAK,gBAAkB,CAAC,CAC/B,MAAOA,EAAK,KAAK,gBAAgB,MACjC,IAAK,GAAGC,OAAa,mBAAmBD,EAAK,EAAE,qBAAqB,mBAAmBI,CAAO,GAChG,CAAC,EAAI,CAAC,CACR,EACMM,EAAI,MAAMZ,EAAK,QAAQ,sBAAsB,CACjD,OAAQO,EAAQ,KAAAC,EAAM,UAAW,WAAY,QAAAG,CAC/C,CAAC,EACDD,EAAY,OAAOE,GAAM,SAAWA,EAAI,WACnC,CACL,IAAMA,EAAI,MAAMZ,EAAK,QAAQ,YAAY,CAAE,OAAQO,EAAQ,KAAAC,EAAM,UAAW,UAAW,CAAC,EACxFE,EAAY,OAAOE,GAAM,SAAWA,EAAI,OAE1C,IAAMC,EAAY,CAChB,GAAIX,EAAK,WAAa,CAAC,EACvB,CAAE,QAAS,WAAY,OAAAK,EAAQ,UAAAG,EAAW,GAAI,IAAI,KAAK,EAAE,YAAY,CAAE,CACzE,EACAV,EAAK,MAAM,KAAK,CAAE,GAAGE,EAAM,UAAAW,CAAU,CAAC,CACxC,OAASC,EAAP,CACAb,EAAI,SAASC,EAAK,4BAA4BI,KAAWC,aAAkBO,GAAG,SAAWA,GAAG,CAC9F,EAEJ,CACF,CAMA,SAAST,EAAkBU,EAA2B,CACpD,GAAI,CAACA,EAAK,gBAAiB,MAAO,GAClC,QAAWC,KAAKD,EAAK,QAAU,CAAC,EAC9B,GAAIC,EAAE,UAAYA,EAAE,eAAiB,OAAW,MAAO,GAEzD,MAAO,EACT,CAEA,SAASP,EAAcP,EAAsBI,EAAiBH,EAAiBC,EAA2B,CACxG,IAAMa,EAAW,GAAGd,iBAAuB,mBAAmBG,CAAO,IAC/DY,EAAkB,CAAC,EAGzB,GAFAA,EAAM,KAAK,eAAQhB,EAAK,SAAS,EAC7BA,EAAK,aAAagB,EAAM,KAAK,GAAIhB,EAAK,WAAW,EACjD,CAACE,GAAYF,EAAK,KAAK,QAAQ,OAAQ,CACzCgB,EAAM,KAAK,GAAI,WAAW,EAC1B,QAAWF,KAAKd,EAAK,KAAK,OAAQ,CAChC,IAAMiB,EAAWH,EAAE,SAAW,gBAAkB,GAChDE,EAAM,KAAK,UAAKF,EAAE,QAAQG,GAAU,GAGxC,OAAIjB,EAAK,OAAOgB,EAAM,KAAK,GAAI,eAAU,IAAI,KAAKhB,EAAK,KAAK,EAAE,eAAe,GAAG,EAC3EE,GAAUc,EAAM,KAAK,GAAI,sBAAiBD,IAAW,EACnDC,EAAM,KAAK;AAAA,CAAI,CACxB","names":["createTelegramTaskRenderer","opts","log","task","baseUrl","oneClick","canOneClickSubmit","actorId","handle","text","buildTaskText","messageId","buttons","r","delivered","e","form","f","inboxUrl","lines","required"]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|