agentix-cli 0.134.0 → 0.135.1

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.
Files changed (116) hide show
  1. package/dist/{absorb-eval-WU7JR3BT.js → absorb-eval-YT5TOTC7.js} +2 -2
  2. package/dist/{agent-2GPJGHHG.js → agent-DRUIMWF5.js} +2 -2
  3. package/dist/{board-dashboard-PUQPL4AX.js → board-dashboard-2524GN5N.js} +2 -2
  4. package/dist/chunk-32LIPTHM.js +55 -0
  5. package/dist/chunk-32LIPTHM.js.map +1 -0
  6. package/dist/{chunk-AHO4CQRK.js → chunk-3D5XUOBL.js} +2 -2
  7. package/dist/{chunk-MM3F74K4.js → chunk-3F4GPRGQ.js} +3 -3
  8. package/dist/chunk-3F4GPRGQ.js.map +1 -0
  9. package/dist/{chunk-MCBTVNPX.js → chunk-H4NSB54D.js} +2 -2
  10. package/dist/{chunk-4VVHGR7H.js → chunk-KEXBEUSU.js} +1 -1
  11. package/dist/{chunk-4VVHGR7H.js.map → chunk-KEXBEUSU.js.map} +1 -1
  12. package/dist/chunk-MTX4UPNB.js +3 -0
  13. package/dist/chunk-MTX4UPNB.js.map +1 -0
  14. package/dist/chunk-MUVE3UAS.js +105 -0
  15. package/dist/chunk-MUVE3UAS.js.map +1 -0
  16. package/dist/chunk-MWNCS57L.js +2 -0
  17. package/dist/chunk-MWNCS57L.js.map +1 -0
  18. package/dist/{chunk-YSXE65O7.js → chunk-NG72DYJR.js} +80 -80
  19. package/dist/chunk-NG72DYJR.js.map +1 -0
  20. package/dist/chunk-QAWKN2N4.js +3 -0
  21. package/dist/chunk-QAWKN2N4.js.map +1 -0
  22. package/dist/{chunk-FEEXOWK5.js → chunk-QDWEM4DG.js} +2 -2
  23. package/dist/{chunk-NXRSIWQA.js → chunk-R6ECKZLE.js} +2 -2
  24. package/dist/chunk-R6KLH34H.js +4 -0
  25. package/dist/chunk-R6KLH34H.js.map +1 -0
  26. package/dist/{chunk-JP5OAMWQ.js → chunk-ROLSSYXE.js} +1 -1
  27. package/dist/{chunk-JP5OAMWQ.js.map → chunk-ROLSSYXE.js.map} +1 -1
  28. package/dist/{chunk-G4HDRJVN.js → chunk-SKSYII3K.js} +2 -2
  29. package/dist/{chunk-RJOA7QBB.js → chunk-SN7YH4E4.js} +2 -2
  30. package/dist/{chunk-TDGDH4RA.js → chunk-TTG3ZW3W.js} +2 -2
  31. package/dist/{chunk-TDGDH4RA.js.map → chunk-TTG3ZW3W.js.map} +1 -1
  32. package/dist/{chunk-XWO6ALJQ.js → chunk-UTW56K5I.js} +2 -2
  33. package/dist/{chunk-VQYVL72V.js → chunk-UXOGNBGL.js} +2 -2
  34. package/dist/chunk-WYEWNN76.js +160 -0
  35. package/dist/chunk-WYEWNN76.js.map +1 -0
  36. package/dist/{chunk-RDEFA6Y6.js → chunk-ZEAT2B5B.js} +2 -2
  37. package/dist/cli.js +1 -1
  38. package/dist/{contributions-74M6YOCZ.js → contributions-ZD6ZGTJR.js} +2 -2
  39. package/dist/enrich-EFIPWA3P.js +16 -0
  40. package/dist/{enrich-NH3WHKD6.js.map → enrich-EFIPWA3P.js.map} +1 -1
  41. package/dist/event-levels-7CY7AJLY.js +3 -0
  42. package/dist/event-levels-7CY7AJLY.js.map +1 -0
  43. package/dist/graph-ZVKV3JBD.js +2 -0
  44. package/dist/{hub-VTDFUAEH.js → hub-MVX3U6A2.js} +2 -2
  45. package/dist/index.d.ts +2 -0
  46. package/dist/index.js +1 -1
  47. package/dist/load-LQ2I2PQI.js +2 -0
  48. package/dist/model-call-J7JATTMW.js +2 -0
  49. package/dist/{program-3URNCVEZ.js → program-WOQXOAYQ.js} +254 -256
  50. package/dist/program-WOQXOAYQ.js.map +1 -0
  51. package/dist/{promote-B2BADAEP.js → promote-X2XQEEQP.js} +2 -2
  52. package/dist/push-bridge-325SXDGN.js +1 -1
  53. package/dist/query-RRHYPXOM.js +2 -0
  54. package/dist/query-runs-WYRNBLLC.js +6 -0
  55. package/dist/query-runs-WYRNBLLC.js.map +1 -0
  56. package/dist/{query-settings-VJ3POZS3.js → query-settings-WKVLQEVZ.js} +2 -2
  57. package/dist/registry-VXK36CCL.js +2 -0
  58. package/dist/roster-BZGKG2ZA.js +2 -0
  59. package/dist/score-LNJ46A67.js +3 -0
  60. package/dist/score-LNJ46A67.js.map +1 -0
  61. package/dist/types-ARP27T6T.js +2 -0
  62. package/dist/types-YZX46RES.js +2 -0
  63. package/dist/{wiki-SXWJ5NJQ.js → wiki-QZ7XNAMJ.js} +2 -2
  64. package/dist/wiki-QZ7XNAMJ.js.map +1 -0
  65. package/dist/wiki-enrich-PMG4T6PP.js +2 -0
  66. package/dist/wiki-enrich-PMG4T6PP.js.map +1 -0
  67. package/package.json +1 -1
  68. package/dist/chunk-5BI2T3WD.js +0 -3
  69. package/dist/chunk-5BI2T3WD.js.map +0 -1
  70. package/dist/chunk-7VLLZQ3N.js +0 -2
  71. package/dist/chunk-7VLLZQ3N.js.map +0 -1
  72. package/dist/chunk-DDEDSYLF.js +0 -105
  73. package/dist/chunk-DDEDSYLF.js.map +0 -1
  74. package/dist/chunk-KFIJDLQ5.js +0 -3
  75. package/dist/chunk-KFIJDLQ5.js.map +0 -1
  76. package/dist/chunk-MM3F74K4.js.map +0 -1
  77. package/dist/chunk-SFVDQNTY.js +0 -156
  78. package/dist/chunk-SFVDQNTY.js.map +0 -1
  79. package/dist/chunk-URN74FTP.js +0 -55
  80. package/dist/chunk-URN74FTP.js.map +0 -1
  81. package/dist/chunk-YSXE65O7.js.map +0 -1
  82. package/dist/enrich-NH3WHKD6.js +0 -16
  83. package/dist/graph-EXDH7EPZ.js +0 -2
  84. package/dist/load-5DNMGHFW.js +0 -2
  85. package/dist/model-call-BGTDPWZD.js +0 -2
  86. package/dist/program-3URNCVEZ.js.map +0 -1
  87. package/dist/query-2VIIGGDS.js +0 -2
  88. package/dist/registry-PRCISYBO.js +0 -2
  89. package/dist/roster-IRXP32XF.js +0 -2
  90. package/dist/score-KDNIUTNV.js +0 -3
  91. package/dist/score-KDNIUTNV.js.map +0 -1
  92. package/dist/types-ROL44T62.js +0 -2
  93. /package/dist/{absorb-eval-WU7JR3BT.js.map → absorb-eval-YT5TOTC7.js.map} +0 -0
  94. /package/dist/{agent-2GPJGHHG.js.map → agent-DRUIMWF5.js.map} +0 -0
  95. /package/dist/{board-dashboard-PUQPL4AX.js.map → board-dashboard-2524GN5N.js.map} +0 -0
  96. /package/dist/{chunk-AHO4CQRK.js.map → chunk-3D5XUOBL.js.map} +0 -0
  97. /package/dist/{chunk-MCBTVNPX.js.map → chunk-H4NSB54D.js.map} +0 -0
  98. /package/dist/{chunk-FEEXOWK5.js.map → chunk-QDWEM4DG.js.map} +0 -0
  99. /package/dist/{chunk-NXRSIWQA.js.map → chunk-R6ECKZLE.js.map} +0 -0
  100. /package/dist/{chunk-G4HDRJVN.js.map → chunk-SKSYII3K.js.map} +0 -0
  101. /package/dist/{chunk-RJOA7QBB.js.map → chunk-SN7YH4E4.js.map} +0 -0
  102. /package/dist/{chunk-XWO6ALJQ.js.map → chunk-UTW56K5I.js.map} +0 -0
  103. /package/dist/{chunk-VQYVL72V.js.map → chunk-UXOGNBGL.js.map} +0 -0
  104. /package/dist/{chunk-RDEFA6Y6.js.map → chunk-ZEAT2B5B.js.map} +0 -0
  105. /package/dist/{contributions-74M6YOCZ.js.map → contributions-ZD6ZGTJR.js.map} +0 -0
  106. /package/dist/{graph-EXDH7EPZ.js.map → graph-ZVKV3JBD.js.map} +0 -0
  107. /package/dist/{hub-VTDFUAEH.js.map → hub-MVX3U6A2.js.map} +0 -0
  108. /package/dist/{load-5DNMGHFW.js.map → load-LQ2I2PQI.js.map} +0 -0
  109. /package/dist/{model-call-BGTDPWZD.js.map → model-call-J7JATTMW.js.map} +0 -0
  110. /package/dist/{promote-B2BADAEP.js.map → promote-X2XQEEQP.js.map} +0 -0
  111. /package/dist/{query-2VIIGGDS.js.map → query-RRHYPXOM.js.map} +0 -0
  112. /package/dist/{query-settings-VJ3POZS3.js.map → query-settings-WKVLQEVZ.js.map} +0 -0
  113. /package/dist/{registry-PRCISYBO.js.map → registry-VXK36CCL.js.map} +0 -0
  114. /package/dist/{roster-IRXP32XF.js.map → roster-BZGKG2ZA.js.map} +0 -0
  115. /package/dist/{types-ROL44T62.js.map → types-ARP27T6T.js.map} +0 -0
  116. /package/dist/{wiki-SXWJ5NJQ.js.map → types-YZX46RES.js.map} +0 -0
@@ -0,0 +1,3 @@
1
+ import{existsSync as U,readFileSync as v,statSync as B}from"fs";import{resolve as I}from"path";var A=new Map;function N(t){if(!A.has(t))try{A.set(t,new RegExp(t,"i"))}catch{A.set(t,null)}return A.get(t)??null}function R(t,a,o){let i=new Set(o.types.map(r=>r.id));if(t.class&&i.has(t.class))return t.class;let n=a.split("/").slice(0,-1).map(r=>r.toLowerCase()),e=new Set((t.tags??[]).map(r=>r.toLowerCase()));for(let r of o.classify){if(!i.has(r.type))continue;let c=0;if(!(r.legacy&&(c++,!t.type||!r.legacy.includes(t.type)))&&!(r.path&&(c++,!r.path.some(m=>n.includes(m.toLowerCase()))))&&!(r.tags&&(c++,!r.tags.some(m=>e.has(m.toLowerCase()))))){if(r.title){c++;let m=N(r.title);if(!m||!m.test(t.title))continue}if(c>0)return r.type}}return i.has(o.fallback_type)?o.fallback_type:o.types[0].id}function u(t){return t.normalize("NFKD").replace(/[̀-ͯ]/g,"").toLowerCase().replace(/\.md$/,"").replace(/[^\p{L}\p{N}]+/gu," ").trim()}function E(t){return u(t).replace(/ /g,"-").slice(0,80)||"page"}var O=/\[\[([^\]|#]+)(?:[|#][^\]]*)?\]\]/g;function G(t){if(t.meta.date)return t.meta.date;let o=(t.path.split("/").pop()??"").match(/^(\d{4}-\d{2}-\d{2})/);if(o)return o[1];let i=t.meta.title.match(/(\d{4}-\d{2}-\d{2})/);return i?i[1]:(t.meta.created||t.meta.lastUpdated||"").slice(0,10)}function $(t,a,o){if(a.size===0||!o.types.some(e=>e.id==="agent"))return;let i=o.types.find(e=>e.id==="agent").pillar,n=new Set;for(let e of t.values()){let r=u(e.title).split(" ");e.type==="person"&&r.length>1&&n.add(r[0])}for(let e of t.values()){if(e.type!=="person"&&e.type!==o.fallback_type||e.pages.some(c=>c.article.meta.class))continue;let r=M(a,[e.title]);if(!r){let c=M(a,e.pages.flatMap(m=>m.article.meta.aliases??[]));c&&(e.review??=`an alias matches agent ${c}; set class on the page to decide`);continue}if(e.type==="person"&&n.has(u(e.title))){e.review=`shares a name with agent ${r} and with a person; set class on the page to decide`;continue}e.type="agent",e.pillar=i}}function L(t,a,o=new Map){let i=new Map(a.types.map(s=>[s.id,s])),n=new Map,e=new Map,r=new Set;for(let s of t){let p=[s.article.meta.title,...s.article.meta.aliases??[]].map(u).filter(Boolean);if(p.length===0)continue;let l=p.map(d=>n.get(d)).find(Boolean);if(!l){l=E(s.article.meta.title);for(let d=2;r.has(l);d++)l=`${E(s.article.meta.title)}-${d}`;r.add(l),e.set(l,[])}e.get(l).push(s);for(let d of p)n.has(d)||n.set(d,l);let f=u(s.article.path);n.has(f)||n.set(f,l)}let c=new Map;for(let[s,p]of e){let l=new Map;for(let g of p){let w=R(g.article.meta,g.article.path,a),W=g.article.meta.class===w?1e3:w===a.fallback_type?.5:1;l.set(w,(l.get(w)??0)+W)}let f=[...l.entries()].sort((g,w)=>w[1]-g[1])[0][0],d=p[0].article,b=p.map(g=>G(g.article)).filter(Boolean).sort(),P=p.map(g=>g.article.meta.importance).find(Boolean)??a.importance.default;c.set(s,{id:s,type:f,pillar:i.get(f)?.pillar??"topics",title:d.meta.title,pages:p,statements:[],date:b[0]??"",updated:p.map(g=>g.article.meta.lastUpdated||"").sort().pop()??"",importance:p.some(g=>g.article.meta.importance==="major")?"major":P,importanceProposed:p.map(g=>g.article.meta.importanceProposed).find(Boolean),rolledUpInto:p.map(g=>g.article.meta.rolledUpInto).find(Boolean),sources:[...new Set(p.flatMap(g=>g.article.meta.sources??[]))]})}$(c,o,a);let m=s=>n.get(u(s)),y=new Map,h=new Map,x=s=>{if(s.from===s.to)return;let p=y.get(s.from)??[];if(s.property==="related"&&p.some(f=>f.to===s.to))return;p.push(s),y.set(s.from,p);let l=h.get(s.to)??[];l.push(s),h.set(s.to,l)};for(let s of c.values())for(let p of s.pages)for(let l of p.article.meta.statements??[]){let f={...l,page:p,target:m(l.value)};s.statements.push(f),f.target&&x({from:s.id,to:f.target,property:l.property,statement:f})}for(let s of c.values())for(let p of s.pages){let l=new Set(p.article.meta.related??[]);for(let f of p.article.content.matchAll(O))l.add(f[1].trim());for(let f of l){let d=m(f);d&&x({from:s.id,to:d,property:"related"})}}for(let s of c.values())s.rolledUpInto&&(s.rolledUpInto=m(s.rolledUpInto));return{ontology:a,entities:c,outgoing:y,incoming:h,names:n}}var S=class{last=null;get(a,o,i=new Map){let n=[];for(let y of a.listAgents([]))for(let h of a.getAgentWiki(y).listAllArticles())n.push({agentId:y,article:h});let e=n.map(y=>y.article),r=[...i].join(`
2
+ `),c=this.last;if(c&&c.ontology===o&&c.roster===r&&c.refs.length===e.length&&c.refs.every((y,h)=>y===e[h]))return c.graph;let m=L(n,o,i);return this.last={refs:e,ontology:o,roster:r,graph:m},m}};function _(t,a=[]){let o=new Map,i=(n,e)=>{let r=n?u(n):"";r&&!o.has(r)&&o.set(r,e)};for(let n of t)(!n.name||u(n.name)!==`${u(n.id)} agent`)&&i(n.id,n.id),i(n.name,n.id),i(n.persona,n.id);for(let n of a)i(n,n);return o}function M(t,a){for(let o of a){let i=u(o),n=t.get(i)??t.get(i.replace(/ (agent|bot)$/,""));if(n)return n;if(/ (agent|bot)( |$)/.test(i)){for(let[e,r]of t)if(i.startsWith(`${e} `))return r}}}var j=["IDENTITY.md","persona.md","PERSONA.md"];function C(t){let o=t.match(/\*\*Name:\*\*[ \t]*([^\n(.,;—–]+)/)?.[1]?.replace(/[*_`]/g,"").trim();return o&&o.length<=40?o:void 0}function K(t){if(t)for(let a of j){let o=I(t,a);try{if(U(o)){let i=C(v(o,"utf-8"));if(i)return i}}catch{}}}var k=null;function D(t){let a=I(t,"..","..","agentx.json"),o;try{o=B(a).mtimeMs}catch{return[]}if(k&&k.file===a&&k.mtimeMs===o)return k.agents;let i=[];try{let n=JSON.parse(v(a,"utf-8"));i=Object.entries(n.agents??{}).map(([e,r])=>({id:e,name:typeof r?.name=="string"?r.name:void 0,persona:K(typeof r?.workspace=="string"?r.workspace:void 0)}))}catch{i=[]}return k={file:a,mtimeMs:o,agents:i},i}function Q(t,a,o=[]){let i=D(t),n=new Set(i.map(e=>e.id));return _([...i,...a.filter(e=>!n.has(e)).map(e=>({id:e}))],o)}export{_ as a,M as b,C as c,D as d,Q as e,u as f,E as g,L as h,S as i};
3
+ //# sourceMappingURL=chunk-QAWKN2N4.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/wiki/ontology/roster.ts","../src/wiki/ontology/classify.ts","../src/wiki/ontology/graph.ts"],"sourcesContent":["// --- Names the fleet's agents go by, so their pages are not people (#819) ---\n//\n// Old pages about an agent often carry `type: person` and a title that is\n// just the agent's name (\"Helper\", or a persona name). The classify rules\n// only catch titles with \"agent\" or \"bot\" in them. The roster lists every\n// name an agent answers to: its id, its configured name, the persona name\n// in its workspace, and any extra names the owner lists under\n// `agent_names` in ontology.yaml (agents on another node, for example).\n// Deterministic: no model call.\n\nimport { existsSync, readFileSync, statSync } from \"fs\"\nimport { resolve } from \"path\"\nimport { normName } from \"./graph\"\n\n/** Normalised name → agent id. */\nexport type AgentRoster = Map<string, string>\n\nexport interface RosterAgent {\n id: string\n name?: string\n /** Persona name read from the agent's workspace. */\n persona?: string\n}\n\nexport function buildRoster(agents: RosterAgent[], extraNames: string[] = []): AgentRoster {\n const roster: AgentRoster = new Map()\n const add = (name: string | undefined, id: string): void => {\n const key = name ? normName(name) : \"\"\n if (key && !roster.has(key)) roster.set(key, id)\n }\n for (const a of agents) {\n // An agent named \"<id> Agent\" is named after a person: the bare id\n // is that person's name, so only the full name points at the agent.\n if (!a.name || normName(a.name) !== `${normName(a.id)} agent`) add(a.id, a.id)\n add(a.name, a.id)\n add(a.persona, a.id)\n }\n for (const n of extraNames) add(n, n)\n return roster\n}\n\n/** The agent a title or alias names, if any. \"<name> agent\" counts too. */\nexport function rosterMatch(roster: AgentRoster, names: string[]): string | undefined {\n for (const n of names) {\n const key = normName(n)\n const hit = roster.get(key) ?? roster.get(key.replace(/ (agent|bot)$/, \"\"))\n if (hit) return hit\n // \"Release Helper (release_helper_bot)\": an agent name, then its handle.\n if (/ (agent|bot)( |$)/.test(key)) {\n for (const [name, id] of roster) if (key.startsWith(`${name} `)) return id\n }\n }\n return undefined\n}\n\nconst PERSONA_FILES = [\"IDENTITY.md\", \"persona.md\", \"PERSONA.md\"]\n\n/** `**Name:** Nova (…)` → \"Nova\". Only a short single name counts. */\nexport function personaName(text: string): string | undefined {\n const m = text.match(/\\*\\*Name:\\*\\*[ \\t]*([^\\n(.,;—–]+)/)\n const name = m?.[1]?.replace(/[*_`]/g, \"\").trim()\n return name && name.length <= 40 ? name : undefined\n}\n\nfunction readPersona(workspace: string | undefined): string | undefined {\n if (!workspace) return undefined\n for (const f of PERSONA_FILES) {\n const file = resolve(workspace, f)\n try {\n if (existsSync(file)) {\n const name = personaName(readFileSync(file, \"utf-8\"))\n if (name) return name\n }\n } catch {\n // An unreadable workspace file just means no persona name.\n }\n }\n return undefined\n}\n\nlet cached: { file: string; mtimeMs: number; agents: RosterAgent[] } | null = null\n\n/** Agents configured in the `agentx.json` next to the wiki (`<root>/.agentx/wiki`). */\nexport function configuredAgents(wikiDir: string): RosterAgent[] {\n const file = resolve(wikiDir, \"..\", \"..\", \"agentx.json\")\n let mtimeMs: number\n try { mtimeMs = statSync(file).mtimeMs } catch { return [] }\n if (cached && cached.file === file && cached.mtimeMs === mtimeMs) return cached.agents\n let agents: RosterAgent[] = []\n try {\n const raw = JSON.parse(readFileSync(file, \"utf-8\")) as { agents?: Record<string, { name?: unknown; workspace?: unknown }> }\n agents = Object.entries(raw.agents ?? {}).map(([id, a]) => ({\n id,\n name: typeof a?.name === \"string\" ? a.name : undefined,\n persona: readPersona(typeof a?.workspace === \"string\" ? a.workspace : undefined),\n }))\n } catch {\n agents = [] // a config being rewritten; the next request reads it again\n }\n cached = { file, mtimeMs, agents }\n return agents\n}\n\n/** Roster for a wiki: its agent folders, the node's config, and `agent_names`. */\nexport function loadRoster(wikiDir: string, agentIds: string[], extraNames: string[] = []): AgentRoster {\n const configured = configuredAgents(wikiDir)\n const known = new Set(configured.map(a => a.id))\n return buildRoster([...configured, ...agentIds.filter(id => !known.has(id)).map(id => ({ id }))], extraNames)\n}\n","// --- Give a wiki page its ontology type (#811) ---\n\nimport type { WikiArticleMeta } from \"../types\"\nimport type { Ontology } from \"./types\"\n\nconst patternCache = new Map<string, RegExp | null>()\n\nfunction titlePattern(src: string): RegExp | null {\n if (!patternCache.has(src)) {\n try {\n patternCache.set(src, new RegExp(src, \"i\"))\n } catch {\n patternCache.set(src, null) // reported by checkOntology; the rule never matches\n }\n }\n return patternCache.get(src) ?? null\n}\n\n/**\n * The page's own `class` when the ontology knows it, else the first\n * classify rule whose every condition matches, else the fallback type.\n * `path` is relative to the agent's wiki, e.g. `clients/example.md`.\n */\nexport function classifyPage(meta: WikiArticleMeta, path: string, o: Ontology): string {\n const known = new Set(o.types.map(t => t.id))\n if (meta.class && known.has(meta.class)) return meta.class\n\n const folders = path.split(\"/\").slice(0, -1).map(f => f.toLowerCase())\n const tags = new Set((meta.tags ?? []).map(t => t.toLowerCase()))\n for (const rule of o.classify) {\n if (!known.has(rule.type)) continue\n let conditions = 0\n if (rule.legacy) {\n conditions++\n if (!meta.type || !rule.legacy.includes(meta.type)) continue\n }\n if (rule.path) {\n conditions++\n if (!rule.path.some(p => folders.includes(p.toLowerCase()))) continue\n }\n if (rule.tags) {\n conditions++\n if (!rule.tags.some(t => tags.has(t.toLowerCase()))) continue\n }\n if (rule.title) {\n conditions++\n const re = titlePattern(rule.title)\n if (!re || !re.test(meta.title)) continue\n }\n if (conditions > 0) return rule.type\n }\n return known.has(o.fallback_type) ? o.fallback_type : o.types[0].id\n}\n","// --- One entity per real thing, across every agent's wiki (#811) ---\n//\n// Agents each keep their own pages, so the same person or server can\n// have several. The graph merges pages that share a name (title or\n// alias) into one entity, gives it a type from the ontology, and turns\n// statements and plain links into edges both ways. It is rebuilt only\n// when a page or the ontology changed.\n\nimport type { WikiArticle } from \"../types\"\nimport type { WikiHub } from \"../hub\"\nimport { classifyPage } from \"./classify\"\nimport { rosterMatch, type AgentRoster } from \"./roster\"\nimport type { Importance, Ontology, WikiStatement } from \"./types\"\n\nexport interface GraphPage {\n agentId: string\n article: WikiArticle\n}\n\nexport interface EntityStatement extends WikiStatement {\n /** Entity id of the value when it names a page. */\n target?: string\n page: GraphPage\n}\n\nexport interface Entity {\n id: string\n type: string\n pillar: string\n title: string\n pages: GraphPage[]\n statements: EntityStatement[]\n /** YYYY-MM-DD: the event date, else the first page's creation date. */\n date: string\n updated: string\n importance: Importance\n /** A level a job suggests; only the owner may set it. */\n importanceProposed?: Importance\n rolledUpInto?: string\n /** Raw entry ids the pages were written from. */\n sources: string[]\n /** Why the type needs a person to check it (#819). */\n review?: string\n}\n\nexport interface Edge {\n from: string\n to: string\n property: string\n statement?: EntityStatement\n}\n\nexport interface WikiGraph {\n ontology: Ontology\n entities: Map<string, Entity>\n outgoing: Map<string, Edge[]>\n incoming: Map<string, Edge[]>\n /** Normalised name or path → entity id, for links. */\n names: Map<string, string>\n}\n\nexport function normName(s: string): string {\n return s.normalize(\"NFKD\").replace(/[̀-ͯ]/g, \"\").toLowerCase().replace(/\\.md$/, \"\").replace(/[^\\p{L}\\p{N}]+/gu, \" \").trim()\n}\n\nexport function slugify(s: string): string {\n return normName(s).replace(/ /g, \"-\").slice(0, 80) || \"page\"\n}\n\nconst WIKILINK = /\\[\\[([^\\]|#]+)(?:[|#][^\\]]*)?\\]\\]/g\n\nfunction eventDate(a: WikiArticle): string {\n if (a.meta.date) return a.meta.date\n const base = a.path.split(\"/\").pop() ?? \"\"\n const fromPath = base.match(/^(\\d{4}-\\d{2}-\\d{2})/)\n if (fromPath) return fromPath[1]\n const fromTitle = a.meta.title.match(/(\\d{4}-\\d{2}-\\d{2})/)\n if (fromTitle) return fromTitle[1]\n return (a.meta.created || a.meta.lastUpdated || \"\").slice(0, 10)\n}\n\n/**\n * Pages named after one of the fleet's agents are agents, not people\n * (#819). A page's own `class` still wins. When the name is also the\n * first name of a person page (\"Sam\" next to \"Sam Lee\"), the page keeps\n * its type and is flagged instead of guessed.\n */\nfunction applyRoster(entities: Map<string, Entity>, roster: AgentRoster, o: Ontology): void {\n if (roster.size === 0 || !o.types.some(t => t.id === \"agent\")) return\n const agentPillar = o.types.find(t => t.id === \"agent\")!.pillar\n const firstNames = new Set<string>()\n for (const e of entities.values()) {\n const words = normName(e.title).split(\" \")\n if (e.type === \"person\" && words.length > 1) firstNames.add(words[0])\n }\n for (const e of entities.values()) {\n // Only people and untyped pages: a project or client can share an agent's name.\n if ((e.type !== \"person\" && e.type !== o.fallback_type) || e.pages.some(p => p.article.meta.class)) continue\n const agent = rosterMatch(roster, [e.title])\n if (!agent) {\n // An alias is weaker evidence: a person can have a nickname that is\n // also an agent's name. Flag it, never retype it.\n const byAlias = rosterMatch(roster, e.pages.flatMap(p => p.article.meta.aliases ?? []))\n if (byAlias) e.review ??= `an alias matches agent ${byAlias}; set class on the page to decide`\n continue\n }\n if (e.type === \"person\" && firstNames.has(normName(e.title))) {\n e.review = `shares a name with agent ${agent} and with a person; set class on the page to decide`\n continue\n }\n e.type = \"agent\"\n e.pillar = agentPillar\n }\n}\n\n/** Build the graph from pages. Pure: tests call it directly. */\nexport function buildGraph(pages: GraphPage[], o: Ontology, roster: AgentRoster = new Map()): WikiGraph {\n const typeDef = new Map(o.types.map(t => [t.id, t]))\n const names = new Map<string, string>()\n const groups = new Map<string, GraphPage[]>()\n const usedIds = new Set<string>()\n\n for (const page of pages) {\n const keys = [page.article.meta.title, ...(page.article.meta.aliases ?? [])].map(normName).filter(Boolean)\n if (keys.length === 0) continue\n let id = keys.map(k => names.get(k)).find(Boolean)\n if (!id) {\n id = slugify(page.article.meta.title)\n for (let n = 2; usedIds.has(id); n++) id = `${slugify(page.article.meta.title)}-${n}`\n usedIds.add(id)\n groups.set(id, [])\n }\n groups.get(id)!.push(page)\n for (const k of keys) if (!names.has(k)) names.set(k, id)\n const pathKey = normName(page.article.path)\n if (!names.has(pathKey)) names.set(pathKey, id)\n }\n\n const entities = new Map<string, Entity>()\n for (const [id, group] of groups) {\n const votes = new Map<string, number>()\n for (const p of group) {\n const t = classifyPage(p.article.meta, p.article.path, o)\n // An explicit class outvotes any number of inferred ones.\n const weight = p.article.meta.class === t ? 1000 : t === o.fallback_type ? 0.5 : 1\n votes.set(t, (votes.get(t) ?? 0) + weight)\n }\n const type = [...votes.entries()].sort((a, b) => b[1] - a[1])[0][0]\n const first = group[0].article\n const dated = group.map(p => eventDate(p.article)).filter(Boolean).sort()\n const importance = group.map(p => p.article.meta.importance).find(Boolean) ?? o.importance.default\n entities.set(id, {\n id,\n type,\n pillar: typeDef.get(type)?.pillar ?? \"topics\",\n title: first.meta.title,\n pages: group,\n statements: [],\n date: dated[0] ?? \"\",\n updated: group.map(p => p.article.meta.lastUpdated || \"\").sort().pop() ?? \"\",\n importance: group.some(p => p.article.meta.importance === \"major\") ? \"major\" : importance,\n importanceProposed: group.map(p => p.article.meta.importanceProposed).find(Boolean),\n rolledUpInto: group.map(p => p.article.meta.rolledUpInto).find(Boolean),\n sources: [...new Set(group.flatMap(p => p.article.meta.sources ?? []))],\n })\n }\n applyRoster(entities, roster, o)\n\n const resolveName = (s: string): string | undefined => names.get(normName(s))\n const outgoing = new Map<string, Edge[]>()\n const incoming = new Map<string, Edge[]>()\n const addEdge = (e: Edge): void => {\n if (e.from === e.to) return\n const out = outgoing.get(e.from) ?? []\n if (e.property === \"related\" && out.some(x => x.to === e.to)) return\n out.push(e)\n outgoing.set(e.from, out)\n const inc = incoming.get(e.to) ?? []\n inc.push(e)\n incoming.set(e.to, inc)\n }\n\n for (const entity of entities.values()) {\n for (const page of entity.pages) {\n for (const s of page.article.meta.statements ?? []) {\n const st: EntityStatement = { ...s, page, target: resolveName(s.value) }\n entity.statements.push(st)\n if (st.target) addEdge({ from: entity.id, to: st.target, property: s.property, statement: st })\n }\n }\n }\n // Plain links after typed ones, so a typed edge wins over \"related\".\n for (const entity of entities.values()) {\n for (const page of entity.pages) {\n const linked = new Set(page.article.meta.related ?? [])\n for (const m of page.article.content.matchAll(WIKILINK)) linked.add(m[1].trim())\n for (const name of linked) {\n const to = resolveName(name)\n if (to) addEdge({ from: entity.id, to, property: \"related\" })\n }\n }\n }\n for (const e of entities.values()) {\n if (e.rolledUpInto) e.rolledUpInto = resolveName(e.rolledUpInto)\n }\n return { ontology: o, entities, outgoing, incoming, names }\n}\n\n/** Builds from a hub, reusing the last graph while no page changed. */\nexport class GraphCache {\n private last: { refs: WikiArticle[]; ontology: Ontology; roster: string; graph: WikiGraph } | null = null\n\n get(hub: WikiHub, o: Ontology, roster: AgentRoster = new Map()): WikiGraph {\n const pages: GraphPage[] = []\n for (const agentId of hub.listAgents([])) {\n for (const article of hub.getAgentWiki(agentId).listAllArticles()) pages.push({ agentId, article })\n }\n // The store hands back the same objects while files are unchanged.\n const refs = pages.map(p => p.article)\n const rosterKey = [...roster].join(\"\\n\")\n const last = this.last\n if (last && last.ontology === o && last.roster === rosterKey && last.refs.length === refs.length && last.refs.every((r, i) => r === refs[i])) {\n return last.graph\n }\n const graph = buildGraph(pages, o, roster)\n this.last = { refs, ontology: o, roster: rosterKey, graph }\n return graph\n }\n}\n"],"mappings":"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","names":["existsSync","readFileSync","statSync","resolve","patternCache","titlePattern","src","classifyPage","meta","path","known","t","folders","f","tags","rule","conditions","p","re","normName","s","slugify","WIKILINK","eventDate","a","fromPath","fromTitle","applyRoster","entities","roster","t","agentPillar","firstNames","words","p","agent","rosterMatch","byAlias","buildGraph","pages","o","typeDef","names","groups","usedIds","page","keys","id","k","n","pathKey","group","votes","classifyPage","weight","type","b","first","dated","importance","resolveName","outgoing","incoming","addEdge","e","out","x","inc","entity","st","linked","m","name","to","GraphCache","hub","agentId","article","refs","rosterKey","last","r","i","graph","buildRoster","agents","extraNames","roster","add","name","id","key","normName","a","rosterMatch","names","n","hit","PERSONA_FILES","personaName","text","readPersona","workspace","f","file","resolve","existsSync","readFileSync","cached","configuredAgents","wikiDir","mtimeMs","statSync","raw","loadRoster","agentIds","configured","known"]}
@@ -1,4 +1,4 @@
1
- import{b as St,c as At}from"./chunk-QSKG754X.js";import{b as xe}from"./chunk-AFRHKA7B.js";import{b as ht,e as wt,f as yt,i as It,k as vt,l as $t,q as Rt}from"./chunk-SSQMFHSM.js";import{c as ft,e as gt,f as mt}from"./chunk-KA2UIZXG.js";import{e as ut,g as ct,h as pt,i as Re}from"./chunk-GQ76J72O.js";import{a as kt}from"./chunk-GV7S5BAX.js";import{c as bt}from"./chunk-SZDACK6O.js";import{y as at,z as Bn}from"./chunk-4XMALK7X.js";import{b as st,f as Kn}from"./chunk-72SZI2UW.js";import{a as dt,b as $e,c as Se,d as lt,e as Ae}from"./chunk-QD6IICK7.js";import{c as ot,d as it}from"./chunk-QOYAX2FT.js";import{f as ve,n as Hn}from"./chunk-UIBUZ2N3.js";import{existsSync as qn,mkdirSync as xt,readFileSync as zn,writeFileSync as Jn,renameSync as Yn}from"fs";import{resolve as Pt,dirname as Gn}from"path";var Tt=class{filePath;log;constructor(e={}){let t=e.baseDir??Pt(process.cwd(),".agentx");this.filePath=Pt(t,"handover.json"),this.log=e.log??console.error.bind(console,"[handover]"),xt(t,{recursive:!0})}key(e,t,r){return`${e}::${t}::${r||"-"}`}load(){if(!qn(this.filePath))return{version:1,overrides:{}};try{let e=zn(this.filePath,"utf-8"),t=JSON.parse(e);return!t||typeof t!="object"?{version:1,overrides:{}}:(t.overrides||(t.overrides={}),t)}catch(e){return this.log(`load failed, starting empty: ${e?.message||e}`),{version:1,overrides:{}}}}save(e){xt(Gn(this.filePath),{recursive:!0});let t=`${this.filePath}.tmp-${process.pid}`;Jn(t,JSON.stringify(e,null,2)),Yn(t,this.filePath)}get(e,t,r){let s=this.load().overrides[this.key(e,t,r)];if(s&&!(s.expiresAt&&Date.parse(s.expiresAt)<Date.now()))return s}set(e){if(!e.channel||!e.chatId||!e.toAgent)throw new Error("channel, chatId and toAgent are required");let t=this.load(),r={...e,createdAt:e.createdAt??new Date().toISOString()};return t.overrides[this.key(r.channel,r.chatId,r.accountId)]=r,this.save(t),r}remove(e,t,r){let o=this.load(),s=this.key(e,t,r);return s in o.overrides?(delete o.overrides[s],this.save(o),!0):!1}consumeSummary(e,t,r){let o=this.load(),s=o.overrides[this.key(e,t,r)];if(!s||!s.summary||s.summaryConsumedAt)return;let a=s.summary;return s.summaryConsumedAt=new Date().toISOString(),this.save(o),a}listActive(){let e=this.load(),t=Date.now();return Object.values(e.overrides).filter(r=>!r.expiresAt||Date.parse(r.expiresAt)>=t)}listByAgent(e){let t=this.listActive();return{incoming:t.filter(r=>r.toAgent===e),outgoing:t.filter(r=>r.fromAgent===e)}}};import{readFileSync as Et,writeFileSync as Pe,appendFileSync as Xn,mkdirSync as er,existsSync as Mt,statSync as tr}from"fs";import{resolve as ne}from"path";var Wt="[Agent Memory \u2014 persistent facts from past conversations]",Vn=2400;function Nt(n,e){return n?`UNVERIFIED (${e===null?"age unknown":`${e}d old`}) `:""}function Qn(n,e=Date.now()){let t=gt(n,e),r=n.undated?"UNVERIFIED (never checked) ":Nt(t,ut(n.verifiedAt,e));return{line:`- [wiki ${n.id}] ${r}${n.subject} \xB7 ${n.attribute}: ${n.value} (${n.source}, checked ${n.verifiedAt.slice(0,10)} by ${n.verifiedBy})`,unverified:t}}function Zn(n,e){let r=!n.source.chatId.startsWith("-")&&/^\d+$/.test(n.source.chatId)?"DM":n.source.chatId,o=pt(n,e);return{line:`- [${n.category}] ${Nt(o,ct(n,e))}${n.content} (${r}, ${n.source.date})`,unverified:o}}function Dt(n,e=[],t=Date.now()){let r=[],o=new Set,s=Wt.length,a=!1;for(let i of n){let d=e.length>0?mt(i.content,e):null,l,p,c=d&&Date.parse(i.createdAt)>Date.parse(d.verifiedAt)&&!i.content.toLowerCase().includes(d.value.toLowerCase());if(d&&!c){if(o.has(d.id))continue;o.add(d.id),l=Qn(d,t),p=d.id}else l=Zn(i,t),d&&(l.line+=` [differs from wiki ${d.id}]`);let f=l.unverified&&!a?Re.length:0;if(s+f+l.line.length>Vn)break;r.push({fact:i,...l,...p?{wikiRef:p}:{}}),s+=f+l.line.length,f&&(a=!0)}return r}function Ot(n){if(n.length===0)return"";let e=n.some(t=>t.unverified)?[Re]:[];return[Wt,...e,...n.map(t=>t.line),"[End Memory]"].join(`
1
+ import{b as St,c as At}from"./chunk-QSKG754X.js";import{c as bt}from"./chunk-SZDACK6O.js";import{b as xe}from"./chunk-AFRHKA7B.js";import{b as ht,e as wt,f as yt,i as It,k as vt,l as $t,q as Rt}from"./chunk-SSQMFHSM.js";import{c as ft,e as gt,f as mt}from"./chunk-KA2UIZXG.js";import{e as ut,g as ct,h as pt,i as Re}from"./chunk-GQ76J72O.js";import{a as kt}from"./chunk-GV7S5BAX.js";import{y as at,z as Bn}from"./chunk-4XMALK7X.js";import{b as st,f as Kn}from"./chunk-72SZI2UW.js";import{a as dt,b as $e,c as Se,d as lt,e as Ae}from"./chunk-QD6IICK7.js";import{c as ot,d as it}from"./chunk-QOYAX2FT.js";import{f as ve,n as Hn}from"./chunk-UIBUZ2N3.js";import{existsSync as qn,mkdirSync as xt,readFileSync as zn,writeFileSync as Jn,renameSync as Yn}from"fs";import{resolve as Pt,dirname as Gn}from"path";var Tt=class{filePath;log;constructor(e={}){let t=e.baseDir??Pt(process.cwd(),".agentx");this.filePath=Pt(t,"handover.json"),this.log=e.log??console.error.bind(console,"[handover]"),xt(t,{recursive:!0})}key(e,t,r){return`${e}::${t}::${r||"-"}`}load(){if(!qn(this.filePath))return{version:1,overrides:{}};try{let e=zn(this.filePath,"utf-8"),t=JSON.parse(e);return!t||typeof t!="object"?{version:1,overrides:{}}:(t.overrides||(t.overrides={}),t)}catch(e){return this.log(`load failed, starting empty: ${e?.message||e}`),{version:1,overrides:{}}}}save(e){xt(Gn(this.filePath),{recursive:!0});let t=`${this.filePath}.tmp-${process.pid}`;Jn(t,JSON.stringify(e,null,2)),Yn(t,this.filePath)}get(e,t,r){let s=this.load().overrides[this.key(e,t,r)];if(s&&!(s.expiresAt&&Date.parse(s.expiresAt)<Date.now()))return s}set(e){if(!e.channel||!e.chatId||!e.toAgent)throw new Error("channel, chatId and toAgent are required");let t=this.load(),r={...e,createdAt:e.createdAt??new Date().toISOString()};return t.overrides[this.key(r.channel,r.chatId,r.accountId)]=r,this.save(t),r}remove(e,t,r){let o=this.load(),s=this.key(e,t,r);return s in o.overrides?(delete o.overrides[s],this.save(o),!0):!1}consumeSummary(e,t,r){let o=this.load(),s=o.overrides[this.key(e,t,r)];if(!s||!s.summary||s.summaryConsumedAt)return;let a=s.summary;return s.summaryConsumedAt=new Date().toISOString(),this.save(o),a}listActive(){let e=this.load(),t=Date.now();return Object.values(e.overrides).filter(r=>!r.expiresAt||Date.parse(r.expiresAt)>=t)}listByAgent(e){let t=this.listActive();return{incoming:t.filter(r=>r.toAgent===e),outgoing:t.filter(r=>r.fromAgent===e)}}};import{readFileSync as Et,writeFileSync as Pe,appendFileSync as Xn,mkdirSync as er,existsSync as Mt,statSync as tr}from"fs";import{resolve as ne}from"path";var Wt="[Agent Memory \u2014 persistent facts from past conversations]",Vn=2400;function Nt(n,e){return n?`UNVERIFIED (${e===null?"age unknown":`${e}d old`}) `:""}function Qn(n,e=Date.now()){let t=gt(n,e),r=n.undated?"UNVERIFIED (never checked) ":Nt(t,ut(n.verifiedAt,e));return{line:`- [wiki ${n.id}] ${r}${n.subject} \xB7 ${n.attribute}: ${n.value} (${n.source}, checked ${n.verifiedAt.slice(0,10)} by ${n.verifiedBy})`,unverified:t}}function Zn(n,e){let r=!n.source.chatId.startsWith("-")&&/^\d+$/.test(n.source.chatId)?"DM":n.source.chatId,o=pt(n,e);return{line:`- [${n.category}] ${Nt(o,ct(n,e))}${n.content} (${r}, ${n.source.date})`,unverified:o}}function Dt(n,e=[],t=Date.now()){let r=[],o=new Set,s=Wt.length,a=!1;for(let i of n){let d=e.length>0?mt(i.content,e):null,l,p,c=d&&Date.parse(i.createdAt)>Date.parse(d.verifiedAt)&&!i.content.toLowerCase().includes(d.value.toLowerCase());if(d&&!c){if(o.has(d.id))continue;o.add(d.id),l=Qn(d,t),p=d.id}else l=Zn(i,t),d&&(l.line+=` [differs from wiki ${d.id}]`);let f=l.unverified&&!a?Re.length:0;if(s+f+l.line.length>Vn)break;r.push({fact:i,...l,...p?{wikiRef:p}:{}}),s+=f+l.line.length,f&&(a=!0)}return r}function Ot(n){if(n.length===0)return"";let e=n.some(t=>t.unverified)?[Re]:[];return[Wt,...e,...n.map(t=>t.line),"[End Memory]"].join(`
2
2
  `)}var Ft=200,nr=7,jt=class{memoryDir;ledger;constructor(e=process.cwd()){this.memoryDir=ne(e,".agentx/memory"),this.ledger=new ft(ne(e,".agentx/wiki")),Mt(this.memoryDir)||er(this.memoryDir,{recursive:!0})}filePath(e){return ne(this.memoryDir,`${e}.jsonl`)}addMemory(e,t){if(t.category==="secret"||$e(t.content))return!1;let r=t.source.trust??dt(t.source.channel),o=t.review??lt(r),s={...t,source:{...t.source,trust:r},...o?{review:o}:{},id:`${Date.now().toString(36)}-${Math.random().toString(36).slice(2,6)}`,createdAt:new Date().toISOString()};return Xn(this.filePath(e),JSON.stringify(s)+`
3
3
  `),!0}parsedCache=new Map;getAll(e){let t=this.filePath(e);if(!Mt(t))return[];try{let{mtimeMs:r,size:o}=tr(t),s=this.parsedCache.get(t);if(s&&s.mtimeMs===r&&s.size===o)return s.facts.slice();let i=Et(t,"utf-8").split(`
4
4
  `).filter(d=>d.trim()).map(d=>{try{return JSON.parse(d)}catch{return null}}).filter(d=>d!==null);return this.parsedCache.set(t,{mtimeMs:r,size:o,facts:i}),i.slice()}catch{return[]}}findRelevant(e,t,r=8){let o=this.getAll(t).filter(Ae);if(o.length===0)return[];let s=o.map(g=>`${g.keywords.join(" ")} ${g.content}`),a=ne(this.memoryDir,`${t}_bm25_cache.json`),i=it(s,a),d=ot(e,i),l=new Map(d.map(g=>[g.docIndex,g.score])),c=o.map((g,m)=>{let y=l.get(m)??0;return g.category==="commitment"&&(y+=1),{memory:g,score:y}}).filter(g=>g.score>0);if(c.length===0)return o.slice(-r);let f=c.sort((g,m)=>m.score-g.score).slice(0,r).map(g=>g.memory);if(f.length>0)try{this.recordAccess(t,f.map(g=>g.id))}catch{}return f}buildContext(e){return Ot(this.contextLines(e))}contextFacts(e){return this.contextLines(e).map(t=>t.fact)}contextLines(e){let t=[];try{t=this.ledger.list()}catch{}return Dt(e,t)}recordAccess(e,t){if(t.length===0)return;let r=this.getAll(e),o=new Set(t),s=!1;for(let a of r)if(o.has(a.id)){a.accessCount=(a.accessCount||0)+1,a.lastAccessed=new Date().toISOString();let i=a.intervalDays||1;a.intervalDays=Math.min(i*2,30),a.nextReview=new Date(Date.now()+a.intervalDays*864e5).toISOString(),s=!0}s&&Pe(this.filePath(e),r.map(a=>JSON.stringify(a)).join(`
@@ -38,4 +38,4 @@ ${n.text}`}async function me(n,e){let t=n.channels[e.channel],r={channel:e.chann
38
38
  `).filter(Boolean),o=[];for(let s=r.length-1;s>=0&&o.length<t;s--)try{let a=JSON.parse(r[s]);(!e||a.person===e)&&o.push(a)}catch{}return o}load(){if(Xe(this.file))try{let e=JSON.parse(et(this.file,"utf-8"));if(e&&Array.isArray(e.devices))return e}catch{}return{devices:[]}}save(e){Sn(xn(this.file),{recursive:!0});let t=`${this.file}.${process.pid}.${Tn(4).toString("hex")}.tmp`;Rn(t,JSON.stringify(e,null,2)+`
39
39
  `,{encoding:"utf-8",mode:384}),An(t,this.file)}};import{extname as li}from"path";var Nn=/<agentx-artifact>([\s\S]*?)<\/agentx-artifact>/gi,Dn=/<agentx-artifact>[\s\S]*$/i;function ld(n,e=1/0,t=!1){let r=[],o=String(n??"");if(!/<agentx-artifact>/i.test(o))return{text:o,artifacts:r};let s=o.replace(Nn,(a,i)=>{try{let d=JSON.parse(i.trim());if(d&&typeof d.filename=="string"&&typeof d.mime=="string"&&r.length<e){let l=d.mime,p=l.startsWith("image/")?"image":l==="application/pdf"?"pdf":l.startsWith("text/")?"text":"file";r.push({type:p,filename:d.filename,mime:l})}}catch{}return""});return{text:(t?s.replace(Dn,""):s).trim(),artifacts:r}}function ud(n,e=!1){let t=String(n??""),r=t;if(/<agentx-artifact>/i.test(t)){let o=t.replace(Nn,(s,a)=>{try{let i=JSON.parse(a.trim());return i&&typeof i.filename=="string"?i.filename:""}catch{return""}});r=(e?o.replace(Dn,""):o).trim()}return r.replace(/```[\s\S]*?```|`[^`\n]*`|!\[([^\]\n]*)\]\(([^)\s]+)\)/g,(o,s,a)=>s===void 0||a===void 0?o:`[${s||a}](${a})`)}var ui={".png":{mime:"image/png",kind:"image"},".jpg":{mime:"image/jpeg",kind:"image"},".jpeg":{mime:"image/jpeg",kind:"image"},".gif":{mime:"image/gif",kind:"image"},".webp":{mime:"image/webp",kind:"image"},".svg":{mime:"image/svg+xml",kind:"file"},".pdf":{mime:"application/pdf",kind:"file"},".txt":{mime:"text/plain; charset=utf-8",kind:"file"},".md":{mime:"text/markdown; charset=utf-8",kind:"file"},".csv":{mime:"text/csv; charset=utf-8",kind:"file"},".json":{mime:"application/json",kind:"file"},".mp3":{mime:"audio/mpeg",kind:"audio"},".m4a":{mime:"audio/mp4",kind:"audio"},".wav":{mime:"audio/wav",kind:"audio"},".mp4":{mime:"video/mp4",kind:"video"},".webm":{mime:"video/webm",kind:"video"}},cd={bytes:20*1024*1024,perMessage:20,pathChars:512};function pd(n){return ui[li(n).toLowerCase()]??null}var ci=["[Phone app: showing files]","To show the owner a file you created (a chart, a screenshot, a PDF, a recording), save or copy it into .agentx/outbox/ in your workspace and end your reply with one line per file:",'<agentx-artifact>{"filename":".agentx/outbox/<name.ext>","mime":"<mime/type>"}</agentx-artifact>',"Files outside your workspace (/tmp, your home folder) are refused. Outbox files are removed after 7 days.","The line is removed from the text. Images show in the reply, audio and video get a player, other files an Open link. Allowed: png, jpg, gif, webp, svg, pdf, txt, md, csv, json, mp3, m4a, wav, mp4, webm; up to 20 MB and 20 files per reply.","A picture already on the web can go inline as ![what it shows](https://...)."].join(`
40
40
  `);function fd(n,e,t){return n==="app"&&e&&t!==!1?ci:void 0}import{lstatSync as nt,mkdirSync as pi,readdirSync as fi,realpathSync as tt,unlinkSync as gi}from"fs";import{join as be,resolve as On}from"path";var En=".agentx/outbox",mi=7*24*60*60*1e3;function hi(n,e){try{let t=tt(On(e,n)),r=be(t,En);return tt(r)===r&&nt(r).isDirectory()?r:null}catch{return null}}function wd(n,e={}){let t=e.cwd??process.cwd();if(e.create!==!1)try{let o=tt(On(t,n));nt(be(o,".agentx"),{throwIfNoEntry:!1})?.isSymbolicLink()||pi(be(o,En),{recursive:!0})}catch{return 0}let r=hi(n,t);return r?Mn(r,(e.now??Date.now())-(e.maxAgeMs??mi)):0}function Mn(n,e){let t=0;for(let r of fi(n)){let o=be(n,r);try{let s=nt(o);s.isDirectory()?t+=Mn(o,e):Math.max(s.mtimeMs,s.ctimeMs)<e&&(gi(o),t++)}catch{}}return t}export{Tt as a,jt as b,Ct as c,Di as d,ir as e,Oi as f,T as g,z as h,S as i,oe as j,Ir as k,Oe as l,F as m,Fe as n,Ce as o,Ge as p,To as q,Mo as r,Qe as s,_r as t,Go as u,Na as v,Da as w,Oa as x,Ea as y,Ma as z,Vo as A,Qo as B,Fa as C,ja as D,Ca as E,_a as F,Ha as G,Ka as H,Ba as I,qa as J,za as K,Ja as L,Ya as M,Ga as N,Va as O,Qa as P,Za as Q,ri as R,Xa as S,ed as T,td as U,nd as V,Wn as W,ld as X,ud as Y,cd as Z,pd as _,fd as $,En as aa,wd as ba};
41
- //# sourceMappingURL=chunk-FEEXOWK5.js.map
41
+ //# sourceMappingURL=chunk-QDWEM4DG.js.map
@@ -1,4 +1,4 @@
1
- import{a as Re}from"./chunk-GTJIKXDB.js";import{q as Kt}from"./chunk-S2GAEH2F.js";import{a as w}from"./chunk-7LN4VKDU.js";import{a as ne}from"./chunk-IMLAPB7G.js";import{b as Jt}from"./chunk-WCXXXAHV.js";import{f as Yt,g as Ae,i as V,j as q,l as Zt}from"./chunk-AKLZ4YO2.js";import{t as Xt,u as Gt}from"./chunk-AHO4CQRK.js";import{d as Ht,e as zt}from"./chunk-AQASR6KR.js";import{X as Nt,Z as te,_ as ve,aa as $e,b as Se}from"./chunk-FEEXOWK5.js";import{a as Vt,b as qt,d as Bt}from"./chunk-Y4CYQV7G.js";import{a as Lt,b as Ot}from"./chunk-SHEUCITK.js";import{g as Mt,h as jt,i as Wt,k as Ft,l as Ut}from"./chunk-FWDRBUMS.js";import{q as Pt}from"./chunk-SSQMFHSM.js";import{a as Ct,b as Dt}from"./chunk-LUYN6RAC.js";import{A as Rt,B as _t,ha as xe,n as ee,o as St,oa as It,p as vt,pa as Et,q as Jr,xa as Tt,y as $t,z as At}from"./chunk-DYGQF3MI.js";import{a as we,h as Yr}from"./chunk-BRK6M6LA.js";import{f as ke,n as Zr}from"./chunk-UIBUZ2N3.js";import{existsSync as Qr,readdirSync as ei,readFileSync as ti,statSync as ni}from"fs";import{resolve as ri}from"path";function Qt(t,e,n=Date.now(),i=7){let r=i*864e5,s=new Map;for(let o of e){let a=s.get(o.workflowId);a?a.push(o):s.set(o.workflowId,[o])}return t.map(o=>{let a=s.get(o.id)??[],l=a.filter(f=>n-f.at<=r),c=a.filter(f=>n-f.at>r&&n-f.at<=r*2),u=l.filter(f=>f.status==="failed").length,d=a.filter(f=>f.status==="paused").length,p=a.length?Math.max(...a.map(f=>f.at)):null,g=a.length?c.length>0&&l.length===0?"dormant":l.length>0&&u*2>=l.length?"failing":l.length>0?"active":"quiet":"never";return{id:o.id,name:o.name||o.id,state:g,lastRunAt:p,recent:l.length,prior:c.length,failed:u,paused:d}}).sort((o,a)=>{let l={dormant:0,failing:1,active:2,quiet:3,never:4};return l[o.state]-l[a.state]||(a.lastRunAt??0)-(o.lastRunAt??0)})}var re=new Map;function Ea(t,e=1e3){if(!Qr(t))return[];let n=ei(t).filter(s=>s.endsWith(".jsonl")).map(s=>{let o=ri(t,s);return{path:o,mtime:ni(o).mtimeMs}}).sort((s,o)=>o.mtime-s.mtime).slice(0,e),i=new Set(n.map(s=>s.path));for(let s of re.keys())i.has(s)||re.delete(s);let r=[];for(let{path:s,mtime:o}of n){let a=re.get(s);if(a&&a.mtime===o){a.summary&&r.push(a.summary);continue}let l=ii(s,o);re.set(s,{mtime:o,summary:l}),l&&r.push(l)}return r}function ii(t,e){try{let n=ti(t,"utf-8").split(`
1
+ import{a as Re}from"./chunk-GTJIKXDB.js";import{a as w}from"./chunk-7LN4VKDU.js";import{a as ne}from"./chunk-IMLAPB7G.js";import{q as Kt}from"./chunk-S2GAEH2F.js";import{b as Jt}from"./chunk-WCXXXAHV.js";import{f as Yt,g as Ae,i as V,j as q,l as Zt}from"./chunk-AKLZ4YO2.js";import{t as Xt,u as Gt}from"./chunk-3D5XUOBL.js";import{d as Ht,e as zt}from"./chunk-AQASR6KR.js";import{X as Nt,Z as te,_ as ve,aa as $e,b as Se}from"./chunk-QDWEM4DG.js";import{a as Vt,b as qt,d as Bt}from"./chunk-Y4CYQV7G.js";import{a as Lt,b as Ot}from"./chunk-SHEUCITK.js";import{g as Mt,h as jt,i as Wt,k as Ft,l as Ut}from"./chunk-FWDRBUMS.js";import{q as Pt}from"./chunk-SSQMFHSM.js";import{a as Ct,b as Dt}from"./chunk-LUYN6RAC.js";import{A as Rt,B as _t,ha as xe,n as ee,o as St,oa as It,p as vt,pa as Et,q as Jr,xa as Tt,y as $t,z as At}from"./chunk-DYGQF3MI.js";import{a as we,h as Yr}from"./chunk-BRK6M6LA.js";import{f as ke,n as Zr}from"./chunk-UIBUZ2N3.js";import{existsSync as Qr,readdirSync as ei,readFileSync as ti,statSync as ni}from"fs";import{resolve as ri}from"path";function Qt(t,e,n=Date.now(),i=7){let r=i*864e5,s=new Map;for(let o of e){let a=s.get(o.workflowId);a?a.push(o):s.set(o.workflowId,[o])}return t.map(o=>{let a=s.get(o.id)??[],l=a.filter(f=>n-f.at<=r),c=a.filter(f=>n-f.at>r&&n-f.at<=r*2),u=l.filter(f=>f.status==="failed").length,d=a.filter(f=>f.status==="paused").length,p=a.length?Math.max(...a.map(f=>f.at)):null,g=a.length?c.length>0&&l.length===0?"dormant":l.length>0&&u*2>=l.length?"failing":l.length>0?"active":"quiet":"never";return{id:o.id,name:o.name||o.id,state:g,lastRunAt:p,recent:l.length,prior:c.length,failed:u,paused:d}}).sort((o,a)=>{let l={dormant:0,failing:1,active:2,quiet:3,never:4};return l[o.state]-l[a.state]||(a.lastRunAt??0)-(o.lastRunAt??0)})}var re=new Map;function Ea(t,e=1e3){if(!Qr(t))return[];let n=ei(t).filter(s=>s.endsWith(".jsonl")).map(s=>{let o=ri(t,s);return{path:o,mtime:ni(o).mtimeMs}}).sort((s,o)=>o.mtime-s.mtime).slice(0,e),i=new Set(n.map(s=>s.path));for(let s of re.keys())i.has(s)||re.delete(s);let r=[];for(let{path:s,mtime:o}of n){let a=re.get(s);if(a&&a.mtime===o){a.summary&&r.push(a.summary);continue}let l=ii(s,o);re.set(s,{mtime:o,summary:l}),l&&r.push(l)}return r}function ii(t,e){try{let n=ti(t,"utf-8").split(`
2
2
  `).filter(Boolean);if(!n.length)return null;let i=JSON.parse(n[0]),r=i.run?.workflowId;if(!r)return null;let s=i.run?.status??"unknown";for(let o=n.length-1;o>=0;o--){let a=JSON.parse(n[o]),l=a.status??a.run?.status;if(l){s=l;break}}return{workflowId:r,status:s,at:e}}catch{return null}}var si={failStreak:3,disabledDays:30,missedFires:2,graceMs:15*6e4},k={routines:200,summary:160,filters:6,filterValue:60,text:120},oi=(t,e,n)=>{try{return Pt(t,e,n)}catch{return null}},en=864e5;function Pa(t){let e=t.now??Date.now(),n={...si,...t.thresholds},i=t.nextFire??oi,r=[];for(let a of t.crons){let l=t.cronRuns.get(a.id)??[],c=a.timezone||"UTC",u=l.map(g=>({at:Date.parse(g.startedAt),status:g.status})),d=l[0],p={id:`cron:${a.id}`,source:"cron",kind:"schedule",name:$(a.id,k.text),trigger:{schedule:$(a.schedule,k.text),timezone:c},agent:a.agent||null,enabled:a.enabled,state:a.enabled?"enabled":"disabled",nextRunAt:a.enabled?_e(a.nextRun)??_e(i(a.schedule,new Date(e),c)):null,lastRun:d?{at:d.startedAt,status:d.status,summary:pi(d.status==="success"?void 0:d.errorSummary,k.summary)}:null,lastSuccessAt:l.find(g=>g.status==="success")?.startedAt??null,consecutiveFailures:tn(u),sampledRuns:l.length,flags:[],attention:null};p.flags=nn(p,u,a.schedule,c,null,e,n,i),r.push(rn(p))}let s=new Map(Qt(t.workflows.map(a=>({id:a.id,name:a.title})),t.workflowRuns,e).map(a=>[a.id,a])),o=new Map;for(let a of t.workflowRuns){let l=o.get(a.workflowId);l?l.push(a):o.set(a.workflowId,[a])}for(let a of t.workflows){let l=a.nodes.find(I=>I.type.startsWith("trigger."));if(!l||l.type!=="trigger.cron"&&l.type!=="trigger.hook")continue;let c=l.config??{},u=a.state||"active",d=u==="active",p=(o.get(a.id)??[]).slice().sort((I,ye)=>ye.at-I.at),g=p.map(I=>({at:I.at,status:ci(I.status)})),f=g[0],h=l.type==="trigger.cron",b=typeof c.spec=="string"?c.spec:"",C=typeof c.timezone=="string"?c.timezone:"UTC",M={id:`workflow:${a.id}`,source:"workflow",kind:h?"schedule":"event",name:$(a.title||a.id,k.text),trigger:h?{schedule:$(b,k.text),timezone:C}:{event:$(typeof c.event=="string"?c.event:"",k.text),filters:di(c.filter,a)},agent:ui(a),enabled:d,state:u,nextRunAt:h&&d&&b?_e(i(b,new Date(e),C)):null,lastRun:f?{at:new Date(f.at).toISOString(),status:f.status}:null,lastSuccessAt:(()=>{let I=g.find(ye=>ye.status==="success");return I?new Date(I.at).toISOString():null})(),consecutiveFailures:tn(g),sampledRuns:p.length,flags:[],attention:null},Q=Date.parse(a.updated||a.created||"");M.flags=h?nn(M,g,b,C,Number.isFinite(Q)?Q:null,e,n,i):ai(M,s.get(a.id)?.state==="dormant",Number.isFinite(Q)?Q:null,e,n),r.push(rn(M))}return r.sort(li).slice(0,k.routines)}function tn(t){let e=0;for(let n of t)if(!(n.status==="running"||n.status==="paused"||n.status==="unknown"))if(n.status==="failed"||n.status==="timeout")e++;else break;return e}function nn(t,e,n,i,r,s,o,a){let l=[],c=e.length?e[0].at:null;if(!t.enabled){let p=on(c,r,s,o);return p&&l.push(p),l}if(t.consecutiveFailures>=o.failStreak&&l.push({reason:"failing",detail:`${t.consecutiveFailures} failed attempts in a row`}),c===null)return l.push({reason:"never-ran",detail:"No persisted attempt"}),l;let u=new Date(c),d=null;for(let p=0;p<o.missedFires&&(d=a(n,u,i),!!d);p++)u=d;return d&&s>d.getTime()+o.graceMs&&l.push({reason:"overdue",detail:`No attempt since ${new Date(c).toISOString()}; ${o.missedFires} scheduled fires missed`}),l}function ai(t,e,n,i,r){let s=[],o=t.lastRun?Date.parse(t.lastRun.at):null;if(!t.enabled){let a=on(o,n,i,r);return a&&s.push(a),s}return t.consecutiveFailures>=r.failStreak&&s.push({reason:"failing",detail:`${t.consecutiveFailures} failed runs in a row`}),o===null?s.push({reason:"never-ran",detail:"No run recorded for this trigger"}):e&&s.push({reason:"dormant",detail:"Fired in the prior week, silent in the last one"}),s}function on(t,e,n,i){let r=Math.max(t??0,e??0);return r&&n-r<i.disabledDays*en?null:{reason:"disabled-long",detail:r?`Disabled; no activity for ${Math.floor((n-r)/en)} days`:"Disabled and never ran"}}function rn(t){let e=new Set(t.flags.map(n=>n.reason));return t.attention=e.has("failing")?"critical":e.has("overdue")||e.has("dormant")||e.has("never-ran")?"warning":e.has("disabled-long")?"info":null,t}var sn={critical:0,warning:1,info:2};function li(t,e){let n=t.attention?sn[t.attention]:3,i=e.attention?sn[e.attention]:3;return n-i||Number(e.enabled)-Number(t.enabled)||t.name.localeCompare(e.name)}function ci(t){switch(t){case"completed":case"ok":return"success";case"failed":return"failed";case"timeout":return"timeout";case"canceled":return"canceled";case"running":return"running";case"paused":return"paused";default:return"unknown"}}function ui(t){if(t.ownerAgent)return $(t.ownerAgent,k.text);let e=t.nodes.find(n=>n.type==="agent"&&typeof n.config?.agentId=="string"&&n.config.agentId);return e?$(String(e.config.agentId),k.text):null}function di(t,e){let n=[],i=e?.project;if(i&&n.push($(`project=${i}`,k.filterValue)),t&&typeof t=="object"&&!Array.isArray(t))for(let[r,s]of Object.entries(t)){let o=Array.isArray(s)?s.map(String):s==null?[]:[String(s)];o.length&&n.push($(`${r}=${o.join(",")}`,k.filterValue))}return n.slice(0,k.filters)}function _e(t){if(!t)return null;let e=t instanceof Date?t:new Date(t);return Number.isFinite(e.getTime())?e.toISOString():null}function $(t,e){return t.length<=e?t:t.slice(0,e-3)+"..."}function pi(t,e){return t===void 0?void 0:$(t,e)}import{existsSync as H,readdirSync as Pi,readFileSync as Ni}from"fs";import{resolve as R}from"path";import{randomBytes as cn}from"crypto";import{existsSync as fi,mkdirSync as mi,readdirSync as hi,readFileSync as bi,renameSync as un,unlinkSync as yi,writeFileSync as wi}from"fs";import{resolve as dn}from"path";function an(t){return[...t.account?["--account",t.account]:[],...t.store?["--store",t.store]:[]]}var gi=t=>t.trim().split(`
3
3
  `).pop()?.slice(0,300)||"no output";async function ln(t,e,n,i=Re){let r=await i(t.bin,[...an(t),"send","text","--to",e,"--message",n,"--no-preview"],{timeoutMs:6e4});return r.code===0?{ok:!0}:{ok:!1,error:`wacli send failed: ${gi(r.stderr||r.stdout)}`}}async function Oa(t,e,n,i,r=Re){return(await r(t.bin,[...an(t),"--read-only","media","download","--chat",e,"--id",n,"--output",i],{timeoutMs:12e4})).code===0}var ki=2e3,xi=t=>ln(t.wacli,t.to,t.text);function Ie(t){return dn(t,".agentx","whatsapp-triage","drafts")}var Si=t=>/^[a-z0-9-]{8,64}$/.test(t),ie=(t,e)=>dn(Ie(t),`${e}.json`);function pn(t,e){mi(Ie(t),{recursive:!0});let n=ie(t,e.id),i=`${n}.${process.pid}.tmp`;wi(i,JSON.stringify(e,null,2)+`
4
4
  `,{mode:384}),un(i,n)}function Va(t,e,n=Date.now()){let i=e.text.trim().slice(0,ki);if(!i)throw new Error("empty draft");let r={...e,text:i,id:`${n.toString(36)}-${cn(4).toString("hex")}`,status:"pending",created_at:new Date(n).toISOString()};return pn(t,r),r}function se(t,e){if(!Si(e))return null;try{return JSON.parse(bi(ie(t,e),"utf-8"))}catch{return null}}function gn(t,e){let n=Ie(t);return fi(n)?hi(n).filter(i=>i.endsWith(".json")).map(i=>se(t,i.slice(0,-5))).filter(i=>!!i&&(!e||i.status===e)).sort((i,r)=>i.created_at.localeCompare(r.created_at)):[]}async function fn(t,e,n,i={}){let r=se(t,e);if(!r||r.status!=="pending")return{ok:!1,error:`no reply draft "${e}" is waiting`};let s=`${ie(t,e)}.${process.pid}.${cn(3).toString("hex")}.claim`;try{un(ie(t,e),s)}catch{return{ok:!1,error:`reply draft "${e}" is already being decided`}}let o={...r,decided_by:i.by??"operator",decided_at:new Date(i.now??Date.now()).toISOString()};if(n==="no")o.status="rejected";else{let a=await(i.send??xi)(r).catch(l=>({ok:!1,error:String(l?.message??l)}));a.ok?o.status="sent":(o.status="failed",o.error=a.error)}pn(t,o);try{yi(s)}catch{}return o.status==="failed"?{ok:!1,error:o.error??"send failed"}:{ok:!0,draft:o}}import{mkdirSync as vi,readFileSync as $i,renameSync as Ai,writeFileSync as Ri}from"fs";import{resolve as _i}from"path";function mn(t){return _i(Ae(t),"_state.json")}function A(t){try{let e=JSON.parse($i(mn(t),"utf-8"));return{snoozed:e&&typeof e.snoozed=="object"&&e.snoozed?e.snoozed:{},...typeof e?.lastDigest=="string"?{lastDigest:e.lastDigest}:{},...e?.popped&&typeof e.popped=="object"?{popped:e.popped}:{},...typeof e?.passAt=="string"?{passAt:e.passAt}:{},...Array.isArray(e?.wanted)?{wanted:e.wanted.filter(n=>typeof n=="string")}:{}}}catch{return{snoozed:{}}}}function B(t,e){vi(Ae(t),{recursive:!0});let n=mn(t),i=`${n}.${process.pid}.tmp`;Ri(i,JSON.stringify(e,null,2)+`
@@ -529,4 +529,4 @@ Document the arguments, commands, or files the skill expects. Example:
529
529
  claude-code ${xt(t)} --arg value
530
530
  \`\`\`
531
531
  `}Tt();var zr={maxPixels:12e5,timeoutMs:5e3,intervalMs:100,changeThreshold:.015,stableMs:400,regions:{},buffer:{enabled:!1,seconds:10,fps:2,region:"screen",maxPixels:3e5}};function Xr(t){return{...zr,...t??{},regions:{...t?.regions??{}},buffer:{...zr.buffer,...t?.buffer??{}}}}function td(t){try{return Xr(Et(t).screen)}catch{return Xr(void 0)}}var Ra=/^[a-z][\w-]*$/,Gr=["window","screen","menubar","notifications"];function Z(t,e,n,i){let r=Number(t);if(t===""||t===null||!Number.isFinite(r)||!n(r))throw new Error(`${e} must be ${i}`);return r}var be=(t,e)=>Z(t,e,n=>Number.isInteger(n)&&n>0,"a whole number above 0");function Kr(t){let e=typeof t=="string"?t.split(",").map(Number):[t?.x,t?.y,t?.width,t?.height].map(Number);if(e.length!==4||!e.every(Number.isFinite)||e[2]<=0||e[3]<=0)throw new Error("a region is x,y,width,height in screen points, with width and height above 0");return{x:e[0],y:e[1],width:e[2],height:e[3]}}function nd(t,e){let n={...t??{}};if("maxPixels"in e&&(n.maxPixels=be(e.maxPixels,"maxPixels")),"timeoutMs"in e&&(n.timeoutMs=be(e.timeoutMs,"timeoutMs")),"intervalMs"in e&&(n.intervalMs=be(e.intervalMs,"intervalMs")),"stableMs"in e&&(n.stableMs=Z(e.stableMs,"stableMs",r=>Number.isInteger(r)&&r>=0,"a whole number, 0 or more")),"changeThreshold"in e&&(n.changeThreshold=Z(e.changeThreshold,"changeThreshold",r=>r>=0&&r<=1,"between 0 and 1")),e.regions&&typeof e.regions=="object"){let r={...n.regions??{}};for(let[s,o]of Object.entries(e.regions)){if(!Ra.test(s))throw new Error(`region name "${s}" must be lowercase letters, digits, - or _`);o===null?delete r[s]:r[s]=Kr(o)}n.regions=r}if(e.buffer&&typeof e.buffer=="object"){let r=e.buffer,s={...n.buffer??{}};"enabled"in r&&(s.enabled=!!r.enabled),"seconds"in r&&(s.seconds=Z(r.seconds,"buffer.seconds",o=>o>0&&o<=120,"above 0 and at most 120")),"fps"in r&&(s.fps=Z(r.fps,"buffer.fps",o=>o>0&&o<=10,"above 0 and at most 10")),"maxPixels"in r&&(s.maxPixels=be(r.maxPixels,"buffer.maxPixels")),"region"in r&&(s.region=String(r.region).trim()),n.buffer=s}let i=n.buffer?.region;if(i!==void 0&&!Gr.includes(i)&&!n.regions?.[i])try{Kr(i)}catch{throw new Error(`buffer.region "${i}" is not a region \u2014 use ${[...Gr,...Object.keys(n.regions??{})].join(", ")}, or x,y,w,h`)}return n}export{Qt as a,Ea as b,tt as c,Ks as d,Js as e,mc as f,hc as g,bc as h,ar as i,wc as j,xc as k,D as l,lr as m,k as n,Pa as o,Oa as p,xi as q,Va as r,fn as s,A as t,Xa as u,Ga as v,Ya as w,oe as x,Za as y,xn as z,il as A,z as B,wl as C,kl as D,_n as E,cr as F,$c as G,_c as H,vr as I,eu as J,tu as K,au as L,qo as M,lu as N,Sl as O,vl as P,$l as Q,Al as R,Rl as S,_l as T,El as U,Tl as V,Wl as W,Me as X,Fl as Y,Ul as Z,X as _,G as $,Ko as aa,bu as ba,yu as ca,wu as da,ku as ea,ct as fa,xu as ga,Fe as ha,Ue as ia,pe as ja,ms as ka,bs as la,zl as ma,qe as na,Be as oa,ys as pa,ws as qa,Xn as ra,Yn as sa,Xl as ta,Ss as ua,Gl as va,Ts as wa,Ps as xa,He as ya,Ls as za,Zn as Aa,P as Ba,Lr as Ca,Or as Da,Mr as Ea,_u as Fa,ia as Ga,Wr as Ha,Fr as Ia,ju as Ja,Wu as Ka,ka as La,Fu as Ma,Vr as Na,ge as Oa,oc as Pa,ir as Qa,ze as Ra,Qe as Sa,gc as Ta,Bu as Ua,Hu as Va,zu as Wa,Xu as Xa,Gu as Ya,Ku as Za,Ju as _a,Yu as $a,Zu as ab,zr as bb,Xr as cb,td as db,Gr as eb,nd as fb};
532
- //# sourceMappingURL=chunk-NXRSIWQA.js.map
532
+ //# sourceMappingURL=chunk-R6ECKZLE.js.map
@@ -0,0 +1,4 @@
1
+ import t from"chalk";import{spawn as S}from"child_process";import{resolve as j}from"path";var E=l=>l||j(process.cwd(),".agentx/wiki");function N(l){return async d=>{let{claudeCliEnv:e}=await import("./workspace-env-ROH45HXN.js"),h=["-p","-","--output-format","json","--max-turns","1","--model",l,"--tools","","--strict-mcp-config","--mcp-config",'{"mcpServers":{}}',"--settings",'{"disableAllHooks":true}',"--setting-sources","","--no-session-persistence"],y=await new Promise(($,a)=>{let s=S("claude",h,{env:e(),stdio:["pipe","pipe","pipe"]}),p="",g="",i=setTimeout(()=>s.kill("SIGTERM"),24e4);s.stdout.on("data",r=>{p+=r}),s.stderr.on("data",r=>{g+=r}),s.on("error",r=>{clearTimeout(i),a(r)}),s.on("close",r=>{clearTimeout(i),r===0?$(p):a(new Error(`claude exited ${r}: ${g.trim().slice(0,200)}`))}),s.stdin.end(d)}),c=JSON.parse(y);if(c.is_error)throw new Error(`model error: ${String(c.result).slice(0,200)}`);return{text:String(c.result??""),costUsd:Number(c.total_cost_usd)||0}}}function I(l){l.command("enrich [entities...]").description("write a full overview, sourced typed facts and linked history on entity pages (person, organization, project, place, assets)").option("--dir <path>","wiki directory (default .agentx/wiki)").option("--types <list>","comma-separated entity types","person,organization,project,place,device,server,app,domain,account").option("--max <n>","most entities per run","10").option("--max-cost <usd>","stop before the next call once the run spent this much","1").option("--model <m>","model","sonnet").option("--force","redo entities whose sources did not change").option("--dry-run","show what would be written, write nothing").option("--create <title>","first create a page for a thing other pages only name, then enrich it (needs --as and --owner)").option("--as <type>","type of the page --create makes, e.g. organization").option("--owner <agent>","agent whose wiki gets the page --create makes").option("--json","print the run as JSON").action(async(d,e)=>{let{WikiHub:h}=await import("./hub-MVX3U6A2.js"),{GraphCache:y}=await import("./graph-ZVKV3JBD.js"),{loadOntology:c}=await import("./load-LQ2I2PQI.js"),{loadRoster:$}=await import("./roster-BZGKG2ZA.js"),{ENRICH_TYPES:a,loadEnrichState:s,runEnrich:p,saveEnrichState:g}=await import("./enrich-EFIPWA3P.js"),i=E(e.dir),r=e.types.split(",").map(o=>o.trim()).filter(Boolean),b=r.filter(o=>!a.includes(o));if(b.length){console.error(t.red(` not an entity type this job handles: ${b.join(", ")} (use ${a.join(", ")})`)),process.exitCode=1;return}let x=Number(e.max);if(!Number.isInteger(x)||x<1){console.error(t.red(" --max must be a whole number of entities, 1 or more")),process.exitCode=1;return}let w=Number(e.maxCost);if(!Number.isFinite(w)||w<=0){console.error(t.red(" --max-cost must be a positive number of dollars")),process.exitCode=1;return}let m=new h(i,()=>{});if(e.create){let{createEntityPage:o}=await import("./enrich-EFIPWA3P.js");if(!e.as||!a.includes(e.as)||!e.owner){console.error(t.red(` --create needs --as <${a.join("|")}> and --owner <agent>`)),process.exitCode=1;return}if(m.syncedFrom(e.owner)){console.error(t.red(` ${e.owner}'s pages are copied from another node; pick an agent that runs here`)),process.exitCode=1;return}if(e.dryRun)console.log(t.dim(` --dry-run: would create "${e.create}" (${e.as}) in ${e.owner}'s wiki`));else{let f=o(m,e.create,e.as,e.owner,new Date().toISOString().slice(0,10));console.log(f?t.green(` created ${e.owner}/${f}`):t.dim(` ${e.owner} already has a page titled "${e.create}"`))}d=[...d,e.create],r.includes(e.as)||r.push(e.as)}let{ontology:v}=c(i),k=new y().get(m,v,$(i,m.listAgents([]),v.agent_names)),C=s(i),u=await p(m,k,N(e.model),{types:r,only:d,max:x,maxCostUsd:w,dryRun:!!e.dryRun,force:!!e.force,today:new Date().toISOString().slice(0,10),save:o=>g(i,o)},C);if(e.json){console.log(JSON.stringify(u,null,2));return}for(let o of u.outcomes){let f=o.status==="written"?t.green("\u2713"):o.status==="dry-run"?t.cyan("~"):t.yellow("\xB7");console.log(` ${f} ${o.title} ${t.dim(`${o.status} \xB7 ${o.page??"no page"} \xB7 overview ${o.overview?"yes":"no"} \xB7 ${o.statements} facts \xB7 ${o.links} links \xB7 $${o.costUsd.toFixed(3)}`)}`);for(let n of o.facts??[])console.log(t.dim(` fact: ${n.property} \u2192 ${n.value}${n.role?` (${n.role})`:""} \xB7 source ${n.source}`));for(let n of o.dropped.slice(0,6))console.log(t.dim(` dropped: ${n}`));o.preview&&console.log(t.dim(o.preview.split(`
2
+ `).slice(0,12).map(n=>` | ${n}`).join(`
3
+ `)))}u.outcomes.length===0&&console.log(t.dim(" nothing to do: no entity of these types changed since the last run (--force redoes them)")),console.log(t.dim(` spent $${u.costUsd.toFixed(3)} of $${w}${u.capped?" \xB7 stopped at the cap":""}`))})}export{N as a,I as b};
4
+ //# sourceMappingURL=chunk-R6KLH34H.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/wiki-enrich.ts"],"sourcesContent":["import type { Command } from \"commander\"\nimport chalk from \"chalk\"\nimport { spawn } from \"child_process\"\nimport { resolve } from \"path\"\n\n// --- agentx wiki enrich: full overview, typed facts and linked history per entity (#820) ---\n//\n// Runs on demand or from a scheduled job with `command`. Each entity\n// costs one model call; `--max-cost` stops the run before the next call\n// once the spend reaches it.\n\nconst wikiDir = (dir?: string) => dir || resolve(process.cwd(), \".agentx/wiki\")\n\n/** One `claude -p` call with no tools; cost from the JSON envelope.\n * The prompt carries raw message text, so no built-in tool, MCP server,\n * hook or settings file is loaded: an injected instruction has nothing\n * to write or send with. */\nexport function claudeCall(model: string): (prompt: string) => Promise<{ text: string; costUsd: number }> {\n return async (prompt) => {\n const { claudeCliEnv } = await import(\"@/utils/workspace-env\")\n const args = [\"-p\", \"-\", \"--output-format\", \"json\", \"--max-turns\", \"1\", \"--model\", model,\n \"--tools\", \"\", \"--strict-mcp-config\", \"--mcp-config\", '{\"mcpServers\":{}}',\n \"--settings\", '{\"disableAllHooks\":true}', \"--setting-sources\", \"\", \"--no-session-persistence\"]\n const out = await new Promise<string>((ok, fail) => {\n const child = spawn(\"claude\", args, { env: claudeCliEnv(), stdio: [\"pipe\", \"pipe\", \"pipe\"] })\n let stdout = \"\"\n let stderr = \"\"\n const timer = setTimeout(() => child.kill(\"SIGTERM\"), 240_000)\n child.stdout.on(\"data\", d => { stdout += d })\n child.stderr.on(\"data\", d => { stderr += d })\n child.on(\"error\", err => { clearTimeout(timer); fail(err) })\n child.on(\"close\", code => {\n clearTimeout(timer)\n if (code === 0) ok(stdout)\n else fail(new Error(`claude exited ${code}: ${stderr.trim().slice(0, 200)}`))\n })\n child.stdin.end(prompt)\n })\n const envelope = JSON.parse(out) as { result?: string; total_cost_usd?: number; is_error?: boolean }\n if (envelope.is_error) throw new Error(`model error: ${String(envelope.result).slice(0, 200)}`)\n return { text: String(envelope.result ?? \"\"), costUsd: Number(envelope.total_cost_usd) || 0 }\n }\n}\n\nexport function registerWikiEnrich(wiki: Command): void {\n wiki\n .command(\"enrich [entities...]\")\n .description(\"write a full overview, sourced typed facts and linked history on entity pages (person, organization, project, place, assets)\")\n .option(\"--dir <path>\", \"wiki directory (default .agentx/wiki)\")\n .option(\"--types <list>\", \"comma-separated entity types\", \"person,organization,project,place,device,server,app,domain,account\")\n .option(\"--max <n>\", \"most entities per run\", \"10\")\n .option(\"--max-cost <usd>\", \"stop before the next call once the run spent this much\", \"1\")\n .option(\"--model <m>\", \"model\", \"sonnet\")\n .option(\"--force\", \"redo entities whose sources did not change\")\n .option(\"--dry-run\", \"show what would be written, write nothing\")\n .option(\"--create <title>\", \"first create a page for a thing other pages only name, then enrich it (needs --as and --owner)\")\n .option(\"--as <type>\", \"type of the page --create makes, e.g. organization\")\n .option(\"--owner <agent>\", \"agent whose wiki gets the page --create makes\")\n .option(\"--json\", \"print the run as JSON\")\n .action(async (entities: string[], opts: { dir?: string; types: string; max: string; maxCost: string; model: string; force?: boolean; dryRun?: boolean; json?: boolean; create?: string; as?: string; owner?: string }) => {\n const { WikiHub } = await import(\"@/wiki/hub\")\n const { GraphCache } = await import(\"@/wiki/ontology/graph\")\n const { loadOntology } = await import(\"@/wiki/ontology/load\")\n const { loadRoster } = await import(\"@/wiki/ontology/roster\")\n const { ENRICH_TYPES, loadEnrichState, runEnrich, saveEnrichState } = await import(\"@/wiki/enrich\")\n const dir = wikiDir(opts.dir)\n const types = opts.types.split(\",\").map(s => s.trim()).filter(Boolean)\n const unknown = types.filter(t => !ENRICH_TYPES.includes(t))\n if (unknown.length) {\n console.error(chalk.red(` not an entity type this job handles: ${unknown.join(\", \")} (use ${ENRICH_TYPES.join(\", \")})`))\n process.exitCode = 1\n return\n }\n const max = Number(opts.max)\n if (!Number.isInteger(max) || max < 1) {\n console.error(chalk.red(\" --max must be a whole number of entities, 1 or more\"))\n process.exitCode = 1\n return\n }\n const maxCostUsd = Number(opts.maxCost)\n if (!Number.isFinite(maxCostUsd) || maxCostUsd <= 0) {\n console.error(chalk.red(\" --max-cost must be a positive number of dollars\"))\n process.exitCode = 1\n return\n }\n const hub = new WikiHub(dir, () => {})\n if (opts.create) {\n const { createEntityPage } = await import(\"@/wiki/enrich\")\n if (!opts.as || !ENRICH_TYPES.includes(opts.as) || !opts.owner) {\n console.error(chalk.red(` --create needs --as <${ENRICH_TYPES.join(\"|\")}> and --owner <agent>`))\n process.exitCode = 1\n return\n }\n if (hub.syncedFrom(opts.owner)) {\n console.error(chalk.red(` ${opts.owner}'s pages are copied from another node; pick an agent that runs here`))\n process.exitCode = 1\n return\n }\n if (opts.dryRun) {\n console.log(chalk.dim(` --dry-run: would create \"${opts.create}\" (${opts.as}) in ${opts.owner}'s wiki`))\n } else {\n const path = createEntityPage(hub, opts.create, opts.as, opts.owner, new Date().toISOString().slice(0, 10))\n console.log(path ? chalk.green(` created ${opts.owner}/${path}`) : chalk.dim(` ${opts.owner} already has a page titled \"${opts.create}\"`))\n }\n entities = [...entities, opts.create]\n if (!types.includes(opts.as)) types.push(opts.as)\n }\n const { ontology } = loadOntology(dir)\n const g = new GraphCache().get(hub, ontology, loadRoster(dir, hub.listAgents([]), ontology.agent_names))\n const state = loadEnrichState(dir)\n const run = await runEnrich(hub, g, claudeCall(opts.model), {\n types, only: entities, max, maxCostUsd, dryRun: !!opts.dryRun, force: !!opts.force, today: new Date().toISOString().slice(0, 10),\n save: s => saveEnrichState(dir, s),\n }, state)\n\n if (opts.json) {\n console.log(JSON.stringify(run, null, 2))\n return\n }\n for (const o of run.outcomes) {\n const mark = o.status === \"written\" ? chalk.green(\"✓\") : o.status === \"dry-run\" ? chalk.cyan(\"~\") : chalk.yellow(\"·\")\n console.log(` ${mark} ${o.title} ${chalk.dim(`${o.status} · ${o.page ?? \"no page\"} · overview ${o.overview ? \"yes\" : \"no\"} · ${o.statements} facts · ${o.links} links · $${o.costUsd.toFixed(3)}`)}`)\n for (const f of o.facts ?? []) console.log(chalk.dim(` fact: ${f.property} → ${f.value}${f.role ? ` (${f.role})` : \"\"} · source ${f.source}`))\n for (const d of o.dropped.slice(0, 6)) console.log(chalk.dim(` dropped: ${d}`))\n if (o.preview) console.log(chalk.dim(o.preview.split(\"\\n\").slice(0, 12).map(l => ` | ${l}`).join(\"\\n\")))\n }\n if (run.outcomes.length === 0) console.log(chalk.dim(\" nothing to do: no entity of these types changed since the last run (--force redoes them)\"))\n console.log(chalk.dim(` spent $${run.costUsd.toFixed(3)} of $${maxCostUsd}${run.capped ? \" · stopped at the cap\" : \"\"}`))\n })\n}\n"],"mappings":"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","names":["chalk","spawn","resolve","wikiDir","dir","claudeCall","model","prompt","claudeCliEnv","args","out","ok","fail","child","stdout","stderr","timer","d","err","code","envelope","registerWikiEnrich","wiki","entities","opts","WikiHub","GraphCache","loadOntology","loadRoster","ENRICH_TYPES","loadEnrichState","runEnrich","saveEnrichState","types","s","unknown","t","max","maxCostUsd","hub","createEntityPage","path","ontology","g","state","run","mark","f","l"]}
@@ -1,2 +1,2 @@
1
1
  var e=["minor","normal","major"];export{e as a};
2
- //# sourceMappingURL=chunk-JP5OAMWQ.js.map
2
+ //# sourceMappingURL=chunk-ROLSSYXE.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/wiki/ontology/types.ts"],"sourcesContent":["// --- Wiki ontology types (#811) ---\n//\n// The ontology says what kinds of things the wiki holds (types grouped\n// into pillars), how they relate (properties), how important an event is,\n// and how each type's page is viewed (lenses). Every value here comes\n// from `ontology.yaml` in the wiki root, falling back to the generic\n// defaults in `defaults.ts`. Nothing about a particular organisation\n// belongs in code.\n\nexport type Importance = \"minor\" | \"normal\" | \"major\"\n\nexport const IMPORTANCE_LEVELS: readonly Importance[] = [\"minor\", \"normal\", \"major\"] as const\n\nexport interface PillarDef {\n id: string\n label: string\n /** One line under the pillar title. */\n description?: string\n /** Icon name from the view's small built-in icon set. */\n icon?: string\n /** Shown in the sidebar. A fallback pillar such as Topics sets false. */\n sidebar?: boolean\n}\n\nexport interface TypeDef {\n id: string\n label: string\n plural?: string\n pillar: string\n /** schema.org class, shown as a hint on the page. */\n schema?: string\n icon?: string\n /** Panels the page opens with, in order. */\n lens?: LensPanel[]\n}\n\nexport interface PropertyDef {\n id: string\n /** Read from the subject: \"Person — role at → Organization\". */\n label: string\n /** Read from the value's side: \"Organization — people ← Person\". */\n inverse?: string\n /** Wikidata property id, kept as a mapping hint. */\n wikidata?: string\n /** Types the value is expected to be. Informational. */\n range?: string[]\n /** Types the subject may be. A lens shows other subjects' statements\n * through the inverse; `wiki enrich` writes it only on these (#820). */\n domain?: string[]\n}\n\nexport type PanelSource = \"statements\" | \"linked\" | \"history\" | \"discussed\" | \"readings\" | \"notes\"\n\nexport interface LensPanel {\n /** Panel id. Used in the zoom URL, so keep it short and stable. */\n panel: string\n title?: string\n /** Where items come from. Inferred from the other fields when omitted. */\n source?: PanelSource\n /** Properties to show (statements panels). `related` covers plain links. */\n from?: string | string[]\n /** Keep only items whose other end has one of these types. */\n types?: string[]\n /** Items shown at the overview level (Z2). The full list is Z3. */\n show?: number\n group_by?: \"role\" | \"property\"\n display?: \"list\" | \"timeline\"\n /** History: importance levels shown unfolded. */\n importance?: Importance[]\n /** History: levels folded under a count. */\n fold?: Importance\n /** History: repeated minor events shown as one recurring line. */\n rollup?: { min: number; days: number }\n /** Readings: only the latest value per metric. */\n latest?: boolean\n /** `owner` marks the panel as private to the owner. */\n access?: \"owner\"\n}\n\nexport interface ClassifyRule {\n /** Type the matching page gets. */\n type: string\n /** Match when the page's legacy `type` is one of these. */\n legacy?: string[]\n /** Match when the page path starts with one of these folders. */\n path?: string[]\n /** Match when the page has any of these tags. */\n tags?: string[]\n /** Case-insensitive regular expression on the title. */\n title?: string\n}\n\nexport interface Ontology {\n version: number\n pillars: PillarDef[]\n types: TypeDef[]\n properties: PropertyDef[]\n importance: {\n default: Importance\n /** Who may set `major`: \"owner\" means agents can only propose it. */\n major_set_by: \"owner\" | \"anyone\"\n rollup: { min: number; days: number }\n }\n sidebar: {\n pins_max: number\n /** Page titles the owner pinned, in order. */\n pins: string[]\n }\n /** Rules that give a page a type, tried in order. The first match wins. */\n classify: ClassifyRule[]\n /** Type for pages no rule matches. */\n fallback_type: string\n /** Extra names of the fleet's agents, for agents that run on another\n * node or answer to a persona name (#819). The node's own agent ids,\n * configured names and persona names are found without this. */\n agent_names?: string[]\n}\n\n/** One typed fact about a page: subject (the page) → property → value. */\nexport interface WikiStatement {\n property: string\n /** A page title (becomes a link) or a literal value. */\n value: string\n role?: string\n since?: string\n until?: string\n /** Metric name for readings (disk_used, uptime). */\n metric?: string\n /** When a reading was taken. */\n at?: string\n /** Where it comes from: entry id, document, gazette reference, article. */\n source?: string\n access?: \"public\" | \"shared\" | \"private\"\n status?: \"proposed\" | \"confirmed\"\n confirmed_by?: string\n checked_at?: string\n /** The job that wrote it, e.g. \"wiki-enrich\"; its next run replaces it. */\n by?: string\n note?: string\n}\n"],"mappings":"AAWO,IAAMA,EAA2C,CAAC,QAAS,SAAU,OAAO","names":["IMPORTANCE_LEVELS"]}
1
+ {"version":3,"sources":["../src/wiki/ontology/types.ts"],"sourcesContent":["// --- Wiki ontology types (#811) ---\n//\n// The ontology says what kinds of things the wiki holds (types grouped\n// into pillars), how they relate (properties), how important an event is,\n// and how each type's page is viewed (lenses). Every value here comes\n// from `ontology.yaml` in the wiki root, falling back to the generic\n// defaults in `defaults.ts`. Nothing about a particular organisation\n// belongs in code.\n\nexport type Importance = \"minor\" | \"normal\" | \"major\"\n\nexport const IMPORTANCE_LEVELS: readonly Importance[] = [\"minor\", \"normal\", \"major\"] as const\n\nexport interface PillarDef {\n id: string\n label: string\n /** One line under the pillar title. */\n description?: string\n /** Icon name from the view's small built-in icon set. */\n icon?: string\n /** Shown in the sidebar. A fallback pillar such as Topics sets false. */\n sidebar?: boolean\n}\n\nexport interface TypeDef {\n id: string\n label: string\n plural?: string\n pillar: string\n /** schema.org class, shown as a hint on the page. */\n schema?: string\n icon?: string\n /** Panels the page opens with, in order. */\n lens?: LensPanel[]\n}\n\nexport interface PropertyDef {\n id: string\n /** Read from the subject: \"Person — role at → Organization\". */\n label: string\n /** Read from the value's side: \"Organization — people ← Person\". */\n inverse?: string\n /** Wikidata property id, kept as a mapping hint. */\n wikidata?: string\n /** Types the value is expected to be. Informational. */\n range?: string[]\n /** Types the subject may be. A lens shows other subjects' statements\n * through the inverse; `wiki enrich` writes it only on these (#820). */\n domain?: string[]\n}\n\nexport type PanelSource = \"statements\" | \"linked\" | \"history\" | \"discussed\" | \"readings\" | \"notes\"\n\nexport interface LensPanel {\n /** Panel id. Used in the zoom URL, so keep it short and stable. */\n panel: string\n title?: string\n /** Where items come from. Inferred from the other fields when omitted. */\n source?: PanelSource\n /** Properties to show (statements panels). `related` covers plain links. */\n from?: string | string[]\n /** Keep only items whose other end has one of these types. */\n types?: string[]\n /** Items shown at the overview level (Z2). The full list is Z3. */\n show?: number\n group_by?: \"role\" | \"property\"\n display?: \"list\" | \"timeline\"\n /** History: importance levels shown unfolded. */\n importance?: Importance[]\n /** History: levels folded under a count. */\n fold?: Importance\n /** History: repeated minor events shown as one recurring line. */\n rollup?: { min: number; days: number }\n /** Readings: only the latest value per metric. */\n latest?: boolean\n /** `owner` marks the panel as private to the owner. */\n access?: \"owner\"\n}\n\nexport interface ClassifyRule {\n /** Type the matching page gets. */\n type: string\n /** Match when the page's legacy `type` is one of these. */\n legacy?: string[]\n /** Match when the page path starts with one of these folders. */\n path?: string[]\n /** Match when the page has any of these tags. */\n tags?: string[]\n /** Case-insensitive regular expression on the title. */\n title?: string\n}\n\n/** Gives an event its importance without a model call (#811). */\nexport interface ImportanceRule {\n level: Importance\n /** Case-insensitive regular expression on the event title. */\n title?: string\n /** Match when the event page has any of these tags. */\n tags?: string[]\n /** Case-insensitive regular expression on the title that stops the rule. */\n unless?: string\n}\n\nexport interface Ontology {\n version: number\n pillars: PillarDef[]\n types: TypeDef[]\n properties: PropertyDef[]\n importance: {\n default: Importance\n /** Who may set `major`: \"owner\" means agents can only propose it. */\n major_set_by: \"owner\" | \"anyone\"\n rollup: { min: number; days: number }\n /** Tried in order by `wiki events` before it asks a model. The first\n * match wins. */\n rules?: ImportanceRule[]\n }\n sidebar: {\n pins_max: number\n /** Page titles the owner pinned, in order. */\n pins: string[]\n }\n /** Rules that give a page a type, tried in order. The first match wins. */\n classify: ClassifyRule[]\n /** Type for pages no rule matches. */\n fallback_type: string\n /** Extra names of the fleet's agents, for agents that run on another\n * node or answer to a persona name (#819). The node's own agent ids,\n * configured names and persona names are found without this. */\n agent_names?: string[]\n}\n\n/** One typed fact about a page: subject (the page) → property → value. */\nexport interface WikiStatement {\n property: string\n /** A page title (becomes a link) or a literal value. */\n value: string\n role?: string\n since?: string\n until?: string\n /** Metric name for readings (disk_used, uptime). */\n metric?: string\n /** When a reading was taken. */\n at?: string\n /** Where it comes from: entry id, document, gazette reference, article. */\n source?: string\n access?: \"public\" | \"shared\" | \"private\"\n status?: \"proposed\" | \"confirmed\"\n confirmed_by?: string\n checked_at?: string\n /** The job that wrote it, e.g. \"wiki-enrich\"; its next run replaces it. */\n by?: string\n note?: string\n}\n"],"mappings":"AAWO,IAAMA,EAA2C,CAAC,QAAS,SAAU,OAAO","names":["IMPORTANCE_LEVELS"]}
@@ -1,4 +1,4 @@
1
- import{b as L}from"./chunk-7VLLZQ3N.js";import{a as H}from"./chunk-4LWFJ4MZ.js";import{c as N}from"./chunk-BIJHMWXK.js";import{b as J}from"./chunk-QD6IICK7.js";var X="note:",v=e=>e.startsWith(X);function Ne(e){let t=new Set(e.types),s=new Map;for(let o of H(e.dir).notes)!o.type||!t.has(o.type)||!o.description&&!o.body||J(`${o.description}
1
+ import{c as L}from"./chunk-MWNCS57L.js";import{a as H}from"./chunk-4LWFJ4MZ.js";import{c as N}from"./chunk-BIJHMWXK.js";import{b as J}from"./chunk-QD6IICK7.js";var X="note:",v=e=>e.startsWith(X);function Ne(e){let t=new Set(e.types),s=new Map;for(let o of H(e.dir).notes)!o.type||!t.has(o.type)||!o.description&&!o.body||J(`${o.description}
2
2
  ${o.body}`)||s.set(`${X}${o.file}`,o);let n=[],i=new Map;for(let[o,r]of s)n.push({path:o,title:r.name,type:"note",tags:[r.type],owner:e.owner,lastUpdated:r.checked}),i.set(o,r.description||r.body.split(`
3
3
  `)[0]);return{pool:n,summaries:i,read(o){let r=s.get(o);return r?ie(o,r,e.owner):null}}}function ie(e,t,s){let n=t.checked?`Checked: ${t.checked}`:"Checked: not recorded",i=t.source?`Source: ${t.source}`:"Source: not recorded",o=t.checked??t.updated?.slice(0,10)??"";return{path:e,content:`${i}
4
4
  ${n}
@@ -84,4 +84,4 @@ Question: ${e}
84
84
  ${t.map(n=>`- ${n.line}`).join(`
85
85
  `)}
86
86
  `}async function Qe(e,t,s,n,i){let o={selectorMs:0,planMs:0,liveMs:0,synthesisMs:0,selectorOutput:""},r=(u,y)=>({status:u,basis:"pages",answer:"",picked:[],live:[],liveAsked:0,error:y,trace:o}),a=()=>`${(i.now?.()??new Date).toISOString().slice(0,16).replace("T"," ")} UTC`,c=async u=>{if(!ee(n.live))return r("no-candidates",u);let y=[],x=0;try{let w=Date.now(),C=await te(e,n.live,i.call,i.timeoutMs);if(o.planMs=Date.now()-w,x=C.length,C.length===0)return r("no-candidates",u);w=Date.now(),y=await q(C,n.live,i.fetch),o.liveMs=Date.now()-w}catch(w){return i.log("source search failed:",w?.message),r("no-candidates",u)}let P=y.filter(w=>!w.line.endsWith(W));if(P.length===0)return{...r("no-candidates",`${u} The source search found no issue.`),live:y,liveAsked:x};let B=Date.now();try{let w=await i.call(Me(e,P,a()),n.answerModel,i.timeoutMs*2);return o.synthesisMs=Date.now()-B,{status:"ok",basis:"search",answer:w.trim(),picked:[],live:P,liveAsked:x,trace:o}}catch(w){return o.synthesisMs=Date.now()-B,i.log("answer failed:",w?.message),{status:"error",basis:"search",answer:"",picked:[],live:P,liveAsked:x,error:`Synthesis: ${w?.message||"unknown"}`,trace:o}}},d=t.pool.filter(u=>t.readable.has(u.path)),g=d.filter(u=>!u.path.startsWith("@")&&!v(u.path)),m=d.filter(u=>u.path.startsWith("@")),l=d.filter(u=>v(u.path)),h=[...M(e,g,s,n.candidates).map(u=>g[u]),...M(e,l,s,n.noteCandidates).map(u=>l[u]),...M(e,m,s,n.sharedCandidates).map(u=>m[u])];if(h.length===0)return c("No page shares a word with the question.");let p=Date.now(),R;try{o.selectorOutput=await i.call(Le(e,h,s,n.maxPages),n.navigatorModel,i.timeoutMs),R=Ae(o.selectorOutput,h.length,n.maxPages)}catch(u){return i.log("navigator failed:",u?.message),r("error",`Selector: ${u?.message||"unknown"}`)}finally{o.selectorMs=Date.now()-p}let $=[];for(let u of R){let y=t.read(h[u].path);y&&$.push({...y,hop:0})}if($.length===0)return c("No page was picked for the question.");let V=xe(e,$,t,s,n),Y=Pe([...$,...V],s,n.pageChars);$.push(...V.map(({chars:u,...y})=>y));let A=[],E=0;if(U(n.live))try{p=Date.now();let u=await ne(e,Y,n.live,i.call,i.timeoutMs);o.planMs=Date.now()-p,E=u.length,p=Date.now(),A=await q(u,n.live,i.fetch),o.liveMs=Date.now()-p}catch(u){i.log("live read failed:",u?.message)}p=Date.now();try{let u=await i.call(_e(e,Y,A,a(),$.some(y=>v(y.path))),n.answerModel,i.timeoutMs*2);return o.synthesisMs=Date.now()-p,{status:"ok",basis:"pages",answer:u.trim(),picked:$,live:A,liveAsked:E,trace:o}}catch(u){return o.synthesisMs=Date.now()-p,i.log("answer failed:",u?.message),{status:"error",basis:"pages",answer:"",picked:$,live:A,liveAsked:E,error:`Synthesis: ${u?.message||"unknown"}`,trace:o}}}export{v as a,Ne as b,Ie as c,Fe as d,Qe as e};
87
- //# sourceMappingURL=chunk-G4HDRJVN.js.map
87
+ //# sourceMappingURL=chunk-SKSYII3K.js.map
@@ -1,4 +1,4 @@
1
- import{a as h}from"./chunk-DDEDSYLF.js";import{resolve as n}from"path";import{existsSync as c,readdirSync as u,mkdirSync as l,readFileSync as p,writeFileSync as f,renameSync as S}from"fs";var m=class{baseDir;agentsDir;mode;sharedStore;agentStores=new Map;log;constructor(e=n(process.cwd(),".agentx/wiki"),r=console.error.bind(console,"[wiki-hub]"),t="graph"){this.baseDir=e,this.agentsDir=n(e,"agents"),this.mode=t,this.log=r,l(this.agentsDir,{recursive:!0}),this.sharedStore=new h(e,r)}getMode(){return this.mode}getAgentWiki(e){let r=`${e}:${this.mode}`;if(this.agentStores.has(r))return this.agentStores.get(r);let t=n(this.agentsDir,e,this.mode),s=new h(t,this.log);return this.agentStores.set(r,s),s}getBaseDir(){return this.baseDir}getSharedStore(){return this.sharedStore}sharedScope(e){return[...this.listAgentDirs().filter(t=>t!==e).map(t=>({id:t,store:this.getAgentWiki(t)})),{id:"shared",store:this.sharedStore,skip:t=>t.startsWith("agents/")}]}listAgentDirs(){return c(this.agentsDir)?u(this.agentsDir).filter(e=>!e.startsWith("_")&&!e.startsWith(".")&&c(n(this.agentsDir,e,this.mode))).sort():[]}listAgents(e=this.sharedStore.listEntries()){let r=new Set;for(let t of e)r.add(t.agentId);if(c(this.agentsDir))for(let t of u(this.agentsDir))!t.startsWith("_")&&!t.startsWith(".")&&r.add(t);return[...r].sort()}getAgentEntries(e){return this.sharedStore.listEntries({agentId:e})}getUnabsorbedEntries(e){return this.classifyEntries(e).pending}markProcessed(e,r,t){let s=new Set(t),g=this.readLedger(e),d=new Date().toISOString();for(let i of r)g.entries[i]={at:d,cited:s.has(i)};let o=this.ledgerPath(e);l(n(o,".."),{recursive:!0});let a=`${o}.tmp`;f(a,`${JSON.stringify(g,null,2)}
1
+ import{a as h}from"./chunk-MUVE3UAS.js";import{resolve as n}from"path";import{existsSync as c,readdirSync as u,mkdirSync as l,readFileSync as p,writeFileSync as f,renameSync as S}from"fs";var m=class{baseDir;agentsDir;mode;sharedStore;agentStores=new Map;log;constructor(e=n(process.cwd(),".agentx/wiki"),r=console.error.bind(console,"[wiki-hub]"),t="graph"){this.baseDir=e,this.agentsDir=n(e,"agents"),this.mode=t,this.log=r,l(this.agentsDir,{recursive:!0}),this.sharedStore=new h(e,r)}getMode(){return this.mode}getAgentWiki(e){let r=`${e}:${this.mode}`;if(this.agentStores.has(r))return this.agentStores.get(r);let t=n(this.agentsDir,e,this.mode),s=new h(t,this.log);return this.agentStores.set(r,s),s}getBaseDir(){return this.baseDir}getSharedStore(){return this.sharedStore}sharedScope(e){return[...this.listAgentDirs().filter(t=>t!==e).map(t=>({id:t,store:this.getAgentWiki(t)})),{id:"shared",store:this.sharedStore,skip:t=>t.startsWith("agents/")}]}listAgentDirs(){return c(this.agentsDir)?u(this.agentsDir).filter(e=>!e.startsWith("_")&&!e.startsWith(".")&&c(n(this.agentsDir,e,this.mode))).sort():[]}listAgents(e=this.sharedStore.listEntries()){let r=new Set;for(let t of e)r.add(t.agentId);if(c(this.agentsDir))for(let t of u(this.agentsDir))!t.startsWith("_")&&!t.startsWith(".")&&r.add(t);return[...r].sort()}getAgentEntries(e){return this.sharedStore.listEntries({agentId:e})}getUnabsorbedEntries(e){return this.classifyEntries(e).pending}markProcessed(e,r,t){let s=new Set(t),g=this.readLedger(e),d=new Date().toISOString();for(let i of r)g.entries[i]={at:d,cited:s.has(i)};let o=this.ledgerPath(e);l(n(o,".."),{recursive:!0});let a=`${o}.tmp`;f(a,`${JSON.stringify(g,null,2)}
2
2
  `),S(a,o)}summary(){let e=this.sharedStore.listEntries(),r=new Map;for(let t of e){let s=r.get(t.agentId);s?s.push(t):r.set(t.agentId,[t])}return this.listAgents(e).map(t=>{let{all:s,cited:g,read:d,pending:o,articles:a}=this.classifyEntries(t,r.get(t)??[]);return{agentId:t,totalEntries:s.length,totalArticles:a.length,cited:g.length,readNotCited:d.length,unabsorbed:o.length,articles:a}})}classifyEntries(e,r=this.getAgentEntries(e)){let t=this.getAgentWiki(e).rebuildIndex().articles,s=new Set;for(let i of t)if(i.sources)for(let y of i.sources)s.add(y);let g=this.readLedger(e).entries,d=[],o=[],a=[];for(let i of r)s.has(i.id)?d.push(i):g[i.id]?o.push(i):a.push(i);return{all:r,cited:d,read:o,pending:a,articles:t}}syncedFrom(e){let r=n(this.agentsDir,e,"_synced.json");if(!c(r))return null;try{return JSON.parse(p(r,"utf-8"))}catch(t){return this.log(`synced marker ${r} is not valid JSON: ${t.message}`),{node:"unknown",peerUrl:"",at:""}}}markSynced(e,r){let t=n(this.agentsDir,e,"_synced.json");l(n(t,".."),{recursive:!0});let s=`${t}.tmp`;f(s,`${JSON.stringify(r,null,2)}
3
3
  `),S(s,t)}ledgerPath(e){return n(this.agentsDir,e,"_absorbed.json")}readLedger(e){let r=this.ledgerPath(e);if(!c(r))return{entries:{}};try{return{entries:JSON.parse(p(r,"utf-8")).entries??{}}}catch(t){throw new Error(`absorb ledger ${r} is not valid JSON: ${t.message}`)}}};export{m as a};
4
- //# sourceMappingURL=chunk-RJOA7QBB.js.map
4
+ //# sourceMappingURL=chunk-SN7YH4E4.js.map
@@ -1,4 +1,4 @@
1
- import{c as w}from"./chunk-XWO6ALJQ.js";function T(t){let r=1779033703^t.length;for(let s=0;s<t.length;s++)r=Math.imul(r^t.charCodeAt(s),3432918353),r=r<<13|r>>>19;let o=r>>>0;return()=>{o=o+1831565813>>>0;let s=o;return s=Math.imul(s^s>>>15,s|1),s^=s+Math.imul(s^s>>>7,s|61),((s^s>>>14)>>>0)/4294967296}}function _(t,r,o){let s=T(o),i=[...t].sort((e,u)=>e.path.localeCompare(u.path));for(let e=i.length-1;e>0;e--){let u=Math.floor(s()*(e+1));[i[e],i[u]]=[i[u],i[e]]}return i.slice(0,Math.max(0,r)).sort((e,u)=>e.path.localeCompare(u.path))}var R=new Set("about after again also because been before being between could does doing during each from have having here into just more most only other over same should some such than that their them then there these they this those through under until very were what when where which while will with would your yours user assistant agent message entry please thanks".split(" "));function k(t){let r=new Set;for(let o of t.toLowerCase().matchAll(/[\p{L}\p{N}][\p{L}\p{N}_-]{3,}/gu))R.has(o[0])||r.add(o[0]);return r}function E(t,r){let o=k(t);if(o.size===0)return 1;let s=k(r),i=0;for(let e of o)s.has(e)&&i++;return i/o.size}var W=.15;function $(t){let r=w(t);return{commits:r.commits,urls:r.urls,numbers:r.numbers}}function j(t,r,o){return t.includes(r)||o.some(s=>s.startsWith(r)||r.startsWith(s))}var y=t=>t.toLowerCase().replace(/\/+$/,"");function N(t,r,o){let s=t.sources.map(a=>r.get(a)).filter(a=>!!a),i=t.sources.filter(a=>!r.has(a)),e=s.filter(a=>k(a.content).size>=5&&E(a.content,t.content)<W).map(a=>a.id),u=[...s.map(a=>`${a.date??""}
1
+ import{c as w}from"./chunk-UTW56K5I.js";function T(t){let r=1779033703^t.length;for(let s=0;s<t.length;s++)r=Math.imul(r^t.charCodeAt(s),3432918353),r=r<<13|r>>>19;let o=r>>>0;return()=>{o=o+1831565813>>>0;let s=o;return s=Math.imul(s^s>>>15,s|1),s^=s+Math.imul(s^s>>>7,s|61),((s^s>>>14)>>>0)/4294967296}}function _(t,r,o){let s=T(o),i=[...t].sort((e,u)=>e.path.localeCompare(u.path));for(let e=i.length-1;e>0;e--){let u=Math.floor(s()*(e+1));[i[e],i[u]]=[i[u],i[e]]}return i.slice(0,Math.max(0,r)).sort((e,u)=>e.path.localeCompare(u.path))}var R=new Set("about after again also because been before being between could does doing during each from have having here into just more most only other over same should some such than that their them then there these they this those through under until very were what when where which while will with would your yours user assistant agent message entry please thanks".split(" "));function k(t){let r=new Set;for(let o of t.toLowerCase().matchAll(/[\p{L}\p{N}][\p{L}\p{N}_-]{3,}/gu))R.has(o[0])||r.add(o[0]);return r}function E(t,r){let o=k(t);if(o.size===0)return 1;let s=k(r),i=0;for(let e of o)s.has(e)&&i++;return i/o.size}var W=.15;function $(t){let r=w(t);return{commits:r.commits,urls:r.urls,numbers:r.numbers}}function j(t,r,o){return t.includes(r)||o.some(s=>s.startsWith(r)||r.startsWith(s))}var y=t=>t.toLowerCase().replace(/\/+$/,"");function N(t,r,o){let s=t.sources.map(a=>r.get(a)).filter(a=>!!a),i=t.sources.filter(a=>!r.has(a)),e=s.filter(a=>k(a.content).size>=5&&E(a.content,t.content)<W).map(a=>a.id),u=[...s.map(a=>`${a.date??""}
2
2
  ${a.content}`),t.previous??""].join(`
3
3
  `),l=u.toLowerCase(),d=$(u),p=new Set(d.urls.map(y)),g=new Set(d.numbers),n=$(t.content),c=[];for(let a of n.commits)j(l,a,d.commits)||c.push(`commit ${a}`);for(let a of n.urls)!p.has(y(a))&&!l.includes(y(a))&&c.push(`link ${a}`);for(let a of n.numbers)/^\d{1,2}$/.test(a)||!g.has(a)&&!l.includes(a)&&c.push(`number ${a}`);let h=[];for(let a of s){let m=$(a.content);if(m.commits.length===0&&m.urls.length===0)continue;let v=o(a.id),S=(v.length>0?v:[t.content]).join(`
4
4
  `).toLowerCase(),x=$(S);for(let b of m.commits)j(S,b,x.commits)||h.push({entry:a.id,fact:`commit ${b}`});for(let b of m.urls)S.includes(y(b))||h.push({entry:a.id,fact:`link ${b}`})}return{path:t.path,title:t.title,type:t.type,sources:t.sources.length,missingSources:i,weakSources:e,ungrounded:c,lost:h}}function U(t,r){let o=r.toLowerCase(),s=w(r).commits,i=[];for(let e of t){let u=w(e.content),l=[...u.commits.filter(d=>!j(o,d,s)).map(d=>`commit ${d}`),...u.urls.filter(d=>!o.includes(y(d))).map(d=>`link ${d}`)];l.length>0&&i.push({entry:e.id,facts:l})}return i}var P=new Set("the and for with from into of on in at to by an a".split(" "));function L(t){return new Set((t||"").toLowerCase().split(/[^\p{L}\p{N}]+/u).filter(r=>(r.length>1||/\d/.test(r))&&!P.has(r)&&!R.has(r)))}function M(t,r){if(t.size===0||r.size===0)return 0;let o=0;for(let s of t)r.has(s)&&o++;return o/(t.size+r.size-o)}function D(t,r,o={}){let s=o.titleMin??.75,i=o.sourcesMin??.5,e=t.map(l=>({...l,tt:L(l.title),ss:new Set(l.sources??[])})),u=[];for(let l=0;l<e.length;l++)for(let d=l+1;d<e.length;d++){let p=e[l],g=e[d];if(r&&!r.has(p.path)&&!r.has(g.path))continue;let n=(p.type??"")===(g.type??"")?M(p.tt,g.tt):0;if(n>=s){u.push({a:p.path,b:g.path,reason:"title",score:n});continue}if(p.ss.size>=2&&g.ss.size>=2){let c=M(p.ss,g.ss);c>=i&&u.push({a:p.path,b:g.path,reason:"sources",score:c})}}return u.sort((l,d)=>d.score-l.score||l.a.localeCompare(d.a))}var A=4e3;function J(t,r){let o=r.map(i=>{let e=i.content.length>A?`${i.content.slice(0,A)}
@@ -33,4 +33,4 @@ Reply with JSON only, no fencing:
33
33
  {"claims": 0, "supported": 0, "unsupported": [], "contradicted": [], "off_subject": [], "missing": [], "verdict": "good"}`}function z(t){if(!t||typeof t!="object"||Array.isArray(t))return null;let r=t,o=l=>Array.isArray(l)?l.map(d=>String(d)):[],s=l=>typeof l=="number"&&Number.isFinite(l)&&l>=0?Math.round(l):NaN,i=s(r.claims);if(Number.isNaN(i))return null;let e=Math.min(i,Number.isNaN(s(r.supported))?i-o(r.unsupported).length:s(r.supported)),u=r.verdict==="good"||r.verdict==="minor"||r.verdict==="major"?r.verdict:null;return u?{claims:i,supported:Math.max(0,e),unsupported:o(r.unsupported),contradicted:o(r.contradicted),offSubject:o(r.off_subject??r.offSubject),missing:o(r.missing),verdict:u}:null}function I(t,r,o,s=[],i=5,e){let u={};for(let n of t)u[n.type??"untyped"]=(u[n.type??"untyped"]??0)+1;let l=new Set,d=new Set;for(let n of t)for(let c of n.lost)l.add(c.entry),d.add(`${c.entry}|${c.fact}`);let p=new Set(r.flatMap(n=>[n.a,n.b]).filter(n=>t.some(c=>c.path===n))),g={sample:{articles:t.length,seed:o.seed,since:o.since,byType:u},citations:{withSources:t.filter(n=>n.sources>0).length,missingSources:t.reduce((n,c)=>n+c.missingSources.length,0),weakSources:t.reduce((n,c)=>n+c.weakSources.length,0),articlesWithWeak:t.filter(n=>n.weakSources.length>0).length},grounding:{articlesWithUngrounded:t.filter(n=>n.ungrounded.length>0).length,ungroundedFacts:t.reduce((n,c)=>n+c.ungrounded.length,0)},lost:{entriesWithLostFacts:l.size,lostFacts:d.size},duplicates:{pairs:r.length,articlesInPair:p.size},...e?{uncited:{entries:e.entries,withLostFacts:e.lost.length}}:{},examples:{ungrounded:t.filter(n=>n.ungrounded.length>0).slice(0,i).map(n=>({path:n.path,facts:n.ungrounded.slice(0,6)})),lost:t.flatMap(n=>n.lost.map(c=>({path:n.path,...c}))).slice(0,i),weak:t.filter(n=>n.weakSources.length>0).slice(0,i).map(n=>({path:n.path,entries:n.weakSources})),duplicates:r.slice(0,i),uncited:(e?.lost??[]).slice(0,i).map(n=>({entry:n.entry,facts:n.facts.slice(0,4)})),judged:s.filter(n=>n.verdict.verdict!=="good").slice(0,i).map(n=>({path:n.path,verdict:n.verdict.verdict,unsupported:n.verdict.unsupported.slice(0,4),contradicted:n.verdict.contradicted.slice(0,4),offSubject:n.verdict.offSubject,missing:n.verdict.missing.slice(0,4)}))}};if(s.length>0){let n=s.map(a=>a.verdict),c=n.reduce((a,m)=>a+m.claims,0),h=n.reduce((a,m)=>a+m.supported,0);g.judge={judged:n.length,claims:c,supported:h,supportRate:c>0?h/c:1,unsupported:n.reduce((a,m)=>a+m.unsupported.length,0),contradicted:n.reduce((a,m)=>a+m.contradicted.length,0),articlesWithWrongMerge:n.filter(a=>a.offSubject.length>0).length,missingFacts:n.reduce((a,m)=>a+m.missing.length,0),verdicts:{good:n.filter(a=>a.verdict==="good").length,minor:n.filter(a=>a.verdict==="minor").length,major:n.filter(a=>a.verdict==="major").length}}}return g}var f=(t,r)=>r>0?`${Math.round(t/r*100)}%`:"\u2013";function O(t){let r=t.sample.articles,o=["# Wiki absorb scorecard","",`Sample: ${r} articles (seed \`${t.sample.seed}\`${t.sample.since?`, changed since ${t.sample.since}`:""}). Types: ${Object.entries(t.sample.byType).map(([e,u])=>`${e} ${u}`).join(", ")||"\u2013"}.`,"","| Check | Result | Lower is better? |","|---|---|---|",`| Articles citing at least one entry | ${t.citations.withSources}/${r} (${f(t.citations.withSources,r)}) | no |`,`| Cited ids with no entry | ${t.citations.missingSources} | yes |`,`| Articles with a weak citation (entry shares <${Math.round(W*100)}% of its words) | ${t.citations.articlesWithWeak}/${r} (${f(t.citations.articlesWithWeak,r)}) | yes |`,`| Articles with an ungrounded commit, link or number | ${t.grounding.articlesWithUngrounded}/${r} (${f(t.grounding.articlesWithUngrounded,r)}) | yes |`,`| Ungrounded facts | ${t.grounding.ungroundedFacts} | yes |`,`| Commits or links lost from cited entries | ${t.lost.lostFacts} across ${t.lost.entriesWithLostFacts} entries | yes |`,`| Sampled articles in a likely duplicate pair | ${t.duplicates.articlesInPair}/${r} (${f(t.duplicates.articlesInPair,r)}) | yes |`];if(t.uncited&&o.push(`| Entries read in the window but cited by no article | ${t.uncited.entries} | \u2013 |`,`| \u2026of which carry a commit or link no article has | ${t.uncited.withLostFacts} | yes |`),t.judge){let e=t.judge;o.push(`| Judge: claims supported by the cited entries | ${e.supported}/${e.claims} (${f(e.supported,e.claims)}) | no |`,`| Judge: unsupported / contradicted claims | ${e.unsupported} / ${e.contradicted} | yes |`,`| Judge: articles with an off-subject source (wrong merge) | ${e.articlesWithWrongMerge}/${e.judged} (${f(e.articlesWithWrongMerge,e.judged)}) | yes |`,`| Judge: facts missing from the article | ${e.missingFacts} | yes |`,`| Judge verdicts good / minor / major | ${e.verdicts.good} / ${e.verdicts.minor} / ${e.verdicts.major} | \u2013 |`)}let s=t.examples,i=(e,u)=>{u.length!==0&&o.push("",`## ${e}`,"",...u)};return i("Examples: ungrounded facts",s.ungrounded.map(e=>`- \`${e.path}\`: ${e.facts.join("; ")}`)),i("Examples: lost facts",s.lost.map(e=>`- \`${e.path}\` \u2190 entry \`${e.entry}\`: ${e.fact}`)),i("Examples: weak citations",s.weak.map(e=>`- \`${e.path}\`: ${e.entries.map(u=>`\`${u}\``).join(", ")}`)),i("Examples: uncited entries with facts no article has",s.uncited.map(e=>`- \`${e.entry}\`: ${e.facts.join("; ")}`)),i("Examples: likely duplicates",s.duplicates.map(e=>`- \`${e.a}\` \u2194 \`${e.b}\` (${e.reason}, ${e.score.toFixed(2)})`)),i("Examples: judge findings",s.judged.map(e=>{let u=[e.unsupported.length?`unsupported: ${e.unsupported.join(" | ")}`:"",e.contradicted.length?`contradicted: ${e.contradicted.join(" | ")}`:"",e.offSubject.length?`off-subject: ${e.offSubject.join(", ")}`:"",e.missing.length?`missing: ${e.missing.join(" | ")}`:""].filter(Boolean);return`- \`${e.path}\` (${e.verdict}): ${u.join("; ")}`})),o.push("","## Method","","- The sample is drawn with a seeded shuffle from the articles changed in the window, so the same seed and window give the same articles.","- Ungrounded: a commit hash, link or number (three digits or more, or a date) in the article that no cited entry and no earlier version of the article contains.","- Lost: a commit hash or link in a cited entry that no article citing that entry contains.","- Uncited: entries the absorb ledger marks as read in the window that no article cites; those carrying a commit or link that no article on the node has are counted as lost, since absorb will not offer them again.","- Weak citation: a cited entry sharing fewer than 15% of its distinctive words with the article.","- Duplicate: two articles of the same type whose titles share at least 75% of their words, or which share at least half their sources.","- Judge (optional): one model call per article reads the article and its cited entries and lists unsupported, contradicted, off-subject and missing items."),o.join(`
34
34
  `)+`
35
35
  `}function V(t){if(!t||typeof t!="object")return{};let r=t,o=r.usage??{},s=i=>typeof i=="number"&&Number.isFinite(i)?i:void 0;return{costUsd:s(r.total_cost_usd)??s(r.cost_usd),apiMs:s(r.duration_api_ms),inputTokens:s(o.input_tokens),outputTokens:s(o.output_tokens),cacheReadTokens:s(o.cache_read_input_tokens),cacheWriteTokens:s(o.cache_creation_input_tokens)}}function C(t,r){return t.length===0?0:t[Math.min(t.length-1,Math.floor(r*t.length))]}function Y(t){let r=new Map,o=i=>{let e=i||"(none)",u=r.get(e);return u||r.set(e,u={calls:[],runs:[]}),u};for(let i of t)i.kind==="call"?o(i.label).calls.push(i):i.kind==="run"&&o(i.label).runs.push(i);let s=[];for(let[i,{calls:e,runs:u}]of r){if(e.length===0)continue;let l=e.map(n=>n.wallMs).sort((n,c)=>n-c),d;if(u.length>0)d=u.reduce((n,c)=>n+(Date.parse(c.endedAt)-Date.parse(c.startedAt)),0);else{let n=e.map(h=>Date.parse(h.at)-h.wallMs),c=e.map(h=>Date.parse(h.at));d=Math.max(...c)-Math.min(...n)}let p=e.reduce((n,c)=>n+c.entries,0),g=e.reduce((n,c)=>n+(c.costUsd??0),0);s.push({label:i,calls:e.length,failed:e.filter(n=>n.failed).length,entries:p,articles:e.reduce((n,c)=>n+c.articles,0),refused:e.reduce((n,c)=>n+c.refused,0),heldCited:e.reduce((n,c)=>n+(c.heldCited??0),0),wallMs:d,callMsP50:C(l,.5),callMsP95:C(l,.95),prepMs:e.reduce((n,c)=>n+(c.prepMs??0),0),costUsd:g,costPerEntry:p>0?g/p:0,entriesPerMinute:d>0?p/d*6e4:0,promptChars:e.reduce((n,c)=>n+c.promptChars,0)})}return s.sort((i,e)=>i.label.localeCompare(e.label))}export{T as a,_ as b,k as c,E as d,W as e,N as f,U as g,D as h,A as i,J as j,z as k,I as l,O as m,V as n,Y as o};
36
- //# sourceMappingURL=chunk-TDGDH4RA.js.map
36
+ //# sourceMappingURL=chunk-TTG3ZW3W.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/wiki/absorb-eval.ts"],"sourcesContent":["// Measuring what `agentx wiki absorb` writes (#808).\n//\n// Absorb turns raw entries into articles, and until now nothing checked\n// the result against the entries it came from. This module is the\n// scorecard: a fixed, seeded sample of articles, five checks per article,\n// and an optional model judge for the questions a string match cannot\n// answer (is this claim supported, does this source belong here).\n//\n// The deterministic checks are deliberately narrow. A commit hash or URL\n// in an article that no cited entry and no earlier version carries is\n// almost certainly made up or mis-attributed; a commit or URL in a cited\n// entry that no article carries was almost certainly lost. Prose claims\n// are left to the judge.\n\nimport { extractFacts } from \"./absorb-context\"\n\nexport interface EvalArticle {\n path: string\n title: string\n type?: string\n content: string\n sources: string[]\n lastUpdated?: string\n /** The article body before this absorb touched it, when one exists. */\n previous?: string\n}\n\nexport interface EvalEntry {\n id: string\n date?: string\n content: string\n}\n\n// --- Sample ---\n\n/** A small seeded PRNG, so a sample can be drawn again from the same seed. */\nexport function seededRandom(seed: string): () => number {\n let h = 1779033703 ^ seed.length\n for (let i = 0; i < seed.length; i++) {\n h = Math.imul(h ^ seed.charCodeAt(i), 3432918353)\n h = (h << 13) | (h >>> 19)\n }\n let a = h >>> 0\n return () => {\n a = (a + 0x6d2b79f5) >>> 0\n let t = a\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** `n` items picked without replacement. Same items and seed, same pick. */\nexport function pickSample<T extends { path: string }>(items: T[], n: number, seed: string): T[] {\n const rng = seededRandom(seed)\n const pool = [...items].sort((a, b) => a.path.localeCompare(b.path))\n for (let i = pool.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1))\n ;[pool[i], pool[j]] = [pool[j], pool[i]]\n }\n return pool.slice(0, Math.max(0, n)).sort((a, b) => a.path.localeCompare(b.path))\n}\n\n// --- Terms ---\n\nconst STOP = new Set(\n (\"about after again also because been before being between could does doing during each from have having here into \"\n + \"just more most only other over same should some such than that their them then there these they this those \"\n + \"through under until very were what when where which while will with would your yours user assistant agent \"\n + \"message entry please thanks\").split(\" \"),\n)\n\n/** Distinctive words: four letters or more, lower-cased, minus common ones. */\nexport function terms(text: string): Set<string> {\n const out = new Set<string>()\n for (const m of text.toLowerCase().matchAll(/[\\p{L}\\p{N}][\\p{L}\\p{N}_-]{3,}/gu)) {\n if (!STOP.has(m[0])) out.add(m[0])\n }\n return out\n}\n\n/** Share of `entry`'s distinctive words that appear in `article`. */\nexport function termOverlap(entry: string, article: string): number {\n const e = terms(entry)\n if (e.size === 0) return 1\n const a = terms(article)\n let hit = 0\n for (const t of e) if (a.has(t)) hit++\n return hit / e.size\n}\n\n// --- Per-article checks ---\n\n/** Below this share of shared words a cited entry is flagged as weak. */\nexport const WEAK_CITATION = 0.15\n\nexport interface ArticleCheck {\n path: string\n title: string\n type?: string\n sources: number\n /** Cited ids with no entry behind them. */\n missingSources: string[]\n /** Cited entries that share almost no words with the article. */\n weakSources: string[]\n /** Commits, links and numbers in the article that neither a cited entry\n * nor the earlier version carries. */\n ungrounded: string[]\n /** Commits and links in a cited entry that no article citing it carries. */\n lost: Array<{ entry: string; fact: string }>\n}\n\n/** What a fact looks like when matched against grounding text. */\nfunction factStrings(text: string): { commits: string[]; urls: string[]; numbers: string[] } {\n const f = extractFacts(text)\n return { commits: f.commits, urls: f.urls, numbers: f.numbers }\n}\n\nfunction hasCommit(haystack: string, commit: string, known: string[]): boolean {\n return haystack.includes(commit) || known.some((k) => k.startsWith(commit) || commit.startsWith(k))\n}\n\nconst urlKey = (u: string) => u.toLowerCase().replace(/\\/+$/, \"\")\n\n/**\n * Check one article against its cited entries.\n *\n * `citersOf` returns every article (in the whole wiki, not only the\n * sample) that cites an entry, so a fact moved to a sibling article is\n * not counted as lost.\n */\nexport function checkArticle(\n article: EvalArticle,\n entries: Map<string, EvalEntry>,\n citersOf: (entryId: string) => string[],\n): ArticleCheck {\n const cited = article.sources.map((id) => entries.get(id)).filter((e): e is EvalEntry => !!e)\n const missingSources = article.sources.filter((id) => !entries.has(id))\n\n const weakSources = cited\n .filter((e) => terms(e.content).size >= 5 && termOverlap(e.content, article.content) < WEAK_CITATION)\n .map((e) => e.id)\n\n // Grounding: entry text with its date (absorb is shown the date in the\n // entry header), plus the earlier version of the article.\n const grounding = [...cited.map((e) => `${e.date ?? \"\"}\\n${e.content}`), article.previous ?? \"\"].join(\"\\n\")\n const groundLower = grounding.toLowerCase()\n const g = factStrings(grounding)\n const groundUrls = new Set(g.urls.map(urlKey))\n const groundNumbers = new Set(g.numbers)\n const mine = factStrings(article.content)\n const ungrounded: string[] = []\n for (const c of mine.commits) if (!hasCommit(groundLower, c, g.commits)) ungrounded.push(`commit ${c}`)\n for (const u of mine.urls) if (!groundUrls.has(urlKey(u)) && !groundLower.includes(urlKey(u))) ungrounded.push(`link ${u}`)\n for (const n of mine.numbers) {\n // Bare small integers (\"2 tests\", \"3 people\") are too common to judge.\n if (/^\\d{1,2}$/.test(n)) continue\n if (!groundNumbers.has(n) && !groundLower.includes(n)) ungrounded.push(`number ${n}`)\n }\n\n const lost: Array<{ entry: string; fact: string }> = []\n for (const e of cited) {\n const facts = factStrings(e.content)\n if (facts.commits.length === 0 && facts.urls.length === 0) continue\n const carriers = citersOf(e.id)\n const text = (carriers.length > 0 ? carriers : [article.content]).join(\"\\n\").toLowerCase()\n const carrierFacts = factStrings(text)\n for (const c of facts.commits) if (!hasCommit(text, c, carrierFacts.commits)) lost.push({ entry: e.id, fact: `commit ${c}` })\n for (const u of facts.urls) if (!text.includes(urlKey(u))) lost.push({ entry: e.id, fact: `link ${u}` })\n }\n\n return {\n path: article.path,\n title: article.title,\n type: article.type,\n sources: article.sources.length,\n missingSources,\n weakSources,\n ungrounded,\n lost,\n }\n}\n\n// --- Uncited entries ---\n\n/**\n * Entries absorb read and cited nowhere that carry a commit or link no\n * article on the node has. A successful run marks every entry it read as\n * done (#762), so a fact in an entry the model passed over is gone from\n * the queue for good; the per-article \"lost\" check cannot see it because\n * no article points back at the entry.\n */\nexport function uncitedWithFacts(\n uncited: EvalEntry[],\n allArticles: string,\n): Array<{ entry: string; facts: string[] }> {\n const lower = allArticles.toLowerCase()\n const known = extractFacts(allArticles).commits\n const out: Array<{ entry: string; facts: string[] }> = []\n for (const e of uncited) {\n const f = extractFacts(e.content)\n const missing = [\n ...f.commits.filter((c) => !hasCommit(lower, c, known)).map((c) => `commit ${c}`),\n ...f.urls.filter((u) => !lower.includes(urlKey(u))).map((u) => `link ${u}`),\n ]\n if (missing.length > 0) out.push({ entry: e.id, facts: missing })\n }\n return out\n}\n\n// --- Duplicates ---\n\nexport interface DuplicatePair {\n a: string\n b: string\n reason: \"title\" | \"sources\"\n score: number\n}\n\nconst TITLE_STOP = new Set(\"the and for with from into of on in at to by an a\".split(\" \"))\n\nfunction titleTerms(title: string): Set<string> {\n return new Set(\n (title || \"\").toLowerCase().split(/[^\\p{L}\\p{N}]+/u).filter((w) => (w.length > 1 || /\\d/.test(w)) && !TITLE_STOP.has(w) && !STOP.has(w)),\n )\n}\n\nfunction jaccard<T>(a: Set<T>, b: Set<T>): number {\n if (a.size === 0 || b.size === 0) return 0\n let inter = 0\n for (const x of a) if (b.has(x)) inter++\n return inter / (a.size + b.size - inter)\n}\n\n/**\n * Pairs of articles that look like one subject filed twice: the same type\n * and near-identical title words, or most of their sources in common.\n * Only pairs touching `focus` are returned when it is given.\n */\nexport function findDuplicates(\n catalog: Array<{ path: string; title: string; type?: string; sources?: string[] }>,\n focus?: Set<string>,\n opts: { titleMin?: number; sourcesMin?: number } = {},\n): DuplicatePair[] {\n const titleMin = opts.titleMin ?? 0.75\n const sourcesMin = opts.sourcesMin ?? 0.5\n const rows = catalog.map((a) => ({ ...a, tt: titleTerms(a.title), ss: new Set(a.sources ?? []) }))\n const pairs: DuplicatePair[] = []\n for (let i = 0; i < rows.length; i++) {\n for (let j = i + 1; j < rows.length; j++) {\n const x = rows[i], y = rows[j]\n if (focus && !focus.has(x.path) && !focus.has(y.path)) continue\n const t = (x.type ?? \"\") === (y.type ?? \"\") ? jaccard(x.tt, y.tt) : 0\n if (t >= titleMin) { pairs.push({ a: x.path, b: y.path, reason: \"title\", score: t }); continue }\n if (x.ss.size >= 2 && y.ss.size >= 2) {\n const s = jaccard(x.ss, y.ss)\n if (s >= sourcesMin) pairs.push({ a: x.path, b: y.path, reason: \"sources\", score: s })\n }\n }\n }\n return pairs.sort((p, q) => q.score - p.score || p.a.localeCompare(q.a))\n}\n\n// --- Model judge ---\n\nexport interface JudgeVerdict {\n claims: number\n supported: number\n unsupported: string[]\n contradicted: string[]\n /** Cited entry ids that are about a different subject (a wrong merge). */\n offSubject: string[]\n /** Facts in the entries that belong in this article and are missing. */\n missing: string[]\n verdict: \"good\" | \"minor\" | \"major\"\n}\n\n/** Per-entry cap in the judge prompt, so one long transcript cannot crowd out the rest. */\nexport const JUDGE_ENTRY_CHARS = 4000\n\nexport function buildJudgePrompt(article: EvalArticle, entries: EvalEntry[]): string {\n const sourceBlock = entries\n .map((e) => {\n const body = e.content.length > JUDGE_ENTRY_CHARS ? `${e.content.slice(0, JUDGE_ENTRY_CHARS)}\\n[…cut]` : e.content\n return `--- ENTRY ${e.id} [${e.date ?? \"\"}] ---\\n${body}\\n--- END ENTRY ---`\n })\n .join(\"\\n\\n\")\n const earlier = article.previous\n ? `\\n## Earlier version of the article (facts here count as supported)\\n\\n${article.previous}\\n`\n : \"\"\n return `You are auditing one wiki article against the raw entries it cites. Do not use outside knowledge.\n\n## Article: ${article.title} (${article.type ?? \"untyped\"}, ${article.path})\n\n${article.content}\n${earlier}\n## Cited entries (${entries.length})\n\n${sourceBlock || \"(none)\"}\n\n## What to report\n\n1. Split the article into factual claims (one per sentence or bullet; skip headings and \"unknown\" placeholders). Count them.\n2. A claim is supported when a cited entry or the earlier version states it or plainly implies it. List each claim that is not supported, quoted briefly.\n3. List each claim an entry contradicts.\n4. List the cited entry ids that are about a different subject than the article (they were merged in by mistake).\n5. List facts from the entries that belong in this article and are missing (names, numbers, identifiers, decisions). Ignore small talk.\n6. Verdict: \"good\" (nothing wrong), \"minor\" (wording or a small omission), \"major\" (an unsupported or contradicted fact, a wrong merge, or a lost identifier).\n\nReply with JSON only, no fencing:\n{\"claims\": 0, \"supported\": 0, \"unsupported\": [], \"contradicted\": [], \"off_subject\": [], \"missing\": [], \"verdict\": \"good\"}`\n}\n\nexport function parseJudgeReply(value: unknown): JudgeVerdict | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null\n const v = value as Record<string, unknown>\n const list = (x: unknown) => (Array.isArray(x) ? x.map((s) => String(s)) : [])\n const num = (x: unknown) => (typeof x === \"number\" && Number.isFinite(x) && x >= 0 ? Math.round(x) : NaN)\n const claims = num(v.claims)\n if (Number.isNaN(claims)) return null\n const supported = Math.min(claims, Number.isNaN(num(v.supported)) ? claims - list(v.unsupported).length : num(v.supported))\n const verdict = v.verdict === \"good\" || v.verdict === \"minor\" || v.verdict === \"major\" ? v.verdict : null\n if (!verdict) return null\n return {\n claims,\n supported: Math.max(0, supported),\n unsupported: list(v.unsupported),\n contradicted: list(v.contradicted),\n offSubject: list(v.off_subject ?? v.offSubject),\n missing: list(v.missing),\n verdict,\n }\n}\n\n// --- Scorecard ---\n\nexport interface Scorecard {\n sample: { articles: number; seed: string; since?: string; byType: Record<string, number> }\n citations: { withSources: number; missingSources: number; weakSources: number; articlesWithWeak: number }\n grounding: { articlesWithUngrounded: number; ungroundedFacts: number }\n lost: { entriesWithLostFacts: number; lostFacts: number }\n duplicates: { pairs: number; articlesInPair: number }\n /** Entries read in the window that no article cites. */\n uncited?: { entries: number; withLostFacts: number }\n judge?: {\n judged: number\n claims: number\n supported: number\n supportRate: number\n unsupported: number\n contradicted: number\n articlesWithWrongMerge: number\n missingFacts: number\n verdicts: Record<\"good\" | \"minor\" | \"major\", number>\n }\n examples: {\n ungrounded: Array<{ path: string; facts: string[] }>\n lost: Array<{ path: string; entry: string; fact: string }>\n weak: Array<{ path: string; entries: string[] }>\n duplicates: DuplicatePair[]\n uncited: Array<{ entry: string; facts: string[] }>\n judged: Array<{ path: string; verdict: string; unsupported: string[]; contradicted: string[]; offSubject: string[]; missing: string[] }>\n }\n}\n\nexport function buildScorecard(\n checks: ArticleCheck[],\n duplicates: DuplicatePair[],\n meta: { seed: string; since?: string },\n judged: Array<{ path: string; verdict: JudgeVerdict }> = [],\n maxExamples = 5,\n uncited?: { entries: number; lost: Array<{ entry: string; facts: string[] }> },\n): Scorecard {\n const byType: Record<string, number> = {}\n for (const c of checks) byType[c.type ?? \"untyped\"] = (byType[c.type ?? \"untyped\"] ?? 0) + 1\n const lostEntries = new Set<string>()\n const lostKeys = new Set<string>()\n for (const c of checks) for (const l of c.lost) { lostEntries.add(l.entry); lostKeys.add(`${l.entry}|${l.fact}`) }\n const inPair = new Set(duplicates.flatMap((d) => [d.a, d.b]).filter((p) => checks.some((c) => c.path === p)))\n\n const card: Scorecard = {\n sample: { articles: checks.length, seed: meta.seed, since: meta.since, byType },\n citations: {\n withSources: checks.filter((c) => c.sources > 0).length,\n missingSources: checks.reduce((s, c) => s + c.missingSources.length, 0),\n weakSources: checks.reduce((s, c) => s + c.weakSources.length, 0),\n articlesWithWeak: checks.filter((c) => c.weakSources.length > 0).length,\n },\n grounding: {\n articlesWithUngrounded: checks.filter((c) => c.ungrounded.length > 0).length,\n ungroundedFacts: checks.reduce((s, c) => s + c.ungrounded.length, 0),\n },\n lost: { entriesWithLostFacts: lostEntries.size, lostFacts: lostKeys.size },\n duplicates: { pairs: duplicates.length, articlesInPair: inPair.size },\n ...(uncited ? { uncited: { entries: uncited.entries, withLostFacts: uncited.lost.length } } : {}),\n examples: {\n ungrounded: checks.filter((c) => c.ungrounded.length > 0).slice(0, maxExamples).map((c) => ({ path: c.path, facts: c.ungrounded.slice(0, 6) })),\n lost: checks.flatMap((c) => c.lost.map((l) => ({ path: c.path, ...l }))).slice(0, maxExamples),\n weak: checks.filter((c) => c.weakSources.length > 0).slice(0, maxExamples).map((c) => ({ path: c.path, entries: c.weakSources })),\n duplicates: duplicates.slice(0, maxExamples),\n uncited: (uncited?.lost ?? []).slice(0, maxExamples).map((u) => ({ entry: u.entry, facts: u.facts.slice(0, 4) })),\n judged: judged\n .filter((j) => j.verdict.verdict !== \"good\")\n .slice(0, maxExamples)\n .map((j) => ({\n path: j.path,\n verdict: j.verdict.verdict,\n unsupported: j.verdict.unsupported.slice(0, 4),\n contradicted: j.verdict.contradicted.slice(0, 4),\n offSubject: j.verdict.offSubject,\n missing: j.verdict.missing.slice(0, 4),\n })),\n },\n }\n\n if (judged.length > 0) {\n const v = judged.map((j) => j.verdict)\n const claims = v.reduce((s, x) => s + x.claims, 0)\n const supported = v.reduce((s, x) => s + x.supported, 0)\n card.judge = {\n judged: v.length,\n claims,\n supported,\n supportRate: claims > 0 ? supported / claims : 1,\n unsupported: v.reduce((s, x) => s + x.unsupported.length, 0),\n contradicted: v.reduce((s, x) => s + x.contradicted.length, 0),\n articlesWithWrongMerge: v.filter((x) => x.offSubject.length > 0).length,\n missingFacts: v.reduce((s, x) => s + x.missing.length, 0),\n verdicts: {\n good: v.filter((x) => x.verdict === \"good\").length,\n minor: v.filter((x) => x.verdict === \"minor\").length,\n major: v.filter((x) => x.verdict === \"major\").length,\n },\n }\n }\n return card\n}\n\nconst pct = (n: number, d: number) => (d > 0 ? `${Math.round((n / d) * 100)}%` : \"–\")\n\n/** The scorecard as Markdown, for the issue or a PR comment. */\nexport function renderScorecard(card: Scorecard): string {\n const n = card.sample.articles\n const lines = [\n `# Wiki absorb scorecard`,\n \"\",\n `Sample: ${n} articles (seed \\`${card.sample.seed}\\`${card.sample.since ? `, changed since ${card.sample.since}` : \"\"}). `\n + `Types: ${Object.entries(card.sample.byType).map(([t, k]) => `${t} ${k}`).join(\", \") || \"–\"}.`,\n \"\",\n \"| Check | Result | Lower is better? |\",\n \"|---|---|---|\",\n `| Articles citing at least one entry | ${card.citations.withSources}/${n} (${pct(card.citations.withSources, n)}) | no |`,\n `| Cited ids with no entry | ${card.citations.missingSources} | yes |`,\n `| Articles with a weak citation (entry shares <${Math.round(WEAK_CITATION * 100)}% of its words) | ${card.citations.articlesWithWeak}/${n} (${pct(card.citations.articlesWithWeak, n)}) | yes |`,\n `| Articles with an ungrounded commit, link or number | ${card.grounding.articlesWithUngrounded}/${n} (${pct(card.grounding.articlesWithUngrounded, n)}) | yes |`,\n `| Ungrounded facts | ${card.grounding.ungroundedFacts} | yes |`,\n `| Commits or links lost from cited entries | ${card.lost.lostFacts} across ${card.lost.entriesWithLostFacts} entries | yes |`,\n `| Sampled articles in a likely duplicate pair | ${card.duplicates.articlesInPair}/${n} (${pct(card.duplicates.articlesInPair, n)}) | yes |`,\n ]\n if (card.uncited) {\n lines.push(\n `| Entries read in the window but cited by no article | ${card.uncited.entries} | – |`,\n `| …of which carry a commit or link no article has | ${card.uncited.withLostFacts} | yes |`,\n )\n }\n if (card.judge) {\n const j = card.judge\n lines.push(\n `| Judge: claims supported by the cited entries | ${j.supported}/${j.claims} (${pct(j.supported, j.claims)}) | no |`,\n `| Judge: unsupported / contradicted claims | ${j.unsupported} / ${j.contradicted} | yes |`,\n `| Judge: articles with an off-subject source (wrong merge) | ${j.articlesWithWrongMerge}/${j.judged} (${pct(j.articlesWithWrongMerge, j.judged)}) | yes |`,\n `| Judge: facts missing from the article | ${j.missingFacts} | yes |`,\n `| Judge verdicts good / minor / major | ${j.verdicts.good} / ${j.verdicts.minor} / ${j.verdicts.major} | – |`,\n )\n }\n const ex = card.examples\n const section = (title: string, rows: string[]) => {\n if (rows.length === 0) return\n lines.push(\"\", `## ${title}`, \"\", ...rows)\n }\n section(\"Examples: ungrounded facts\", ex.ungrounded.map((e) => `- \\`${e.path}\\`: ${e.facts.join(\"; \")}`))\n section(\"Examples: lost facts\", ex.lost.map((e) => `- \\`${e.path}\\` ← entry \\`${e.entry}\\`: ${e.fact}`))\n section(\"Examples: weak citations\", ex.weak.map((e) => `- \\`${e.path}\\`: ${e.entries.map((x) => `\\`${x}\\``).join(\", \")}`))\n section(\"Examples: uncited entries with facts no article has\", ex.uncited.map((u) => `- \\`${u.entry}\\`: ${u.facts.join(\"; \")}`))\n section(\"Examples: likely duplicates\", ex.duplicates.map((d) => `- \\`${d.a}\\` ↔ \\`${d.b}\\` (${d.reason}, ${d.score.toFixed(2)})`))\n section(\n \"Examples: judge findings\",\n ex.judged.map((e) => {\n const parts = [\n e.unsupported.length ? `unsupported: ${e.unsupported.join(\" | \")}` : \"\",\n e.contradicted.length ? `contradicted: ${e.contradicted.join(\" | \")}` : \"\",\n e.offSubject.length ? `off-subject: ${e.offSubject.join(\", \")}` : \"\",\n e.missing.length ? `missing: ${e.missing.join(\" | \")}` : \"\",\n ].filter(Boolean)\n return `- \\`${e.path}\\` (${e.verdict}): ${parts.join(\"; \")}`\n }),\n )\n lines.push(\n \"\",\n \"## Method\",\n \"\",\n \"- The sample is drawn with a seeded shuffle from the articles changed in the window, so the same seed and window give the same articles.\",\n \"- Ungrounded: a commit hash, link or number (three digits or more, or a date) in the article that no cited entry and no earlier version of the article contains.\",\n \"- Lost: a commit hash or link in a cited entry that no article citing that entry contains.\",\n \"- Uncited: entries the absorb ledger marks as read in the window that no article cites; those carrying a commit or link that no article on the node has are counted as lost, since absorb will not offer them again.\",\n \"- Weak citation: a cited entry sharing fewer than 15% of its distinctive words with the article.\",\n \"- Duplicate: two articles of the same type whose titles share at least 75% of their words, or which share at least half their sources.\",\n \"- Judge (optional): one model call per article reads the article and its cited entries and lists unsupported, contradicted, off-subject and missing items.\",\n )\n return lines.join(\"\\n\") + \"\\n\"\n}\n\n// --- Run telemetry ---\n\n/** One compile call, as appended to `_absorb-runs.jsonl`. */\nexport interface AbsorbCallRecord {\n kind: \"call\"\n at: string\n label?: string\n agent: string\n model: string\n entries: number\n articles: number\n refused: number\n /** Entries held by a refused save that another saved article cites:\n * they leave the queue, so the refused update is never retried. */\n heldCited?: number\n /** Wiki notes (#831) the call was given, patched, and recorded. */\n notes?: number\n notesPatched?: number\n notesRecorded?: number\n failed: boolean\n /** The model call alone. */\n wallMs: number\n /** Retrieval, fact lookups and prompt building before the call. */\n prepMs?: number\n promptChars: number\n promptParts?: Record<string, number>\n costUsd?: number\n apiMs?: number\n inputTokens?: number\n outputTokens?: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n}\n\n/** One whole `wiki absorb` invocation. */\nexport interface AbsorbRunRecord {\n kind: \"run\"\n label?: string\n startedAt: string\n endedAt: string\n max: number\n model: string\n}\n\n/** Cost and timing fields from the claude CLI's `--output-format json` envelope. */\nexport function envelopeUsage(envelope: unknown): Partial<AbsorbCallRecord> {\n if (!envelope || typeof envelope !== \"object\") return {}\n const e = envelope as Record<string, any>\n const u = (e.usage ?? {}) as Record<string, unknown>\n const n = (x: unknown) => (typeof x === \"number\" && Number.isFinite(x) ? x : undefined)\n return {\n costUsd: n(e.total_cost_usd) ?? n(e.cost_usd),\n apiMs: n(e.duration_api_ms),\n inputTokens: n(u.input_tokens),\n outputTokens: n(u.output_tokens),\n cacheReadTokens: n(u.cache_read_input_tokens),\n cacheWriteTokens: n(u.cache_creation_input_tokens),\n }\n}\n\nexport interface RunSummary {\n label: string\n calls: number\n failed: number\n entries: number\n articles: number\n refused: number\n heldCited: number\n /** First call start to last call end, or the run records' span when present. */\n wallMs: number\n callMsP50: number\n callMsP95: number\n /** Time spent before the model calls, summed. */\n prepMs: number\n costUsd: number\n costPerEntry: number\n entriesPerMinute: number\n promptChars: number\n}\n\nfunction percentile(sorted: number[], p: number): number {\n if (sorted.length === 0) return 0\n return sorted[Math.min(sorted.length - 1, Math.floor(p * sorted.length))]\n}\n\n/** Totals per run label from the telemetry lines. Calls without a label group under \"(none)\". */\nexport function summariseRuns(records: Array<AbsorbCallRecord | AbsorbRunRecord>): RunSummary[] {\n const groups = new Map<string, { calls: AbsorbCallRecord[]; runs: AbsorbRunRecord[] }>()\n const group = (label?: string) => {\n const key = label || \"(none)\"\n let g = groups.get(key)\n if (!g) groups.set(key, (g = { calls: [], runs: [] }))\n return g\n }\n for (const r of records) {\n if (r.kind === \"call\") group(r.label).calls.push(r)\n else if (r.kind === \"run\") group(r.label).runs.push(r)\n }\n const out: RunSummary[] = []\n for (const [label, { calls, runs }] of groups) {\n if (calls.length === 0) continue\n const ms = calls.map((c) => c.wallMs).sort((a, b) => a - b)\n let wallMs: number\n if (runs.length > 0) {\n wallMs = runs.reduce((s, r) => s + (Date.parse(r.endedAt) - Date.parse(r.startedAt)), 0)\n } else {\n const starts = calls.map((c) => Date.parse(c.at) - c.wallMs)\n const ends = calls.map((c) => Date.parse(c.at))\n wallMs = Math.max(...ends) - Math.min(...starts)\n }\n const entries = calls.reduce((s, c) => s + c.entries, 0)\n const costUsd = calls.reduce((s, c) => s + (c.costUsd ?? 0), 0)\n out.push({\n label,\n calls: calls.length,\n failed: calls.filter((c) => c.failed).length,\n entries,\n articles: calls.reduce((s, c) => s + c.articles, 0),\n refused: calls.reduce((s, c) => s + c.refused, 0),\n heldCited: calls.reduce((s, c) => s + (c.heldCited ?? 0), 0),\n wallMs,\n callMsP50: percentile(ms, 0.5),\n callMsP95: percentile(ms, 0.95),\n prepMs: calls.reduce((s, c) => s + (c.prepMs ?? 0), 0),\n costUsd,\n costPerEntry: entries > 0 ? costUsd / entries : 0,\n entriesPerMinute: wallMs > 0 ? (entries / wallMs) * 60_000 : 0,\n promptChars: calls.reduce((s, c) => s + c.promptChars, 0),\n })\n }\n return out.sort((a, b) => a.label.localeCompare(b.label))\n}\n"],"mappings":"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","names":["seededRandom","seed","h","i","a","t","pickSample","items","n","rng","pool","b","j","STOP","terms","text","out","m","termOverlap","entry","article","e","hit","WEAK_CITATION","factStrings","f","extractFacts","hasCommit","haystack","commit","known","k","urlKey","u","checkArticle","entries","citersOf","cited","id","missingSources","weakSources","grounding","groundLower","g","groundUrls","groundNumbers","mine","ungrounded","c","lost","facts","carriers","carrierFacts","uncitedWithFacts","uncited","allArticles","lower","missing","TITLE_STOP","titleTerms","title","w","jaccard","inter","x","findDuplicates","catalog","focus","opts","titleMin","sourcesMin","rows","pairs","y","s","p","q","JUDGE_ENTRY_CHARS","buildJudgePrompt","sourceBlock","body","earlier","parseJudgeReply","value","v","list","num","claims","supported","verdict","buildScorecard","checks","duplicates","meta","judged","maxExamples","byType","lostEntries","lostKeys","l","inPair","d","card","pct","renderScorecard","lines","ex","section","parts","envelopeUsage","envelope","percentile","sorted","summariseRuns","records","groups","group","label","key","r","calls","runs","ms","wallMs","starts","ends","costUsd"]}
1
+ {"version":3,"sources":["../src/wiki/absorb-eval.ts"],"sourcesContent":["// Measuring what `agentx wiki absorb` writes (#808).\n//\n// Absorb turns raw entries into articles, and until now nothing checked\n// the result against the entries it came from. This module is the\n// scorecard: a fixed, seeded sample of articles, five checks per article,\n// and an optional model judge for the questions a string match cannot\n// answer (is this claim supported, does this source belong here).\n//\n// The deterministic checks are deliberately narrow. A commit hash or URL\n// in an article that no cited entry and no earlier version carries is\n// almost certainly made up or mis-attributed; a commit or URL in a cited\n// entry that no article carries was almost certainly lost. Prose claims\n// are left to the judge.\n\nimport { extractFacts } from \"./absorb-context\"\n\nexport interface EvalArticle {\n path: string\n title: string\n type?: string\n content: string\n sources: string[]\n lastUpdated?: string\n /** The article body before this absorb touched it, when one exists. */\n previous?: string\n}\n\nexport interface EvalEntry {\n id: string\n date?: string\n content: string\n}\n\n// --- Sample ---\n\n/** A small seeded PRNG, so a sample can be drawn again from the same seed. */\nexport function seededRandom(seed: string): () => number {\n let h = 1779033703 ^ seed.length\n for (let i = 0; i < seed.length; i++) {\n h = Math.imul(h ^ seed.charCodeAt(i), 3432918353)\n h = (h << 13) | (h >>> 19)\n }\n let a = h >>> 0\n return () => {\n a = (a + 0x6d2b79f5) >>> 0\n let t = a\n t = Math.imul(t ^ (t >>> 15), t | 1)\n t ^= t + Math.imul(t ^ (t >>> 7), t | 61)\n return ((t ^ (t >>> 14)) >>> 0) / 4294967296\n }\n}\n\n/** `n` items picked without replacement. Same items and seed, same pick. */\nexport function pickSample<T extends { path: string }>(items: T[], n: number, seed: string): T[] {\n const rng = seededRandom(seed)\n const pool = [...items].sort((a, b) => a.path.localeCompare(b.path))\n for (let i = pool.length - 1; i > 0; i--) {\n const j = Math.floor(rng() * (i + 1))\n ;[pool[i], pool[j]] = [pool[j], pool[i]]\n }\n return pool.slice(0, Math.max(0, n)).sort((a, b) => a.path.localeCompare(b.path))\n}\n\n// --- Terms ---\n\nconst STOP = new Set(\n (\"about after again also because been before being between could does doing during each from have having here into \"\n + \"just more most only other over same should some such than that their them then there these they this those \"\n + \"through under until very were what when where which while will with would your yours user assistant agent \"\n + \"message entry please thanks\").split(\" \"),\n)\n\n/** Distinctive words: four letters or more, lower-cased, minus common ones. */\nexport function terms(text: string): Set<string> {\n const out = new Set<string>()\n for (const m of text.toLowerCase().matchAll(/[\\p{L}\\p{N}][\\p{L}\\p{N}_-]{3,}/gu)) {\n if (!STOP.has(m[0])) out.add(m[0])\n }\n return out\n}\n\n/** Share of `entry`'s distinctive words that appear in `article`. */\nexport function termOverlap(entry: string, article: string): number {\n const e = terms(entry)\n if (e.size === 0) return 1\n const a = terms(article)\n let hit = 0\n for (const t of e) if (a.has(t)) hit++\n return hit / e.size\n}\n\n// --- Per-article checks ---\n\n/** Below this share of shared words a cited entry is flagged as weak. */\nexport const WEAK_CITATION = 0.15\n\nexport interface ArticleCheck {\n path: string\n title: string\n type?: string\n sources: number\n /** Cited ids with no entry behind them. */\n missingSources: string[]\n /** Cited entries that share almost no words with the article. */\n weakSources: string[]\n /** Commits, links and numbers in the article that neither a cited entry\n * nor the earlier version carries. */\n ungrounded: string[]\n /** Commits and links in a cited entry that no article citing it carries. */\n lost: Array<{ entry: string; fact: string }>\n}\n\n/** What a fact looks like when matched against grounding text. */\nfunction factStrings(text: string): { commits: string[]; urls: string[]; numbers: string[] } {\n const f = extractFacts(text)\n return { commits: f.commits, urls: f.urls, numbers: f.numbers }\n}\n\nfunction hasCommit(haystack: string, commit: string, known: string[]): boolean {\n return haystack.includes(commit) || known.some((k) => k.startsWith(commit) || commit.startsWith(k))\n}\n\nconst urlKey = (u: string) => u.toLowerCase().replace(/\\/+$/, \"\")\n\n/**\n * Check one article against its cited entries.\n *\n * `citersOf` returns every article (in the whole wiki, not only the\n * sample) that cites an entry, so a fact moved to a sibling article is\n * not counted as lost.\n */\nexport function checkArticle(\n article: EvalArticle,\n entries: Map<string, EvalEntry>,\n citersOf: (entryId: string) => string[],\n): ArticleCheck {\n const cited = article.sources.map((id) => entries.get(id)).filter((e): e is EvalEntry => !!e)\n const missingSources = article.sources.filter((id) => !entries.has(id))\n\n const weakSources = cited\n .filter((e) => terms(e.content).size >= 5 && termOverlap(e.content, article.content) < WEAK_CITATION)\n .map((e) => e.id)\n\n // Grounding: entry text with its date (absorb is shown the date in the\n // entry header), plus the earlier version of the article.\n const grounding = [...cited.map((e) => `${e.date ?? \"\"}\\n${e.content}`), article.previous ?? \"\"].join(\"\\n\")\n const groundLower = grounding.toLowerCase()\n const g = factStrings(grounding)\n const groundUrls = new Set(g.urls.map(urlKey))\n const groundNumbers = new Set(g.numbers)\n const mine = factStrings(article.content)\n const ungrounded: string[] = []\n for (const c of mine.commits) if (!hasCommit(groundLower, c, g.commits)) ungrounded.push(`commit ${c}`)\n for (const u of mine.urls) if (!groundUrls.has(urlKey(u)) && !groundLower.includes(urlKey(u))) ungrounded.push(`link ${u}`)\n for (const n of mine.numbers) {\n // Bare small integers (\"2 tests\", \"3 people\") are too common to judge.\n if (/^\\d{1,2}$/.test(n)) continue\n if (!groundNumbers.has(n) && !groundLower.includes(n)) ungrounded.push(`number ${n}`)\n }\n\n const lost: Array<{ entry: string; fact: string }> = []\n for (const e of cited) {\n const facts = factStrings(e.content)\n if (facts.commits.length === 0 && facts.urls.length === 0) continue\n const carriers = citersOf(e.id)\n const text = (carriers.length > 0 ? carriers : [article.content]).join(\"\\n\").toLowerCase()\n const carrierFacts = factStrings(text)\n for (const c of facts.commits) if (!hasCommit(text, c, carrierFacts.commits)) lost.push({ entry: e.id, fact: `commit ${c}` })\n for (const u of facts.urls) if (!text.includes(urlKey(u))) lost.push({ entry: e.id, fact: `link ${u}` })\n }\n\n return {\n path: article.path,\n title: article.title,\n type: article.type,\n sources: article.sources.length,\n missingSources,\n weakSources,\n ungrounded,\n lost,\n }\n}\n\n// --- Uncited entries ---\n\n/**\n * Entries absorb read and cited nowhere that carry a commit or link no\n * article on the node has. A successful run marks every entry it read as\n * done (#762), so a fact in an entry the model passed over is gone from\n * the queue for good; the per-article \"lost\" check cannot see it because\n * no article points back at the entry.\n */\nexport function uncitedWithFacts(\n uncited: EvalEntry[],\n allArticles: string,\n): Array<{ entry: string; facts: string[] }> {\n const lower = allArticles.toLowerCase()\n const known = extractFacts(allArticles).commits\n const out: Array<{ entry: string; facts: string[] }> = []\n for (const e of uncited) {\n const f = extractFacts(e.content)\n const missing = [\n ...f.commits.filter((c) => !hasCommit(lower, c, known)).map((c) => `commit ${c}`),\n ...f.urls.filter((u) => !lower.includes(urlKey(u))).map((u) => `link ${u}`),\n ]\n if (missing.length > 0) out.push({ entry: e.id, facts: missing })\n }\n return out\n}\n\n// --- Duplicates ---\n\nexport interface DuplicatePair {\n a: string\n b: string\n reason: \"title\" | \"sources\"\n score: number\n}\n\nconst TITLE_STOP = new Set(\"the and for with from into of on in at to by an a\".split(\" \"))\n\nfunction titleTerms(title: string): Set<string> {\n return new Set(\n (title || \"\").toLowerCase().split(/[^\\p{L}\\p{N}]+/u).filter((w) => (w.length > 1 || /\\d/.test(w)) && !TITLE_STOP.has(w) && !STOP.has(w)),\n )\n}\n\nfunction jaccard<T>(a: Set<T>, b: Set<T>): number {\n if (a.size === 0 || b.size === 0) return 0\n let inter = 0\n for (const x of a) if (b.has(x)) inter++\n return inter / (a.size + b.size - inter)\n}\n\n/**\n * Pairs of articles that look like one subject filed twice: the same type\n * and near-identical title words, or most of their sources in common.\n * Only pairs touching `focus` are returned when it is given.\n */\nexport function findDuplicates(\n catalog: Array<{ path: string; title: string; type?: string; sources?: string[] }>,\n focus?: Set<string>,\n opts: { titleMin?: number; sourcesMin?: number } = {},\n): DuplicatePair[] {\n const titleMin = opts.titleMin ?? 0.75\n const sourcesMin = opts.sourcesMin ?? 0.5\n const rows = catalog.map((a) => ({ ...a, tt: titleTerms(a.title), ss: new Set(a.sources ?? []) }))\n const pairs: DuplicatePair[] = []\n for (let i = 0; i < rows.length; i++) {\n for (let j = i + 1; j < rows.length; j++) {\n const x = rows[i], y = rows[j]\n if (focus && !focus.has(x.path) && !focus.has(y.path)) continue\n const t = (x.type ?? \"\") === (y.type ?? \"\") ? jaccard(x.tt, y.tt) : 0\n if (t >= titleMin) { pairs.push({ a: x.path, b: y.path, reason: \"title\", score: t }); continue }\n if (x.ss.size >= 2 && y.ss.size >= 2) {\n const s = jaccard(x.ss, y.ss)\n if (s >= sourcesMin) pairs.push({ a: x.path, b: y.path, reason: \"sources\", score: s })\n }\n }\n }\n return pairs.sort((p, q) => q.score - p.score || p.a.localeCompare(q.a))\n}\n\n// --- Model judge ---\n\nexport interface JudgeVerdict {\n claims: number\n supported: number\n unsupported: string[]\n contradicted: string[]\n /** Cited entry ids that are about a different subject (a wrong merge). */\n offSubject: string[]\n /** Facts in the entries that belong in this article and are missing. */\n missing: string[]\n verdict: \"good\" | \"minor\" | \"major\"\n}\n\n/** Per-entry cap in the judge prompt, so one long transcript cannot crowd out the rest. */\nexport const JUDGE_ENTRY_CHARS = 4000\n\nexport function buildJudgePrompt(article: EvalArticle, entries: EvalEntry[]): string {\n const sourceBlock = entries\n .map((e) => {\n const body = e.content.length > JUDGE_ENTRY_CHARS ? `${e.content.slice(0, JUDGE_ENTRY_CHARS)}\\n[…cut]` : e.content\n return `--- ENTRY ${e.id} [${e.date ?? \"\"}] ---\\n${body}\\n--- END ENTRY ---`\n })\n .join(\"\\n\\n\")\n const earlier = article.previous\n ? `\\n## Earlier version of the article (facts here count as supported)\\n\\n${article.previous}\\n`\n : \"\"\n return `You are auditing one wiki article against the raw entries it cites. Do not use outside knowledge.\n\n## Article: ${article.title} (${article.type ?? \"untyped\"}, ${article.path})\n\n${article.content}\n${earlier}\n## Cited entries (${entries.length})\n\n${sourceBlock || \"(none)\"}\n\n## What to report\n\n1. Split the article into factual claims (one per sentence or bullet; skip headings and \"unknown\" placeholders). Count them.\n2. A claim is supported when a cited entry or the earlier version states it or plainly implies it. List each claim that is not supported, quoted briefly.\n3. List each claim an entry contradicts.\n4. List the cited entry ids that are about a different subject than the article (they were merged in by mistake).\n5. List facts from the entries that belong in this article and are missing (names, numbers, identifiers, decisions). Ignore small talk.\n6. Verdict: \"good\" (nothing wrong), \"minor\" (wording or a small omission), \"major\" (an unsupported or contradicted fact, a wrong merge, or a lost identifier).\n\nReply with JSON only, no fencing:\n{\"claims\": 0, \"supported\": 0, \"unsupported\": [], \"contradicted\": [], \"off_subject\": [], \"missing\": [], \"verdict\": \"good\"}`\n}\n\nexport function parseJudgeReply(value: unknown): JudgeVerdict | null {\n if (!value || typeof value !== \"object\" || Array.isArray(value)) return null\n const v = value as Record<string, unknown>\n const list = (x: unknown) => (Array.isArray(x) ? x.map((s) => String(s)) : [])\n const num = (x: unknown) => (typeof x === \"number\" && Number.isFinite(x) && x >= 0 ? Math.round(x) : NaN)\n const claims = num(v.claims)\n if (Number.isNaN(claims)) return null\n const supported = Math.min(claims, Number.isNaN(num(v.supported)) ? claims - list(v.unsupported).length : num(v.supported))\n const verdict = v.verdict === \"good\" || v.verdict === \"minor\" || v.verdict === \"major\" ? v.verdict : null\n if (!verdict) return null\n return {\n claims,\n supported: Math.max(0, supported),\n unsupported: list(v.unsupported),\n contradicted: list(v.contradicted),\n offSubject: list(v.off_subject ?? v.offSubject),\n missing: list(v.missing),\n verdict,\n }\n}\n\n// --- Scorecard ---\n\nexport interface Scorecard {\n sample: { articles: number; seed: string; since?: string; byType: Record<string, number> }\n citations: { withSources: number; missingSources: number; weakSources: number; articlesWithWeak: number }\n grounding: { articlesWithUngrounded: number; ungroundedFacts: number }\n lost: { entriesWithLostFacts: number; lostFacts: number }\n duplicates: { pairs: number; articlesInPair: number }\n /** Entries read in the window that no article cites. */\n uncited?: { entries: number; withLostFacts: number }\n judge?: {\n judged: number\n claims: number\n supported: number\n supportRate: number\n unsupported: number\n contradicted: number\n articlesWithWrongMerge: number\n missingFacts: number\n verdicts: Record<\"good\" | \"minor\" | \"major\", number>\n }\n examples: {\n ungrounded: Array<{ path: string; facts: string[] }>\n lost: Array<{ path: string; entry: string; fact: string }>\n weak: Array<{ path: string; entries: string[] }>\n duplicates: DuplicatePair[]\n uncited: Array<{ entry: string; facts: string[] }>\n judged: Array<{ path: string; verdict: string; unsupported: string[]; contradicted: string[]; offSubject: string[]; missing: string[] }>\n }\n}\n\nexport function buildScorecard(\n checks: ArticleCheck[],\n duplicates: DuplicatePair[],\n meta: { seed: string; since?: string },\n judged: Array<{ path: string; verdict: JudgeVerdict }> = [],\n maxExamples = 5,\n uncited?: { entries: number; lost: Array<{ entry: string; facts: string[] }> },\n): Scorecard {\n const byType: Record<string, number> = {}\n for (const c of checks) byType[c.type ?? \"untyped\"] = (byType[c.type ?? \"untyped\"] ?? 0) + 1\n const lostEntries = new Set<string>()\n const lostKeys = new Set<string>()\n for (const c of checks) for (const l of c.lost) { lostEntries.add(l.entry); lostKeys.add(`${l.entry}|${l.fact}`) }\n const inPair = new Set(duplicates.flatMap((d) => [d.a, d.b]).filter((p) => checks.some((c) => c.path === p)))\n\n const card: Scorecard = {\n sample: { articles: checks.length, seed: meta.seed, since: meta.since, byType },\n citations: {\n withSources: checks.filter((c) => c.sources > 0).length,\n missingSources: checks.reduce((s, c) => s + c.missingSources.length, 0),\n weakSources: checks.reduce((s, c) => s + c.weakSources.length, 0),\n articlesWithWeak: checks.filter((c) => c.weakSources.length > 0).length,\n },\n grounding: {\n articlesWithUngrounded: checks.filter((c) => c.ungrounded.length > 0).length,\n ungroundedFacts: checks.reduce((s, c) => s + c.ungrounded.length, 0),\n },\n lost: { entriesWithLostFacts: lostEntries.size, lostFacts: lostKeys.size },\n duplicates: { pairs: duplicates.length, articlesInPair: inPair.size },\n ...(uncited ? { uncited: { entries: uncited.entries, withLostFacts: uncited.lost.length } } : {}),\n examples: {\n ungrounded: checks.filter((c) => c.ungrounded.length > 0).slice(0, maxExamples).map((c) => ({ path: c.path, facts: c.ungrounded.slice(0, 6) })),\n lost: checks.flatMap((c) => c.lost.map((l) => ({ path: c.path, ...l }))).slice(0, maxExamples),\n weak: checks.filter((c) => c.weakSources.length > 0).slice(0, maxExamples).map((c) => ({ path: c.path, entries: c.weakSources })),\n duplicates: duplicates.slice(0, maxExamples),\n uncited: (uncited?.lost ?? []).slice(0, maxExamples).map((u) => ({ entry: u.entry, facts: u.facts.slice(0, 4) })),\n judged: judged\n .filter((j) => j.verdict.verdict !== \"good\")\n .slice(0, maxExamples)\n .map((j) => ({\n path: j.path,\n verdict: j.verdict.verdict,\n unsupported: j.verdict.unsupported.slice(0, 4),\n contradicted: j.verdict.contradicted.slice(0, 4),\n offSubject: j.verdict.offSubject,\n missing: j.verdict.missing.slice(0, 4),\n })),\n },\n }\n\n if (judged.length > 0) {\n const v = judged.map((j) => j.verdict)\n const claims = v.reduce((s, x) => s + x.claims, 0)\n const supported = v.reduce((s, x) => s + x.supported, 0)\n card.judge = {\n judged: v.length,\n claims,\n supported,\n supportRate: claims > 0 ? supported / claims : 1,\n unsupported: v.reduce((s, x) => s + x.unsupported.length, 0),\n contradicted: v.reduce((s, x) => s + x.contradicted.length, 0),\n articlesWithWrongMerge: v.filter((x) => x.offSubject.length > 0).length,\n missingFacts: v.reduce((s, x) => s + x.missing.length, 0),\n verdicts: {\n good: v.filter((x) => x.verdict === \"good\").length,\n minor: v.filter((x) => x.verdict === \"minor\").length,\n major: v.filter((x) => x.verdict === \"major\").length,\n },\n }\n }\n return card\n}\n\nconst pct = (n: number, d: number) => (d > 0 ? `${Math.round((n / d) * 100)}%` : \"–\")\n\n/** The scorecard as Markdown, for the issue or a PR comment. */\nexport function renderScorecard(card: Scorecard): string {\n const n = card.sample.articles\n const lines = [\n `# Wiki absorb scorecard`,\n \"\",\n `Sample: ${n} articles (seed \\`${card.sample.seed}\\`${card.sample.since ? `, changed since ${card.sample.since}` : \"\"}). `\n + `Types: ${Object.entries(card.sample.byType).map(([t, k]) => `${t} ${k}`).join(\", \") || \"–\"}.`,\n \"\",\n \"| Check | Result | Lower is better? |\",\n \"|---|---|---|\",\n `| Articles citing at least one entry | ${card.citations.withSources}/${n} (${pct(card.citations.withSources, n)}) | no |`,\n `| Cited ids with no entry | ${card.citations.missingSources} | yes |`,\n `| Articles with a weak citation (entry shares <${Math.round(WEAK_CITATION * 100)}% of its words) | ${card.citations.articlesWithWeak}/${n} (${pct(card.citations.articlesWithWeak, n)}) | yes |`,\n `| Articles with an ungrounded commit, link or number | ${card.grounding.articlesWithUngrounded}/${n} (${pct(card.grounding.articlesWithUngrounded, n)}) | yes |`,\n `| Ungrounded facts | ${card.grounding.ungroundedFacts} | yes |`,\n `| Commits or links lost from cited entries | ${card.lost.lostFacts} across ${card.lost.entriesWithLostFacts} entries | yes |`,\n `| Sampled articles in a likely duplicate pair | ${card.duplicates.articlesInPair}/${n} (${pct(card.duplicates.articlesInPair, n)}) | yes |`,\n ]\n if (card.uncited) {\n lines.push(\n `| Entries read in the window but cited by no article | ${card.uncited.entries} | – |`,\n `| …of which carry a commit or link no article has | ${card.uncited.withLostFacts} | yes |`,\n )\n }\n if (card.judge) {\n const j = card.judge\n lines.push(\n `| Judge: claims supported by the cited entries | ${j.supported}/${j.claims} (${pct(j.supported, j.claims)}) | no |`,\n `| Judge: unsupported / contradicted claims | ${j.unsupported} / ${j.contradicted} | yes |`,\n `| Judge: articles with an off-subject source (wrong merge) | ${j.articlesWithWrongMerge}/${j.judged} (${pct(j.articlesWithWrongMerge, j.judged)}) | yes |`,\n `| Judge: facts missing from the article | ${j.missingFacts} | yes |`,\n `| Judge verdicts good / minor / major | ${j.verdicts.good} / ${j.verdicts.minor} / ${j.verdicts.major} | – |`,\n )\n }\n const ex = card.examples\n const section = (title: string, rows: string[]) => {\n if (rows.length === 0) return\n lines.push(\"\", `## ${title}`, \"\", ...rows)\n }\n section(\"Examples: ungrounded facts\", ex.ungrounded.map((e) => `- \\`${e.path}\\`: ${e.facts.join(\"; \")}`))\n section(\"Examples: lost facts\", ex.lost.map((e) => `- \\`${e.path}\\` ← entry \\`${e.entry}\\`: ${e.fact}`))\n section(\"Examples: weak citations\", ex.weak.map((e) => `- \\`${e.path}\\`: ${e.entries.map((x) => `\\`${x}\\``).join(\", \")}`))\n section(\"Examples: uncited entries with facts no article has\", ex.uncited.map((u) => `- \\`${u.entry}\\`: ${u.facts.join(\"; \")}`))\n section(\"Examples: likely duplicates\", ex.duplicates.map((d) => `- \\`${d.a}\\` ↔ \\`${d.b}\\` (${d.reason}, ${d.score.toFixed(2)})`))\n section(\n \"Examples: judge findings\",\n ex.judged.map((e) => {\n const parts = [\n e.unsupported.length ? `unsupported: ${e.unsupported.join(\" | \")}` : \"\",\n e.contradicted.length ? `contradicted: ${e.contradicted.join(\" | \")}` : \"\",\n e.offSubject.length ? `off-subject: ${e.offSubject.join(\", \")}` : \"\",\n e.missing.length ? `missing: ${e.missing.join(\" | \")}` : \"\",\n ].filter(Boolean)\n return `- \\`${e.path}\\` (${e.verdict}): ${parts.join(\"; \")}`\n }),\n )\n lines.push(\n \"\",\n \"## Method\",\n \"\",\n \"- The sample is drawn with a seeded shuffle from the articles changed in the window, so the same seed and window give the same articles.\",\n \"- Ungrounded: a commit hash, link or number (three digits or more, or a date) in the article that no cited entry and no earlier version of the article contains.\",\n \"- Lost: a commit hash or link in a cited entry that no article citing that entry contains.\",\n \"- Uncited: entries the absorb ledger marks as read in the window that no article cites; those carrying a commit or link that no article on the node has are counted as lost, since absorb will not offer them again.\",\n \"- Weak citation: a cited entry sharing fewer than 15% of its distinctive words with the article.\",\n \"- Duplicate: two articles of the same type whose titles share at least 75% of their words, or which share at least half their sources.\",\n \"- Judge (optional): one model call per article reads the article and its cited entries and lists unsupported, contradicted, off-subject and missing items.\",\n )\n return lines.join(\"\\n\") + \"\\n\"\n}\n\n// --- Run telemetry ---\n\n/** One compile call, as appended to `_absorb-runs.jsonl`. */\nexport interface AbsorbCallRecord {\n kind: \"call\"\n at: string\n label?: string\n agent: string\n model: string\n entries: number\n articles: number\n refused: number\n /** Entries held by a refused save that another saved article cites:\n * they leave the queue, so the refused update is never retried. */\n heldCited?: number\n /** Wiki notes (#831) the call was given, patched, and recorded. */\n notes?: number\n notesPatched?: number\n notesRecorded?: number\n failed: boolean\n /** The model call alone. */\n wallMs: number\n /** Retrieval, fact lookups and prompt building before the call. */\n prepMs?: number\n promptChars: number\n promptParts?: Record<string, number>\n costUsd?: number\n apiMs?: number\n inputTokens?: number\n outputTokens?: number\n cacheReadTokens?: number\n cacheWriteTokens?: number\n}\n\n/** One whole `wiki absorb` invocation. */\nexport interface AbsorbRunRecord {\n kind: \"run\"\n label?: string\n startedAt: string\n endedAt: string\n max: number\n model: string\n /** Compile calls in this run that failed. Absent on runs before #603. */\n failed?: number\n}\n\n/** Cost and timing fields from the claude CLI's `--output-format json` envelope. */\nexport function envelopeUsage(envelope: unknown): Partial<AbsorbCallRecord> {\n if (!envelope || typeof envelope !== \"object\") return {}\n const e = envelope as Record<string, any>\n const u = (e.usage ?? {}) as Record<string, unknown>\n const n = (x: unknown) => (typeof x === \"number\" && Number.isFinite(x) ? x : undefined)\n return {\n costUsd: n(e.total_cost_usd) ?? n(e.cost_usd),\n apiMs: n(e.duration_api_ms),\n inputTokens: n(u.input_tokens),\n outputTokens: n(u.output_tokens),\n cacheReadTokens: n(u.cache_read_input_tokens),\n cacheWriteTokens: n(u.cache_creation_input_tokens),\n }\n}\n\nexport interface RunSummary {\n label: string\n calls: number\n failed: number\n entries: number\n articles: number\n refused: number\n heldCited: number\n /** First call start to last call end, or the run records' span when present. */\n wallMs: number\n callMsP50: number\n callMsP95: number\n /** Time spent before the model calls, summed. */\n prepMs: number\n costUsd: number\n costPerEntry: number\n entriesPerMinute: number\n promptChars: number\n}\n\nfunction percentile(sorted: number[], p: number): number {\n if (sorted.length === 0) return 0\n return sorted[Math.min(sorted.length - 1, Math.floor(p * sorted.length))]\n}\n\n/** Totals per run label from the telemetry lines. Calls without a label group under \"(none)\". */\nexport function summariseRuns(records: Array<AbsorbCallRecord | AbsorbRunRecord>): RunSummary[] {\n const groups = new Map<string, { calls: AbsorbCallRecord[]; runs: AbsorbRunRecord[] }>()\n const group = (label?: string) => {\n const key = label || \"(none)\"\n let g = groups.get(key)\n if (!g) groups.set(key, (g = { calls: [], runs: [] }))\n return g\n }\n for (const r of records) {\n if (r.kind === \"call\") group(r.label).calls.push(r)\n else if (r.kind === \"run\") group(r.label).runs.push(r)\n }\n const out: RunSummary[] = []\n for (const [label, { calls, runs }] of groups) {\n if (calls.length === 0) continue\n const ms = calls.map((c) => c.wallMs).sort((a, b) => a - b)\n let wallMs: number\n if (runs.length > 0) {\n wallMs = runs.reduce((s, r) => s + (Date.parse(r.endedAt) - Date.parse(r.startedAt)), 0)\n } else {\n const starts = calls.map((c) => Date.parse(c.at) - c.wallMs)\n const ends = calls.map((c) => Date.parse(c.at))\n wallMs = Math.max(...ends) - Math.min(...starts)\n }\n const entries = calls.reduce((s, c) => s + c.entries, 0)\n const costUsd = calls.reduce((s, c) => s + (c.costUsd ?? 0), 0)\n out.push({\n label,\n calls: calls.length,\n failed: calls.filter((c) => c.failed).length,\n entries,\n articles: calls.reduce((s, c) => s + c.articles, 0),\n refused: calls.reduce((s, c) => s + c.refused, 0),\n heldCited: calls.reduce((s, c) => s + (c.heldCited ?? 0), 0),\n wallMs,\n callMsP50: percentile(ms, 0.5),\n callMsP95: percentile(ms, 0.95),\n prepMs: calls.reduce((s, c) => s + (c.prepMs ?? 0), 0),\n costUsd,\n costPerEntry: entries > 0 ? costUsd / entries : 0,\n entriesPerMinute: wallMs > 0 ? (entries / wallMs) * 60_000 : 0,\n promptChars: calls.reduce((s, c) => s + c.promptChars, 0),\n })\n }\n return out.sort((a, b) => a.label.localeCompare(b.label))\n}\n"],"mappings":"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","names":["seededRandom","seed","h","i","a","t","pickSample","items","n","rng","pool","b","j","STOP","terms","text","out","m","termOverlap","entry","article","e","hit","WEAK_CITATION","factStrings","f","extractFacts","hasCommit","haystack","commit","known","k","urlKey","u","checkArticle","entries","citersOf","cited","id","missingSources","weakSources","grounding","groundLower","g","groundUrls","groundNumbers","mine","ungrounded","c","lost","facts","carriers","carrierFacts","uncitedWithFacts","uncited","allArticles","lower","missing","TITLE_STOP","titleTerms","title","w","jaccard","inter","x","findDuplicates","catalog","focus","opts","titleMin","sourcesMin","rows","pairs","y","s","p","q","JUDGE_ENTRY_CHARS","buildJudgePrompt","sourceBlock","body","earlier","parseJudgeReply","value","v","list","num","claims","supported","verdict","buildScorecard","checks","duplicates","meta","judged","maxExamples","byType","lostEntries","lostKeys","l","inPair","d","card","pct","renderScorecard","lines","ex","section","parts","envelopeUsage","envelope","percentile","sorted","summariseRuns","records","groups","group","label","key","r","calls","runs","ms","wallMs","starts","ends","costUsd"]}